什麼是事務?
數據庫事務(簡稱:事務)是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。
事務應該具有4個屬性:原子性、一致性、隔離性、持久性,這四個屬性通常稱爲ACID特性。
* 原子性(Atomicity):事務作爲一個整體被執行,包含在其中的對數據庫的操作要麼全部被執行,要麼都不執行。
* 一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變爲另一個一致狀態。一致狀態的含義是數據庫中的數據應滿足完整性約束。
* 隔離性(Isolation):多個事務併發執行時,一個事務的執行不應影響其他事務的執行。
* 持久性(Durability):已被提交的事務對數據庫的修改應該永久保存在數據庫中。
事務的隔離級別
在數據庫操作中,爲了有效保證併發讀取數據的正確性,提出的事務隔離級別。我們的數據庫鎖,也是爲了構建這些隔離級別存在的。
隔離級別 | 髒讀(Dirty Read) | 不可重複讀(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
讀未提交(Read uncommitted) | 可能 | 可能 | 可能 |
讀已提交(Read committed) | 不可能 | 可能 | 可能 |
可重複讀(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
說明:
* 讀未提交(Read Uncommitted):允許髒讀,也就是可能讀取到其他會話中未提交事務修改的數據
* 讀已提交(Read Committed):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重複讀)
* 可重複讀(Repeated Read):可重複讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重複讀,但是還存在幻象讀
* 串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞
Read Uncommitted這種級別,數據庫一般都不會用,而且任何操作都不會加鎖,這裏就不討論了。MySQL默認的事務隔離級別爲可重複讀(Repeated Read)。
一、編程式事務
在 Spring 出現以前,編程式事務管理對開發者來說是唯一選擇。熟悉Java JDBC 的人都知道,我們需要在代碼中顯式調用Connection 的setAutoCommit()、commit()、rollback()等事務管理相關的方法,這就是編程式事務管理。
1、JDBC 的編程式事務管理
public void save(User user) throws SQLException{
Connection conn = datasource.getConnection();
conn.setAutoCommit(false);
try {
PreparedStatement ps = conn.prepareStatement("insert into user(name,age) value(?,?)");
ps.setString(1,user.getName());
ps.setInt(2,user.getAge());
ps.execute();
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
}finally{
conn.close();
}
}
2、Hibernate 的編程式事務管理
Hibernate 中需要顯式調用beginTransaction()、commit()、rollback()等事務管理相關的方法,如下:
public void save(User user){
Session session = hibernateDao.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.save(user);
tx.commit();
} catch (Exception e) {
if(tx!=null){
tx.rollback();
}
}finally{
session.close();
}
}
3、MyBatis 的編程式事務管理
MyBatis中,我們可以通過org.apache.ibatis.session.SqlSession
的commit()、rollback() 等事務管理相關的方法,代碼如下:
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sqlSessionFactory.openSession(false); // 打開會話,事務開始
try {
IUserMapper mapper = session.getMapper(IUserMapper.class);
User user = new User(9, "Test transaction");
int affectedCount = mapper.updateUser(user); // 因後面的異常而未執行commit語句
User user = new User(10, "Test transaction continuously");
int affectedCount2 = mapper.updateUser(user2); // 因後面的異常而未執行commit語句
int i = 2 / 0; // 觸發運行時異常
session.commit(); // 提交會話,即事務提交
} catch (Exception e) {
session.rollback(); //回滾
} finally {
session.close(); // 關閉會話,釋放資源
}
參考資料
分佈式事務系列(1.2)Spring的事務體系:https://yq.aliyun.com/articles/39046?spm=5176.100239.blogcont39044.27.mmXvhw