【问题标题】:Savepoint on JDBCJDBC 上的保存点
【发布时间】:2023-03-19 16:55:02
【问题描述】:

我有一个 JDBC 代码,其中存在多个 Savepoints;像这样:

1st insert statement
2nd insert statement
savepoint = conn.setSavepoint("S1");
1st insert statement
2nd update statement
savepoint = conn.setSavepoint("S2");
1st delete statement
2nd delete statement
savepoint = conn.setSavepoint("S3");
1st insert statement
2nd delete statement
savepoint = conn.setSavepoint("S4");

现在在 catch 块中,我正在捕获异常并检查 Savepoint 是否为 null;如果是,则回滚整个连接,否则回滚到Savepoint。但我无法理解我应该回滚到哪个Savepoint

如果我将所有保存点名称更改为 "S1" 会好吗?在那种情况下,我将如何理解 Savepoint 之前有多少工作正常?

请告知如何理解直到Savepoint的工作被正确执行?

【问题讨论】:

    标签: java oracle jdbc savepoints


    【解决方案1】:

    会将此视为多笔交易。因此,您可以使用多个 try/catch 块来处理这个问题。您似乎还覆盖了保存点对象,因此回滚是不可行的。

    更多信息。 JDBC 还支持设置保存点,然后回滚到指定的保存点。以下方法可用于定义保存点。

    SavePoint savePoint1 = connection.setSavePoint();
    

    使用带有参数的回滚调用将事务回滚到已定义的保存点。

    connection.rollback(savePoint1);
    

    参考。 http://www.sourcetricks.com/2014/08/jdbc-handling-transactions.html

    【讨论】:

      【解决方案2】:

      在这种情况下,我发现棘手的部分是确保仅在所有插入成功时才提交事务,但在任何插入失败时回滚所有更新。我使用保存点堆栈来处理这种情况。高度简化的代码如下:

      一个连接包装类:

      public class MyConnection {
          Connection conn;
          static DataSource ds;
          Stack<Savepoint> savePoints = null;
      
          static {
              //... stuff to initialize datasource.
          }
      
          public MyConnection() {
              conn = ds.getConnection();
          }
      
          public void beginTransaction() {
              if (savePoints == null) {
                  savePoints = new Stack<Savepoint>();
                  conn.setAutoCommit(false);
                  conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
              } else {
                  savePoints.push(conn.setSavepoint());
              }
          }
      
          public void commit() throws SQLException {
              if (savePoints == null || savePoints.empty()) {
                  conn.commit();
              } else {
                  Savepoint sp = savePoints.pop();
                  conn.releaseSavepoint(sp);
              }
          }
      
          public void rollback() throws SQLException {
              if (savePoints == null || savePoints.empty()) {
                  conn.rollback();
              } else {
                  Savepoint sp = savePoints.pop();
                  conn.rollback(sp);
              }
          }
      
          public void releaseConnection() {
              conn.close();
          }
      }
      

      然后你可以有各种可以独立或组合调用的方法。在下面的示例中,methodA 可以单独调用,也可以作为调用 methodB 的结果。

      public class AccessDb {
      
          public void methodA(MyConnection myConn) throws Exception {
              myConn.beginTransaction();
              try {
                  // update table A
                  // update table B
                  myConn.commit();
              } catch (Exception e) {
                  myConn.rollback();
                  throw e;
              } finally {
      
              }
          }
      
          public void methodB(MyConnection myConn) throws Exception {
              myConn.beginTransaction();
              try {
                  methodA(myConn);
                  // update table C
                  myConn.commit();
              } catch (Exception e) {
                  myConn.rollback();
                  throw e;
              } finally {
      
              }
          }
      }
      

      这样,如果出现任何问题,它会完全回滚(作为异常处理的结果),但它只会提交整个事务,而不是提交部分完成的事务。

      【讨论】:

        猜你喜欢
        • 2011-11-09
        • 1970-01-01
        • 2012-05-26
        • 1970-01-01
        • 1970-01-01
        • 2020-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多