1.概念
事務指邏輯上的一組操作,組成這組操作的各個單元,要麼全部成功,要麼全部不成功。
2.管理事務
2.1數據庫默認的事務
數據庫默認支持事務,默認的事務是:一條sql一個事務。
2.2手動控制事務
sql控制事務
開啓事務:start transaction;
開啓事務後,在這條語句之後的所有sql都將處在同一個事務中
提交事務:commit;
從開啓事務到提交事務之間的所有sql一起執行
回滾事務:rollback;
從開啓事務到回滾事務之間的所有sql一起失效
sql腳本示例如下:
說明:當開啓事務START TRANSACTION;後執行給a用戶扣款100,給b用戶加錢100,如果commit,那麼a被扣除100,b則增加100。如果執行rollback,那麼a和b的金額就會保持不變。
#slq控制事務,
#開啓事務
START TRANSACTION;
UPDATE myuser SET money= money-100 WHERE name='a';
UPDATE myuser SET money= money+100 WHERE name='b';
COMMIT;
#回滾會取消開啓到結束的所有sql
ROLLBACK;
#查看修改後的數據,驗證事務
SELECT* FROM myuser;
JDBC控制事務
開啓事務:conn.setAutoCommit(false);
jdbc中sql會自動提交,如果設置sql不自動提交,就需要我們手動提交,即提交前的所有sql在一個事務中
提交事務:conn.commit();
回滾事務:conn.rollback();
示例代碼如下:
說明:測試類TransactionDemo1用於測試JDBC的事務控制,其中會用到JdbcUtils工具類(代碼可參見:https://blog.csdn.net/qq_32224047/article/details/106722020)
情況一:不開啓事務,在sql1和sql2中間加入異常int i = 1/0;
package cn.wy.jdbc.transaction;
import cn.wy.jdbc.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* JDBC 事務測試類
* @author wyy
* @date 2020/6/23 10:33
*/
public class TransactionDemo1 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql1 = "UPDATE myuser SET money= money-100 WHERE name=?";
ps = conn.prepareStatement(sql1);
ps.setString(1,"a");
ps.executeUpdate();
//加入錯誤,驗證事務不開啓,jdbc自動提交(a被扣錢,b沒有加錢,因爲默認提交,上面的sql1執行,slq2 爲執行)
int i = 1/0;
String sql2 = "UPDATE myuser SET money= money+100 WHERE name=?";
ps = conn.prepareStatement(sql2);
ps.setString(1,"b");
ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
JdbcUtils.close(rs,conn,ps);
}
}
}
代碼執行前a和b用戶金額都是1000
執行上面代碼拋出異常
查詢數據庫a用戶被扣除100元,但是b用戶並沒有增加100元,因爲jdbc默認是開啓了事務管理,當執行完成sql1後,出現異常,無法在執行到sql2,所有sql1執行後被提交,之後的數據就不在執行,就出現了下面的查詢結果。
注意:在執行事務相關的操作時需要先檢查一下當前數據庫的隔離級別,MySQL默認隔離級別:repeatable read。可以用select @@tx_isolation;查詢當前的隔離級別。當前的測試都是在默認的隔離級別下執行。如果是其他的隔離級別可能無法得到該測試想要的結果。
隔離級別的檢查如下:
情況二:開啓事務,在sql1和sql2中間加入異常int i = 1/0;
package cn.wy.jdbc.transaction;
import cn.wy.jdbc.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* JDBC 事務測試類
* @author wyy
* @date 2020/6/23 10:33
*/
public class TransactionDemo1 {
public static void main(String[] args) {
//開啓事務
conn.setAutoCommit(false);
conn = JdbcUtils.getConnection();
String sql1 = "UPDATE myuser SET money= money-100 WHERE name=?";
ps = conn.prepareStatement(sql1);
ps.setString(1,"a");
ps.executeUpdate();
//加入錯誤,驗證事務(a被扣錢,b沒有加錢,因爲默認提交,上面的sql1執行,slq2 爲執行)
int i = 1/0;
String sql2 = "UPDATE myuser SET money= money+100 WHERE name=?";
ps = conn.prepareStatement(sql2);
ps.setString(1,"b");
ps.executeUpdate();
//提交事務
conn.commit();
}catch (Exception e){
e.printStackTrace();
//如果出現任何異常則,執行回滾
if (conn!=null){
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}finally {
JdbcUtils.close(rs,conn,ps);
}
}
}
執行代碼前先將a和b用戶的賬戶金額都還原爲1000
執行程序,拋出NullPointerException異常
查詢數據庫,結果並未改變,由於開啓了事務,當出現異常時,後在catch中捕獲後執行回滾(rollback),從而保證一個事務要麼全部成功,要麼全部不成功。