1、spring 事物api
spring 定义事物主要有三个api
TransactionDefinition 定义事物属性
PlatformTransactionManager 管理事物,进行提交或者回滚
TransactionStatus 表示一个事物运行的状态
事物的定义 TransactionDefinition
public interface TransactionDefinition {
//隔离级别
int getIsolationLevel();
//传播行为
int getPropagationBehavior();
//超时时间
int getTimeout();
//是否只读
boolean isReadOnly();
}
默认实现类DefaultTransactionDefinition,这个类有一个子类 TransactionTemplate,TransactionTemplate的execute 方法就可以直接进行编程式事物。
事物的管理 PlatformTransactionManager
public interface PlatformTransactionManager {
//查询当前事物状态
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
//提交事物
void commit(TransactionStatus var1) throws TransactionException;
//回滚事物
void rollback(TransactionStatus var1) throws TransactionException;
}
实现:
DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。
HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。
JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。
RabbitTransactionManager Rabbitmq 支持与spring事物的集成
还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等等。
事物状态 TransactionStatus
public interface TransactionStatus extends SavepointManager, Flushable {
//是否是一个新的事物(参与到一个已经存在的事物中,或者嵌套时第一层调用没有事物嵌套开启了事物)
boolean isNewTransaction();
//是否有保存点
boolean hasSavepoint();
//设置回滚事物
void setRollbackOnly();
//是否回滚事物
boolean isRollbackOnly();
//刷新当前线程的session到数据库,对Hibernate/JPA sessions 有影响
void flush();
//事物是否完成,提交或者回滚
boolean isCompleted();
}
实现:
DefaultTransactionStatus,SimpleTransactionStatus
2、使用spring实现事物的管理有两种方式,编程式事物和声明式事物。下面看看两种方式的实现
2.1 编程式事物:
编程式事务管理有两种方式
1、使用 PlatformTransactionManager 提交回滚事物
2、使用TransactionTemplate
示例代码:
@Service
public class ProgrammaticService {
@Autowired
private RuleDOMapper ruleDOMapper;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private TransactionDefinition txDefinition;
@Autowired
private TransactionTemplate transactionTemplate;
public void useTXManager(){
TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
boolean result = false;
try {
ruleDOMapper.insert(new RuleDO());
transactionManager.commit(txStatus);
} catch (Exception e) {
transactionManager.rollback(txStatus);
System.out.println("Transfer Error!");
}
}
public void useTXTemplate(){
transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
ruleDOMapper.insert(new RuleDO());
return 1;
}
});
}
}
public class RuleDOMapper {
SqlSession sqlSession;
public int insert(RuleDO record){
return sqlSession.insert(getNameSpace()+"insert",record);
}
public String getNameSpace(){
return this.getClass().getName()+".";
}
//get set 方法
}
配置:
<!-- 配置数据源信息 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.30.96:3306/test?autoReconnect=true&useUnicode=true&characterset=utf8"/>
<property name="username" value="quanyan"/>
<property name="password" value="quanyan888"/>
</bean>
<!-- 配置spring的PlatformTransactionManager,名字为默认值 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事物模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!-- 事物定义 -->
<bean id="txDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
<!-- 配置ibatis映射信息 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.masz.springtest.dao.*"/>
<property name="sqlSessionTemplateBeanName" value="sqlSession"/>
</bean>
<!-- 定义mapper -->
<bean id="ruleDOMapper" class="com.masz.springtest.dao.RuleDOMapper">
<property name="sqlSession" ref="sqlSession"/>
</bean>
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:application-context.xml")
public class SpringTest {
@Autowired
ProgrammaticService programmaticService;
@Autowired
TransactionInterceptorTest transactionInterceptorTest;
@Test
public void programmaticTxTest(){
programmaticService.useTXManager();
// programmaticService.useTXTemplate();
}
}
测试结果
2.2 声明式事物
Spring 的声明式事务管理在底层是建立在 AOP的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。上面的编程式事物,都是模板代码。既然是模板代码,spring就要解决。
编程事物和声明式事务对比
1、声明式事务增加配置就行,不用在代码中添加事物管理的代码。所以用起来比编程工事物用的方便,业务代码不会被事物代码“污染”
2、声明式事物最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。也可以把声明式事物的代码块放到事物方式中单独管理。
实现方式:
1、TransactionInterceptor
2、TransactionProxyFactoryBean
3、基于 <tx> 命名空间实现声明式事物
4、@Transactional 注解
2.2.1 使用TransactionInterceptor
示例代码:
/**
* TransactionInterceptor实现声明式事物,目标类
*/
public class TransactionInterceptorTarget {
private RuleDOMapper mapper;
public void save(){
mapper.insert(new RuleDO());
}
//get set 方法
}
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:application-context.xml")
public class SpringTest {
@Resource(name = "transactionInterceptorTarget")
TransactionInterceptorTarget transactionInterceptorTarget;
@Resource(name = "transactionInterceptorTargetProxy")
TransactionInterceptorTarget transactionInterceptorTargetProxy;
@Test
public void transactionInterceptorTest(){
//直接使用是没有事物的
transactionInterceptorTarget.save();
//使用代理对象,可以被事物增强
transactionInterceptorTargetProxy.save();
}
}
配置(datasource transactionManager 看参考文章上面的配置):
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<!-- 定义事物属性 -->
<props>
<!-- 配置目标类方法名使用的事物属性,或者方法通配符 -->
<prop key="save*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 目标对象,直接使用是没有事物的-->
<bean id="transactionInterceptorTarget" class="com.masz.springtest.transaction.TransactionInterceptorTarget">
<property name="mapper" ref="ruleDOMapper"/>
</bean>
<!-- 通过 ProxyFactoryBean 获取目标对象的增强对象 -->
<bean id="transactionInterceptorTargetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 被代理的目标类 -->
<property name="target" ref="transactionInterceptorTarget"/>
<!-- 使用interceptor增强 -->
<property name="interceptorNames">
<list>
<idref bean="transactionInterceptor"/>
</list>
</property>
</bean>
测试结果:
直接使用目标类,没有开启spring事物
使用代理类,开启spring事物
使用TransactionInterceptor 缺点就是配置太多,每一个使用事物的target类都要加一个TransactionInterceptor 和ProxyFactoryBean的配置。
spring 又提供了TransactionProxyFactoryBean 将TransactionInterceptor 和ProxyFactoryBean的配置合在一起缓解这个问题。
2.2.2、TransactionProxyFactoryBean 实现
示例代码:
/**
* TransactionProxyFactoryBean 测试
*/
public class ProxyFactoryBeanTestTarget {
private RuleDOMapper mapper;
public void save(){
mapper.insert(new RuleDO());
}
//get set 方法
}
配置文件
<!-- 目标对象 -->
<bean id="proxyFactoryBeanTestTarget" class="com.masz.springtest.transaction.TransactionInterceptorTarget">
<property name="mapper" ref="ruleDOMapper"/>
</bean>
<!-- 代理对象 -->
<bean id="proxyFactoryBeanProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="proxyFactoryBeanTestTarget"/>
<!-- 事物管理属性 -->
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<!-- save 开头的方法被事物增强 -->
<prop key="save*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:application-context.xml")
public class SpringTest {
//使用代码对象才能被事物增强
@Resource(name = "proxyFactoryBeanProxy")
TransactionInterceptorTarget proxyFactoryBeanProxy;
@Test
public void proxyFactoryBeanTest(){
proxyFactoryBeanProxy.save();
}
}
测试结果:
使用TransactionProxyFactoryBean,一个类也需要有两个配置。并且需要注入代理类。
2.2.3、基于 <tx> 命名空间实现声明式事物
测试代码:
/**
* tx 配置命名空间配置测试
*/
public class TransactionTXNameSpaceTest {
private RuleDOMapper mapper;
public void add(){
mapper.insert(new RuleDO());
}
//get set 方法...
}
配置:
<!-- 目标对象 -->
<bean id="transactionTXNameSpaceTest" class="com.masz.springtest.transaction.TransactionTXNameSpaceTest">
<property name="mapper" ref="ruleDOMapper"/>
</bean>
<!-- 声明式容器事务管理 advice -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- pointcut 匹配 -->
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="upd*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config expose-proxy="true">
<!-- pointcut -->
<aop:pointcut id="txPointcut" expression="execution(* com.masz.springtest.transaction..*.*(..))"/>
<!-- Advisor -->
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
</aop:config>
测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:application-context.xml")
public class SpringTest {
@Autowired
TransactionTXNameSpaceTest transactionTXNameSpaceTest;
@Test
public void transactionTXNameSpaceTest(){
//直接使用目标类
transactionTXNameSpaceTest.add();
}
}
测试结果:
只要把配置的切面配置好以后,就可以放心使用事物增强。
4、基于 @Transactional 实现声明式事务
添加配置:
<!-- 基于 @Transactional 实现声明式事务配置 -->
<!-- transaction-manager 属性的默认值是 transactionManager,如果事务管理器 Bean 的名字即为该值,则可以省略该属性。 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
测试比较简单,就不写了。
使用@Transactional 需要注意:
1、 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
2、@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
3、@Transactional 的方式必须在每一个需要使用事务的方法或者类上用 @Transactional 标注,尽管可能大多数事务的规则是一致的,但是对 @Transactional 而言,也无法重用,必须逐个指定。
结束:
虽然上面共列举了四种声明式事务管理方式,但是这样的划分只是为了便于理解,其实后台的实现方式是一样的,只是用户使用的方式不同而已。