在SSM專案中使用AOP管理事務的配置教程
今天來記錄一下在SSM專案中經常使用到的事務管理的配置,其實在spring中事務管理有很多方法,但今天我用的是最簡單的這種(還不是因為懶~)aop自動管理事務。
1.首先我們需要有一個整合好的SSM框架專案,具體搭建步驟可以參考我的另一篇文章:最新版的SSM框架spring5.0搭建教程
2.下面就進入正題 了,我們只需要在spring-mybatis.xml檔案中配置幾個地方就可以了。
---->匯入aop的引用:
xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
---->配置spring的事務管理器(在我之前的配置檔案中已經配置過了):
<!-- 事務核心管理器,封裝了所有事務操作. 依賴於連線池 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
---->配置aop全域性事務管理(我本機寫的demo中業務不算複雜,也為了方便演示,所以直接切到controller層了,切入點可以根據自己的專案需要自行設定):
<!--aop全域性事務管理(下面基於controller層)--> <aop:config> <aop:pointcut id="allControllerMethods" expression="execution(* com..controller..*(..))"/> <aop:advisor advice-ref="defaultTransactionAdvice" pointcut-ref="allControllerMethods"/> </aop:config>
---->配置事務管理的通知(具體指定aop攔截到的哪些方法需要建立/使用事務):
<tx:advice id="defaultTransactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--<tx:method name="*" isolation="DEFAULT"-->
<!--propagation="REQUIRED" no-rollback-for="java.lang.RuntimeException" timeout="100"/>-->
<!--<tx:method name="*" read-only="true"/>-->
<!-- 以方法為單位,指定方法應用什麼事務屬性 isolation:隔離級別 propagation:傳播行為 read-only:是否只讀 -->
<!--PROPAGATION_REQUIRED:支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。-->
<!--PROPAGATION_SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行。-->
<!--PROPAGATION_MANDATORY:支援當前事務,如果當前沒有事務,就丟擲異常。-->
<!--PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。-->
<!--PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。-->
<!--PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則丟擲異常。-->
<!--PROPAGATION_NESTED:支援當前事務,如果當前事務存在,則執行一個巢狀事務,如果當前沒有事務,就新建一個事務。-->
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="Borrow*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="create*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="merge*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="put*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="use*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
配置到這裡就完成了。
提別提醒:這幾個bean的id可以自定義,但前提是能搞清楚他們用在哪裡,不要改了名字又不知道怎麼用了!
3.編寫一個測試方法,我這邊自己寫了一個測試方法,模擬使用者借書的操作。
一個人要去圖書館借書,那麼我們需要記錄兩條記錄,第一條是在借書人的資訊表中記錄這個人借閱的圖書的book_id,第二條是在書庫中記錄這本書是被哪些user_id借走了。
兩條記錄都完整記錄表示借閱成功,如果只登記了借書人借閱的書本資訊,而沒有登記圖示的借閱人資訊,就算借閱失敗~
下面看一下實現程式碼:
---->controller層:
package com.ssm.controller;
import com.ssm.model.Book;
import com.ssm.model.User;
import com.ssm.service.BookServie;
import com.ssm.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by viking on 2018/07/15
* Library 介面類
*/
@RestController
@RequestMapping("library")
public class LibraryController {
@Autowired
private UserService userService;
@Autowired
private BookServie bookServie;
@RequestMapping("borrow")
public Object Borrow(String uname,String bname) throws Exception{
User user = userService.getUserByName(uname);
Book book = bookServie.getBookByName(bname);
if (user.getBid()==null) user.setBid(book.getId()+"");
else user.setBid(user.getBid()+";"+book.getId());
if (book.getUid()==null) book.setUid(user.getId()+"");
else book.setUid(book.getUid()+";"+user.getId());
userService.updateBid(user);
bookServie.updateUid(book);
return "借閱成功";
}
}
具體的service層和dao層程式碼就沒必要展示了,重點邏輯都在這~ 如果有需要完整程式碼的評論聯絡。
4.下面我們測試一下:
正常操作之後兩張表都記錄了對應的資料。
再測試一下異常後的事務回滾:
在controller層方法中製造一個執行時異常:
然後測試執行效果:
DEBUG 2018-07-15 17:14:09,318 org.mybatis.spring.SqlSessionUtils: Creating a new SqlSession
DEBUG 2018-07-15 17:14:09,334 org.mybatis.spring.SqlSessionUtils: Registering transaction synchronization for SqlSession [[email protected]]
DEBUG 2018-07-15 17:14:09,359 org.mybatis.spring.transaction.SpringManagedTransaction: JDBC Connection [jdbc:mysql://localhost:3306/mydatabase_test, [email protected], MySQL Connector Java] will be managed by Spring
DEBUG 2018-07-15 17:14:09,369 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: SELECT * FROM USER WHERE name=?
DEBUG 2018-07-15 17:14:09,442 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 測試二(String)
DEBUG 2018-07-15 17:14:09,473 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
DEBUG 2018-07-15 17:14:09,479 org.mybatis.spring.SqlSessionUtils: Releasing transactional SqlSession [[email protected]]
DEBUG 2018-07-15 17:14:09,480 org.mybatis.spring.SqlSessionUtils: Fetched SqlSession [[email protected]] from current transaction
DEBUG 2018-07-15 17:14:09,482 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: SELECT * FROM BOOK WHERE book_name=?
DEBUG 2018-07-15 17:14:09,482 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 《時間移民》(String)
DEBUG 2018-07-15 17:14:09,484 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
DEBUG 2018-07-15 17:14:09,485 org.mybatis.spring.SqlSessionUtils: Releasing transactional SqlSession [[email protected]]
DEBUG 2018-07-15 17:14:09,486 org.mybatis.spring.SqlSessionUtils: Fetched SqlSession [[email protected]] from current transaction
DEBUG 2018-07-15 17:14:09,487 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: UPDATE USER SET bid=? WHERE id=?
DEBUG 2018-07-15 17:14:09,499 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1;2(String), 2(Integer)
DEBUG 2018-07-15 17:14:09,530 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Updates: 1
DEBUG 2018-07-15 17:14:09,532 org.mybatis.spring.SqlSessionUtils: Releasing transactional SqlSession [[email protected]]
-----------------製造一個執行時異常--------------
DEBUG 2018-07-15 17:14:09,534 org.mybatis.spring.SqlSessionUtils$SqlSessionSynchronization: Transaction synchronization deregistering SqlSession [[email protected]]
DEBUG 2018-07-15 17:14:09,535 org.mybatis.spring.SqlSessionUtils$SqlSessionSynchronization: Transaction synchronization closing SqlSession [[email protected]]
控制檯日誌記錄中執行了一條update語句後報了異常,程式終止執行,再看看資料庫結果:
依然還是第一條記錄,說明事務回滾了,測試通過~
通過上面幾步簡單的配置可以讓我們很方便,很靈活的使用aop來管理我們的事務。
如果有不對的地方,歡迎各位大佬指教,如果有不懂的地方,也歡迎大家留言,我們一起討論。