默認情況下,當我們創建一個數據庫連接時,會運行在自動提交模式(Auto-commit)下。這意味着,任何時候我們執行一條SQL完成之后,事務都會自動提交。所以我們執行的每一條SQL都是一個事務,並且如果正在運行DML或者DDL語句,這些改變會在每一條SQL語句結束的時存入數據庫。有時候我們想讓一組SQL語句成為事務的一部分,那樣我們就可以在所有語句運行成功的時候提交,並且如果出現任何異常,這些語句作為事務的一部分,我們可以選擇將其全部回滾。
讓我們通過一個簡單的示例理解一下,這里使用JDBC的事務管理來支持數據的完整性
/*
* 發生異常回滾所有執行更新的sql
* */
public void insrolledAll() {
Connection connection = null;
PreparedStatement preparedStatement = null;
PreparedStatement preparedStatement1 = null;
//這里參數用?占位符表示
String sqlTeacher = "insert into teacher ( name, age, hobby, addr) values (?,?,?,?)";
String sqlStudent = "insert into student (name, age, addr, hobby) values (?,?,?,?)";
try {
//獲取數據連接
connection = basicUse.getConnection();
//設置事務自動提交為手動提交
connection.setAutoCommit(false);
//獲取發送sql指令執行sql進行預編譯
preparedStatement = connection.prepareStatement(sqlStudent);
//設置sql語句參數索引從1開始
preparedStatement.setObject(1, "Tom");
preparedStatement.setObject(2, 24);
preparedStatement.setObject(3, "上海");
preparedStatement.setObject(4, "籃球");
preparedStatement.executeUpdate();
System.out.println("執行sqlStudent" + sqlStudent);
//手動制造異常
int i = 10 / 0;
preparedStatement1 = connection.prepareStatement(sqlTeacher);
preparedStatement1.setObject(1, "kenx");
preparedStatement1.setObject(2, 24);
preparedStatement1.setObject(3, "足球");
preparedStatement1.setObject(4, "北京");
preparedStatement1.executeUpdate();
System.out.println("執行sqlTeacher" + sqlTeacher);
//提交事務
connection.commit();
} catch (Exception e) {
//回滾事務
try {
connection.rollback();
System.out.println("JDBC Transaction rolled back successfully");
} catch (SQLException e1) {
System.out.println("SQLException in rollback" + e1.getMessage());
e1.printStackTrace();
}
e.printStackTrace();
} finally {
//執行完數據庫操作后記得關閉數據庫連接資源
try {
preparedStatement.close();
preparedStatement1.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JDBC Savepoint示例
有時候一個事務可能是一組復雜的語句,因此可能想要回滾到事務中某個特殊的點。JDBC Savepoint幫我們在事務中創建檢查點(checkpoint),這樣就可以回滾到指定點。當事務提交或者整個事務回滾后,為事務產生的任何保存點都會自動釋放並變為無效。把事務回滾到一個保存點
/*
* 發生異常回滾到指定的位置
* */
public void insrolledSavepoint() {
Savepoint savepoint = null;
Connection connection = null;
PreparedStatement preparedStatement = null;
PreparedStatement preparedStatement1 = null;
//這里參數用?占位符表示
String sqlTeacher = "insert into teacher ( name, age, hobby, addr) values (?,?,?,?)";
String sqlStudent = "insert into student (name, age, addr, hobby) values (?,?,?,?)";
try {
//獲取數據連接
connection = basicUse.getConnection();
//設置事務自動提交為手動提交
connection.setAutoCommit(false);
//獲取發送sql指令執行sql進行預編譯
preparedStatement = connection.prepareStatement(sqlStudent);
//設置sql語句參數索引從1開始
preparedStatement.setObject(1, "JDBC");
preparedStatement.setObject(2, 24);
preparedStatement.setObject(3, "上海");
preparedStatement.setObject(4, "籃球");
preparedStatement.executeUpdate();
System.out.println("執行sqlStudent" + sqlStudent);
//設置回滾的點 失敗只會回滾老師信息
savepoint = connection.setSavepoint("teacher");
preparedStatement1 = connection.prepareStatement(sqlTeacher);
preparedStatement1.setObject(1, "kenx");
preparedStatement1.setObject(2, 24);
preparedStatement1.setObject(3, "足球");
preparedStatement1.setObject(4, "北京");
preparedStatement1.executeUpdate();
System.out.println("執行sqlTeacher" + sqlTeacher);
//手動制造異常
int i = 10 / 0;
//提交事務
connection.commit();
} catch (Exception e) {
//回滾事務
try {
if (StrUtil.isEmptyIfStr(savepoint)) {
//回滾所有更新sql
connection.rollback();
System.out
.println("JDBC Transaction rolled back successfully");
} else {
connection.rollback(savepoint);
System.out.println("JDBC Transaction rolled back successfully");
connection.commit();
}
} catch (SQLException e1) {
System.out.println("SQLException in rollback" + e1.getMessage());
e1.printStackTrace();
}
e.printStackTrace();
} finally {
//執行完數據庫操作后記得關閉數據庫連接資源
try {
preparedStatement.close();
preparedStatement1.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
完整項目案例
點擊這里 github