SpringBoot 使用 @Transactional 注解配置事务

本文深入探讨SpringBoot中@Transactional注解的使用方法,包括事务的传播行为、隔离级别、超时时间等属性详解,以及在实际编码中需要注意的关键点。

pringBoot项目中需要配置事务管理,所以在这里系统地整理下关于@Transactional 注解相关的知识。

1、详细介绍

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。 
编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。本文将着重介绍基于 @Transactional 注解的事务管理。

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

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

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

2、使用方法

注:SpringBoot项目会自动配置一个 DataSourceTransactionManager,所以我们只需在方法(或者类)加上 @Transactional 注解,就自动纳入 Spring 的事务管理了

如下在方法加上 @Transactional 注解:

@Transactional
public void insertUser() {
    User user = new User("abysscat");
    userMapper.insertOneUser(user);          //向数据库插入一条记录

    throw new RuntimeException("发生异常");   //手动模拟抛出异常
}



抛出异常之后,事务会自动回滚,数据不会插入到数据库。

3、Spring事务的默认回滚机制

Spring的事务管理默认是针对unchecked exception回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须抛出异常,若使用try-catch对其异常捕获则不会进行回滚!(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);而checked exception 则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它,所以必须在service捕获异常,然后再次抛出,这样事务方才起效。

4、Java 异常介绍

Java标准库内建了一些通用的异常,这些类以 Throwable 为顶层父类。 
Throwable又派生出 Error 类和 Exception 类。

错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。

异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

 
总体上我们根据Javac对异常的处理要求,将异常类分为2类。

非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

需要明确的是:检查和非检查是对于javac来说的,这样就很好理解和区分了。

5、@Transactional 注解属性介绍

value 和 transactionManager 属性 
它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。

propagation 属性 
事务的传播行为,默认值为 Propagation.REQUIRED。 
可选的值有:

Propagation.REQUIRED

如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

Propagation.SUPPORTS

如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

Propagation.MANDATORY

如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

Propagation.REQUIRES_NEW

重新创建一个新的事务,如果当前存在事务,暂停当前的事务。这个属性可以实现: 
类A中的a方法加上默认注解@Transactional(propagation = Propagation.REQUIRED),类B中的b方法加上注解@Transactional(propagation = Propagation.REQUIRES_NEW),然后在a方法中调用b方法操作数据库,再在a方法最后抛出异常,会发现a方法中的b方法对数据库的操作没有回滚,因为Propagation.REQUIRES_NEW会暂停a方法的事务。

Propagation.NOT_SUPPORTED

以非事务的方式运行,如果当前存在事务,暂停当前的事务。

Propagation.NEVER

以非事务的方式运行,如果当前存在事务,则抛出异常。

Propagation.NESTED

和 Propagation.REQUIRED 效果一样。

isolation 属性 
事务的隔离级别,默认值为 Isolation.DEFAULT。

可选的值有:

Isolation.DEFAULT 
使用底层数据库默认的隔离级别。

Isolation.READ_UNCOMMITTED

Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
timeout 属性 
事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

readOnly 属性 
指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

rollbackFor 属性 
用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

noRollbackFor 属性 
抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

6、@Transactional事务几点注意(转载)

这里面有几点需要大家留意: 
A. 一个功能是否要事务,必须纳入设计、编码考虑。不能仅仅完成了基本功能就ok。 
B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常、测试回滚),确保事务生效。 
C. 以下列了事务使用过程的注意事项,请大家留意。

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.spring的事务在抛异常的时候会回滚,如果是catch捕获了,事务无效。可以在catch里面加上throw new RuntimeException();
使用例子

@EnableTransactionManagement
@SpringBootApplication
@EnableScheduling
@MapperScan("com.test.modelmanager.domain.mapper")
@ComponentScan(
		basePackages = {"com.test"}
)
public class ModelManagerApplication extends SpringBootServletInitializer {
	
	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		return builder.sources(ModelManagerApplication.class);
	}
	public static void main(String[] args){
		SpringApplication app = new SpringApplication(ModelManagerApplication.class);

		app.setWebEnvironment(true);
		app.run(args);
	}
}
@Transactional(propagation=Propagation.REQUIRED)
public void addUser(User user) {
    userDao.addUser(user);
    String string  = null;
    if(string.equals("")) {
        int i = 0;
    }
}
@Test  
public void addUserTest(){  
    User user = new User();
    user.setUserName("luoguohui1");
    user.setUserPassword("luoguohui1");
    userService.addUser(user);
}  

发现无法插入进去,但是如果把@Transactional去掉,即代码如下,虽然出现异常,但是数据库中还是有添加对应数据的:

参看文档:

1.Spring3.0配置多个事务管理器(即操作多个数据源)的方法

2.spring配置多数据源涉及事务无法切换解决方案

3.springboot多数据源读写分离和主库数据源service层事务控制

<think>好的,我现在需要帮用户介绍Spring Boot的@Transactional注解。用户之前已经询问过Java分布式事务,现在可能是在继续深入学习Spring Boot中的事务管理。首先,我应该回想一下用户之前的对话,了解他们的知识背景,可能他们已经对事务的基本概念有了一定了解,特别是分布式事务,所以现在需要更具体到Spring Boot中的事务实现。 用户的问题是关于@Transactional注解的,我需要详细解释这个注解的作用、使用方式以及相关注意事项。首先,要明确@TransactionalSpring框架提供的声明式事务管理的关键注解,它简化了事务配置,通过AOP实现。接下来,我需要分点介绍它的核心功能,比如自动提交和回滚规则,隔离级别,传播行为,以及只读事务等。 然后,我需要考虑用户可能的实际应用场景,比如在Service层的方法上添加@Transactional来管理事务。这时候要举一个简单的代码示例,说明如何在Spring Boot项目中使用这个注解。同时,用户可能对注解的参数感兴趣,比如propagation和isolation,这些需要详细解释每个参数的作用和可选值。 另外,用户可能在使用过程中遇到一些常见问题,比如注解不生效的情况。这时候需要总结一些常见的原因,比如方法是否为public、是否被同一个类中的其他方法调用导致AOP失效、异常类型未被正确捕获等。这些注意事项对于用户正确使用@Transactional非常重要,避免踩坑。 还需要比较声明式事务和编程式事务的区别,说明@Transactional属于声明式,而编程式事务则需要使用TransactionTemplate。用户可能想知道两者的适用场景,比如声明式更简洁,编程式更灵活。 最后,要给出最佳实践,比如在Service层使用注解,避免在Controller层使用,以及正确处理异常类型。这些建议能帮助用户更好地应用@Transactional,确保事务管理既有效又高效。 需要确保整个解释清晰易懂,结合代码示例和实际场景,让用户能够快速理解并应用。同时,要注意与之前介绍的分布式事务解决方案(如Seata)进行区分,说明@Transactional主要适用于本地事务,而在分布式系统中可能需要结合其他方案。这样用户就不会混淆两者的使用场景。</think>好的!我来详细介绍一下 Spring Boot 中 **`@Transactional`** 注解的作用、使用方法和注意事项。 --- ### **什么是 @Transactional?** `@Transactional` 是 Spring 框架提供的**声明式事务管理**的核心注解,用于简化数据库事务配置。通过该注解,可以**自动管理事务的提交、回滚**,无需手动编写 `try-catch` 和事务边界代码(如 `commit()` 或 `rollback()`)。 --- ### **核心功能** 1. **自动事务控制** - 方法执行前开启事务,方法正常结束时提交事务。 - 方法抛出异常时回滚事务(默认对 `RuntimeException` 和 `Error` 回滚)。 2. **灵活配置** - 支持配置事务的**传播行为**、**隔离级别**、**超时时间**、**只读模式**等。 3. **声明式编程** - 基于 AOP 实现,通过注解声明事务规则,代码侵入性低。 --- ### **基本使用** #### 1. **在 Spring Boot 中启用事务** 确保主类或配置类开启事务管理: ```java @SpringBootApplication @EnableTransactionManagement // 启用事务管理(Spring Boot 默认已启用,可省略) public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` #### 2. **在 Service 层使用注解** 在需要事务管理的方法或类上添加 `@Transactional`: ```java @Service public class UserService { @Autowired private UserRepository userRepository; // 默认事务配置:对 RuntimeException 回滚 @Transactional public void createUser(User user) { userRepository.save(user); // 操作1 sendWelcomeEmail(user); // 操作2(假设可能抛出异常) } // 自定义回滚规则 @Transactional(rollbackFor = Exception.class) public void updateUser(User user) throws Exception { userRepository.update(user); if (someCondition) { throw new Exception("自定义异常"); // 默认不回滚,需通过 rollbackFor 指定 } } } ``` --- ### **关键参数详解** | 参数 | 说明 | |------| | `propagation` | 事务传播行为(默认 `Propagation.REQUIRED`) | | `isolation` | 事务隔离级别(默认 `Isolation.DEFAULT`,使用数据库默认级别) | | `timeout` | 事务超时时间(秒),超时后自动回滚(默认 -1,不超时) | | `readOnly` | 是否开启只读事务(默认 `false`,优化查询性能时设为 `true`) | | `rollbackFor` | 指定触发回滚的异常类型(如 `rollbackFor = {SQLException.class}`) | | `noRollbackFor` | 指定不触发回滚的异常类型 | #### **常见传播行为(Propagation)** - **REQUIRED**(默认):如果当前存在事务,则加入;否则新建一个事务。 - **REQUIRES_NEW**:始终新建事务,挂起当前事务(如果存在)。 - **NESTED**:在当前事务中嵌套子事务(依赖数据库支持,如 MySQL 的保存点)。 - **SUPPORTS**:如果当前有事务,则加入;否则以非事务方式运行。 --- ### **常见问题与注意事项** #### 1. **注解不生效的常见原因** - **方法非 public**:`@Transactional` 只能应用到 public 方法上。 - **自调用问题**:同一类中方法 A 调用方法 B(带有 `@Transactional`),注解会失效(因 AOP 代理机制)。 - **异常被捕获**:若在方法内捕获了异常未重新抛出,事务不会回滚。 - **数据库引擎不支持**:如 MySQL 的 MyISAM 引擎不支持事务,需使用 InnoDB。 #### 2. **事务嵌套与传播行为** - 不同传播行为会影响事务的边界,需根据业务场景谨慎选择。 - 示例:方法 A(`REQUIRED`)调用方法 B(`REQUIRES_NEW`),B 会在独立事务中执行。 #### 3. **声明式 vs 编程式事务** - **声明式**:通过 `@Transactional` 注解(推荐,代码简洁)。 - **编程式**:通过 `TransactionTemplate` 手动控制事务(更灵活)。 --- ### **最佳实践** 1. **在 Service 层使用**:事务管理通常放在业务逻辑层(Service),而非 DAO 或 Controller。 2. **明确指定回滚异常**:根据业务需要配置 `rollbackFor`,避免因异常类型不符导致事务未回滚。 3. **避免长事务**:设置合理的 `timeout`,避免事务长时间占用数据库连接。 4. **只读查询优化**:对查询方法设置 `@Transactional(readOnly = true)`。 --- ### **示例:事务传播行为** ```java @Service public class OrderService { @Transactional(propagation = Propagation.REQUIRED) public void placeOrder(Order order) { // 操作1:扣减库存 inventoryService.deductStock(order); // 操作2:创建订单(独立事务,不受外部事务回滚影响) orderRepository.createOrder(order); } } @Service public class InventoryService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void deductStock(Order order) { // 扣减库存逻辑 } } ``` --- ### **总结** `@Transactional` 是 Spring Boot 中管理数据库事务的核心工具,通过简单注解即可实现复杂的ACID特性。使用时需注意传播行为、异常回滚规则以及常见失效场景,结合业务需求合理配置参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值