Spring事务管理

来源:互联网 发布:mac vnc 编辑:程序博客网 时间:2024/06/14 14:37

Spring事务管理

事务

  • 含义:
    • 访问并可能更新数据库中各种数据项的一个程序执行单元(unit),是一个序列的对数据库的读/写的操作。
  • 目的:
    • 保证用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性。
    • 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
    • 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
  • 特性:
    • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
    • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
    • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
    • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

Spring事务管理

  • 方式
    • 编程式事务管理(精确控制事务边界)
    • 声明式事务管理(低耦合)
  • 事务管理器

    • DataSourceTransactionManager:用于Spring对JDBC抽象的支持,也可用于iBATIS进行持久化的场景。

      <!-- JDBC事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    <!-- 属性为引用了一个id为dataSource的bean-->    <property name="dataSource" ref="dataSource"/></bean>

      DataSourceTransactionManager通过调用java.sql.Connection来管理事务,后者是通过DataSource获取的,通过调用连接的commit()方法来提交事务。事务失败,则调用其rollback()方法进行回滚。

    • HibernateTransactionManager:用于对Hibernate进行持久化





      sessionFactory需要装配一个Hibernate的SessionFactory。
    • JpaTransactionManager:用于对Java持久化API进行持久化
    • JtaTransactionManager:用于分布式事务,如:跨多个事务资源(一个以上数据库)

一个转账示例(伪代码)

业务层的转账方法代码

public void transferMoney(String from, String to, double money) {    //调用持久层Dao类的相关方法    //outMoney():转出方法   inMoney():转入方法    accountDao.outMoney(from, money);    accountDao.inMoney(to, money);}   @Testpublic void testTransfer() {    //调用业务层的转账方法    accountService.transferMoney("lily", "lucy", 200d);}

上边这个转账方法就必须进行事务管理,要么成功,要么失败。但是,如果

public void transferMoney(String from, String to, double money) {    //调用持久层Dao类的相关方法    //outMoney():转出方法   inMoney():转入方法    accountDao.outMoney(from, money);    int i = 10 / 0;//这里出现异常    accountDao.inMoney(to, money);}

转账期间出现异常时,转账失败,造成钱转出,对方却没收到的情况,so,交给事务吧!
+ 采用编程式事务管理
+ 首先,我们需要使用Spring的TransactionTemplate来添加事务边界,TransactionTemplate提供了一种回调机制。在配置文件中注入TransactionTemplate

            <!-- 配置c3p0连接池 -->        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">            <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>        <!-- 加载jdbc的属性文件 -->        <context:property-placeholder location="classpath:db.properties"/>        <!-- 配置Dao层类 -->        <bean id="accountDao" class="com.mvbin.dao.AccountDaoImpl">            <property name="dataSource" ref="dataSource"/>        </bean>        <!-- 配置业务层类 -->        <bean id="accountService" class="com.mvbin.service.AccountServiceImpl">            <property name="accountDao" ref="accountDao"/>            <property name="transactionTemplate" ref="transactionTemplate"/>        </bean>        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">            <!-- 需要注入transactionManager -->            <property name="transactionManager" ref="transactionManager"/>        </bean>        <!-- JDBC事务管理器 -->        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">            <!-- 属性为引用了一个id为dataSource的bean-->            <property name="dataSource" ref="dataSource"/>        </bean> + 其次,修改`transferMoney()`        public void transferMoney(final String from, final String to, final double money) {            /*set注入transactionTemplate             * 调用execut(),传入TransactionCallback<T>对象,匿名内部类             *              */            transactionTemplate.execute(new TransactionCallback<String>() {                public String doInTransaction(TransactionStatus txStatus) {                    try {                        accountDao.outMoney(from, money);                        int i = 10 / 0;//异常                        accountDao.inMoney(to, money);                    } catch (RuntimeException e) {                        txStatus.setRollbackOnly();                        throw e;                    }                    return "";                }            });        }这种就是编程式事务管理了,对程序有较大的侵入性

+ 采用声明式事务管理:通过事务属性进行定义,通过AOP框架进行实现。
+ 事务属性
+ 传播行为
+ 隔离级别
+ 回滚规则
+ 事务超时
+ 是否只读
+ 使用tx命名空间配置事务

            <!-- 配置事务:配置所需管理器,配置事务属性 -->        <tx:advice id="txAdvice" transaction-manager="transactionManager">            <!-- 事务属性配置 -->            <!--                 name: 方法名称,可用通配符进行匹配一系列方法                isolation: 事务隔离级别                propagation: 事务传播行为                read-only: 事务是否只读                timeout: 定义事务超时时间                no-rollback-for:对于指定异常出现时不进行回滚                rollback-for: 对于指定异常出现时进行回滚             -->            <tx:attributes>                <!-- 以 transfer开头的方法需要在事务中运行-->                <tx:method name="transfer*" propagation="REQUIRED"/>                <!-- 其他方法,如果存在当前事务,就在事务中运行,否则不运行 -->                <tx:method name="*" propagation="SUPPORTS"/>            </tx:attributes>        </tx:advice>        <!-- 定义一个通知器 -->        <aop:config>            <!--                 advice-ref:应用一个通知                pointcut:定义一个切面,以AspectJ切入点表达式来声明通知器适用于com.mvbin.service.AccountService接口中的所有方法             -->            <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.mvbin.service.AccountService.*(..))"/>        </aop:config>+ 通过注解驱动配置事务            <!--             配置这样一句话即可,注解驱动会检查Spring上下文中所有的Bean,并找出使用@Transactional注解            的Bean,不管这个注解是在类上还是方法上,<tx:annotation-driven>会自动为其添加事务通知。            通知事务的属性通过@Transactional注解的参数来定义的。         -->        <tx:annotation-driven transaction-manager="transactionManager"/>业务类代码            @Transactional(propagation=Propagation.SUPPORTS, readOnly=true)        public class AccountServiceImpl implements AccountService {            private AccountDaoImpl accountDao;            private TransactionTemplate transactionTemplate;            @Transactional(propagation=Propagation.REQUIRED, readOnly=false)            public void transferMoney(final String from, final String to, final double money) {            //此处省略一万行代码。。。。            }
0 0