Spring事务管理与设计模式
Spring事务管理与设计模式
1. Spring事务管理概述
1.1 事务基础概念
什么是事务?
- 在一个业务流程中,通常需要多条DML(insert、delete、update)语句共同联合才能完成
- 这多条DML语句必须同时成功,或者同时失败,这样才能保证数据的安全
- 多条DML要么同时成功,要么同时失败,这叫做事务
事务的四个处理过程:
- 开启事务 (start transaction)
- 执行核心业务代码
- 提交事务(如果核心业务处理过程中没有出现异常)(commit transaction)
- 回滚事务(如果核心业务处理过程中出现异常)(rollback transaction)
事务的四个特性(ACID):
- A 原子性:事务是最小的工作单元,不可再分
- C 一致性:事务要求要么同时成功,要么同时失败。事务前和事务后的总量不变
- I 隔离性:事务和事务之间因为有隔离性,才可以保证互不干扰
- D 持久性:持久性是事务结束的标志
1.2 Spring事务管理方式
Spring实现事务的两种方式:
- 编程式事务:通过编写代码的方式来实现事务的管理
- 声明式事务:
- 基于注解方式
- 基于XML配置方式
1.3 Spring事务管理API
Spring对事务的管理底层实现方式是基于AOP实现的。核心接口:
- PlatformTransactionManager接口:Spring事务管理器的核心接口
- DataSourceTransactionManager:支持JdbcTemplate、MyBatis、Hibernate等事务管理
- JtaTransactionManager:支持分布式事务管理
2. 声明式事务实现
2.1 注解方式实现事务
第一步:在spring配置文件中配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
第二步:在spring配置文件中引入tx命名空间
<?xml version="1.0" encoding="UTF-8"?>
<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: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">
第三步:配置事务注解驱动器
<tx:annotation-driven transaction-manager="transactionManager"/>
第四步:在service类上或方法上添加@Transactional注解
@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
@Resource(name = "accountDao")
private AccountDao accountDao;
@Override
public void transfer(String fromActno, String toActno, double money) {
// 查询账户余额是否充足
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
throw new RuntimeException("账户余额不足");
}
// 余额充足,开始转账
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
int count = accountDao.update(fromAct);
count += accountDao.update(toAct);
if (count != 2) {
throw new RuntimeException("转账失败,请联系银行");
}
}
}
2.2 事务属性详解
2.2.1 事务传播行为
事务传播行为定义了当一个事务方法被另一个事务方法调用时,事务如何传播:
- REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
- MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】
- REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务】
- NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
- NEVER:以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】
- NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中【有事务的话,就在这个事务里再嵌套一个完全独立的事务】
@Transactional(propagation = Propagation.REQUIRED)
public void save(Account act) {
// 业务逻辑
}
2.2.2 事务隔离级别
事务隔离级别解决数据库读取数据存在的三大问题:
- 脏读:读取到没有提交到数据库的数据
- 不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样
- 幻读:读到的数据是假的
四个隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 有 | 有 | 有 |
读提交 | 无 | 有 | 有 |
可重复读 | 无 | 无 | 有 |
序列化 | 无 | 无 | 无 |
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transfer(String fromActno, String toActno, double money) {
// 业务逻辑
}
2.2.3 事务超时
@Transactional(timeout = 10) // 设置事务超时时间为10秒
public void save(Account act) {
// 业务逻辑
}
注意:事务的超时时间指的是在当前事务当中,最后一条DML语句执行之前的时间。
2.2.4 只读事务
@Transactional(readOnly = true)
public Account getByActno(String actno) {
return accountDao.selectByActno(actno);
}
将当前事务设置为只读事务,在该事务执行过程中只允许select语句执行,delete、insert、update均不可执行。该特性的作用是启动Spring的优化策略,提高select语句执行效率。
2.2.5 异常回滚设置
// 设置哪些异常回滚事务
@Transactional(rollbackFor = RuntimeException.class)
// 设置哪些异常不回滚事务
@Transactional(noRollbackFor = NullPointerException.class)
2.3 全注解式事务开发
@Configuration
@ComponentScan("com.powernode.bank")
@EnableTransactionManagement
public class Spring6Config {
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring6");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
2.4 XML方式实现事务
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="transfer*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.powernode.bank.service..*(..))" />
<!--切面 = 通知 + 切点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
3. Spring中的八大设计模式
3.1 简单工厂模式
BeanFactory的getBean()方法,通过唯一标识来获取Bean对象,是典型的简单工厂模式(静态工厂模式)。
3.2 工厂方法模式
FactoryBean是典型的工厂方法模式。在配置文件中通过factory-method属性来指定工厂方法,该方法是一个实例方法。
3.3 单例模式
Spring用的是双重判断加锁的单例模式。在Bean的循环依赖处理中可以看到这种实现。
3.4 代理模式
Spring的AOP就是使用了动态代理实现的,包括JDK动态代理和CGLIB动态代理。
3.5 装饰器模式
Spring中配置DataSource的时候,这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL等,也可能是不同的数据源。Spring根据每次请求的不同,将dataSource属性设置成不同的数据源,以达到切换数据源的目的。
Spring中类名中带有Decorator和Wrapper单词的类,都是装饰器模式。
3.6 观察者模式
定义对象间的一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。Spring中观察者模式一般用在listener的实现。
Spring中的事件编程模型就是观察者模式的实现。在Spring中定义了一个ApplicationListener接口,用来监听Application的事件,ApplicationContext内置了几个事件:
- ContextRefreshedEvent
- ContextStartedEvent
- ContextStoppedEvent
- ContextClosedEvent
3.7 策略模式
策略模式是行为性模式,调用不同的方法,适应行为的变化,强调父类的调用子类的特性。
比如我们写了AccountDao接口,然后这个接口下有不同的实现类:AccountDaoForMySQL,AccountDaoForOracle。对于service来说不需要关心底层具体的实现,只需要面向AccountDao接口调用,底层可以灵活切换实现,这就是策略模式。
3.8 模板方法模式
Spring中的JdbcTemplate类就是一个模板类。它就是一个模板方法设计模式的体现。在模板类的模板方法execute中编写核心算法,具体的实现步骤在子类中完成。
4. 总结
本文详细介绍了Spring框架中的事务管理以及Spring中常用的设计模式:
Spring事务管理:
- 掌握了事务的基本概念和ACID特性
- 学习了声明式事务的注解和XML两种实现方式
- 深入理解了事务的各种属性:传播行为、隔离级别、超时、只读等
Spring中的设计模式:
- 认识了Spring框架中应用的八大设计模式
- 理解了每种模式在Spring中的具体应用场景
- 有助于更好地理解Spring框架的设计思想
通过学习这些内容,我们对Spring框架有了更深入的理解,能够在实际项目中更好地应用Spring的事务管理功能,并从设计模式的角度理解Spring框架的设计思想。