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();
}
}
}
}

