zoukankan      html  css  js  c++  java
  • Spring 事务模型

    一、三种事务模型

    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();
    }
    InitialContext ctx = new InitialContext();
    UserTransaction txn = (UserTransaction)ctx.lookup("UserTransaction");
    try {
      txn.begin();
      //业务代码                
      txn.commit();
    } catch (Exception up) {
      txn.rollback();
      throw up;
    }

    3、声明式事务模型:开发人员完全不用关心事务,事务的提交和回滚操作全部交给Spring来管理,所以Spring是事务的管理者

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

     二、编程式事务

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

    2.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;
        }
    });

    1、TransactionTemplate继承了DefaultTransactionDefinition,有了默认的事务定义,也可以自定义设置隔离级别、传播属性等;

    2、TransactionTemplate需要一个PlatformTransactionManager事务管理器,来执行事务的操作;

    3、TransactionTemplate在TransactionCallback中执行业务代码,try catch的事务模板代码,则被封装起来,包裹在业务代码的周围,详细见TransactionTemplate的execute方法,如下:

     

    详细过程如下:

    1、第一步:根据事务定义获取事务

         由于TransactionTemplate继承了DefaultTransactionDefinition,所以使用PlatformTransactionManager事务管理器来根据TransactionTemplate来获取事务;

    2、第二步:执行业务代码

        在TransactionCallback中的doInTransaction中执行相应的业务代码。如果使用的是DataSourceTransactionManager,你就可以使用JdbcTemplate来执行业务逻辑;或者直接使用Connection,但是必须使用DataSourceUtils来获取Connection。如果使用的是HibernateTransactionManager,就可以使用HibernateTemplate来执行业务逻辑,或者则可以使用SessionFactory的getCurrentSession方法来获取当前线程绑定的Session,不可使用SessionFactory的openSession方法。也是不可乱用的,下面详细解释。

    3、第三步:如果业务代码出现异常,则回滚事务,没有异常则提交事务。回滚与提交都是通过PlatformTransactionManager事务管理器来进行的

    三、Spring声明式事务

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

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

    上面使用的就是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);
        }
    }

    代码分析如下:

    1、首先需要一个原始的UserDao,我们需要对它进行AOP代理,产生代理对象proxyUserDao,之后保存的功能就是使用proxyUserDao来执行

    2、对UserDao具体的代理过程如下:

    • 使用代理工厂,设置要代理的对象,即target
    proxyFactory.setTarget(userDao);
    • 对代理对象加入拦截器:分成2种情况,一种默认拦截原UserDao的所有方法,一种是指定Pointcut,即拦截原UserDao的某些方法

       这里使用proxyFactory.addAdvice(transactionInterceptor);就表示默认拦截原UserDao的所有方法。如果使用proxyFactory.addAdvisor(advisor),这里的Advisor可以简单看成是Pointcut和Advice的组合,Pointcut则是用于指定是否拦截某些方法。上述addAdvice就是使用了默认的Pointcut,表示对所有方法都拦截,源码如下:

    addAdvisor(pos, new DefaultPointcutAdvisor(advice));

        DefaultPointcutAdvisor内容如下:

    public DefaultPointcutAdvisor(Advice advice) {
        this(Pointcut.TRUE, advice); // Pointcut.TRUE便表示拦截所有方法。
    }
    • 设置好代理工厂要代理的对象和拦截器后,便可以创建代理对象
    proxyUserDao=(UserDao) proxyFactory.getProxy();

         之后,我们在使用创建出的proxyUserDao时,就会首先进入拦截器,执行相关拦截器代码,因此我们可以在这里实现事务的处理。

    3.1 事务拦截器的原理分析

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

    1、事务配置的提供者:用于指定哪些方法具有什么样的事务配置。可以通过属性配置方式,或者通过其他一些配置方式,如下三种方式都是为了获取事务配置提供者:

     2、事务管理器PlatformTransactionManager:有了事务的配置,我们就可以通过事务管理器来获取事务了

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

    第一步:首先获取所执行方法的对应的事务配置

    第二步:然后获取指定的事务管理器PlatformTransactionManager

    第三步:根据事务配置,使用事务管理器创建出事务

    第四步:继续执行下一个拦截器,最终会执行到代理的原始对象的方法

    第五步:一旦执行过程发生异常,使用事务拦截器进行事务的回滚

    第六步:如果没有异常,则使用事务拦截器提交事务

    3.2 Spring的三种事务配置形式

    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>

    上面有三大配置:

    • 事务管理器transactionManager
    • 事务配置的提供者transactionAttributes(用于指定哪些方法具有什么样的事务配置)

        有了以上2个元素,我们就可以创建出一个事务拦截器TransactionInterceptor

    • 要代理的对象target

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

    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="pc" expression="execution(public * com.qding..service.*.*(..))" />
        <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
    </aop:config>
    • tx:advice:有事务管理器transactionManager和事务配置提供者attributes,就可以产生一个事务拦截器TransactionInterceptor
    • aop:config:这里会对符合pointcut的bean创建出代理对象,同时加入上述创建的事务拦截器

     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注解中取出对应的事务配置和事务管理器配置,进而可以执行事务的创建等操作。

    参见:http://my.oschina.net/xianggao/blog/541527?fromerr=f37Nd3l5

  • 相关阅读:
    codeforces 368B
    codeforces 651A
    codeforces 651B
    codeforces 732B
    codeforces 313B
    codeforces 550A
    codeforces 498B
    Linux C/C++基础——内存分区
    Linux C/C++基础——变量作用域
    Linux C/C++基础——Windows远程登录Linux
  • 原文地址:https://www.cnblogs.com/moonandstar08/p/5374011.html
Copyright © 2011-2022 走看看