主要是将事务由Dao层提升至Service层


  1. 事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层, Service 层。这样做是为了能够使用事务的特性来管理具体的业务。<br /> Spring 中通常可以通过以下三种方式来实现对事务的管理: <br /> 1)使用 Spring 的事务代理工厂管理事务<br /> 2)使用 Spring 的事务注解管理事务 <br /> 3)使用 AspectJ AOP 配置管理事务

Spring事务管理的API

一,平台事务管理器接口

A,常用的两个实现类

DataSourceTransactionManager:适用于使用JDBC和iBatis(mybatis)进行数据持久化操作的情况。
HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况(既连接DB),使用该实现类进行事务管理
//JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。

B,Spring默认的回滚方式

发生运行时异常回滚,发生受查异常提交,

-受查异常:发生指定异常后回滚
                    +运行时异常:发生指定异常后提交
                 -->
                <prop                     key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException</prop>

二,事务定义接口

A,五个事务隔离级别
事务隔离级别
        隔离级别是指若干个并发的事务之间的隔离程度。
    TransactionDefinition接口中定义了五个表示隔离级别的常量:

TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
  Mysql默认的事务隔离级别:可重复度,既4级
  Oracle默认的事务隔离级别是:读己提交  既2级
B,七个传播行为
事务传播行为
       所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

      TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。(Spring默认传播行为)

      TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

      TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

      TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

      TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

      TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

      TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition

.PROPAGATION_REQUIRED。这里需要指出的是,前面的六种事务传播行为是Spring从 EJB 中引入的,它们共享相同的概念。而 PROPAGATION_NESTED是Spring所特有的。以PROPAGATION_NESTED启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

1 Spring 事务管理 API

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

(1)事务管理器接口

 事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回 滚,及获取事务的状态信息。查看 SpringAPI 帮助文档:Spring 框架解压目录下的 docs/javadoc-api/index.html。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637069210045-ad066d52-5cea-4837-a767-1a5444b42254.png#clientId=ue6b36aef-b379-4&from=paste&height=164&id=ub8bda36f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=328&originWidth=742&originalType=binary&ratio=1&size=77579&status=done&style=none&taskId=ua7160845-a39e-432b-9026-5a71338a72a&width=371)

A、常用的两个实现类

PlatformTransactionManager 接口有两个常用的实现类:
 DataSourceTransactionManager:使用 JDBC 或 iBatis 进行持久化数据时使用。
 HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

B、Spring 的回滚方式

 Spring 事务的默认回滚方式是:发生运行时异常时回滚,发生受查异常时提交。不过, 对于受查异常,程序员也可以手工设置其回滚方式。

(2)事务定义接口 (csdn)

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、 事务传播行为、事务默认超时时限,及对它们的操作
image.png

2 程序举例环境搭建

 举例:购买股票—transaction_buystock 项目 本例要实现模拟购买股票。存在两个实体:银行账户 Account 与股票账户 Stock。当要 购买股票时,需要从 Account 中扣除相应金额的存款,然后在 Stock 中增加相应的股票数量。 而在这个过程中,可能会抛出一个用户自定义的异常。异常的抛出,将会使两个操作回滚。 实现步骤:  <br /> Step1:创建数据库表 <br />创建两个数据库表 account、stock  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076323243-e6e5bbb0-25b2-4917-b400-96d9bb36229d.png#clientId=u47af0123-014a-4&from=paste&height=106&id=uc731550b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=212&originWidth=519&originalType=binary&ratio=1&size=48082&status=done&style=none&taskId=u4d763673-173d-41e2-8bb6-8dbefd889f9&width=259.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076330063-9bf6aaea-0c54-40d7-afb7-0593aa87d4dc.png#clientId=u47af0123-014a-4&from=paste&height=99&id=u6a8a7043&margin=%5Bobject%20Object%5D&name=image.png&originHeight=197&originWidth=516&originalType=binary&ratio=1&size=47083&status=done&style=none&taskId=u38fd3110-fccf-440c-8c07-907141d7560&width=258)<br /> Step2:创建实体类  <br /> 创建实体类 Account 与 Stock  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076361227-c6a4dff7-c2ee-48be-adfc-ad2189316a3b.png#clientId=u47af0123-014a-4&from=paste&height=188&id=u20d4a3ea&margin=%5Bobject%20Object%5D&name=image.png&originHeight=375&originWidth=376&originalType=binary&ratio=1&size=56544&status=done&style=none&taskId=ub95c41cf-e2fa-41d8-8730-b1ca9b6e33e&width=188)<br /> Step3:定义 dao 接口  <br /> 定义两个 dao 的接口 IAccountDao 与 IStockDao  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076382222-7f7cd224-6e6e-4d96-9555-f0670ce776a3.png#clientId=u47af0123-014a-4&from=paste&height=59&id=u7fe11c73&margin=%5Bobject%20Object%5D&name=image.png&originHeight=117&originWidth=567&originalType=binary&ratio=1&size=38116&status=done&style=none&taskId=u9c740d6d-e373-40d4-9895-cfc6c606d65&width=283.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076388533-2b4beb1c-62fc-4c57-b31d-6c5492f334f4.png#clientId=u47af0123-014a-4&from=paste&height=66&id=ub9ff3d28&margin=%5Bobject%20Object%5D&name=image.png&originHeight=131&originWidth=581&originalType=binary&ratio=1&size=37080&status=done&style=none&taskId=uca1d5574-e8d6-40cf-a0c0-bf1af62c714&width=290.5)<br /> Step4:定义 dao 实现类  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076401816-44655862-61f8-4b79-b0f2-e81c54a71c04.png#clientId=u47af0123-014a-4&from=paste&height=173&id=u17f53724&margin=%5Bobject%20Object%5D&name=image.png&originHeight=345&originWidth=780&originalType=binary&ratio=1&size=123367&status=done&style=none&taskId=u25b0f4be-ea72-419e-a2f5-6959cc4f80d&width=390)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076409921-b7f3fd68-0eec-44bd-89b6-8e54b323f6ff.png#clientId=u47af0123-014a-4&from=paste&height=180&id=ue29d9f63&margin=%5Bobject%20Object%5D&name=image.png&originHeight=359&originWidth=771&originalType=binary&ratio=1&size=120992&status=done&style=none&taskId=u531b4657-fb58-49ab-aa2b-2de08fd2c96&width=385.5)<br /> Step5:定义异常类  <br /> 定义 service 层可能会抛出的异常类 StockException  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076427266-e84050c9-f845-4706-be87-e731f3c8b6a6.png#clientId=u47af0123-014a-4&from=paste&height=107&id=uba9ba348&margin=%5Bobject%20Object%5D&name=image.png&originHeight=213&originWidth=450&originalType=binary&ratio=1&size=30039&status=done&style=none&taskId=u17418eca-95b0-4c98-bf56-f31db47df4b&width=225)<br /> Step6:定义 Service 接口  <br />     定义 Service 接口 IStockProcessService  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076441740-19f4a038-0b98-4edf-85f3-9b5a1589ea91.png#clientId=u47af0123-014a-4&from=paste&height=95&id=u0843ee13&margin=%5Bobject%20Object%5D&name=image.png&originHeight=189&originWidth=814&originalType=binary&ratio=1&size=69126&status=done&style=none&taskId=ue9c84752-98b6-411c-a683-1bacfaf9533&width=407)<br /> Step7:定义 service 的实现类 <br />定义 service 层接口的实现类 StockProcessServiceImpl  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076477410-6ef74783-7790-4663-b280-572058cb9fbc.png#clientId=u47af0123-014a-4&from=paste&height=277&id=ucd8ad230&margin=%5Bobject%20Object%5D&name=image.png&originHeight=433&originWidth=753&originalType=binary&ratio=1&size=99354&status=done&style=none&taskId=u9eb57c48-d145-409b-b7b2-b8c3a9f640d&width=482.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076496120-c3ea712c-b7e9-4d2d-af2c-33b24ba61235.png#clientId=u47af0123-014a-4&from=paste&height=234&id=u12087de8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=468&originWidth=791&originalType=binary&ratio=1&size=95861&status=done&style=none&taskId=u9654b2a0-f699-4ac7-b8ec-b25119355dc&width=395.5)<br /> Step8:Spring 配置文件中添加最全约束  <br /> 本例中将使用到 Spring 中 DI、AOP、事务等众多功能,所以将之前用过的所有约束进行 了综合。综合后的约束为:  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/tx 
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop 
 http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

Step9:修改 Spring 配置文件内容
image.png
image.png
Step10:定义测试类
定义 view 层测试类 MyTest。现在就可以在无事务代理的情况下运行了
image.png

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

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

Step1:复制项目
复制 transaction_buystock 项目,并重命名为 transaction_proxy。在此基础上修改
Step2:导入 Jar 包
这里使用到的 Spring 的 AOP,所以需要引入 AOP 的两个 Jar 包:aop 联盟,及 Spring 对 AOP 实现的 Jar 包
image.png
image.png
Step3:在容器中添加事务管理器 DataSourceTransactionManager
由于本项目使用的是 JDBC 进行持久化,所以使用 DataSourceTransactionManager 类作为 事务管理器。
image.png
Step4:在容器中添加事务代理 TransactionProxyFactoryBean
image.png Step5:修改测试类
现在就可以通过事务代理来运行了。
image.png

4 使用 Spring 的事务注解管理事务

 通过@Transactional 注解方式,也可将事务织入到相应方法中。而使用注解方式,只需 在配置文件中加入一个 tx 标签,以告诉 spring 使用注解来完成事务的织入。该标签只需指 定一个属性,事务管理器。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637076864330-1a57a160-af47-4cb5-b7ff-f5f1f636f279.png#clientId=u47af0123-014a-4&from=paste&height=37&id=u7cfbaa02&margin=%5Bobject%20Object%5D&name=image.png&originHeight=53&originWidth=777&originalType=binary&ratio=1&size=23267&status=done&style=none&taskId=ucd275e3b-d702-4647-b95f-7147086b275&width=536.5)<br />     @Transactional 的所有可选属性如下所示:<br />  propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为 Propagation.REQUIRED。<br />  isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为 Isolation.DEFAULT。<br />  readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值 为 false。 <br /> timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为 -1,即没有时限。<br />  rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有 一个异常类时,可以不使用数组  <br />  rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。 当然,若只有一个异常类时,可以不使用数组。<br />  noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若 只有一个异常类时,可以不使用数组。<br />  noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空 数组。当然,若只有一个异常类时,可以不使用数组。 需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public 方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该 方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。<br /> 若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。  

Step1:复制项目
复制 transaction_buystock 项目,并重命名为 transaction_annotation。在此基础上修改。
Step2:在容器中添加事务管理器
image.png
Step3:在 Service 实现类方法上添加注解
image.png
Step4:修改配置文件内容
image.png
Step5:修改测试类
由于配置文件中已不存在事务代理对象,所以测试类中要从容器中获取的将不再是事务 代理对象,而是原来的目标对象。
image.png

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

使用 XML 配置事务代理的方式的不足是,每个目标类都需要配置事务代理。当目标类 较多,配置文件会变得非常臃肿。
使用 XML 配置顾问方式可以自动为每个符合切入点表达式的类生成事务代理。其用法 很简单,只需将前面代码中关于事务代理的配置删除,再替换为如下内容即可。

Step1:复制项目
复制 transaction_buystock 项目,并重命名为 transaction_advisor。在此基础上修改。
Step2:导入 Jar 包
这里使用 Spring 的 AspectJ 方式将事务进行的织入,所以,这里除了前面导入的 aop 的 两个 Jar 包外,还需要两个 Jar 包:AspectJ 的 Jar 包,及 Spring 整合 AspectJ 的 Jar 包。
image.png
image.png
Step3:在容器中添加事务管理器
image.png
Step4:配置事务通知
为事务通知设置相关属性。用于指定要将事务以什么方式织入给哪些方法。 例如,应用到 buyStock 方法上的事务要求是必须的,且当 buyStock 方法发生 StockException 后,要回滚。
image.png
Step5:进行 AOP 配置
指定将配置好的事务通知织入给谁
image.png
注意,不能写为下面的形式,切入点表达式一定要指明切入点在 Service 层,否则将会 抛出对数据源的循环引用异常。因为下面的写法同时会把 Service 层与 Dao 层的方法均作为 切入点,Service 与 Dao 中均注入了数据源,而 Service 又调用了 Dao,所以就出现了循环调 用的异常
Step6:修改测试类
测试类中要从容器中获取的将不再是事务代理对象,而是目标对象
image.png