Spring的事务管理
一 、 事务管理的定义
传统的数据库操作(增删改),进行统一的提交(成功)或者回滚(失败)时。要么一起成功,要么一起失败。
二、 事务管理的三个核心接口
1. PlatformTransactionManager 事务平台管理器。
完成事务的提交或者回滚。
实现类 DataSourceTransactionManager
1.1该接口提供的三个事务操作方法
用于获取事务状态信息
TransactionStatus getTransaction(TransactionDefinition definition);
用于提交事务
void commit(TransactionStatus status);
用于回滚事务
void rollback(TransactionStatus status);
2. TransactionDefinition事务定义对象
该接口提供的获取事务相关信息的方法:
2.1获取事务的隔离级别
int getIsolationLevel();
2.2获取事务对象名称
String getName();
2.3获取事务的传播行为
int getPropagationBehavior();
2.4获取事务的超时时间
int getTimeout();
2.5获取事务是否只读
boolean isReadOnly();
3. TransactionStatus 事务的状态
方法名 | 方法作用 |
---|---|
void flush(); | 刷新事务 |
boolean hasSavepoint(); | 获取是否存在保存点 |
boolean isCompleted(); | 获取事务是否完成 |
boolean isNewTransaction(); | 获取是否为新事务 |
boolean isRollbackOnly(); | 获取事务是否回滚 |
void setRollbackOnly(); | 设置事务回滚 |
4. 事务管理的方式
Spring中的事务管理分为两种方式:一种是传统的编程式事务管理,另一种是声明式事务管理
编程式事务管理:是通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
声明式事务管理:是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码织入到业务目标类中
三、 声明式事务管理
1. xml的步骤
1.1 事务管理器,依赖于数据源
事务平台管理器 DataSourceTransactionManager
<!-- 事务管理平台-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dateSource"/>
</bean>
1.2 编写通知
给事务管理器传参,并指定对那些方法起作用
<!-- 通知的编写,就是给事务管理平台添加属性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--“*”表示任意方法名称-->
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
</tx:attributes>
</tx:advice>
1.3 织入
编写AOP,让 spring自动对目标生成代理,需要使用 Aspect的表达式
<!-- 织入,将事务添加到需要执行的方法上,也就是把事务放到数据库操作的方法上-->
<aop:config>
<aop:pointcut id="txPointCat" expression="execution(* com.soft.jdbc.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCat"/>
</aop:config>
2. 注解的步骤
2.1 事务管理器,依赖于数据源
事务平台管理器 DataSourceTransactionManager
<!-- 事务管理平台-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dateSource"/>
</bean>
2.2 在方法上添加注释 @Transactional
@Transactional 放在需要事务的方法上(可以指定事务的属性)
2.3 注册事务管理驱动
开启事务管理驱动
<tx:annotation-driven transaction-manager="transactionManager"/>
四、 案例实现
***基于 Annotation方式的声明式事务
***
1.创建数据库
在 MYSQL中,创建一个名为 spring的数据库
利用在 JDBCTemplate 学习中所建表 account2
2.创建web项目,导入jar包
导入所需JAR包到项目的lib目录中,并发布到类路径下。
3.配置文件 applicationContext
在 src下,创建配置文件 applicationContext.xml,并编写相关配置,添加命名空间并编写事务管理的相关配置代码。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="dateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dateSource"/>
</bean>
<bean id="userDao" class="com.soft.jdbc.AccountImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 事务管理平台-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dateSource"/>
</bean>
<!-- 注册事务管理驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
4.创建Account类
在该web项目下的 com.soft.jdbc 包中创建 Account类,在该类中定义id,username和 balance属性,以及其对应的 getter/setter方法及重写tostring方法。
package com.soft.jdbc;
public class Account {
private int id;
private String username;
private double balance;
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public double getBalance() {return balance;}
public void setBalance(double balance) {this.balance = balance;}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", username='" + username + '\'' +
", balance=" + balance +
'}';
}
}
5.创建接口 IAccountDao
在 com.soft.jdbc 包中,创建接口 IAccountDao,并在接口中定义添加、更新、删除账户、查询及转账方法 transfer()。
package com.soft.jdbc;
import java.util.List;
public interface IAccountDao {
public int addAccount(Account account);
public int updateAccount(Account account);
public int deleteAccount(Account account);
public Account findAccountById(int id);
public List<Account> findAllAccount();
public void trasfer(String outer,String inner,double money);
}
6.创建接口的实现类 Accountdaolmpl
在其实现类 Accountdaolmpl 中实现 transfer()方法。
package com.soft.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public class AccountImpl implements IAccountDao {
public JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
@Transactional
public void trasfer(String outer, String inner, double money) {
//收款时,收款用户的余额=现有余额+所汇金额
jdbcTemplate.update("update account1 set balance=balance-? where username=?", money, outer);
//模拟系统运行时的突发性问题
// int i=1/0;
//汇款时,汇款用户的余额=现有余额-所汇金额
jdbcTemplate.update("update account1 set balance=balance+? where username=?", money, inner);
}
}
7.创建测试类
在com.soft.jdbc包中,创建测试类txTest,并在类中编写测试方法annoTest()。
package com.soft.jdbc;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class txTest {
@Test
public void annoTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-anno.xml");
IAccountDao Dao = (IAccountDao) applicationContext.getBean("userDao");
Dao.trasfer("Bob", "Lily", 20);
System.out.println("转账成功");
}
}
8.运行结果
模拟系统运行时的突发性问题:
Junit控制合中报出了“ by zero”的算术异常信息。此时如果再次查询数据表 account2,会发现表中Bob和Lily的账户余额并没有发生任何变化。