配置文件:
<!-- 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
