目录
事务的概念
事务引入
Spring事务管理
介绍
基于注解方式实现声明式事务管理
声明式事务管理参数配置
基于XML方式实现声明式事务管理
基于完全注解方式实现声明式事务管理
事务的概念
什么是事务
①:事务是数据库操作最基本单元,逻辑上一组操作,要么都成功要么都失败,如果有一个失败,那么所有操作都失败。
②:典型场景:银行转账。
事务的四大特性(ACID)
①:原子性
②:一致性
③:隔离性
④:持久性
事务引入
我们使用常见的银行转账案例来引入事务的操作。
思路:将数据库中一个人的账户金额减去转账金额,另一个人的加上转账金额。
我们使用常见的银行转账案例来引入事务的操作。
思路:将数据库中一个人的账户金额减去转账金额,另一个人的加上转账金额。
步骤
①:创建数据库,创建对应表,添加相应的记录
CREATE TABLE t_account( id VARCHAr(20), username VARCHAr(50), money INT )
②:创建 service ,搭建 dao,完成对象创建和对象注入关系。
service ---> dao, dao ---> JdbcTemplate,JdbcTemplate ---> DataSource
③:在 dao 中创建两个方法:多钱与少钱,在 service 中创建转账的方法
UserDao接口
public interface UserDao {
//少钱
public void reduceMoney();
//多钱
public void addMoney();
}
UserDaoImp
@Repository
public class UserDaoImp implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void reduceMoney() {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql,100,"张三");
}
@Override
public void addMoney() {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql,100,"李四");
}
}
UserService
@Service
public class UserService {
@Autowired
private UserDao userDao;
//转账方法
public void moveMoney(){
userDao.reduceMoney();
userDao.addMoney();
}
}
④:测试
public class UserTest {
@Test
public void test1(){
ApplicationContext context = new FileSystemXmlApplicationContext("G:\SpringStudy\Spring-TranOperation\bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.moveMoney();
}
}
注意点:
如果上面代码正常执行,那么不会出现问题;但是如果在执行过程中出现了异常,那么可能就会导致一些问题。
根据上面案例我们来模拟银行转账过程中断电的情况
表初始状态
手动添加异常:在张三执行少钱操作后,添加一个除 0 操作,其余不变
//转账方法
public void moveMoney(){
userDao.reduceMoney();
//使用除 0 操作来模拟转账过程中断电
int num = 1 / 0;
userDao.addMoney();
}
执行后表结果:
我们可以发现张三账户的钱已经转出了,但李四的账户却没有收到,导致总钱数发生了变化。这种情况我们就可以使用事务来进行解决。
Spring事务管理
介绍
1、事务一般是添加到 JavaEE 三层结构里面的 Service 层(业务逻辑层)
2、在 Spring 进行事务管理操作,有两种方式
编程式事务管理和声明式事务管理(常用)
3、声明式事务管理(底层使用了 AOP 原理)
① 基于注解方式(常用)
② 基于 XML 配置方式
4、Spring 事务管理常用 API (提供了一个事务管理器接口 PlatformTransactionManager ,针对 不同的框架提供不同的实现类)
基于注解方式实现声明式事务管理
1、在 spring 配置文件中配置事务管理
JdbcTemplate 使用的是DataSourceTransactionManager 实现类
2、在 spring 配置文件中开启事务注解
①:在 spring 配置文件中引入名称空间 tx ,和之前配置名称空间方法一样
②:开启事务注解
3、在 service 类上面(或者 service 类里面的方法上面)添加事务注解
①:@Transactional,该注解可以添加在类上面,也可以添加到方法上面
②:如果把这个注解添加到类上面,表明这个类里面的所有方法都被添加了事务
如果添加到方法上面,表明该方法被添加了事务
4、基于上面的小例子进行测试
修改后的 UserService 类
@Service
public class UserService {
@Autowired
private UserDao userDao;
//转账方法
@Transactional
public void moveMoney(){
userDao.reduceMoney();
//使用除 0 操作来模拟转账过程中断电
int num = 1 / 0;
userDao.addMoney();
}
}
异常
结果:可以发现当出现异常后,执行了回滚操作,恢复已经被修改的数据
声明式事务管理参数配置
在 @Transactional 注解里面配置事务相关参数,主要参数如下
①:propagation:事务传播行为
事务传播行为:多事务方法直接进行调用,这个过程中事务是如何进行管理的
事务方法:使数据库表数据进行变化的操作
事务的传播行为可以有由传播属性指定。Spring指定了 7 种传播行为
常用的是 REQUIRED(默认) 和 REQUIRES_NEW, 配置如下
②:isolation:事务隔离级别
<1> 事务有隔离性的特性,多事务操作之间不会产生影响
<2> 如果不考虑隔离性会产生脏读、不可重复读、虚(幻)读 等问题。
脏读:一个未提交的事务读取到另一个未提交事务的数据,若后者执行了回滚操作,就导致前者读到了脏数据
不可重复读:一个未提交事务读取到了一个已提交事务修改后的数据,导致几次读取到的数据不一致
虚读:一个未提交事务读取到另一提交事务修改数据的记录,导致几次读取到的数据记录不一致
通俗理解可查看该博客【数据库】快速理解脏读、不可重复读、幻读阳阳的博客-CSDN博客脏读
<3> 解决:通过设置事务隔离性,解决读问题,MySql 中默认是可重复读级别
③:timeout:超时时间
事务需要在一定时间内提交,如果不提交就会进行事务回滚
默认是 -1,也就是不回滚;但可以自己设置,单位是秒
④:readOnly:是否只读
读:查询操作;写:增、删、改
默认值是 false,可以执行增、删、改、查操作
可以设置为 true,只可以执行查操作
⑤:rollbackFor:回滚
设置查询哪些异常进行事务回滚
⑥:noRollbackFor:不回滚
设置出现哪些异常不进行回滚
基于XML方式实现声明式事务管理
在 spring 配置文件中进行配置
1、配置事务管理器
2、配置通知
3、配置切入点和切面
基于完全注解方式实现声明式事务管理
创建配置类,使用配置类来替代 xml 配置文件
@Configuration //配置类
@ComponentScan(basePackages = "com.zhouyue")
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
//创建 JdbcTemplate 对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//到 IOC 容器中根据类型找到 dataSource
//注入dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
测试方法
@Test
public void test2(){
ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.moveMoney();
}



