1. 程式人生 > >在SSM專案中使用AOP管理事務的配置教程

在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來管理我們的事務。

如果有不對的地方,歡迎各位大佬指教,如果有不懂的地方,也歡迎大家留言,我們一起討論。