Java EE学习笔记(五)

Spring事务管理

1、Spring事务管理概述

1)、在实际开发中,操作数据库时都会涉及到事务管理问题,为此Spring提供了专门用于事务处理的API(事务特性:ACID,原子性,一致性,隔离性,持久性)

2)、事务管理的核心接口:在Spring的所有JAR包中,包含一个名为spring-tx-4.3.6.RELEASE的JAR包,该包就是Spring提供的用于事务管理的依赖包。在该JAR包的org.springframework.transaction包中,有3个接口文件PlatformTransactionManager、TransactionDefinition和TransactionStatus,如下图所示:

3)、Platform TransactionManager接口

a)、PlatformTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。该接口中提供了三个事务操作的方法,具体如下:

用于获取事务状态信息:TransactionStatus getTransaction(TransactionDefinition definition);

用于提交事务:void commit(TransactionStatus status);

用于回滚事务:void rollback(TransactionStatus status);

b)、PlatformTransactionManager接口只是代表事务管理的接口并不知道底层是如何管理事务的,具体如何管理事务则由它的实现类来完成。该接口常见的几个实现类如下:

1、用于配置JDBC数据源的事务管理器:
org.springframework.jdbc.datasource.DataSourceTransactionManager
2、用于配置Hibernate的事务管理器:
org.springframework.orm.hibernate4.HibernateTransactionManager
3、用于配置全局事务管理器:
org.springframework.transaction.jta.JtaTransactionManager

当底层采用不同的持久层技术时,系统只需使用不同的PlatformTransactionManager实现类即可。

4)、TransactionDefinition接口

a)、TransactionDefinition接口事务定义(描述)的对象,该对象中定义了事务规则(事物详情),并提供了获取事务相关信息的方法,具体如下:

1、获取事务对象名称:String getName( );
2、获取事务的隔离级别:int getIsolationLevel( );
3、获取事务的传播行为:int getPropagationBehavior( );
4、获取事务的超时时间:int getTimeout( );
5、获取事务是否只读:boolean isReadOnly( );

 上述方法中,事务的传播行为是指在同一个方法中,不同操作前后所使用的事务。传播行为有很多种,具体如下表所示:

 

 在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务,通常情况下,数据的查询不会影响原数据的改变,所以不需要进行事务管理,而对于数据的插入、更新和删除操作,必须进行事务管理。如果没有指定事务的传播行为,Spring默认传播行为是REQUIRED(required)

5)、TransactionStatus接口

a)、TransactionStatus接口事务的状态,它描述了某一时间点上事务的状态信息。该接口中包含6个方法,具体如下:

1、刷新事务:void flush();
2、获取是否存在保存点:boolean hasSavepoint();
3、获取事务是否完成:boolean isCompleted();
4、获取是否为新事务:boolean isNewTransaction();
5、获取事务是否回滚:boolean isRollbackOnly();
6、设置事务回滚:void setRollbackOnly();

 6)、事务管理的方式:

 

 声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务只需在配置文件中进行相关的事务规则声明,就可以将事务应用到业务逻辑中

2、声明式事务管理

1)、Spring的声明式事务管理可以通过两种方式来实现,一种是基于XML的方式,另一种是基于Annotation的方式

2)、基于XML方式的声明式事务:在配置文件中通过<tx:advice>元素配置事务规则来实现的。当配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生成代理。<tx:advice>元素及其子元素如下图所示:

3)、配置<tx:advice>元素的重点是配置<tx:method>子元素,上图中使用灰色标注的几个属性是<tx:method>元素中的常用属性。其属性描述具体如下:

4)、src->com.itheima.jdbc

①普通用户类:Account.java

 1 package com.itheima.jdbc;
 2 public class Account {
 3     
 4     private Integer id;       // 账户id
 5     private String username; // 用户名
 6     private Double balance;  // 账户余额
 7     
 8     public Integer getId() {
 9         return id;
10     }
11     
12     public void setId(Integer id) {
13         this.id = id;
14     }
15     
16     public String getUsername() {
17         return username;
18     }
19     
20     public void setUsername(String username) {
21         this.username = username;
22     }
23     
24     public Double getBalance() {
25         return balance;
26     }
27     
28     public void setBalance(Double balance) {
29         this.balance = balance;
30     }
31     
32     public String toString() {
33         return "Account [id=" + id + ", "+ "username=" + username + ", balance=" + balance + "]";
34     }
35 }

②操作用户接口类:AccountDao.java

 1 package com.itheima.jdbc;
 2 
 3 import java.util.List;
 4 
 5 public interface AccountDao {
 6     
 7     // 添加
 8     public int addAccount(Account account);
 9     
10     // 更新
11     public int updateAccount(Account account);
12     
13     // 删除
14     public int deleteAccount(int id);
15     
16     // 通过id查询
17     public Account findAccountById(int id);
18     
19     // 查询所有账户
20     public List<Account> findAllAccount();
21     
22     // 添加一个转账方法
23     public void transfer(String outUser,String inUser,Double money);
24 }

③在AccountDaoImpl.java实现transfer()方法:

 1 /**
 2      *  转账
 3      *  inUser:收款人
 4      *  outUser:汇款人
 5      *  money:收款金额
 6     */
 7     public void transfer(String outUser, String inUser, Double money) {
 8         // 收款时,收款用户的余额=现有余额+所汇金额
 9         this.jdbcTemplate.update("update account set balance = balance +? "
10                 + "where username = ?",money, inUser);
11         // 模拟系统运行时的突发性问题
12         int i = 1/0;
13         // 汇款时,汇款用户的余额=现有余额-所汇金额
14         this.jdbcTemplate.update("update account set balance = balance-? "
15                 + "where username = ?",money, outUser);
16     }

③配置文件applicationContext.xml(注意数据库登陆密码需要自己的数据库密码)

 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:aop="http://www.springframework.org/schema/aop"
 5 xmlns:tx="http://www.springframework.org/schema/tx" 
 6 xmlns:context="http://www.springframework.org/schema/context"
 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8                 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 9                 http://www.springframework.org/schema/tx 
10                 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
11                 http://www.springframework.org/schema/context 
12                 http://www.springframework.org/schema/context/spring-context-4.3.xsd
13                 http://www.springframework.org/schema/aop 
14                 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
15     
16     <!-- 1、配置数据源 -->
17     <bean id="dataSourceID" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
18     
19         <!--1.1、数据库驱动 -->
20         <property name="driverClassName" value="com.mysql.jdbc.Driver" />
21         
22         <!--1.2、连接数据库的url(即数据库所在地址) -->
23         <property name="url" value="jdbc:mysql://localhost:3306/spring" />
24         
25         <!--1.3、连接数据库的用户名 -->
26         <property name="username" value="root" />
27         
28         <!--1.4、连接数据库的密码 -->
29         <property name="password" value="******" />
30     </bean>
31     
32     <!-- 2、配置JDBC模板,并且注入数据源 -->
33     <bean id="jdbcTemplateID" class="org.springframework.jdbc.core.JdbcTemplate">
34     
35         <!-- 2.1、默认必须使用数据源 -->
36         <property name="dataSource" ref="dataSourceID" />
37     </bean>
38     
39     <!--3、定义id为accountDaoID的Bean,即配置DAO层-->
40     <bean id="accountDaoID" class="com.itheima.jdbc.AccountDaoImpl">
41     
42         <!-- 3.1、将jdbcTemplate注入到accountDao类实例中 -->
43         <property name="jdbcTemplate" ref="jdbcTemplateID" />
44     </bean>
45     
46    <!-- 4、事务管理器,依赖于数据源(注入DataSource),事务从Connection获得,连接从连接池DataSource获得 -->
47    <bean id="transactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
48         <property name="dataSource" ref="dataSourceID" />
49    </bean>    
50    
51    <!-- 5、编写通知:对事务进行增强(通知),需要编写对切入点和具体执行事务细节
52        <tx:attributes>用于配置事务的属性
53        <tx:method name=""/>详情的具体配置
54        propagation:指定事务的传播行为,REQUIRED是必须的
55        isolation:指定事务的隔离级别    
56     -->
57    <tx:advice id="txAdviceID" transaction-manager="transactionManagerID">
58    
59         <tx:attributes>
60             <!-- 5.1、name:*表示任意方法名称 -->
61             <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
62         </tx:attributes>
63     </tx:advice>
64     
65     <!-- 6.编写aop,让spring自动对目标生成代理,需要使用AspectJ的表达式 -->
66     <aop:config>
67     
68         <!-- 6.1、切入点 -->
69         <aop:pointcut expression="execution(* com.itheima.jdbc.*.*(..))" id="txPointCutID" />
70         
71         <!-- 6.2、切面:将切入点与通知整合 -->
72         <aop:advisor advice-ref="txAdviceID" pointcut-ref="txPointCutID" />
73     </aop:config>
74 </beans>

④交易测试类:TransactionTest.java

 1 package com.itheima.jdbc;
 2 import org.junit.Test;
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 //测试类
 6 public class TransactionTest {
 7     
 8     @Test
 9     public void xmlTest(){
10         
11         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
12         
13         // 1、获取AccountDao的Bean实例accountDaoID
14         AccountDao accountDao = (AccountDao)applicationContext.getBean("accountDaoID");
15         
16         // 2、调用实例中的转账方法
17         accountDao.transfer("Jack", "Rose", 100.0);
18         
19         // 3、输出提示信息
20         System.out.println("转账成功!");
21     }
22 }

⑤运行结果:(异常/by zero报错,说明Spring中的事务管理配置已经生效)

5)、基于Annotation方式的声明式事务

a)、在Spring容器中注册事务注解驱动

<tx:annotation-driven transaction-manager="transactionManager"/>

b)、在需要事务管理的类或方法上使用@Transactional注解
如果将注解添加在Bean类上,则表示事务的设置对整个Bean类的所有方法都起作用;如果将注解添加在Bean类中的某个方法上,则表示事务的设置只对该方法有效

c)、使用@Transactional注解时,可以通过参数配置事务详情:

d)src->applicationContext-annotation.xml

 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:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx" 
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 9     http://www.springframework.org/schema/tx 
10     http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
11     http://www.springframework.org/schema/context 
12     http://www.springframework.org/schema/context/spring-context-4.3.xsd
13     http://www.springframework.org/schema/aop 
14     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
15     <!-- 1.配置数据源 -->
16     <bean id="dataSourceID" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
17     
18         <!--数据库驱动 -->
19         <property name="driverClassName" value="com.mysql.jdbc.Driver" />
20         
21         <!--连接数据库的url -->
22         <property name="url" value="jdbc:mysql://localhost/spring" />
23         
24         <!--连接数据库的用户名 -->
25         <property name="username" value="root" />
26         
27         <!--连接数据库的密码 -->
28         <property name="password" value="******" />
29         
30     </bean>
31     <!-- 2.配置JDBC模板 -->
32     <bean id="jdbcTemplateID" class="org.springframework.jdbc.core.JdbcTemplate">
33     
34         <!-- 默认必须使用数据源 -->
35         <property name="dataSource" ref="dataSourceID" />
36         
37     </bean>
38     
39     <!--3.定义id为accountDao的Bean实例 -->
40     <bean id="accountDaoID" class="com.itheima.jdbc.AccountDaoImpl">
41     
42         <!-- 将jdbcTemplate注入到AccountDao实例中 -->
43         <property name="jdbcTemplate" ref="jdbcTemplateID" />
44     </bean>
45     
46     <!-- 4.事务管理器,依赖于数据源 -->
47     <bean id="transactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
48         <property name="dataSource" ref="dataSourceID" />
49     </bean>    
50     
51     <!-- 5.注册事务管理器驱动,替换编写aop和编写通知 -->
52     <tx:annotation-driven transaction-manager="transactionManagerID"/>
53 </beans>

e)、在AccountDaoImpl.java的transfer()方法上添加事务注解(定义方法接口)

 1     @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
 2     public void transfer(String outUser, String inUser, Double money) {
 3         
 4         // 收款时,收款用户的余额=现有余额+所汇金额
 5         this.jdbcTemplate.update("update account set balance = balance + ? " +
 6                  "where username = ?",money, inUser);
 7         
 8         // 模拟系统运行时的突发性问题
 9         int i = 1/0;
10         
11         // 汇款时,汇款用户的余额=现有余额-所汇金额
12         this.jdbcTemplate.update("update account set balance = balance-? " +
13                                  "where username = ?",money, outUser);
14     }

f)、运行结果:(异常/by zero报错,说明Spring中的事务管理配置已经生效)

个人总结:

事务处理这方面比较陌生,对某些参数还有些不熟悉,sql很重要!!!其实操作的方式和前面的知识点大同小异。

原文地址:https://www.cnblogs.com/acgoto/p/10612268.html