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