分布式事务系列(1.2)Spring的事务体系

时间:2015-05-18 09:18:45   收藏:0   阅读:180

1 系列目录

2 三种事务模型

三种事务模型如下:

先来看几个例子:

案例1:

Connection conn=jdbcDao.getConnection();
PreparedStatement ps=conn.prepareStatement("insert into user(name,age) value(?,?)");
ps.setString(1,user.getName());
ps.setInt(2,user.getAge());
ps.execute();

案例2:

Connection conn=jdbcDao.getConnection();
conn.setAutoCommit(false);
try {
   PreparedStatement ps=conn.prepareStatement("insert into user(name,age) value(?,?)");
   ps.setString(1,user.getName());
   ps.setInt(2,user.getAge());
   ps.execute();
   conn.commit();
} catch (Exception e) {
   e.printStackTrace();
   conn.rollback();
}finally{
   conn.close();
}

案例3:

InitialContext ctx = new InitialContext();
UserTransaction txn = (UserTransaction)ctx.lookup("UserTransaction");
try {
   txn.begin();
    //业务代码          
   txn.commit();
} catch (Exception up) {
   txn.rollback();
   throw up;
}

案例4:

@Transactional
public void save(User user){
    jdbcTemplate.update("insert into user(name,age) value(?,?)",user.getName(),user.getAge());
}

我的看法:

我认为他们各自的特点在于:谁在管理着事务的提交和回滚等操作?

这里有三个角色:数据库、开发人员、spring(等第三方)

上述的特点也不是全部合理,如下文提到的Spring的TransactionTemplate,虽然属于编程式事务,但是它的确是把事务的提交和回滚交给了Spring来管理。总之不用过分纠结于划分事务模型

3 编程式事务

编程式事务:即通过手动编程方式来实现事务操作,大部分情况,都是类似于上述案例2、3情况,开发人员来管理事务的提交和回滚,但也可能是Spring自己来管理事务,如Spring的TransactionTemplate。

3.1 Spring的TransactionTemplate

在前一篇文章中了解到,使用jdbc操作事务,编程非常麻烦,老是需要写一套模板式的try catch代码,所以我们可以将try catch代码封装成一个模板,这就引出了Spring的TransactionTemplate:

案例如下:

TransactionTemplate template=new TransactionTemplate();
template.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.setTransactionManager(transactionManager);
template.execute(new TransactionCallback<User>() {

    @Override
    public User doInTransaction(TransactionStatus status) {
       //可以使用DataSourceUtils获取Connection来执行sql
       //jdbcTemplate.update(sql2);

       //可以使用SessionFactory的getCurrentSession获取Session来执行
       //hibernateTemplate.save(user1)
       return null;
    }

});

详细过程如下:

3.2 事务代码和业务代码可以实现分离的原理

我们可以看到,使用TransactionTemplate,其实就做到了事务代码和业务代码的分离,分离之后的代价就是,必须保证他们使用的是同一类型事务。之后的声明式事务实现分离也是同样的原理,这里就提前说明一下。

4 Spring的声明式事务

Spring可以有三种形式来配置事务拦截,不同配置形式仅仅是外在形式不同,里面的拦截原理都是一样的,所以先通过一个小例子了解利用AOP实现事务拦截的原理

4.1 利用AOP实现声明式事务的原理

4.1.1 简单的AOP事务例子

从上面的Spring的TransactionTemplate我们可以实现了,事务代码和业务代码的分离,但是分离的还不彻底,还是需要如下的代码:

TransactionTemplate template=new TransactionTemplate();
template.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.setTransactionManager(transactionManager);

一大堆的设置,同时业务代码必须嵌套在TransactionCallback中,如何才能做到如下方式的彻底分离呢?

@Transactional
public void save(User user){
    jdbcTemplate.update("insert into user(name,age) value(?,?)",user.getName(),user.getAge());
}

这就需要使用Spring的AOP机制,SpringAOP的原理就不再详细说明了,可以参考这里的源码分析Spring AOP源码分析,这里给出一个简单的AOP事务拦截原理的小例子:

@Repository
public class AopUserDao implements InitializingBean{

    @Autowired
    private UserDao userDao;

    private UserDao proxyUserDao;

    @Resource(name="transactionManager")
    private PlatformTransactionManager transactionManager;

    @Override
    public void afterPropertiesSet() throws Exception {
       ProxyFactory proxyFactory = new ProxyFactory();
       proxyFactory.setTarget(userDao);

       TransactionInterceptor transactionInterceptor=new TransactionInterceptor();
       transactionInterceptor.setTransactionManager(transactionManager);
       Properties properties=new Properties();
       properties.setProperty("*","PROPAGATION_REQUIRED");
       transactionInterceptor.setTransactionAttributes(properties);

       proxyFactory.addAdvice(transactionInterceptor);
       proxyUserDao=(UserDao) proxyFactory.getProxy();
    }

    public void save(User user){
       proxyUserDao.save(user);
    }
}

代码分析如下:

4.1.2 事务拦截器的原理分析

事务拦截器需要2个参数:

在执行代理proxyUserDao的save(user)方法时,会先进入事务拦截器中,具体的拦截代码如下:

技术分享

一个事务拦截器的内容大致就是这样。

4.2 Spring的三种事务配置形式

4.2.1 使用TransactionProxyFactoryBean

配置案例如下:

<bean id="proxy"
   class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <!-- 为事务代理工厂Bean注入事务管理器 -->
    <property name="transactionManager" ref="transactionManager" />
    <!-- 要在哪个Bean上面创建事务代理对象 -->
    <property name="target" ref="productDao" />
    <!-- 指定事务属性 -->
    <property name="transactionAttributes">
        <props>
            <prop key="*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

案例分析:

上面有三大配置:

TransactionProxyFactoryBean这个工厂bean创建代理对象的原理就是:通过ProxyFactory来对target创建出代理对象了,同时加入上述事务拦截器,就可以实现事务拦截功能了

4.2.2 使用aop:config和tx:advice

使用TransactionProxyFactoryBean的方式只能针对一个target进行代理,如果想再代理一个target,就需要再配置一个TransactionProxyFactoryBean,比较麻烦,所以使用apo:config的配置形式,就可以针对符合Pointcut的所有target都可以进行代理。

配置案例如下:

<tx:advice id="txAdvice" transaction-manager="transactionManager">  
    <tx:attributes>  
        <tx:method name="add*" propagation="REQUIRED" />  
        <tx:method name="delete*" propagation="REQUIRED" />  
        <tx:method name="update*" propagation="REQUIRED" />  
        <tx:method name="add*" propagation="REQUIRED" />    
    </tx:attributes>  
</tx:advice>  

<aop:config>  
    <aop:pointcut id="pointcut" 
        expression="XXXX" />  
    <aop:advisor advice-ref="txAdvice" 
        pointcut-ref="pointcut" />  
</aop:config>

这里有2种配置:

4.2.3 使用@Transactional

使用aop:config可以在xml中进行代理的配置,有时候想在代码中直接进行配置,这时候就需要使用注解@Transactional。

案例如下:

xml中启动@Transactional注解扫描

<tx:annotation-driven transaction-manager="transactionManager" />

在代码中就可以通过配置@Transactional来实现事务拦截了

@Transactional(propagation=Propagation.REQUIRED)
public void save(User user){
    xxxx
}

在xml配置中启动注解扫描,会把那些加入了@Transactional标记的容器bean创建出代理对象,同时加入事务拦截器。在执行事务拦截的时候,会从@Transactional注解中取出对应的事务配置和事务管理器配置,进而可以执行事务的创建等操作。

5 结束语

至此Spring的事务体系大概就讲完了,接下来就要说明如何实现分布式事务了,在涉及jotm、atomikos这些第三方框架之前,先要了解下,分布式事务的一些概念

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!