我们什么时候需要调用java.sql.Connection.rollback()?
我必须在一个函数中修改几个表。他们必须全部成功,否则全部失败。如果一个操作失败,我希望所有操作都失败。我有以下资料:我们什么时候需要调用java.sql.Connection.rollback()?,java,jdbc,Java,Jdbc,我必须在一个函数中修改几个表。他们必须全部成功,否则全部失败。如果一个操作失败,我希望所有操作都失败。我有以下资料: public void foo() throws Exception { Connection conn = null; try { conn = ...; conn.setAutoCommit(false); grok(conn); conn.commit(); } catch (E
public void foo() throws Exception {
Connection conn = null;
try {
conn = ...;
conn.setAutoCommit(false);
grok(conn);
conn.commit();
}
catch (Exception ex) {
// do I need to call conn.rollback() here?
}
finally {
if (conn != null) {
conn.close();
conn = null;
}
}
}
private void grok(Connection conn) throws Exception {
PreparedStatement stmt = null;
try {
// modify table "apple"
stmt = conn.prepareStatement(...);
stmt.executeUpdate();
stmt.close();
// modify table "orange"
stmt = conn.prepareStatement(...);
stmt.executeUpdate();
stmt.close();
...
}
finally {
if (stmt != null) {
stmt.close();
}
}
}
我想知道,如果在此过程中出现问题,是否需要调用rollback()
其他信息:我正在使用连接池。在上面的示例中,我还确保使用finally语句来关闭每个PreparedStatement,只是为了简洁起见
谢谢您不需要调用
rollback()
。如果连接在未完成commit()
的情况下关闭,它将回滚
您也不需要将
conn
设置为null
;由于try
块在conn
初始化后启动(假设..
的计算结果不能为空),因此您不需要!=在finally
中为空。如果调用“提交”,则事务将被提交。如果有多个insert/update语句,但其中一个语句失败,则提交将导致未提交到数据库的insert/update语句失败。因此,是的,如果不希望其他语句提交到db,则需要调用rollback。通过将autocommit设置为false,您实际上可以同时提交或回滚多个语句。否则,每个语句将自动提交 是的,如果任何语句失败或在调用commit之前检测到异常,则需要调用rollback。这是一篇老文章,但公认的答案是错误的。您可以自己尝试,在提交之前抛出一个异常,并观察如果不手动回滚,您的插入仍然会进入数据库
JDBC文档
文档中的正确用法示例
public void updateCoffeeSales(HashMap salesForWeek)
抛出SQLException{
PreparedStatement updateSales=null;
PreparedStatement updateTotal=null;
字符串更新字符串=
更新“+dbName+”.coffes”+
“设置销售额=?其中COF_NAME=?”;
字符串更新语句=
更新“+dbName+”.coffes”+
“设置总计=总计+?”+
“其中COF_NAME=?”;
试一试{
con.setAutoCommit(假);
updateSales=con.prepareStatement(updateString);
updateTotal=con.prepareStatement(updateStatement);
对于(Map.Entry e:salesForWeek.entrySet()){
updateSales.setInt(1,e.getValue().intValue());
setString(2,例如getKey());
updateSales.executeUpdate();
updateTotal.setInt(1,e.getValue().intValue());
setString(2,例如getKey());
updateTotal.executeUpdate();
con.commit();
}
}捕获(SQLE异常){
jdbcttutorialutilities.printSQLException(e);
如果(con!=null){
试一试{
系统错误打印(“事务正在回滚”);
con.rollback();
}捕获(SQLException例外){
JDBCTutorialUtilities.printSQLException(excep);
}
}
}最后{
if(updateSales!=null){
updateSales.close();
}
if(updateTotal!=null){
updateTotal.close();
}
con.setAutoCommit(真);
}
}
为了澄清我的答案,这假设您将有多个语句。在您的示例中,如果只有一条语句,那么它将自动回滚。但是如果在Java代码中调用了多个语句,那么您可能希望回滚。我更新了以显示有多个语句(变量被重用)-我仍然需要回滚吗?谢谢你应该防御性地编码。上面的代码将使用单个连接,您不需要显式调用rollback。但是,如果您将该代码放入一个有连接池的环境中,那么当其他事务重用同一个连接时,您肯定会遇到奇怪的行为,因为池中的连接通常从不关闭。就我个人而言,我会一直使用回滚,除非您能够确定它会导致性能下降(回滚非常昂贵),并且您完全了解您所处环境的事务行为。Ok更新了示例以反映conn可能为空,因此即使进行了更新,我仍然不需要调用rollback()?谢谢你,你不需要叫rollback。您前面的示例中,conn
在try
之前就已初始化。这根本不是真的。您必须在连接上调用rollback以使更改回滚。它们不会自动为您回滚。看到我的答案了,但是在调用commit之前抛出一个异常很容易测试自己。嗨@UsmanMutawakil那么如果你既不commit
也不rollback
,会发生什么呢?@可悲的Hello。如果既不提交也不回滚插入,则插入将被持久化,但它们是否可以读取将取决于您使用的事务隔离级别。在我的例子中,我可以看到数据“脏读”。驱动程序和数据库使用设置的事务隔离级别,但通过显式回滚,问题解决了一半。
public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
throws SQLException {
PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
if (updateSales != null) {
updateSales.close();
}
if (updateTotal != null) {
updateTotal.close();
}
con.setAutoCommit(true);
}
}