什么是事务?
事务时数据库操作最基本单元,事务要么都成功,要么都失败
事务四个特性
1 原子性 :不可分割 要么成功 要么都失败
2 一致性:操作之前和操作之后 总量不变
3 隔离性:多事务操作的时候,不会受影响
4 持久性:操作完数据发生变化
比如:在dao层写了对数据库的一些操作,而Service层调用dao层中的方法,
在service层中,进行事务的开启,保证事务的一致性,这是编程方法,但是编程式不利于管理,所以开发我们一般不用它,而是用声明式(@Transactional注解实现) 底层使用AOP
如果不考虑隔离性,会产生很多问题比如三个读的问题
脏读:,脏读指一个事务读取其他事务正在更新的数据。脏读 顾名思义,就是读取了脏的数据,什么是脏的数据,就是未提交的事务就是脏数据
幻读: 就是一个事务对一份数据进行两次读取,出现不同的结果,原因是,再第二次读取时 其它事务操作了此份数据且未提交 导致两次读取结果不一致 称之为幻读。就叫它 ” 一 , 一, 二 “ 一份数据 一份表 两次读取 结果不一样
不可重复读::就是一个事务对一份数据进行两次读取,出现不同的结果,原因是,再第二次读取时 其它事务操作了此份数据 且已提交 导致两次读取结果不一致 称之为幻读。就叫它 ” 一 , 一, 二 “ 一份数据 一份表 两次读取 结果一样
设置事务的隔离级别:
@Transactional(isolation=REPEATABLE_READ)
//默认隔离级别
拓展一下数据库的隔离级别
数据库默认隔离级别:Repeatable read 可重复读 与spring默认的隔离级别一样
事务的操作(完全注解开发)
1 在配置类中添加注解@EnableTransactionManagemet //开启事务的注解
再来看看示例 springboot的
Spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。
关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
你可以在启动类中添加如下方法,Debug测试,就能知道自动注入的是 PlatformTransactionManager 接口的哪个实现类。
@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
这些SpringBoot为我们自动做了,这些对我们并不透明,如果你项目做的比较大,添加的持久化依赖比较多,我们还是会选择人为的指定使用哪个事务管理器。
代码如下:
@EnableTransactionManagement
@SpringBootApplication
public class ProfiledemoApplication {
// 其中 dataSource 框架会自动为我们注入
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager) {
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。
然后在Service中,被 @Transactional 注解的方法,将支持事务。如果注解在类上,则整个类的所有方法都默认支持事务。
对于同一个工程中存在多个事务管理器要怎么处理,请看下面的实例,具体说明请看代码中的注释。
@EnableTransactionManagement // 开启注解事务管理,等同于xml配置文件中的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication implements TransactionManagementConfigurer {
@Resource(name="txManager2")
private PlatformTransactionManager txManager2;
// 创建事务管理器1
@Bean(name = "txManager1")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 创建事务管理器2
@Bean(name = "txManager2")
public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
// 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager2;
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
@Component
public class DevSendMessage implements SendMessage {
// 使用value具体指定使用哪个事务管理器
@Transactional(value="txManager1")
@Override
public void send() {
System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
send2();
}
// 在存在多个事务管理器的情况下,如果使用value具体指定
// 则默认使用方法 annotationDrivenTransactionManager() 返回的事务管理器
@Transactional
public void send2() {
System.out.println(">>>>>>>>Dev Send2()<<<<<<<<");
}
}
注:
如果Spring容器中存在多个 PlatformTransactionManager 实例,并且没有实现接口 TransactionManagementConfigurer 指定默认值,在我们在方法上使用注解 @Transactional 的时候,就必须要用value指定,如果不指定,则会抛出异常。
对于系统需要提供默认事务管理的情况下,实现接口 TransactionManagementConfigurer 指定。
对有的系统,为了避免不必要的问题,在业务中必须要明确指定 @Transactional 的 value 值的情况下。不建议实现接口 TransactionManagementConfigurer,这样控制台会明确抛出异常,开发人员就不会忘记主动指定。