JDBC事务管理和保存点示例

时间:2020-01-09 10:35:22  来源:igfitidea点击:

在本文中,我们将介绍如何在Java应用程序中使用JDBC管理事务。

事务表示单个工作单元,其中一组一个或者多个语句作为一个单元执行。在事务中,所有语句或者成功执行,或者都不执行。

JDBC中的事务

在JDBC API中,Connection接口提供以下用于事务管理的方法

  • setAutoCommit()–默认情况下,在JDBC中,连接处于自动提交模式,这意味着其所有SQL语句将作为单个事务执行并提交。因此,第一件事就是将自动提交模式设置为false,以便将SQL语句分组为事务并作为单个工作单元执行。

  • commit()–此方法使在事务中进行的所有更改永久生效。还释放此Connection对象当前持有的所有数据库锁。

  • rollback()–如果任何一条语句失败,则此方法用于撤消当前事务中所做的所有更改。还释放此Connection对象当前持有的所有数据库锁。

使用这些方法,JDBC中的事务步骤可以总结为:

1.要启动事务,请通过调用setAutoCommit(false)方法将自动提交模式设置为false。
2.如果事务中所有带有的语句执行有任何错误,请通过调用commit()方法使更改永久生效。
3.如果任何语句执行不正确,则通过回滚作为事务一部分的所有更改来中止事务。
4.我们还可以在事务中设置保存点,然后回滚到特定的保存点,这使我们可以选择保存事务中正在进行的某些工作,而不是全部丢失。

设置事务隔离级别的方法

我们还可以使用Connection对象设置事务隔离级别。连接接口为不同的事务隔离级别定义以下常量。

TRANSACTION_NONE –表示交易不支持。
TRANSACTION_READ_UNCOMMITTED –指示可能发生脏读,不可重复读和幻像读。
TRANSACTION_READ_COMMITTED –指示防止脏读;可能会发生不可重复的读取和幻像读取。
TRANSACTION_REPEATABLE_READ –指示防止脏读和不可重复读;可能会发生幻像读取。
TRANSACTION_SERIALIZABLE –指示防止脏读,不可重复读和幻像读。

我们可以在setTransactionIsolation(int level)方法中传递任何这些常量,以设置所需的隔离级别。

例如,我们想要将事务隔离级别设置为未提交。

connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

JDBC事务管理示例

让我们举一个将金额从一个帐户转移到另一个帐户的示例,该步骤涉及以下两个要执行的步骤,或者都不执行。

1.从发送者的帐户中提取金额。
2.将金额存入收款人的帐户。

例如,帐户表与acct_num,名称,余额列一起使用。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTransactionDemo {
  public static void main(String[] args) {
    Connection connection = null;
    try {
      // Connection info
      Class.forName("com.mysql.cj.jdbc.Driver");
      connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/theitroad", "root", "admin");
      // Auto commit disabled
      connection.setAutoCommit(false);
      
      int fromAccount = 2;
      int toAccount = 7;
      int amount = 200;
      withdrawAmount(connection, fromAccount, amount);
      depositAmount(connection, toAccount, amount);
      // Commit transaction
      connection.commit();
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }catch(SQLException e) {
      e.printStackTrace();
      if(connection != null){
        try {
          // Rolling back transaction
          connection.rollback();
        } catch (SQLException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
      }
    }finally{
      if(connection != null){         
        try {
          connection.close();
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      } 
    }	
  }
  
  private static void withdrawAmount(Connection connection, int accountNum, int amount) throws SQLException{
    String sql = "UPDATE ACCOUNT SET balance = balance - ? WHERE acct_num = ?";
    PreparedStatement stmt = null;
    try {
      stmt = connection.prepareStatement(sql);
      stmt.setInt(1, amount);
      stmt.setInt(2, accountNum);
      int count = stmt.executeUpdate();
      if(count == 0){
        throw new SQLException("Account number not found " + accountNum);
      }
    }finally{
      if(stmt != null){
        stmt.close();
      }
    }
  }	 
  
  private static void depositAmount(Connection connection, int accountNum, int amount) throws SQLException{
    String sql = "UPDATE ACCOUNT SET balance = balance + ? WHERE acct_num = ?";
    PreparedStatement stmt = null;
    try {
      stmt = connection.prepareStatement(sql);
      stmt.setInt(1, amount);
      stmt.setInt(2, accountNum);
      int count = stmt.executeUpdate();
      if(count == 0){
        throw new SQLException("Account number not found " + accountNum);
      }
    }finally{
      if(stmt != null){
        stmt.close();
      }
    }    
  }
}

JDBC事务中的保存点

使用保存点对象,可以标记当前事务中的点。当事务回滚到保存点时,在该保存点之后进行的所有更改都将被撤消,直到更改该保存点为止。如果事务中有很多语句,并且如果某件事失败我们不想放弃所有工作,则可以间隔设置保存点,以便有机会至少在该保存点之前提交工作。

连接界面提供了两种重载方法来设置保存点

  • setSavepoint()–在当前事务中创建一个未命名的保存点,并返回创建的保存点对象。

  • setSavepoint(String name)–在当前事务中使用给定名称创建一个保存点,并返回创建的Savepoint对象。

还有一种释放保存点的方法。

  • releaseSavepoint(保存点保存点)-从当前事务中删除指定的保存点。

在JDBC事务示例中设置保存点

在示例中,在事务中插入了很少的记录,并在几次插入后创建了一个保存点,这样我们就有机会在回滚的情况下将记录插入到那里。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

public class SavePointDemo {
  public static void main(String[] args) {
    Connection connection = null;
    Savepoint sp = null;
    try {
      // Load driver
      Class.forName("com.mysql.cj.jdbc.Driver");
      // connection object
      connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/theitroad", "root", "admin");
      SavePointDemo sd = new SavePointDemo();
      
      // Auto commit disabled
      connection.setAutoCommit(false);

      sd.insertEmployee(connection, "Don", "Keaton", "HR");
      sd.insertEmployee(connection, "Virag", "Sharma", "IT");
      // Setting named savepoint
      sp = connection.setSavepoint("MySavePoint");
      sd.insertEmployee(connection, "Kelly", "Dorji", "IT");
      // Commit transaction
      connection.commit();
    } catch (ClassNotFoundException e) {
    	// TODO Auto-generated catch block
        e.printStackTrace();
    }catch(SQLException e) {
      e.printStackTrace();
      if(connection != null){
        try {
          // savepoint is not reached, rollback the whole transaction
          if(sp == null){
            System.out.println("Rollingback the transaction");
            connection.rollback();
          }else{
            System.out.println("Rollingback to savepoint");
            // rollback to created savepoint
            connection.rollback(sp);
            // Commit till the savepoint
            connection.commit();
          }
        } catch (SQLException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
      }
    }finally{
      if(connection != null){         
        try {
          connection.close();
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      } 
    }	      	     
  }

  private void insertEmployee(Connection connection, String fName, String lName, String dept) throws SQLException{
    String insertSQL = "INSERT INTO EMPLOYEE (FIRST_NAME, LAST_NAME, DEPARTMENT) values (?, ?, ?)";
    PreparedStatement prepStmt = null;
    try {
      prepStmt = connection.prepareStatement(insertSQL);
      prepStmt.setString(1, fName);
      prepStmt.setString(2, lName);
      prepStmt.setString(3, dept);
      int count = prepStmt.executeUpdate();
      System.out.println("Number of records inserted- " + count);
    }finally{
      if(prepStmt != null){
        prepStmt.close();
      }
    }
  }
}