spring事物---发展历程

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&amp;useUnicode=true&amp;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 而言,也无法重用,必须逐个指定。

结束:
虽然上面共列举了四种声明式事务管理方式,但是这样的划分只是为了便于理解,其实后台的实现方式是一样的,只是用户使用的方式不同而已。

猜你喜欢

转载自blog.csdn.net/convict_eva/article/details/83274037