Spring事务管理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务管理相关的知识,希望对你有一定的参考价值。

一、什么是事务

  事务:指逻辑上的一组操作,这组操作要么全部成功,要么全部失败。有以下特性:
原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性:事务前后数据的完整性必须保持一致(张三给李四转账,张三+李四的总额不变)。
隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发事务之间的数据相互隔离。
持久性:一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不该对其有任何影响。

二、事务管理接口

  Spring的事务管理接口主要有三个:

  ①TransactionDefinition,事务定义信息(隔离,传播,超时,只读)

  在Spring中,事务是通过TransactionDefinition接口来定义的,该接口包含与事务属性相关的方法,TransactionDefinition定义了五个表示隔离级别的常量,代表传播行为的常量,在TransactionDefinition中以int值表示超时时间。

  • 隔离:防止数据库脏读、虚读等行为
  • 传播:解决业务层方法之间的调用,传递事务
    • 情形一,支持当前事务,如果不存在就创建一个;
    • 情形二,如果有事务存在,挂起当前事务,创建一个新事务;
    • 情形三,如果当前事务存在,嵌套事务执行。

  这里找到一篇关于TransactionDefinition的文章:http://blog.csdn.net/japanstudylang/article/details/52916529

  Platform TransactionManager,平台事务管理器
  Spring为不同的持久化框架提供了不同的平台事务管理器的接口实现:

  • 使用Spring JDBC或者IBatis进行持久化数据时:org.springframework.jdbc.datasource.DataSourceTransactionManager
  • 使用Hibernate5.0版本进行持久化数据时:org.springframework.orm.hibernate5.HibernateTransactionManager

  Platform TransactionManager.getInstance()方法返回一个Transaction Status对象,返回的Transaction Status对象可能代表一个新的或已经存在的事务(如果当前调用堆栈中有一个符合条件的事务)。  

  ③TransactionStatus,事务具体运行状态

  Transaction Status接口提供了一个简单的控制事务查询和执行的方法。

三、Spring事务管理的方式

  Spring的事务管理有两种方式:

  ①编程式的事务管理:编程式即采用注解的方式,需要注意的是,使用注解的方式需要在Spring的配置文件中加入一句话:<context:annotation-config />,其作用是开启注解的方式。这种方式在实际应用开发中很少使用,通过TransactionTemlate手动管理事务。

  ②使用XML配置声明式事务:声明式就是在Spring的配置文件中进行相关配置,Spring的声明式事务是通过AOP实现的,实质就是在方法执行前后进行拦截,然后在方法执行前创建并加入事务,执行完目标方法后根据执行情况提交事务或者回滚事务。开发中推荐使用(代码侵入性最小)。

  Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSourceTransactionManager代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 DataSource、 TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为 SessionFactory,TransactionManager的实现为HibernateTransactionManager。

四、转账案例

  ①编程式的事务控制

  •  在AccountService中使用TransactionTemplate
  •  TransactionTemplate依赖HibernateTransactionManager
  • HibernateTransactionManager依赖LocalSessionFactoryBean
<!-- 组件扫描 -->
<context:component-scan base-package="com.entor.transfer"></context:component-scan>

<!-- 引入jdbc配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />

<!-- 配置c3p0连接池 -->
<bean id="dataSource" destroy-method="close"
    class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driverClass}" />
    <property name="jdbcUrl" value="${jdbc.url}" />
    <property name="user" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<!-- 配置hibernate SessionFactory -->
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <!-- 指定hibernate的配置文件位置 -->
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hiberante.format_sql">true</prop>
        </props>
    </property>
</bean>

<!-- 配置事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置事务管理模版 -->
<bean id="transactionTemplate"
    class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager" />
</bean>
@Repository("accountDAO")
/* 转账案例的DAO层接口实现类 */
public class AccountDAOImpl implements AccountDAO {

    @Autowired
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * 
     * @param out
     *            转出帐号
     * @param money
     *            转出金额
     */
    @Override
    public void outMoney(String out, double money) {
        Session session = sessionFactory.getCurrentSession();

        String hql = "from Account where name = :name";
        Query query = session.createQuery(hql);
        query.setString("name", out);

        Account account = (Account) query.uniqueResult();
        account.setMoney(account.getMoney() - money);

        session.save(account);
    }

    /**
     * 
     * @param in
     *            转入帐号
     * @param money
     *            转入金额
     */
    @Override
    public void inMoney(String in, double money) {
        Session session = sessionFactory.getCurrentSession();

        String hql = "from Account where name = :name";
        Query query = session.createQuery(hql);
        query.setString("name", in);

        Account account = (Account) query.uniqueResult();
        account.setMoney(account.getMoney() + money);

        session.save(account);
    }

}
@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDAO accountDAO;

    // 注入事务模板
    @Autowired
    private TransactionTemplate transactionTemplate;

    /**
     * 
     * @param out
     *            转出帐号
     * @param in
     *            转入帐号
     * @param money
     *            转账金额
     */
    @Override
    public void transfer(final String out, final String in, final double money) {

        // 模版回调处理事务
        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
             @Override
             protected void doInTransactionWithoutResult(
                   TransactionStatus transactionStatus) {
                   try {
                        accountDAO.outMoney(out, money);
                        // int i = 1 / 0;// 异常会导致入账失败
                        accountDAO.inMoney(in, money);
                   } catch (Exception e) {
                        System.out.println(e.getMessage());
                        transactionStatus.setRollbackOnly();// 事务回滚
                   }
             }
         });
    }
}

  ②声明式的事务控制

  • 第一种方式:基于TransactionProxyFactoryBean
<!-- 配置事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置业务层代理 -->
<bean id="accountServiceProxy"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <!-- 配置目标对象 -->
    <property name="target" ref="accountService" />
    <!-- 注入事务管理器 -->
    <property name="transactionManager" ref="transactionManager" />
    <!-- 注入事务属性 -->
    <property name="transactionAttributes">
        <props>
            <!-- 
                prop格式: 
                    * PROPAGATION : 事务的传播行为
                    * ISOLATIONI : 事务隔离级别
                    * readOnly : 只读
                    * -Exception : 发生某些异常回滚
                    * +Exception : 发生某些异常不回滚
            -->
            <!-- 保证不同的数据库操作在同一个事务当中 -->
            <prop key="transfer">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-account2.xml")
public class UnitTest {

    /**
     * 注入代理类
     */
    @Resource(name = "accountServiceProxy")
    private AccountService accountService;

    /**
     * 注入工厂
     */
    @Resource
    private SessionFactory sessionFactory;

    @Test
    public void testAccount() {
        accountService.transfer("张三", "李四", 200);
    }
}
  • 第二种方式:基于AspectJ的XML配置方式(经常使用,配置清晰)
<!-- 配置事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 
                tx:method属性: 
                    * propagation : 事务的传播行为
                    * isolationi : 事务隔离级别
                    * readonly : 只读
                    * rollback-for : 发生某些异常回滚
                    * no-rollback-for : 发生某些异常不回滚
                    * timeout : 过期信息
        -->
        <tx:method name="transfer" propagation="REQUIRED" timeout="-1"/>
    </tx:attributes>
</tx:advice>

<!-- 配置切面 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut
        expression="execution(* com.entor.transfer3.service.impl.AccountServiceImpl.*(..))"
        id="pointcut" />
    <!-- 配置切面 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
  • 第三种方式:基于注解方式(经常使用,配置简单)
<!-- 配置事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
@Service("accountService")
@Transactional
// (propagation="",isolation="",readOnly="",noRollbackFor="",rollbackFor="",timeout="")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDAO accountDAO;

    /**
     * 
     * @param out
     *            转出帐号
     * @param in
     *            转入帐号
     * @param money
     *            转账金额
     */
    @Override
    public void transfer(String out, String in, double money) {

        accountDAO.outMoney(out, money);
        int i = 1 / 0;// 异常会导致入账失败
        accountDAO.inMoney(in, money);
    }
}

  注:找到一篇关于Spring事务管理的写得挺好的文章:https://www.cnblogs.com/newsouls/p/3988216.html

 






以上是关于Spring事务管理的主要内容,如果未能解决你的问题,请参考以下文章

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

Spring boot:thymeleaf 没有正确渲染片段

What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]

使用 Git 来管理 Xcode 中的代码片段