事务在之前就已经了解过了,这里简单的来讲一下,在一条或多条DML语句执行时,确保同时成功或者同时失败
模型搭建举一个非常典型的问题,银行转账问题,Jerry向Tom转账100元,其中一共产生两条sql语句,第一Jerry余额少100,第二Tom余额多100,且必须要保证这两条语句要么同时成功要么同时失败。
之前在JavaWeb中解决办法是关闭当前数据库连接的自动提交,在成功的时候提交,失败的时候回滚。来看一下Spring5中该如何处理
首先我们先搭建上述问题的模型
-
数据库模型搭建
做的非常简单,只需要用户名和余额即可
-
Service及Dao
@Service public class BankService { @Autowired public BankDaoImpl bankDao; public void accountMoney(){ bankDao.addMoney(); //模拟异常 int i = 10/0; bankDao.removeMoney(); } public void setBankDao(BankDaoImpl bankDao) { this.bankDao = bankDao; } } @Repository public class BankDaoImpl implements BankDao { @Autowired public JdbcTemplate jdbcTemplate; @Override public void addMoney() { String sql = "update t_bank set `balance` = balance + 100 where id = 1"; jdbcTemplate.execute(sql); } @Override public void removeMoney() { String sql = "update t_bank set `balance` = balance - 100 where id = 2"; jdbcTemplate.execute(sql); } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } } -
xml配置文件
-
测试类
@Test public void test3(){ ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml"); BankService bankService = context.getBean("bankService", BankService.class); bankService.accountMoney(); }
Spring5事务管理介绍
- 事务添加到Java EE三层架构中的Service层(业务逻辑层)
- 在Spring进行事务管理操作有两种方式
- 编程式事务管理(即上文所讲的成功提交,失败回滚)
- 声明式事务管理
- 基于注解方式(推荐使用)
- 基于XML配置文件方式
- 在Spring进行声明式事务管理,底层使用AOP原理
使用Spring管理事务
-
在xml文件中配置事务管理器并注入数据源
-
开启事务注解
需要在头部文件配置tx空间 -
在方法或类上添加@Transactional注解
在方法上添加代表对此方法进行事务管理,在类上添加代表对类中的所有方法进行事务管理
@Service @Transactional public class BankService { @Autowired private BankDaoImpl bankDao; public void accountMoney(){ bankDao.addMoney(); //模拟异常 int i = 10/0; bankDao.removeMoney(); } public void setBankDao(BankDaoImpl bankDao) { this.bankDao = bankDao; } }
Spring事务管理:事务参数
先来看一下几个比较重要的事务参数
- propagation:传播行为
- isolation:隔离级别
- timeout:存活时长:事务在一定时间内进行提交,不提交则回滚
- readOnly:只读:设置true代表数据库只读,只能进行查询操作
- rollbackFor:回滚:出现哪些异常进行回滚
- noRollbackFor:不回滚:出现哪些异常不回滚
传播行为
spring定义了7种事务传播行为,最常用的是REQUIRED或REQUIRED_NEW
- REQUIRED:如果有事务在运行,当前的方法就在这个事务内运行,否则启动一个新事务,并在自己的事务内运行(默认)
- REQUIRED_NEW:当前方法必须启动新事务,并在自己的事务内运行,如果有事务正在运行,应该将他挂起
- SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
- NOT_SUOPPORTS:当前的方法不应该运行在事务中,如果有运行的事务,将他挂起
- MANDATORY:当前的方法必须运行在事务中,如果没有正在运行的事务,就抛出异常
- NEVER:当前方法不应该运行在事务中,如果有运行的事务,就抛出异常
- NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在他自己的事务内运行
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class BankService {
隔离级别
事务的隔离级别分四种
- 读未提交
- 读已提交
- 可重复读
- 序列化读
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class BankService {
完全注解开发
创建配置类,代替xml配置文件
- 创建配置类
- 开启注解扫描
- 开启事务
- 创建数据库连接池
- 创建JdbcTemplate对象
- 创建事务管理器
@Configuration //配置类
@ComponentScan(basePackages = "com.yellowstar") //组件扫描
@EnableTransactionManagement //开启事务
public class TransactionConfig {
//创建数据库连接池对象
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true");
dataSource.setUsername("root");
dataSource.setPassword("Hkx123");
return dataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource){
//到 ioc 容器中根据类型找到 dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}



