当项目的数据须要持久化存储时,不可避免要和数据库交互。在交互过程当中,对事务的支持则是尤其重要。JDBC规范支持对事务的操做,在深刻浅出JDBC(一) - Connection与事务介绍一章中简要介绍了JDBC事务相关的概念。JDBC将对不一样数据库的交互规范化,包括事务的操做,让开发者能够屏蔽不一样数据库的差别使用接口编程。但事务的开启和关闭,以及事务的控制和配置仍是须要手动编码控制,未免繁琐且易出错。Spring基于此之上,开放出一套事务管理机制,将开发者从繁琐的事务控制中解脱出来,能够便捷地执行事务控制。然而做为开发者,便捷以后的原理也须要了解,才能更好地把控程序。接下来,我将从Spring事务管理的配置到原理逐步介绍其运行机制,本篇先介绍三种从原始到简化的配置方式。mysql
以mybatis+mysql为基础,基本的xml配置以下spring
<!-- 数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.1.160:3306/lichao"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> <property name="connectionProperties" value="useUnicode=true;autoReconnect=true;failOverReadOnly=false;characterEncoding=utf8;zeroDateTimeBehavior=convertToNull;allowMultiQueries=true"></property> </bean> <!-- mybatis的Session工厂 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="mybatis-config.xml"></property> <property name="dataSource" ref="dataSource"></property> </bean> <!-- UserMapper代理 --> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.lcifn.spring.transaction.mapper.UserMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean> <!-- userManager实例 --> <bean id="userManager" class="com.lcifn.spring.transaction.manager.UserManager"> <property name="userMapper" ref="userMapper"></property> </bean>
这里对mybatis的配置就不过多介绍了,事务定义在UserManager层,UserManager中定义一个批量操做的方法,来验证事务。sql
@Slf4j public class UserManager { @Getter @Setter private UserMapper userMapper; public void batchOperator(){ User user = new User("lily", 25); // 插入一条user记录 int insertRows = userMapper.insert(user); if(insertRows > 0){ user = userMapper.getUser(user.getId()); log.info(user.toString()); user.setName("jack"); user.setAge(28); // 更新user记录 userMapper.update(user); } } }
使用TransactionProxyFactoryBean直接代理UserManager数据库
<!-- userManager事务代理类 --> <bean id="userManagerTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 原始对象 --> <property name="target" ref="userManager"/> <!-- 事务属性 --> <property name="transactionAttributes"> <props> <prop key="batchOperator">PROPAGATION_REQUIRED</prop> </props> </property> <!-- 事务管理器 --> <property name="transactionManager"> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </property> </bean>
TransactionProxyFactoryBean使用ProxyFactory生成代理类完成对事务的处理。transactionAttributes接收一个properties对象,key为方法名,value为方法对应的事务配置,配置规则的解析类是TransactionAttributeEditor,配置规则以下express
配置名称 | 前缀 | 配置选项 | 默认配置 |
---|---|---|---|
传播属性 | PROPAGATION_ | REQUIRED|MANDATORY|REQUIRES_NEW|NESTED等 | REQUIRED |
隔离级别 | ISOLATION_ | READ_UNCOMMITTED|READ_COMMITTED|REPEATABLE_READ|SERIALIZABLE | DEFAULT |
超时时间 | timeout_ | 单位:秒 | -1 |
只读 | readOnly | 不配置表示可读可写,配置了便可读 | 可读可写 |
不回滚异常规则(noRollbackFor) | + | 异常类包路径 | 无 |
回滚异常规则(rollbackFor | - | 异常类包路径 | 运行时异常及Error |
事务管理器则是PlatformTransactionManager的子类,实现其获取事务,提交事务和回滚事务三个方法,这里使用jdbc的事务管理器DataSourceTransactionManager。apache
写一个测试类编程
public class TransactionProxyFactoryBeanTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("TransactionProxyFactoryBean.xml"); UserManager userManager = context.getBean("userManagerTransactionProxy", UserManager.class); userManager.batchOperator(); } }
经过上下文获取userManager的代理对象,并执行操做方法,能够在日志中看到事务的运行。mybatis
上面的配置方式只能对一个类代理,用来研究Spring事务管理的机制很好,但在项目中,须要批量地配置事务管理。使用Spring AOP中的Advice和Pointcut的方式就能完成,并且Spring定义了tx:advice标签来支持配置。app
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 事务Advice加强配置 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" /> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置事务aop --> <aop:config> <aop:pointcut id="userPointcut" expression="execution(* com.lcifn.spring.transaction.manager.UserManager.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="userPointcut" /> </aop:config>
在tx:advice中,须要配置事务管理器,在其子标签中tx:attributes中配置方法和事务配置的关系。方法配置支持通配符匹配多个方法,事务的配置同上面的基本一致,只是Spring提供了属性标签来方便配置。配置aop:pointcut切入点,决定哪些类哪些方法须要被代理。测试
写一个测试类,这里直接获取userManager,由于aop:config自动代理的缘由,返回的已是代理对象了。
public class TransactionTxAdvice { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("transaction-tx-advice.xml"); UserManager userManager = context.getBean("userManager", UserManager.class); userManager.batchOperator(); } }
XML配置的方式已经能大大地节约配置操做,但若是要对每一个方法细粒度地配置,xml也会变得很繁琐。怎么办?这时咱们想到了注解。Spring提供了超级便捷的方式,经过注解在类或方法上完成事务的控制和配置。
<!-- 事务注解驱动 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
xml中的就是上面简单的配置,而在类或方法上使用@Transactional注解,好比上面的batchOperator方法能够配置以下
// 事务传播属性为必须,超时时间为2秒 @Transactional(propagation = Propagation.REQUIRED, timeout = 2)
每一个方法是否开启事务,以及事务配置什么属性均可以经过这种方式精细化地控制。
Spring的事务隔离级别支持sql标准定义的四个级别:Read Uncommitted(未受权读取),Read Committed(受权读取),Repeatable Read(可重复读取),Serializable(序列化)。可在深刻浅出JDBC(一) - Connection与事务介绍中了解详细。
Spring定义了事务传播方式,指的是多个层次(非同一个类)的事务方法执行时的规则,即事务在方法中的传播方式。Spring定义了七种传播行为(包括事务的新建和回滚行为):
对于事务的建立,有一点须要着重强调。JDBC默认链接的提交方式为自动,若是开启事务,即将自动提交改成手动。所以开启一个新的事务,便是获取一个新的链接, ,具体实现也会在以后的源码解析里提示。下一章咱们来解析TransactionProxyFactoryBean的实现原理。