好的,我知道提交和回滚之间的区别以及这些操作应该做什么。 但是,在使用commit(),rollback()和/或什么都不做的情况下我可以实现相同的行为时,我不确定该怎么做。
例如,假设我有以下代码执行查询而无需写入db: 我正在开发一个与SQLite数据库通信的应用程序。
try {
doSomeQuery()
// b) success
} catch (SQLException e) {
// a) failed (because of exception)
}
或者更有趣的是,请考虑以下代码,删除单行:
try {
if (deleteById(2))
// a) delete successful (1 row deleted)
else
// b) delete unsuccessful (0 row deleted, no errors)
} catch (SQLException e) {
// c) delete failed (because of an error (possibly due to constraint violation in DB))
}
从语义角度观察,在情况b)和c)中进行提交或回滚会导致相同的行为。
通常,每种情况下都有几种选择(a,b,c):
选择特定操作是否有任何指导或性能优势?什么是正确的方法?
注意:假设禁用了自动提交。
答案 0 :(得分:2)
如果它只是一个选择,我不会打开一个交易,因此,无论如何都无事可做。可能你已经知道是否有更新/插入,因为你已经传递了参数。 您打算进行操作的情况更有趣。删除的情况很明显,你要提交;如果存在异常,您应该回滚以保持数据库一致,因为某些操作失败并且您可以做很多事情。如果删除失败,因为没有删除任何内容我会提交3个原因:
从语义上讲,它似乎更正确,因为操作是 技术上成功并按规定执行。
这是更具未来性的证明,所以如果有人添加更多代码 交易他们不会感到惊讶,因为它回滚 一个删除只是没有做任何事情(他们会期望例外 事务被回滚)
当有操作要做时,提交更快,但在这种情况下我 不要认为这很重要。
答案 1 :(得分:1)
任何非平凡的应用程序都需要完成需要多个SQL语句的操作。在第一个SQL语句之后和最后一个SQL语句之前发生的任何故障都将导致数据不一致。
事务旨在使多语句操作与您当前使用的单语句操作一样原子化。
答案 2 :(得分:1)
你所说的取决于所调用的代码,它是否会返回一个标记供你测试,或者它是否会在出现问题时专门抛出异常?
API会抛出异常,但也返回一个布尔值(true | false):
这种情况发生了很多,并且使得调用代码难以处理这两个条件,正如您在OP中指出的那样。在这种情况下你可以做的一件事是:
// Initialize a var we can test against later
// Lol, didn't realize this was for java, please excuse the var
// initialization, as it's demonstrative only
$queryStatus = false;
try {
if (deleteById(2)) {
$queryStatus = true;
} else {
// I can do a few things here
// Skip it, because the $queryStatus was initialized as false, so
// nothing changes
// Or throw an exception to be caught
throw new SQLException('Delete failed');
}
} catch (SQLException $e) {
// This can also be skipped, because the $queryStatus was initialized as
// false, however you may want to do some logging
error_log($e->getMessage());
}
// Because we have a converged variable which covers both cases where the API
// may return a bool, or throw an exception we can test the value and determine
// whether rollback or commit
if (true === $queryStatus) {
commit();
} else {
rollback();
}
API专门抛出异常(无返回值):
没问题。我们可以假设如果没有捕获到异常,则操作完成且没有错误,我们可以在try / catch块中添加rollback()/ commit()。
try {
deleteById(2);
// Because the API dev had a good grasp of error handling by using
// exceptions, we can also do some other calls
updateById(7);
insertByName('Chargers rule');
// No exception thrown from above API calls? sweet, lets commit
commit();
} catch (SQLException $e) {
// Oops exception was thrown from API, lets roll back
rollback();
}
API不会抛出任何异常,只返回一个bool(true | false):
这可以追溯到旧学校的错误处理/检查
if (deleteById(2)) {
commit();
} else {
rollback();
}
如果您有多个查询组成事务,您可以从方案#1中借用单个var构思:
$queryStatus = true;
if (!deleteById(2)) {
$queryStatus = false;
}
if (!updateById(7)) {
$queryStatus = false;
}
...
if (true === $queryStatus) {
commit();
} else {
rollback();
}
“注意:假设禁用了自动提交。”
答案 3 :(得分:1)
我问自己完全相同的问题。最后,我找到了一个解决方案,我总是提交成功的交易,并且总是回滚它的非成功交易 ofles 。这简化了许多代码,使其更清晰,更易于阅读。
我在.net上使用NHibernate + SQLite的应用程序中没有任何重大性能问题。你的milage可能会有所不同。
答案 4 :(得分:1)
正如其他人在答案中所述,这不是性能问题(对于您描述的等效案例),我认为这可以忽略不计,而是可维护性问题,这一点非常重要!
为了使您的代码能够很好地维护,我建议(无论如何)始终在try
块的最底部提交,并始终关闭Connection
中的finally
阻止。在finally
块中,如果存在未提交的事务(也就是说您未在try
块结束时到达提交),则还应该回滚。
这个例子展示了我认为最好的做法(miantainability-wise):
public boolean example()
{
Connection conn;
...
try
{
...
//Do your SQL magic (that can throw exceptions)
...
conn.commit();
return true;
}
catch(...)
{
...
return false;
}
finally
{//Close statements, connections, etc
...
closeConn(conn);
}
}
public static void closeConn(Connection conn)
{
if (conn != null)
if (!conn.isClosed())
{
if (!conn.getAutoCommit())
conn.rollback();//If we need to close() but there are uncommitted transacitons (meaning there have been problems)
conn.close();
conn = null;
}
}