配置文件:
<!-- dataSource --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${db.master.url}" /> <property name="username" value="${db.master.user}" /> <property name="password" value="${db.master.password}" /> <!-- 配置監控統計攔截的filters --> <property name="filters" value="mergeStat,wall,log4j2" /> <property name="initialSize" value="5" /> <property name="maxActive" value="100" /> <property name="minIdle" value="10" /> <property name="maxWait" value="60000" /> <property name="validationQuery" value="SELECT 'x'" /> <property name="testOnBorrow" value="true" /> <property name="testOnReturn" value="true" /> <property name="testWhileIdle" value="true" /> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="removeAbandoned" value="true" /> <property name="removeAbandonedTimeout" value="1800" /> <property name="logAbandoned" value="true" /> </bean> <!-- Spring整合Mybatis --> <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 自動掃描Mapping.xml文件 --> <property name="mapperLocations" value="classpath*:/sqlMapperXml/*.xml"></property> <property name="configLocation" value="classpath:xml/mybatis-config.xml"></property> <property name="typeAliasesPackage" value="com.mingwork.model"/> <property name="globalConfig" ref="globalConfig"/> <property name="plugins"> <array> <!-- 分頁插件配置 --> <bean id="paginationInterceptor" class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"> <property name="dialectType" value="mysql"/> <property name="optimizeType" value="aliDruid" /> </bean> </array> </property> </bean> <!-- MP 全局配置 --> <bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration"> <property name="idType" value="0"/> <property name="dbColumnUnderline" value="true"/> </bean> <!-- MyBatis 動態實現 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 對Dao 接口動態實現,需要知道接口在哪 --> <property name="basePackage" value="com.mingwork.mapper"/> </bean> <!-- 事務管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 事務注解 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> <!-- 事務管理 屬性 --> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="select*" propagation="REQUIRED" read-only="true" /> <tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception" /> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config expose-proxy="true" proxy-target-class="true"> <aop:advisor advice-ref="transactionAdvice" pointcut="execution(* com.mingwork.service..*.*(..))"/> </aop:config>
上面配置的意思就是配置面向切面,只要servcie中的方法拋出Exception,那么insert,update,delete的sql方法都會回滾。測試時,可以在service方法中故意拋出一個異常,throw new Exception("test"); 那么數據庫就不會執行成功。所以在開發時,如果需要對多張表進行操作,而又需要保持事務的一致性的時候,我們就可以把對多張表的操作,寫在一個service中的方法中,這樣如果有一張表執行失敗,拋出異常,其他表的操作也會跟着回滾。
@Transactional參數說明
參數 | 說明 |
---|---|
readOnly | 是否是只讀事務,true表示只讀,false表示讀寫 |
timeout | 事務超時秒數,默認值-1表示永不超時 |
isolation | 隔離級別,例如(isolation = Isolation.READ_UNCOMMITTED) |
propagation | 事務傳播行為,見表propagation說明,例如@Transactional(propagation=Propagation.REQUIRED) |
rollbackFor | 需要回滾的異常類數組,例如</br>單一異常類:@Transactional(rollbackFor=RuntimeException.class)</br> 多個異常類:@Transactional(rollbackFor={IndexOutOfBoundsException.class, OutOfMemoryException.class}) |
noRollbackFor | 不需要進行回滾的異常類數組,...... |
rollbackForClassName | 需要進行回滾的異常類名稱數組,例如</br>單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException") </br>多個異常類名稱:@Transactional(rollbackForClassName={"IndexOutOfBoundsException","OutOfMemoryException.class"}) |
noRollbackForClassName | 不需要進行回滾的異常類名稱數組,...... |
propagation說明
參數 | 說明 |
---|---|
REQUIRED | 有事務,加入事務,沒有新建一個 |
NOT_SUPPORTED | 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起 |
REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起 |
MANDATORY | 使用當前的事務,如果當前沒有事務,就拋出異常 |
NEVER | 以非事務方式執行,如果當前存在事務,則拋出異常 |
SUPPORTS | 支持當前事務,如果當前沒有事務,就以非事務方式執行 |
NESTED | 如果當前存在事務,則在嵌套事務內執行,如果當前沒有事務,則執行與REQUIRED類似的操作 |
Q:我們的工程里,事務的開啟跟關閉是由Spring負責的,但具體的SQL語句卻是由Mybatis執行的。那么問題來了,Mybatis怎么保證自己執行的SQL語句是處在Spring的事務上下文中?
仔細思考一下這個過程,@Transactional是由spring進行處理的,spring做的事情是從數據源(一般為數據庫連接池,比如說druid,c3p0等)獲取一個數據庫連接,然后在進入方法邏輯前執行setAutoCommit(false)操作,最后在處理成功或者出現異常的時候分別執行commit或者rollback操作。
那么問題來了,開啟跟結束事務是由spring獲取到數據庫連接以后進行操作的,但我們實際執行的update或者insert語句卻是由mybatis獲取數據庫連接進行操作的,可以想到如果想讓事務生效,那么spring跟mybatis使用的必須是同一個連接,真實情況是什么樣呢?它們之間如何進行無縫銜接?讓我們通過源碼來分析一下。
具體可以參考下面的鏈接
https://www.jianshu.com/p/6a880d20a61f