一文搞懂?MyBatis的事務(wù)管理機(jī)制
一、事務(wù)概述
事務(wù)是指要么全部執(zhí)行成功,要么全部回滾的一組操作。在數(shù)據(jù)庫中,一般使用 ACID 規(guī)則來約束事務(wù),即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)。在 MyBatis 中,也遵循了這一規(guī)則。
MyBatis中的事務(wù)是指一系列數(shù)據(jù)庫操作,這些操作要么全部執(zhí)行成功,要么全部執(zhí)行失敗。如果操作過程中發(fā)生錯(cuò)誤,所有對(duì)數(shù)據(jù)庫的修改都將被回滾,即還原到最初狀態(tài)。事務(wù)是確保數(shù)據(jù)一致性和完整性的關(guān)鍵機(jī)制之一。
在MyBatis中,我們可以通過三種方式來管理事務(wù):
編程式管理事務(wù):在代碼中顯式開啟、提交或回滾事務(wù)。
聲明式管理事務(wù):通過AOP代理實(shí)現(xiàn)事務(wù)管理,可以讓代碼更簡潔,更容易維護(hù)。
注解式管理事務(wù):通過注解方式管理事務(wù),是聲明式管理事務(wù)的一種擴(kuò)展方式。
以上三種方式,最常用的是聲明式管理事務(wù),它可以將事務(wù)管理和業(yè)務(wù)邏輯分離,降低代碼的耦合度,提高代碼的可讀性和可維護(hù)性。在MyBatis中,聲明式事務(wù)管理需要借助Spring框架來實(shí)現(xiàn)。
在使用聲明式事務(wù)管理時(shí),我們需要在Spring配置文件中配置TransactionManager和TransactionProxyFactoryBean兩個(gè)bean,其中TransactionManager負(fù)責(zé)事務(wù)管理,TransactionProxyFactoryBean負(fù)責(zé)AOP代理。
下面是一個(gè)示例:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.example.UserMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <bean id="userMapperProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userMapper" /> <property name="transactionAttributes"> <props> <prop key="select*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="insert*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="delete*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
在編寫完上述配置后,我們就可以對(duì)UserMapper接口中的方法進(jìn)行事務(wù)管理了。例如,在UserService類中,我們可以注入userMapperProxy,并調(diào)用其中的方法:
@Service public class UserService { @Autowired UserMapper userMapperProxy; @Transactional public void addUser(User user) { userMapperProxy.addUser(user); } }
在代碼中,通過@Transactional注解聲明了該方法需要進(jìn)行事務(wù)管理,當(dāng)方法執(zhí)行過程中發(fā)生異常時(shí),事務(wù)將會(huì)回滾。
MyBatis支持多種事務(wù)管理方式,但是建議使用聲明式事務(wù)管理,因?yàn)樗軌蜃畲蟪潭鹊亟档痛a的耦合度,并提高代碼的可讀性和可維護(hù)性。
二、MyBatis 實(shí)現(xiàn)事務(wù)的方式
MyBatis 實(shí)現(xiàn)事務(wù)也有多種方式,其中最常用的方式有兩種:編程式事務(wù)和聲明式事務(wù)。接下來我們將分別來介紹這兩種方式。
1. 編程式事務(wù)
編程式事務(wù)就是通過編寫代碼來手動(dòng)管理事務(wù),主要包括以下幾個(gè)步驟:
1)獲取 SqlSession 對(duì)象;
2)設(shè)置自動(dòng)提交為 false;
3)執(zhí)行數(shù)據(jù)庫操作;
4)提交事務(wù);
5)關(guān)閉 SqlSession。
代碼示例如下:
SqlSession sqlSession = sqlSessionFactory.openSession(false); try { // 執(zhí)行數(shù)據(jù)庫操作 sqlSession.insert("insertUser", user); sqlSession.update("updateUser", user); // 提交事務(wù) sqlSession.commit(); } catch (Exception e) { // 回滾事務(wù) sqlSession.rollback(); } finally { // 關(guān)閉 SqlSession sqlSession.close(); }
以上代碼中,通過設(shè)置自動(dòng)提交為 false 來關(guān)閉自動(dòng)提交事務(wù)的方式,然后在 try 塊中執(zhí)行需要進(jìn)行事務(wù)管理的數(shù)據(jù)庫操作,如果出現(xiàn)異常則回滾事務(wù),否則提交事務(wù),并在 finally 中關(guān)閉 SqlSession。
2. 聲明式事務(wù)
聲明式事務(wù)就是在配置文件中通過事務(wù)管理器來管理事務(wù),這種方式更加靈活和方便,我們只需要在配置文件中配置好即可。MyBatis 內(nèi)置了兩個(gè)事務(wù)管理器:JDBC 和 Spring。
(1)JDBC 事務(wù)管理器
JDBC 事務(wù)管理器是 MyBatis 內(nèi)置的一個(gè)事務(wù)管理器,在 SqlSessionFactory 的配置文件中進(jìn)行配置:
<transactionManager type="JDBC" />
(2)Spring 事務(wù)管理器
Spring 事務(wù)管理器是基于 Spring 框架的事務(wù)管理器,需要引入 Spring 的包,并在 MyBatis 的配置文件中配置:
<transactionManager type="SPRING" />
在使用 Spring 事務(wù)管理器時(shí),還需要在 Spring 的配置文件中配置事務(wù)管理器和數(shù)據(jù)源,具體可以參考 Spring 的文檔。
三、事務(wù)源碼理解
(1)TransactionFactory
在 MyBatis 中,事務(wù)工廠需要實(shí)現(xiàn) org.apache.ibatis.transaction.TransactionFactory
接口,該接口有一個(gè)方法:
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
該方法用于創(chuàng)建一個(gè)新的 Transaction
實(shí)例。
例如,如果你要使用 JDBC 來管理事務(wù),可以使用默認(rèn)提供的 JdbcTransactionFactory
類來創(chuàng)建事務(wù)工廠。以下是一個(gè)創(chuàng)建事務(wù)工廠的示例代碼:
import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import javax.sql.DataSource; public class MyTransactionFactory { public static TransactionFactory createTransactionFactory() { return new JdbcTransactionFactory(); } }
以上代碼中,我們使用 JdbcTransactionFactory
創(chuàng)建一個(gè)新的事務(wù)工廠。實(shí)際上,在 MyBatis 的配置文件中,可以直接引用這個(gè)工廠。
例如:
<configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!-- 數(shù)據(jù)源配置 --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- Mapper 配置 --> </configuration>
以上代碼中,我們使用了 <transactionManager type="JDBC"/>
來配置事務(wù)管理器的類型,并且在 <dataSource>
標(biāo)簽中指定了數(shù)據(jù)源的類型為 POOLED
,也就是一個(gè)連接池?cái)?shù)據(jù)源。
在實(shí)際應(yīng)用中,還可以通過自定義事務(wù)工廠來創(chuàng)建其他類型的事務(wù)。例如,如果你要使用 Atomikos 來管理事務(wù),可以自定義一個(gè) AtomikosTransactionFactory
類來創(chuàng)建事務(wù)工廠。
(2)JdbcTransaction
在 MyBatis 中,Transaction
接口的默認(rèn)實(shí)現(xiàn)是 JdbcTransaction
類。這個(gè)類的創(chuàng)建是在 JdbcTransactionFactory
工廠類中完成的。
以下是 JdbcTransactionFactory
工廠類中用于創(chuàng)建 Transaction
實(shí)例的代碼:
@Override public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) { return new JdbcTransaction(dataSource, level, autoCommit); }
在這段代碼中,我們可以看到工廠方法 newTransaction()
的實(shí)現(xiàn)。該方法接收三個(gè)參數(shù):數(shù)據(jù)源、事務(wù)隔離級(jí)別和是否自動(dòng)提交。
在 newTransaction
方法中,我們使用這些參數(shù)創(chuàng)建一個(gè)新的 JdbcTransaction
實(shí)例,并將其返回。JdbcTransaction
類中實(shí)現(xiàn)了 Transaction
接口,因此可以直接返回。
以下是 JdbcTransaction
類的主要源碼:
public class JdbcTransaction implements Transaction { protected Connection connection; protected DataSource dataSource; protected TransactionIsolationLevel level; protected boolean autoCommit; public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) throws SQLException { dataSource = ds; level = desiredLevel; autoCommit = desiredAutoCommit; openConnection(); setDesiredAutoCommit(); setDesiredTransactionIsolation(); } // ...其他方法 }
在 JdbcTransaction
類中,我們創(chuàng)建了一個(gè)基本的事務(wù)對(duì)象,它包含了一些屬性,例如事務(wù)管理器、連接、事務(wù)隔離級(jí)別等等。在構(gòu)造函數(shù)中,我們使用傳入的參數(shù)來初始化這些屬性值。
除了構(gòu)造函數(shù)之外,還有一些其他的方法用于對(duì)事務(wù)進(jìn)行操作,例如提交、回滾、關(guān)閉等。這些方法都是從 Transaction
接口中繼承來的。
因此,在 MyBatis 中,創(chuàng)建一個(gè)新的事務(wù)就是通過事務(wù)工廠和相應(yīng)的創(chuàng)建方法來創(chuàng)建一個(gè)特定類型的事務(wù)對(duì)象。而在事務(wù)對(duì)象中,我們可以對(duì)事務(wù)進(jìn)行各種操作。
(3)ManagedTransaction
在 MyBatis 中,除了 JdbcTransaction
之外,還提供了另外一種稱為 ManagedTransaction
的事務(wù)實(shí)現(xiàn),它是一種由容器或框架來管理的事務(wù),而不是 MyBatis 內(nèi)部來管理。
ManagedTransaction
接口的默認(rèn)實(shí)現(xiàn)是 SpringManagedTransaction
類。該類實(shí)現(xiàn)了 MyBatis 的 Transaction
接口,但其本身并不處理任何事務(wù)邏輯,而是委托給 Spring 框架進(jìn)行處理。
以下是 SpringManagedTransaction
類的主要源碼:
public class SpringManagedTransaction implements Transaction { private final DataSource dataSource; private Connection connection; private boolean isConnectionTransactional; private boolean autoCommit; public SpringManagedTransaction(DataSource dataSource) { this.dataSource = dataSource; } @Override public Connection getConnection() throws SQLException { if (this.connection == null) { openConnection(); } return this.connection; } // ...其他方法 }
在 SpringManagedTransaction
類中,我們使用傳入的數(shù)據(jù)源來創(chuàng)建一個(gè)新的事務(wù)對(duì)象。在 getConnection()
方法中,如果當(dāng)前連接不存在,則會(huì)打開一個(gè)新的連接并返回;否則直接返回已經(jīng)存在的連接。
需要注意的是,SpringManagedTransaction
并不自行管理事務(wù),而是將事務(wù)管理工作交給了 Spring 框架。因此,如果要使用 SpringManagedTransaction
,我們需要在 Spring 配置文件中配置相應(yīng)的事務(wù)管理器,例如 DataSourceTransactionManager
或 JpaTransactionManager
等。
總之,相比于 JdbcTransaction
,ManagedTransaction
更適合在容器或框架中使用,而不是在 MyBatis 內(nèi)部使用。如果你使用 Spring 或其他類似的框架來管理事務(wù),則可以考慮使用 ManagedTransaction
來實(shí)現(xiàn) MyBatis 的事務(wù)管理。
四、測試用例
接下來我們將通過一個(gè)示例來詳細(xì)介紹 MyBatis 的事務(wù)管理機(jī)制。假設(shè)我們需要執(zhí)行一個(gè)復(fù)雜的業(yè)務(wù)操作,需要同時(shí)更新多個(gè)表,我們就可以使用編程式事務(wù)來管理事務(wù)。
在 Mapper.xml 文件中,編寫如下 SQL 語句:
<insert id="insertUser" parameterType="com.example.User"> insert into user (id, name, age) values (#{id}, #{name}, #{age}) </insert> <update id="updateAccount" parameterType="com.example.Account"> update account set balance = #{balance} where id = #{id} </update>
然后我們編寫一個(gè) Service 類來調(diào)用 Mapper 文件中的方法,代碼如下:
public class UserService { private SqlSessionFactory sqlSessionFactory; public UserService(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } public void addUserAndAccount(User user, Account account) { SqlSession sqlSession = sqlSessionFactory.openSession(false); try { // 執(zhí)行數(shù)據(jù)庫操作 sqlSession.insert("insertUser", user); sqlSession.update("updateAccount", account); // 提交事務(wù) sqlSession.commit(); } catch (Exception e) { // 回滾事務(wù) sqlSession.rollback(); } finally { // 關(guān)閉 SqlSession sqlSession.close(); } } }
以上代碼中,我們通過 openSession(false) 來獲取 SqlSession 對(duì)象,并設(shè)置自動(dòng)提交為 false,然后在 try 塊中執(zhí)行數(shù)據(jù)庫操作,如果出現(xiàn)異常則回滾事務(wù),否則提交事務(wù),并在 finally 中關(guān)閉 SqlSession。在實(shí)際開發(fā)中,我們可以將 SqlSession 放入 Spring 管理中,這樣就更加方便了。
以上就是一文搞懂 MyBatis的事務(wù)管理機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于MyBatis 事務(wù)管理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
idea在用Mybatis時(shí)xml文件sql不提示解決辦法(提示后背景顏色去除)
mybatis的xml文件配置的時(shí)候,有時(shí)候會(huì)沒有提示,這讓我們很頭疼,下面這篇文章主要給大家介紹了關(guān)于idea在用Mybatis時(shí)xml文件sql不提示的解決辦法,提示后背景顏色去除的相關(guān)資料,需要的朋友可以參考下2023-03-03java字節(jié)碼框架ASM的深入學(xué)習(xí)
這篇文章主要給大家介紹了java中字節(jié)碼框架ASM的相關(guān)資料,文中介紹的非常詳細(xì),相信對(duì)大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。2017-01-01Spring整合CXF webservice restful實(shí)例詳解
這篇文章主要為大家詳細(xì)介紹了Spring整合CXF webservice restful的實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08SpringCloud 服務(wù)注冊和消費(fèi)實(shí)現(xiàn)過程
這篇文章主要介紹了SpringCloud 服務(wù)注冊和消費(fèi)實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07java實(shí)現(xiàn)html轉(zhuǎn)pdf方法步驟
這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)html轉(zhuǎn)pdf方法的相關(guān)資料,要將HTML轉(zhuǎn)換成PDF,我們需要借助Java中的第三方庫,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08Java常見基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
這篇文章主要介紹了Java常見數(shù)據(jù)結(jié)構(gòu)面試題,帶有答案及解釋,希望對(duì)廣大的程序愛好者有所幫助,同時(shí)祝大家有一個(gè)好成績,需要的朋友可以參考下,希望可以幫助到你2021-07-07Spring?Cloud?Feign?使用對(duì)象參數(shù)的操作
這篇文章主要介紹了Spring?Cloud?Feign?如何使用對(duì)象參數(shù)的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02