Java--SpringBoot使用@Transactional注解添加事务

一、Java事务

1、通常的观念认为,事务仅与数据库相关。

事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)一致性(consistency)隔离性 (isolation)和持久性(durability)的缩写。

事务的原子性:表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。
事务的一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。
事务的隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。
事务的持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确。

通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行

既然事务的概念从数据库而来,那Java事务是什么?之间有什么联系? 实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到Java程序代码中。 因此,数据库操作的事务习惯上就称为Java事务。

2、为什么需要Java事务

事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。

举一个简单例子: 比如银行转帐业务,账户A要将自己账户上的1000元转到B账 户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束, B因为网络中断而操作 失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操作就需要事务,将A账户资金减少和B账户资金增加方 到一个事务里面,要么全部执行成功,要么操作全部撤销,这样就保持了数据的安全性。

二、SpringBoot事务

在Spring中,事务有两种实现方式,分别是编程式事务管理和声明式事务管理两种方式。 
1、编程式事务管理: 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。 
2、声明式事务管理: 建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。 
声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单。推荐使用

在我们使用Springboot框架来开发时,Springboot已经帮我们封装好对底层数据库事务的操作(Springboot版本2.3.1.RELEASE,整合了mybatis,数据库使用MySQL),降低了我们学习、操作使用数据库事务的成本。

三、SpringBoot事务配置使用

1、依赖的Jar包pom.xml

Spring Boot中实现事务没有额外的Jar包,还是基本的数据库访问包,比如mybatis

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.13</version>
</dependency>

2、配置事务,使用注解@Transactional事务

在Mybatis中使用事务,非常简单,只需要在方法上增加注解@Transactional,无需任何配置

@Service
public class UserServiceImpl implements UserService
{
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private AuthService authService;

    /**
    * 更新user的权限,角色信息
    */
    @Override
    @Transactional
    public updateUser(User user){
        userMapper.updateUser(user);

        roleService.insertRoleUser(user);

        authService.updateAuthUser(user);
    }
}

@Transactional可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。因此可以在Service层和Controller层使用

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

我看网上说还需要导入Spring boot提供的JDBC或JPA依赖,

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
   <scope>test</scope>
</dependency>
 
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
   <scope>test</scope>
</dependency>

@SpringBootApplication上添加注解@EnableTransactionManagement,如下:

关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。

我们通过Debug测试,就能知道自动注入的是 PlatformTransactionManager 接口的哪个实现类

//网上说在springboot的启动中添加注解@EnableTransactionManagement
@SpringBootApplication
@EnableTransactionManagement //开启事务管理注解模式 最新的版本可以省略
@MapperScan("com.xz.springboot.mapper") //扫描该包下所有的接口并为该接口生成实现类
public class SpringbootApplication {

    @Bean
    public Object testBean(PlatformTransactionManager platformTransactionManager){
        System.out.println(">>>>>>>>>>" +         
        platformTransactionManager.getClass().getName());
        return new Object();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}

注:

博主亲测,现在 版本Springboot确实不需要上述导入包和启动类添加@EnableTransactionManagement注解

传统工程中使用XML配置事务时,需要给DataSourceTransactionManager事务管理器配置数据源DataSource,那么Spring Boot进行自动配置的话,
Spring Boot在注入DataSourceTransactionManager事务管理器时,Spring Boot会自动到Spring容器中寻找我们配置好的DataSource。也即是之前我们的手动操作,现在使用Spring Boot变成了自动化操作。

四、事务注解详解

默认遇到throw new RuntimeException(“…”);会回滚 
需要捕获的throw new Exception(“…”);不会回滚

指定回滚

@Transactional(rollbackFor=Exception.class) 
    public void methodName() {
       // 不会回滚
       throw new Exception("...");
    } 

指定不回滚

@Transactional(noRollbackFor=Exception.class)
    public ItimDaoImpl getItemDaoImpl() {
        // 会回滚
        throw new RuntimeException("注释");
    } 

如果有事务,那么加入事务,没有的话新建一个(不写的情况下)

@Transactional(propagation=Propagation.REQUIRED) 

容器不为这个方法开启事务

@Transactional(propagation=Propagation.NOT_SUPPORTED)

readOnly=true只读,不能更新,删除

@Transactional (propagation = Propagation.REQUIRED,readOnly=true) 

设置超时时间

@Transactional (propagation = Propagation.REQUIRED,timeout=30)

设置数据库隔离级别

@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)

五、事务注解 -- @Transactional 使用注意事项

1、不要在接口上声明 @Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。

2、将 @Transactional  放置在类级的声明中 放在类声明 会使得所有方法都有事务 故 @Transactional应该放在方法级别 不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。

3、使用了 @Transactional的方法,对同一个类里面的方法调用, @Transactional无效。比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)

4、使用了 @Transactional 的方法, 只能是public, @Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。

5、经过在ICORE-CLAIM中测试,效果如下:

6、抛出受查异常XXXException,事务会回滚。

7、抛出运行时异常NullPointerException,事务会回滚。

8、Quartz中,execute直接调用加了 @Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)

9、异步任务中,execute直接调用加了 @Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)

10、在action中加上 @Transactional,不会回滚。切记不要在action中加上事务。

11、在service中加上 @Transactional,如果是action直接调该方法,会回滚,如果是间接调,不会回滚。(即上文3提到的)

12、在service中的private加上 @Transactional,事务不会回滚。

  • 25
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值