所谓事务就是保证一个操作只有两种状态,完成状态或者初始状态,不会出现完成了99%的情况。我们为什么需要事务呢?用个最经典例子说明一下,甲在银行转账,账户有1000元,转给乙500元,如果转账成功,那么甲账户只有500元,乙账户增加500元。但是如果甲转账时,银行停电了呢,甲账户少了500元,但乙账户却因为停电中断了转账操作导致账户还是原来的数额。这样的情况明显是不合理,既然转账失败了,那钱就应该退回原账户才对。所以这时候我们就要用事务来保证转账操作要么成功,甲少了500元,乙增加500元,要么转账失败,甲还是原来的数额,乙也是原来的数额。
Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
1.原子性,事务由一系列动作组成,但这个操作无法被继续分割。原子性保证动作要么全部完成,要么一个都没有完成。
2.一致性,事务一旦完成,无论是成功还是失败,所有业务数据都是一致的,不能出现甲转了500,但乙却没有增加500的情况。
3.隔离性,可能有多个事务同事处理同一个数据,应保证事务之间隔离性,防止数据损坏。
4.持久性,一旦事务完成,不管发生什么系统错误,事务结果都不能再可变动,通常会将事务结果写入持久化存储器中。
隔离级别定义了一个事务可能受其他并发事务影响的程度。 当多个事务并发处理相同的数据时就有可能出现下列问题。
脏读:后一个事务读取了前一个事务修改了却没有提交的值,如果此时发生异常,数据回滚了,此时后一个事务获得的数据就是无效的。
不可重复读:一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读:幻读与不可重复读类似。它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据时。在随后的查询中,第一个事务就会发现多了一些原本不存在的记录。
隔离级别就是为了防止这些现象的出现,而定义的。
事务的传播行为是指,当一个事务方法被另一个事务方法调用时,两个事务是怎样的关系。例如方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
1.引入maven依赖
org.springframework
spring-jdbc
5.1.7.RELEASE
mysql
mysql-connector-java
8.0.16
2.配置事务管理器
Spring并不直接管理事务,而提供一个Spring事务管理器的接口,通过这个接口,向各个框架提供了对应的事务管理器接口,将具体的事务管理的实现交给了各个框架。
org.springframework.transaction.PlatformTransactionManager
上面的事务管理器是基于jdbc的事务管理器
基于Hibernate的事务管理器
Java持久化API事务(JPA)
Java原生API事务
如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:
JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。
tx:method标签代表哪些方法需要开启事务管理。
rollback-for属性指定发生什么异常时回滚事务。
propagation属性指定事务的传播行为,默认为REQUIRED
(1) REQUIRED,,如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
(2) SUPPORTS,如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
(3) MANDATORY,如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
(4) REQUIRES_NEW,重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
(5) NOT_SUPPORTED,以非事务的方式运行,如果当前存在事务,暂停当前的事务。
(6) NEVER,以非事务的方式运行,如果当前存在事务,则抛出异常。
(7) NESTED,和 Propagation.REQUIRED 效果一样。
isolation属性指定事务的隔离级别,默认值为 Isolation.DEFAULT。
PS:若未指定rollback-for属性的值,或值为空,则只有发生RuntimeException异常时才会回滚事务。
package FrameWork;
import FrameWork.Advice.myAdvice;
import FrameWork.Service.MusicService;
import FrameWork.Service.UserService;
import FrameWork.ServiceImpl.MusicServiceImpl;
import FrameWork.ServiceImpl.UserServiceImpl;
import FrameWork.bean.Music;
import FrameWork.bean.User;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfig {
@Bean
public Music music()
{
return new Music();
}
@Bean
public User user()
{
return new User();
}
@Bean
public UserService userService()
{
return new UserServiceImpl();
}
@Bean
public MusicService musicService()
{
return new MusicServiceImpl();
}
@Bean
public myAdvice myAdvice()
{
return new myAdvice();
}
@Bean
public DriverManagerDataSource driverManagerDataSource(){
DriverManagerDataSource driverManagerDataSource=new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/mybatistest?characterEncoding=utf8&useSSL=true&serverTimezone=UTC");
driverManagerDataSource.setPassword("root");
driverManagerDataSource.setUsername("root");
return driverManagerDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(){
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(driverManagerDataSource());
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager transactionManager(){
DataSourceTransactionManager dataSourceTransactionManager=new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(driverManagerDataSource());
return dataSourceTransactionManager;
}
}
@Transactional(rollbackFor = Exception.class)
public void getUser(String name,String sign) throws Exception {
jdbcTemplate.update("insert into user(name,sign) values('"+name+"','"+sign+"')");
throw new Exception("异常");
}
从代码上可以看出,使用注解配置事务要比xml方便许多。
@EnableTransactionManagement注解代表开启事务支持,标注在配置类上。
@Transactional注解标记使用事务,如果注解在类上,则类中所有public方法使用事务,注解在方法上则只有该方法使用。
PS:@Transactional注解只能对public方法生效,注解到其他非public方法上时不会报错,但不使用事务。
这里是完全采用java配置的,你也可以选择使用xml配置bean和事务管理器,不配置tx:advice和aop:advisor标签,只需要在xml中加入下面的配置
此标签作用相当于@EnableTransactionManagement注解。你现在已经可以使用@Transactional注解配置事务了。
更多关于Spring事务的详细内容,请参考博客:https://www.cnblogs.com/yixianyixian/p/8372832.html