JDBC如何處理事務和隔離級別
事務和JDBC相關內容不再做介紹,可以去本人博客中看看:
這裏主要說一下在JDBC編程中如何處理事務?
和MySQL一樣,JDBC默認不開啓事務,每執行一個SQL便提交一次,在MySQL中我們需要set autocommit=0來開啓事務功能,然後用begin開始一個事務,用savepoint point1設置名爲point1的保存點,用rollback來回滾,用commit來提交。而在JDBC中,我們需要調用connection對象的一些方法:
- connection.setAutoCommit(false);開啓事務功能
- connection.commit();提交一個事務
我們先來看看上面兩個方法的使用:
import java.sql.*;
public class TranDemo {
public static void select(Connection connection,String sql1) throws SQLException {
PreparedStatement ps= null;
try {
ps = connection.prepareStatement(sql1);//預編譯sql1
ps.setString(1,"08");
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()){
System.out.println("SID="+resultSet.getString(1)+
" Sname="+resultSet.getString(2));
}//我這裏爲了方便只打印SID和Sname
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是這個方法的局部變量,方法結束需要關閉ps
ps=null;//防止內存泄露
}
}
}
public static void delete(Connection connection,String sql3){
PreparedStatement ps= null;
try {
ps=connection.prepareStatement(sql3);//預編譯sql3
ps.setString(1,"08");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是這個方法的局部變量,方法結束需要關閉ps
ps=null;//防止內存泄露
}
}
}
public static void main(String[] args) {
Connection connection=null;
try {
String sql1="select * from Student where SID=?";
String sql3="delete from Student where SID=?";
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_sql", "root", "123456");
//開啓事務功能
connection.setAutoCommit(false);//false--不自動提交
//此時事務功能就已經開啓
//爲了看起來有條理,我把查詢、刪除SID='08'這條記錄的三個SQL語句的執行過程封裝起來
select(connection,sql1);//先執行sql1,查詢SID='08',打印SID和Sname
delete(connection,sql3);//再執行sql3,刪除SID='08'這條記錄
select(connection,sql1);//查詢SID='08',看還能不能查到
connection.commit();//對比下調用這個方法和不調用的區別
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(connection!=null){
connection.close();
connection=null;
}
}
}
}
當把 connection.commit();註釋起來時,執行結果如下:
可以看到兩次查詢同樣的數據只有一次的打印結果,證明刪除成功;
去MySQL中確認一下:
還查詢的出來,證明刪除失敗,Java代碼中執行的結果並沒有提交(commit)給MySQL。
來看看放開connection.commit();的註釋後的打印結果:
刪除成功,再看看數據庫中的數據:
查詢不到,證明刪除成功。
注意:事務中的緩存機制是將一個事務訪問的數據複製一份爲緩存(這裏說成副本/快照更合適)去操作,當調用commit時才把緩存的內容提交給數據庫。
- connection.setSavepoint();設置一個保存點
- connection.rollback()/connection.rollback(point1);回滾到初始狀態/point1狀態
注意!!!正常寫程序時回滾語句要寫在拋SQLException異常的catch塊中,如果某個SQL語句執行失敗拋異常就可以回滾。
這裏爲了掩飾回滾效果就放在try塊中,方便演示:
import java.sql.*;
public class TranDemo {
public static void select(Connection connection,String sql1) throws SQLException {
PreparedStatement ps= null;
try {
ps = connection.prepareStatement(sql1);//預編譯sql1
ps.setString(1,"02");
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()){
System.out.println("SID="+resultSet.getString(1)+
" Sname="+resultSet.getString(2));
}//我這裏爲了方便只打印SID和Sname
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是這個方法的局部變量,方法結束需要關閉ps
ps=null;//防止內存泄露
}
}
}
public static void delete(Connection connection,String sql3){
PreparedStatement ps= null;
try {
ps=connection.prepareStatement(sql3);//預編譯sql3
ps.setString(1,"02");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是這個方法的局部變量,方法結束需要關閉ps
ps=null;//防止內存泄露
}
}
}
public static void main(String[] args) throws SQLException {
Connection connection=null;
Savepoint point1=null;
try {
String sql1="select * from Student where SID=?";
String sql3="delete from Student where SID=?";
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_sql", "root", "123456");
//開啓事務功能
connection.setAutoCommit(false);//false--不自動提交
//此時事務功能就已經開啓
//爲了看起來有條理,我把查詢、刪除SID='02'這條記錄的三個SQL語句的執行過程封裝起來
select(connection,sql1);//1.先執行sql1,查詢SID='02',打印SID和Sname
point1= connection.setSavepoint();//設置一個保存點point1
delete(connection,sql3);//2.執行sql3,刪除SID='02'這條記錄
select(connection,sql1);//查詢SID='02',不打印則刪除成功
connection.rollback(point1);//回滾到point1
select(connection,sql1);//打印出SID='02'的SID和Sname則回滾成功
connection.commit();//對比下調用這個方法和不調用的區別,不能寫在rollback操作前
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(point1!=null){
connection.releaseSavepoint(point1);
point1=null;
}
if(connection!=null){
connection.close();
connection=null;
}
}
}
}
運行結果:
打印了兩次而不是三次,證明在事務的緩存(副本)中確實刪除了一次,最後又回滾到point1,刪除掉的數據又回來了。
- connection.releaseSavepoint(point1);刪除point1保存點
- connection.setTransactionIsolation(int level);設置事務的隔離級別,這個隔離級別只對當前連接有效,在MySQL中一個連接就是打開的一個窗口(終端);在JDBC中,一個連接就是一個connection。
剖析源碼看一下隔離級別對應的int值: