天天看点

Spring专题(十)-Spring之事务管理

1.事务简介

  事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。

  这里我们以取钱的例子来讲解:比如你去ATM机取1000块钱,大体有两个步骤:第一步输入密码金额,银行卡扣掉1000元钱;第二步从ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。

  如何保证这两个步骤不会出现一个出现异常了,而另一个执行成功呢?事务就是用来解决这样的问题。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 在企业级应用程序开发中,事务管理是必不可少的技术,用来确保数据的完整性和一致性。

2.事务四大特性(ACID)

①.原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。

②.一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。

③.隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

④.持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

3.PlatformTransactionManager 平台事务管理器–核心API

PlatformTransactionManager 事务管理器 Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,如上图所示,Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

我们进入到 PlatformTransactionManager 接口,查看源码:

Spring专题(十)-Spring之事务管理

①.TransactionStatus getTransaction(TransactionDefinition definition) ,事务管理器 通过TransactionDefinition,获得“事务状态”,从而管理事务。

②.void commit(TransactionStatus status) 根据状态提交

③.void rollback(TransactionStatus status) 根据状态回滚

也就是说Spring事务管理的为不同的事务API提供一致的编程模型,具体的事务管理机制由对应各个平台去实现。

比如下面我们导入实现事务管理的两种平台:JDBC和Hibernate、Mybatis.

Spring专题(十)-Spring之事务管理

然后我们再次查看PlatformTransactionManager接口,会发现它多了几个实现类,如下:

Spring专题(十)-Spring之事务管理

Ctrl+shift+T:打开

Spring专题(十)-Spring之事务管理

Ctrl+T:打开类的集成结构树:

Spring专题(十)-Spring之事务管理

3.基本事务属性的定义

那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:

Spring专题(十)-Spring之事务管理
3.1 传播行为(面试题)

传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。事务传播的策略!

Spring 定义了如下七中传播行为,这里以A业务和B业务之间如何传播事务为例说明:

B包容A

①、PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。

②、PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。

③、PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。

④、PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。

⑤、PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。

⑥、PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。

⑦、PROPAGATION_NESTED :nested ,嵌套。如果A当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

Spring专题(十)-Spring之事务管理

3.2 隔离级别

隔离级别:定义了一个事务可能受其他并发事务影响的程度。

  • 并发事务引起的问题:

    在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下的问题。

    ①、脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。

    ②、不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。

    ③、幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

    注意:不可重复读重点是修改,而幻读重点是新增或删除。

  • 在 Spring 事务管理中,为我们定义了如下的隔离级别:

    ①、ISOLATION_DEFAULT:使用后端数据库默认的隔离级别(不同的数据隔离级别不同)

    ②、ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读

    ③、ISOLATION_READ_COMMITTED(Oracle):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

    ④、ISOLATION_REPEATABLE_READ(mysql):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生

    ⑤、ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的。

    上面定义的隔离级别,在 Spring 的 TransactionDefinition.class 中也分别用常量 -1,0,1,2,4,8表示。比如 ISOLATION_DEFAULT 的定义:

    Spring专题(十)-Spring之事务管理
    Spring专题(十)-Spring之事务管理

3.3只读

这是事务的第三个特性,是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。

Spring会管理事务,但是查询一般都设置成只读事务,性能会高!

3.4 事务超时

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

3.5 回滚规则

事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的) 。但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

可以指定何种类型的异常西是否需要回滚撤销!!

4. Spring 编程式事务和声明式事务的区别

Spring专题(十)-Spring之事务管理
  • 编程式事务处理:

    所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

  • 声明式事务处理:

    管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

    简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

4.1 转账案例(不用事务实现转账)

我们还是以转账为实例。不用事务看如何实现转账。在数据库中有如下表 account ,内容如下:

Spring专题(十)-Spring之事务管理

创建项目

  • 项目名: spring-tx
  • 引入jar包
    Spring专题(十)-Spring之事务管理
  • 引入日志: log4j.properties
  • 复制数据库配置:db.properties
  • 创建数据库

    在springjdbc库中直接创建表

/*Table structure for table `ar_account` */

CREATE TABLE `ar_account` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(20) NOT NULL,
  `money` DECIMAL(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `ar_account` */

INSERT  INTO `ar_account`(`id`,`username`,`money`) VALUES (1,'cat','1000.00');
INSERT  INTO `ar_account`(`id`,`username`,`money`) VALUES (2,'Tom','1000.00');      

创建dao接口

public interface AccountDao {

  /**
   * 加钱方法
   * 
   * @param id
   * @param money
   */
  void increaseMoney(Integer id, Double money);

  /**
   * 减钱方法
   * 
   * @param id
   * @param money
   */
  void decreaseMoney(Integer id, Double money);
}      

创建dao实现类

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public void increaseMoney(Integer id, Double money) {
    jdbcTemplate.update("update ar_account set money = money + ? where id = ? ;", money, id);
  }

  public void decreaseMoney(Integer id, Double money) {
    jdbcTemplate.update("update ar_account set money = money - ? where id = ? ;", money, id);
  }
}      

创建service接口

public interface AccountService {

  // 转账业务
  void transfer(Integer from, Integer to, Double money);
}      

创建service实现类

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

  @Autowired
  private AccountDao accountDao;

  public void transfer(Integer from, Integer to, Double money) {
    accountDao.decreaseMoney(from, money);
    accountDao.increaseMoney(to, money);
  }
}      

添加ioc配置文件

先不处理事务问题

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

  <context:component-scan base-package="com.bruceliu.*" />
  <context:property-placeholder location="db.properties" />

  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    p:driverClass="${jdbc.driverClass}" p:jdbcUrl="${jdbc.jdbcUrl}"
    p:user="${jdbc.user}" p:password="${jdbc.password}" />

  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>

</beans>      

测试代码:

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService service = (AccountService) context.getBean("accountService");
    service.transfer(1, 2, 100.0);
  }      
4.2 编程式的事务管理(缺点)

在实际应用中,很少需要通过编程来进行事务管理,即便如此,Spring还是为编程式事务管理提供了模板类 org.springframework.transaction.support.TransactionTemplate,以满足一些特殊场合的需求!以满足

一些特殊场合的需求.

TransactionTemplate和那些持久化模板类一样是线程安全的,因此,可以在多个业务类中共享TranscationTemplate实例进行事务管理.TransactionTemplate拥有多个设置事务的属性方法.如setReadOnly(boolean only), setIsolationLevel(int isolationLevel)等.

TransactionTemplate有两个主要方法:

void setTransactionManager(PlatformTransactionManager transactionManager) 设置事务管理器.

Object execute(TransactionCallback action): 在TransactionCallback回调接口中定义需要以事务方式组织的数据访问逻辑.

TransactionCallback 接口只有一个方法: Object doInTransaction(TransactionStatus status).如果操作不会返回结果,则可以使用TransactionCallback的子接口 TransactionCallbackWithoutResult.

配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

  <context:component-scan base-package="com.bruceliu.*" />

  <context:property-placeholder location="db.properties" />

  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    p:driverClass="${jdbc.driverClass}" p:jdbcUrl="${jdbc.jdbcUrl}"
    p:user="${jdbc.user}" p:password="${jdbc.password}" />


  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>


  <!-- 平台事务管理器 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>



  <!-- 配置事务管理类 -->
  <bean id="template"
    class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager" />
  </bean>

</beans>      

测试代码

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    
  @Autowired
  private AccountDao accountDao;

  @Autowired
  TransactionTemplate template;

  public void transfer(final Integer from, final Integer to, final Double money) {
    // 事务管管理类
    template.execute(new TransactionCallbackWithoutResult() {
      @Override
      protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
        accountDao.decreaseMoney(from, money);
        //int i = 1/0;
        accountDao.increaseMoney(to, money);
      }
    });
  }
}      
4.3 配置声明事务(基于AOP的 xml 配置)

4.3.1 TransactionManager

在不同平台,操作事务的代码各不相同,因此spring提供了一个 TransactionManager 接口:

  • DateSourceTransactionManager 用于 JDBC 的事务管理
    Spring专题(十)-Spring之事务管理
  • HibernateTransactionManager 用于 Hibernate 的事务管理
  • JpaTransactionManager 用于 Jpa 的事务管理
    Spring专题(十)-Spring之事务管理

4.3.2 接口的定义

事务的属性介绍:这里定义了传播行为、隔离级别、超时时间、是否只读

package org.springframework.transaction;
public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0; //支持当前事务,如果不存在,就新建一个
    int PROPAGATION_SUPPORTS = 1; //支持当前事务,如果不存在,就不使用事务
    int PROPAGATION_MANDATORY = 2; //支持当前事务,如果不存在,就抛出异常
    int PROPAGATION_REQUIRES_NEW = 3;//如果有事务存在,挂起当前事务,创建一个新的事物
    int PROPAGATION_NOT_SUPPORTED = 4;//以非事务方式运行,如果有事务存在,挂起当前事务
    int PROPAGATION_NEVER = 5;//以非事务方式运行,如果有事务存在,就抛出异常
    int PROPAGATION_NESTED = 6;//如果有事务存在,则嵌套事务执行
    
    int ISOLATION_DEFAULT = -1;//默认级别,MYSQL: 默认为REPEATABLE_READ级别 SQLSERVER: 默认为READ_COMMITTED
    int ISOLATION_READ_UNCOMMITTED = 1;//读取未提交数据(会出现脏读, 不可重复读) 基本不使用
    int ISOLATION_READ_COMMITTED = 2;//读取已提交数据(会出现不可重复读和幻读)
    int ISOLATION_REPEATABLE_READ = 4;//可重复读(会出现幻读)
    int ISOLATION_SERIALIZABLE = 8;//串行化
    
    int TIMEOUT_DEFAULT = -1;//默认是-1,不超时,单位是秒

    //事务的传播行为
    int getPropagationBehavior();
    //事务的隔离级别
    int getIsolationLevel();
    //事务超时时间
    int getTimeout();
    //是否只读
    boolean isReadOnly();
    String getName();
}      

4.3.3 添加tx命名空间

事务基础组件,对 DAO 的支持

修改 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

  

</beans>      
4.3.4 添加事务相关配置

修改applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

  <context:component-scan base-package="com.bruceliu.*" />
  <context:property-placeholder location="db.properties" />

  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    p:driverClass="${jdbc.driverClass}" p:jdbcUrl="${jdbc.jdbcUrl}"
    p:user="${jdbc.user}" p:password="${jdbc.password}" />


  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>


  <!-- 平台事务管理器 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <!-- 通知 -->
  <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <!-- 传播行为 -->
      <!-- REQUIRED:如果有事务,则在事务中执行;如果没有事务,则开启一个新的事物 -->
      <tx:method name="save*" propagation="REQUIRED"/>
      <tx:method name="insert*" propagation="REQUIRED" />
      <tx:method name="add*" propagation="REQUIRED" />
      <tx:method name="create*" propagation="REQUIRED" />
      <tx:method name="delete*" propagation="REQUIRED" />
      <tx:method name="update*" propagation="REQUIRED" />
      <tx:method name="transfer" propagation="REQUIRED" />
      <!-- SUPPORTS:如果有事务,则在事务中执行;如果没有事务,则不会开启事物 -->
      <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
      <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
      <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
    </tx:attributes>
  </tx:advice>

  <aop:config>
    <aop:pointcut id="txPointCut"
      expression="execution(* com.bruceliu.service.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
  </aop:config>

</beans>      

配置介绍:

tx:advice 是用于配置事务相关信息, transaction-manager属性是引入对应类型的事务管理;

jdbc/mybatias : DataSourceTransactionManager

hibernate: HibernateTransactionManager

JPA:JPATransactionManager

tx:attributes 此标签所配置的是 哪些方法可以作为事务方法(为后面切点进行补充)

tx:method 标签设置具体要添加事务的方法和其他属性

name 是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符*可以用来指定一批关联到相同的事务属性的方法。如:‘get*’、‘handle*’、'on*Event’等等.

propagation 不是必须的 ,默认值是REQUIRED 表示事务传播行为, 包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED

isolation 不是必须的 默认值DEFAULT

timeout 不是必须的 默认值-1(永不超时) 表示事务超时的时间(以秒为单位)

read-only 不是必须的 默认值false不是只读的 表示事务是否只读?

rollback-for 不是必须的 表示将被触发进行回滚的 Exception(s);以逗号分开。 如:‘com.itqf.MyBusinessException,ServletException’

no-rollback-for 不是必须的 表示不被触发进行回滚的 Exception(s);以逗号分开。 如:‘com.foo.MyBusinessException,ServletException’

aop:config标签 设置事务的切点,配置参与事务的类和对应的方法.

注意: aop:config和tx:advice 但是两者并不冲突, aop:config面向切面编程的切点,选择对应的方法进行切入,而tx:adivce是设置事务的相关的属性和描述,换句话说,aop:config选择了对应的切入点,tx:advice是在这些切入点上根据 method name属性再次进行筛选!!!

4.4 配置声明事务(基于AOP的 注解 配置)

除了基于XML的事务配置,Spring还提供了基于注解的事务配置,即通过@Transactional对需要事务增强的Bean接口,实现类或者方法进行标注,在容器中配置基于注解的事务增强驱动,即可启用注解的方式声明事务!

4.4.1 使用@Transactional注解

顺着原来的思路,使用@Transactional对基于aop/tx命名空间的事务配置进行改造!

  • 修改service类添加@Transactional注解
//对业务类进行事务增强的标注
@Transactional
@Service("accountService")
public class AccountServiceImpl implements AccountService {
  @Autowired
  private AccountDao accountDao;
  public void transfer(final Integer from, final Integer to, final Double money) {
    accountDao.decreaseMoney(from, money);
    int i = 1 / 0;
    accountDao.increaseMoney(to, money);
  }
}      

因为注解本身具有一组默认的事务属性,所以往往只要在需要事务的业务类中添加一个@Transactional注解,就完成了业务类事务属性的配置!

当然,注解只能提供元数据,它本身并不能完成事务切面织入的功能.因此,还需要在Spring的配置中通过一行配置’通知’Spring容器对标注@Transactional注解的Bean进行加工处理!

配置:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

  <context:component-scan base-package="com.bruceliu.*" />

  <context:property-placeholder location="db.properties" />

  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    p:driverClass="${jdbc.driverClass}" p:jdbcUrl="${jdbc.jdbcUrl}"
    p:user="${jdbc.user}" p:password="${jdbc.password}" />

  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <!-- 平台事务管理器 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <!--对标注@Transactional注解的Bean进行加工处理,以织入事物管理切面 -->
  <tx:annotation-driven transaction-manager="transactionManager" />

</beans>      

在默认情况, <tx:annotation-driven /> 中transaction-manager属性会自动使用名为 “transactionManager” 的事务管理器.所以,如果用户将事务管理器的id定义为 transactionManager , 则可以进一步将①处的配置简化为 <tx:annotation-driven />.

使用以上测试用例即可使用以上测试用例即可

4.4.2 @Transactional其他方面介绍
  • 关于@Transactional的属性

    基于@Transactional注解的配置和基于xml的配置一样,它拥有一组普适性很强的默认事务属性,往往可以直接使用默认的属性.

  • 事务传播行为: PROPAGATION_REQUIRED.
  • 事务隔离级别: ISOLATION_DEFAULT.
  • 读写事务属性:读/写事务.
  • 超时时间:依赖于底层的事务系统默认值
  • 回滚设置:任何运行期异常引发回滚,任何检查型异常不会引发回滚.

    默认值可能适应大部分情况,但是我们依然可以可以自己设定属性,具体属性表如下:

    Spring专题(十)-Spring之事务管理
  • 在何处标注@Transactional注解

    @Transactional注解可以直接用于接口定义和接口方法,类定义和类的public方法上.

    但Spring建议在业务实现类上使用@Transactional注解,当然也可以添加到业务接口上,但是这样会留下一些容易被忽视的隐患,因为注解不能被继承,所以业务接口中标注的

    @Transactional注解不会被业务类实现继承.

  • 在方法出使用注解

    方法出添加注解会覆盖类定义处的注解,如果有写方法需要使用特殊的事务属性,则可以在类注解的基础上提供方法注解,如下:

@Transactional
@Service("accountService")
public class AccountServiceImpl implements AccountService {
  @Autowired
  private AccountDao accountDao;

  //不等于默认值!可以覆盖类注解
  @Transactional(readOnly = false , isolation = Isolation.READ_COMMITTED)
  public void transfer(final Integer from, final  Integer to, final  Double money) {

       accountDao.decreaseMoney(from,money);

      // int i = 1/0;

       accountDao.increaseMoney(to,money);

  }
}      
  • 使用不同的事务管理器

一般情况下,一个应用仅需要使用一个事务管理器.如果希望在不同的地方使用不同的事务管理,@Transactional注解同样支持!

实现代码:

@Transactional("事务管理器的名字") //此处添加指定事务管理器的名字
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(final Integer from, final  Integer to, final  Double money) {

         accountDao.decreaseMoney(from,money);

        // int i = 1/0;

         accountDao.increaseMoney(to,money);

    }
}      

对应事务查找事务管理器的名字应该在xml中进行定义!如下:

<!--声明一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource"/>
          <qualifier value="定义事务管理器的名字,可以被注解查找" />
</bean>      

附录:Spring模块介绍

GroupId ArtifactId 说明
org.springframework spring-beans Beans 支持,包含 Groovy
org.springframework spring-aop 基于代理的AOP支持
org.springframework spring-aspects 基于AspectJ 的切面
org.springframework spring-context 应用上下文运行时,包括调度和远程抽象
org.springframework spring-context-support 支持将常见的第三方类库集成到 Spring 应用上下文
org.springframework spring-core 其他模块所依赖的核心模块
org.springframework spring-expression Spring 表达式语言,SpEL
org.springframework spring-instrument JVM 引导的仪表(监测器)代理
org.springframework spring-instrument-tomcat Tomcat 的仪表(监测器)代理
org.springframework spring-jdbc 支持包括数据源设置和 JDBC 访问支持
org.springframework spring-jms 支持包括发送/接收JMS消息的助手类
org.springframework spring-messaging 对消息架构和协议的支持
org.springframework spring-orm 对象/关系映射,包括对 JPA 和 Hibernate 的支持
org.springframework spring-oxm 对象/XML 映射(Object/XML Mapping,OXM)
org.springframework spring-test 单元测试和集成测试支持组件
org.springframework spring-tx 事务基础组件,包括对 DAO 的支持及 JCA 的集成
org.springframework spring-web web支持包,包括客户端及web远程调用
org.springframework spring-webmvc REST web 服务及 web 应用的 MVC 实现
org.springframework spring-webmvc-portlet 用于 Portlet 环境的MVC实现
org.springframework