JDBC事务管理和保存点示例
在本文中,我们将介绍如何在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(); } } } }