什麼是事務?
資料庫事務(簡稱:事務)是資料庫管理系統執行過程中的一個邏輯單位,由一個有限的資料庫操作序列構成。
事務應該具有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