Spring4笔记9--Spring的事务管理(AOP应用的例子)

Spring的事务管理:html

  事务本来是数据库中的概念,在 Dao 层。但通常状况下,须要将事务提高到业务层,即 Service 层。这样作是为了可以使用事务的特性来管理具体的业务。  
  在 Spring 中一般能够经过如下三种方式来实现对事务的管理:
    (1)使用 Spring 的事务代理工厂管理事务
    (2)使用 Spring 的事务注解管理事务
    (3)使用 AspectJ 的 AOP 配置管理事务

java

  Spring事务管理API:程序员

    Spring 的事务管理,主要用到两个事务相关的接口。spring

    (1)事务管理器接口
        事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。查看 SpringAPI 帮助文档:Spring 框架解压目录下的docs/javadoc-api/index.html。 sql

Modifier and Type Method and Description
void commit(TransactionStatus status)
Commit the given transaction, with regard to its status.
TransactionStatus getTransaction(TransactionDefinition definition)
Return a currently active transaction or create a new one, according to the specified propagation behavior.
void rollback(TransactionStatus status)
Perform a rollback of the given transaction.

      A、经常使用的两个实现类
        PlatformTransactionManager 接口有两个经常使用的实现类:  
          DataSourceTransactionManager:使用 JDBC 或 iBatis  进行持久化数据时使用。  
          HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。
      B、Spring 的回滚方式
        Spring 事务的默认回滚方式是:发生运行时异常时回滚,发生受查异常时提交。不过,对于受查异常,程序员也能够手工设置其回滚方式数据库

    (2)事务定义接口:
      事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别事务传播行为事务默认超时时限,及对它们的操做express

      A、定义了五个事务隔离级别常量:
        这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。
          DEFAULT:采用DB默认的事务隔离级别。MySql的默认为REPEATABLE_READ;  Oracle默认为 READ_COMMITTED。
          READ_UNCOMMITTED:读未提交。未解决任何并发问题。
          READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。  
          REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读  
          SERIALIZABLE:串行化。不存在并发问题。api

      B、定义了七个事务传播行为常量
        所谓事务传播行为是指,处于不一样事务中的方法在相互调用时,执行期间事务的维护状况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护状况,就称为事务传播行为。事务传播行为是加在方法上的。  
事务传播行为常量都是以 PROPAGATION_  开头,形如 PROPAGATION_XXX。  
          REQUIRED:指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则建立一个新事务。这种传播行为是最多见的选择,也是Spring 默认的事务传播行为。  
          如该传播行为加在 doOther()方法上。若 doSome()方法在执行时就是在事务内的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法没有在事务内执行,则 doOther()方法会建立一个事务,并在其中执行。 数组

          SUPPORTS:指定的方法支持当前事务,但若当前没有事务,也能够以非事务方式执行。  并发

          MANDATORY:指定的方法必须在当前事务内执行,若当前没有事务,则直接抛出异常。

          REQUIRES_NEW:老是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。

          NOT_SUPPORTED:指定的方法不能在事务环境中执行,若当前存在事务,就将当前事务挂起。

          NEVER:指定的方法不能在事务环境下执行,若当前存在事务,就直接抛出异常。

          NESTED:指定的方法必须在事务内执行。若当前存在事务,则在嵌套事务内执行;若当前没有事务,则建立一个新事务。

      C、定义了默认事务超时时限
        常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,及不支持事务超时时限设置的 none 值。
        注意,事务的超时时限起做用的条件比较多,且超时的时间计算点较复杂。因此,该值通常就使用默认值便可。

 

  Spring事务代码详解:

    要求:实现模拟购买股票。存在两个实体:银行帐户 Account 与股票帐户 Stock。当要购买股票时,须要从 Account 中扣除相应金额的存款,而后在 Stock 中增长相应的股票数量。而在这个过程当中,可能会抛出一个用户自定义的异常。异常的抛出,将会使两个操做回滚。

    Step1:建立数据库表 account、stock
    Step2:建立实体类 Account 与 Stock (略)
    Step3:定义 Dao 接口  IAccountDao 与 IStockDao

1 package com.tongji.dao;
2 
3 public interface IAccountDao {
4 
5     void insertAccount(String aname, double money);
6 
7     void updateAccount(String aname, double money, boolean isBuy);
8 
9 }
1 package com.tongji.dao;
2 
3 public interface IStockDao {
4 
5     void insertStock(String sname, int amount);
6 
7     void updateStock(String sname, int amount, boolean isBuy);
8 
9 }

    Step4:定义 Dao 实现类 AccountDaoImpl 与 StockDaoImpl

 1 package com.tongji.dao;
 2 
 3 import org.springframework.jdbc.core.support.JdbcDaoSupport;
 4 
 5 public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
 6 
 7     @Override
 8     public void insertAccount(String aname, double money) {
 9         String sql = "insert into account(aname, balance) values(?,?)";
10         this.getJdbcTemplate().update(sql, aname, money);
11     }
12 
13     @Override
14     public void updateAccount(String aname, double money, boolean isBuy) {
15         String sql = "update account set balance=balance+? where aname=?";
16         if (isBuy) {
17             sql = "update account set balance=balance-? where aname=?";
18         }
19         this.getJdbcTemplate().update(sql, money, aname);
20         
21     }
22 
23 }
 1 package com.tongji.dao;
 2 
 3 import org.springframework.jdbc.core.support.JdbcDaoSupport;
 4 
 5 public class StockDaoImpl extends JdbcDaoSupport implements IStockDao {
 6 
 7     @Override
 8     public void insertStock(String sname, int amount) {
 9         String sql = "insert into stock(sname, count) values (?,?)";
10         this.getJdbcTemplate().update(sql , sname, amount);
11     }
12 
13     @Override
14     public void updateStock(String sname, int amount, boolean isBuy) {
15         //isBuy为true,则表示购买股票,此时应增长股票帐户中的股票数量
16         String sql = "update stock set count=count-? where sname=?";
17         if (isBuy) {
18             sql = "update stock set count=count+? where sname=?";
19         }
20         this.getJdbcTemplate().update(sql, amount, sname);
21     }
22 
23 }

    Step5:定义异常类 StockException

 1 package com.tongji.beans;
 2 
 3 public class StockException extends Exception {
 4     private static final long serialVersionUID = 5377570098437361228L;
 5 
 6     public StockException() {
 7         super();
 8     }
 9 
10     public StockException(String message) {
11         super(message);
12     }
13     
14 }

    Step6:定义 Service 接口 IStockProcessService

1 package com.tongji.service;
2 
3 public interface IStockProcessService {
4     void openAccount(String aname, double money);
5     void openStock(String sname, int amount);
6     void buyStock(String aname, double money, String sname, int amount);
7 }

    Step7:定义 service 的实现类 StockProcessServiceImpl

 1 package com.tongji.service;
 2 
 3 import org.springframework.transaction.annotation.Isolation;
 4 import org.springframework.transaction.annotation.Propagation;
 5 import org.springframework.transaction.annotation.Transactional;
 6 
 7 import com.tongji.beans.StockException;
 8 import com.tongji.dao.IAccountDao;
 9 import com.tongji.dao.IStockDao;
10 
11 public class StockProcessServiceImpl implements IStockProcessService{
12      private IAccountDao accountDao;
13      private IStockDao stockDao;
14     
15     public void setAccountDao(IAccountDao accountDao) {
16         this.accountDao = accountDao;
17     }
18 
19     public void setStockDao(IStockDao stockDao) {
20         this.stockDao = stockDao;
21     }
22 
23     @Override
24     public void openAccount(String aname, double money) {
25         accountDao.insertAccount(aname, money);
26     }
27 
28     @Override
29     public void openStock(String sname, int amount) {
30         stockDao.insertStock(sname, amount);
31     }
32 
33     @Override
34     public void buyStock(String aname, double money, String sname, int amount) throws StockException {
35         boolean isBuy = true;
36         accountDao.updateAccount(aname, money, isBuy);
37         //故意抛出异常
38         if (true) {
39             throw new StockException("购买股票异常");
40         }
41         stockDao.updateStock(sname, amount, isBuy);
42     }
43 
44 }

    Step8:定义Spring 配置文件 

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
 5         http://www.springframework.org/schema/beans 
 6         http://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://www.springframework.org/schema/context 
 8         http://www.springframework.org/schema/context/spring-context.xsd">
 9     
10     <!-- 注册数据源:C3P0数据源 -->
11     <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
12         <property name="driverClass" value="${jdbc.driverClass}" />
13         <property name="jdbcUrl" value="${jdbc.url}" />
14         <property name="user" value="${jdbc.user}" />
15         <property name="password" value="${jdbc.password}" />
16     </bean>
17     
18     <!-- 注册JDBC属性文件 -->
19     <context:property-placeholder location="classpath:jdbc.properties"/>
20     
21     <!-- 注册Dao -->
22     <bean id="accountDao" class="com.tongji.dao.AccountDaoImpl">
23         <property name="dataSource" ref="myDataSource"/>
24     </bean>
25     <bean id="stockDao" class="com.tongji.dao.StockDaoImpl">
26         <property name="dataSource" ref="myDataSource"/>
27     </bean>
28     <!-- 注册Service -->
29     <bean id="stockService" class="com.tongji.service.StockProcessServiceImpl">
30         <property name="accountDao" ref="accountDao"/>
31         <property name="stockDao" ref="stockDao"/>
32     </bean>    
33 </beans>

    Step9:测试类

 1 package com.tongji.test;
 2 
 3 import org.junit.Before;
 4 import org.junit.Test;
 5 import org.springframework.context.ApplicationContext;
 6 import org.springframework.context.support.ClassPathXmlApplicationContext;
 7 
 8 import com.tongji.service.IStockProcessService;
 9 
10 public class MyTest {
11     
12     private IStockProcessService service;
13 
14     @Before
15     public void before() {
16         //建立容器
17         @SuppressWarnings("resource")
18         ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
19         service = (IStockProcessService) ac.getBean("stockService");
20     }
21     
22     @Test
23     public void testOpen() {
24         service.openAccount("张三", 10000);
25         service.openStock("华为", 5);
26     }
27     
28     @Test
29     public void testBuyStock() {
30         service.buyStock("张三", 2000, "华为", 5);
31     }
32     
33 }

    此配置文件没有采用事务管理,因此购买股票的时候,出现了数据库中帐户金额减小了,可是股票数目没有增长的不一致状况。

  

  使用 Spring 的事务代理工厂管理事务:

    该方式是,须要为目标类,即 Service 的实现类建立事务代理。事务代理使用的类是TransactionProxyFactoryBean,该类须要初始化以下一些属性:
      (1)transactionManager:事务管理器
      (2)target:目标对象,即 Service 实现类对象
      (3)transactionAttributes:事务属性设置
      对于 XML 配置代理方式实现事务管理时,受查异常的回滚方式,程序员能够经过如下方式进行设置:经过“-异常”方式,可以使发生指定的异常时事务回滚;经过“+异常”方式,可以使发生指定的异常时事务提交。

    修改Spring配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
 5         http://www.springframework.org/schema/beans 
 6         http://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://www.springframework.org/schema/context 
 8         http://www.springframework.org/schema/context/spring-context.xsd">
 9     
10     <!-- 注册数据源:C3P0数据源 -->
11     <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
12         <property name="driverClass" value="${jdbc.driverClass}" />
13         <property name="jdbcUrl" value="${jdbc.url}" />
14         <property name="user" value="${jdbc.user}" />
15         <property name="password" value="${jdbc.password}" />
16     </bean>
17     
18     <!-- 注册JDBC属性文件 -->
19     <context:property-placeholder location="classpath:jdbc.properties"/>
20     
21     <!-- 注册Dao -->
22     <bean id="accountDao" class="com.tongji.dao.AccountDaoImpl">
23         <property name="dataSource" ref="myDataSource"/>
24     </bean>
25     <bean id="stockDao" class="com.tongji.dao.StockDaoImpl">
26         <property name="dataSource" ref="myDataSource"/>
27     </bean>
28     <!-- 注册Service -->
29     <bean id="stockService" class="com.tongji.service.StockProcessServiceImpl">
30         <property name="accountDao" ref="accountDao"/>
31         <property name="stockDao" ref="stockDao"/>
32     </bean>    
33     
34     <!-- 事务 -->
35     <!-- 注册事务管理器 -->
36     <bean id="myTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
37         <property name="dataSource" ref="myDataSource"/>
38     </bean>
39     <!-- 生成事务代理 -->
40     <bean id="stockServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
41         <property name="transactionManager" ref="myTxManager"/>
42         <property name="target" ref="stockService"/>
43         <property name="transactionAttributes">
44             <props>
45                 <prop key="open*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
46                 <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException</prop>
47             </props>
48         </property>
49     </bean>
50 </beans>

    因为本项目使用的是 JDBC 进行持久化,因此使用 DataSourceTransactionManager 类做为事务管理器。

    修改测试类:

     public void before() {
           //建立容器
           @SuppressWarnings("resource")
           ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
           service = (IStockProcessService) ac.getBean("stockServiceProxy");
       }

 

  使用 Spring 的事务注解管理事务:
    经过@Transactional 注解方式,也可将事务织入到相应方法中。而使用注解方式,只需在配置文件中加入一个 tx 标签,以告诉 spring 使用注解来完成事务的织入。该标签只需指定一个属性,事务管理器。

    修改Spring配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xmlns:context="http://www.springframework.org/schema/context"
 5         xmlns:aop="http://www.springframework.org/schema/aop"
 6         xmlns:tx="http://www.springframework.org/schema/tx" 
 7         xsi:schemaLocation="
 8         http://www.springframework.org/schema/beans 
 9         http://www.springframework.org/schema/beans/spring-beans.xsd
10         http://www.springframework.org/schema/context 
11         http://www.springframework.org/schema/context/spring-context.xsd
12         http://www.springframework.org/schema/tx 
13         http://www.springframework.org/schema/tx/spring-tx.xsd
14         http://www.springframework.org/schema/aop 
15         http://www.springframework.org/schema/aop/spring-aop.xsd">
16     
17     <!-- 注册数据源:C3P0数据源 -->
18     <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
19         <property name="driverClass" value="${jdbc.driverClass}" />
20         <property name="jdbcUrl" value="${jdbc.url}" />
21         <property name="user" value="${jdbc.user}" />
22         <property name="password" value="${jdbc.password}" />
23     </bean>
24     
25     <!-- 注册JDBC属性文件 -->
26     <context:property-placeholder location="classpath:jdbc.properties"/>
27     
28     <!-- 注册Dao -->
29     <bean id="accountDao" class="com.tongji.dao.AccountDaoImpl">
30         <property name="dataSource" ref="myDataSource"/>
31     </bean>
32     <bean id="stockDao" class="com.tongji.dao.StockDaoImpl">
33         <property name="dataSource" ref="myDataSource"/>
34     </bean>
35     <!-- 注册Service -->
36     <bean id="stockService" class="com.tongji.service.StockProcessServiceImpl">
37         <property name="accountDao" ref="accountDao"/>
38         <property name="stockDao" ref="stockDao"/>
39     </bean>    
40     
41     <!-- 事务 -->
42     <!-- 注册事务管理器 -->
43     <bean id="myTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
44         <property name="dataSource" ref="myDataSource"/>
45     </bean>
46     <!-- 开启注解驱动 -->
47     <tx:annotation-driven transaction-manager="myTxManager"/>
48 </beans>

    修改 service 的实现类 StockProcessServiceImpl:

 1 package com.tongji.service;
 2 
 3 import org.springframework.transaction.annotation.Isolation;
 4 import org.springframework.transaction.annotation.Propagation;
 5 import org.springframework.transaction.annotation.Transactional;
 6 
 7 import com.tongji.beans.StockException;
 8 import com.tongji.dao.IAccountDao;
 9 import com.tongji.dao.IStockDao;
10 
11 public class StockProcessServiceImpl implements IStockProcessService{
12      private IAccountDao accountDao;
13      private IStockDao stockDao;
14     
15     public void setAccountDao(IAccountDao accountDao) {
16         this.accountDao = accountDao;
17     }
18 
19     public void setStockDao(IStockDao stockDao) {
20         this.stockDao = stockDao;
21     }
22 
23     @Override
24     @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED)
25     public void openAccount(String aname, double money) {
26         accountDao.insertAccount(aname, money);
27     }
28 
29     @Override
30     @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED)
31     public void openStock(String sname, int amount) {
32         stockDao.insertStock(sname, amount);
33     }
34 
35     @Override
36     @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, rollbackFor=StockException.class)
37     public void buyStock(String aname, double money, String sname, int amount) throws StockException {
38         boolean isBuy = true;
39         accountDao.updateAccount(aname, money, isBuy);
40         if (true) {
41             throw new StockException("购买股票异常");
42         }
43         stockDao.updateStock(sname, amount, isBuy);
44     }
45 
46 }

    @Transactional 的全部可选属性以下所示:
      propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
      isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
      readOnly:用于设置该方法对数据库的操做是不是只读的。该属性为 boolean,默认值为 false。
      timeout:用于设置本操做与数据库链接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
      rollbackFor:指定须要回滚的异常类。类型为 Class[],默认值为空数组。固然,若只有一个异常类时,能够不使用数组。
      rollbackForClassName:指定须要回滚的异常类类名。类型为 String[],默认值为空数组。固然,若只有一个异常类时,能够不使用数组。
      noRollbackFor:指定不须要回滚的异常类。类型为 Class[],默认值为空数组。固然,若只有一个异常类时,能够不使用数组。
      noRollbackForClassName:指定不须要回滚的异常类类名。类型为 String[],默认值为空数组。固然,若只有一个异常类时,能够不使用数组。
     须要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其余非public 方法,若是加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。由于 Spring 会忽略掉全部非 public 方法上的@Transaction 注解。  
    若@Transaction 注解在类上,则表示该类上全部的方法均将在执行时织入事务。

  使用 AspectJ 的 AOP 配置管理事务(重点):

    使用 XML 配置事务代理的方式的不足是,每一个目标类都须要配置事务代理。当目标类较多,配置文件会变得很是臃肿。使用 XML 配置顾问方式能够自动为每一个符合切入点表达式的类生成事务代理。

    修改Spring配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xmlns:context="http://www.springframework.org/schema/context"
 5         xmlns:aop="http://www.springframework.org/schema/aop"
 6         xmlns:tx="http://www.springframework.org/schema/tx" 
 7         xsi:schemaLocation="
 8         http://www.springframework.org/schema/beans 
 9         http://www.springframework.org/schema/beans/spring-beans.xsd
10         http://www.springframework.org/schema/context 
11         http://www.springframework.org/schema/context/spring-context.xsd
12         http://www.springframework.org/schema/tx 
13         http://www.springframework.org/schema/tx/spring-tx.xsd
14         http://www.springframework.org/schema/aop 
15         http://www.springframework.org/schema/aop/spring-aop.xsd">
16     
17     <!-- 注册数据源:C3P0数据源 -->
18     <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
19         <property name="driverClass" value="${jdbc.driverClass}" />
20         <property name="jdbcUrl" value="${jdbc.url}" />
21         <property name="user" value="${jdbc.user}" />
22         <property name="password" value="${jdbc.password}" />
23     </bean>
24     
25     <!-- 注册JDBC属性文件 -->
26     <context:property-placeholder location="classpath:jdbc.properties"/>
27     
28     <!-- 注册Dao -->
29     <bean id="accountDao" class="com.tongji.dao.AccountDaoImpl">
30         <property name="dataSource" ref="myDataSource"/>
31     </bean>
32     <bean id="stockDao" class="com.tongji.dao.StockDaoImpl">
33         <property name="dataSource" ref="myDataSource"/>
34     </bean>
35     <!-- 注册Service -->
36     <bean id="stockService" class="com.tongji.service.StockProcessServiceImpl">
37         <property name="accountDao" ref="accountDao"/>
38         <property name="stockDao" ref="stockDao"/>
39     </bean>    
40     
41     <!-- 事务 -->
42     <!-- 注册事务管理器 -->
43     <bean id="myTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
44         <property name="dataSource" ref="myDataSource"/>
45     </bean>
46     <!-- 注册事务通知 -->
47     <tx:advice id="txAdvice" transaction-manager="myTxManager">
48         <tx:attributes>
49             <!-- 指定在链接点方法上应用的事务属性 -->
50             <tx:method name="open*" isolation="DEFAULT" propagation="REQUIRED"/>
51             <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="StockException"/>
52         </tx:attributes>
53     </tx:advice>
54     
55     <!-- AOP配置 -->
56     <aop:config>
57         <!-- 指定切入点 -->
58         <aop:pointcut expression="execution(* *..service.*.*(..))" id="stockPointCut"/>
59         <aop:advisor advice-ref="txAdvice" pointcut-ref="stockPointCut"/>
60     </aop:config>
61 </beans>

  总结:Spring 的事务管理,是 AOP 的应用,将事务做为切面织入到了 Service 层的业务方法中。

相关文章
相关标签/搜索