6 个Spring tx 事务注解:4种隔离&7种事务传播业务案例(必须收藏)

image.png

Spring框架的事务管理模块,通过一系列注解提供了一种强大而灵活的方式来处理企业级Java应用程序中的事务。@Transactional注解是这一模块的核心,它支持多种事务属性,如传播行为、隔离级别和超时设置,允许开发者以声明式的方式管理事务的边界和特性。此外,@Propagation@Isolation@EnableTransactionManagement等注解进一步丰富了Spring的事务管理能力,使得开发者可以根据不同的业务场景定制事务策略。这些注解的使用不仅简化了事务代码的编写,还提高了应用程序的健壮性和可测试性。Spring事务管理的声明式特性,让开发者能够将业务逻辑与事务管理逻辑分离,从而创建出更加清晰、可维护的代码结构。

肖哥弹架构 跟大家“弹弹” 框架注解使用,需要代码关注

历史热点文章

6个注解说明

@Transactional

1.1 注解作用介绍

@Transactional 注解用于声明方法或类,确保它们在事务的上下文中执行。这是Spring中处理事务的最常用方式。
1.2 注解属性介绍

  • value: 指定事务管理器的名称。
  • timeout: 以秒为单位设置事务的超时时间。
  • readOnly: 指示事务是否为只读事务。
  • rollbackFor: 指定导致事务回滚的异常类数组。
  • noRollbackFor: 指定即使抛出这些异常也不触发事务回滚的类数组。
  • propagation: 指定事务的传播行为,默认为Propagation.REQUIRED
  • isolation: 指定事务的隔离级别,默认使用底层数据库的默认隔离级别。

1.3 注解业务案例

@Transactional(
    value = "customTransactionManager", 
    timeout = 5, 
    readOnly = false, 
    rollbackFor = {IllegalArgumentException.class, CustomException.class}, 
    noRollbackFor = {NoRollbackException.class}, 
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED
)
public void updateUserBalance(Long userId, int amount) {
    // 业务逻辑,如更新用户余额
    accountRepository.updateBalance(userId, amount);
    if (accountRepository.getBalance(userId) < 0) {
        throw new CustomException("Insufficient funds");
    }
}

@EnableTransactionManagement

2.1 注解作用介绍

@EnableTransactionManagement 注解用于开启基于注解的事务管理,允许在Spring应用程序中使用@Transactional注解。

2.2 注解属性介绍

  • 无特定属性。

2.3 注解业务案例

@Configuration
@EnableTransactionManagement
public class TransactionManagementConfig {
    // 其他配置
}

@TransactionManagementConfigurer

3.1 注解作用介绍

@TransactionManagementConfigurer 注解用于自定义事务管理器的配置,适用于需要自定义事务管理器时。

3.2 注解属性介绍

  • 无特定属性。

3.3 注解业务案例

@Configuration
public class CustomTransactionManagementConfig implements TransactionManagementConfigurer {
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new MyCustomTransactionManager();
    }
}

@Propagation

4.1 注解作用介绍

@Propagation 注解用于指定事务的传播行为,与@Transactional注解的propagation属性配合使用。

4.2 注解属性介绍

  • value: 指定事务的传播行为,如Propagation.REQUIREDPropagation.REQUIRES_NEW等。

4.3 注解业务案例

java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void someTransactionalMethod() {
    // 业务逻辑
}

@Isolation

5.1 注解作用介绍 @Isolation 注解用于设置事务的隔离级别,与@Transactional注解的isolation属性配合使用。

5.2 注解属性介绍

  • value: 指定隔离级别,如Isolation.READ_COMMITTEDIsolation.REPEATABLE_READ等。

5.3 注解业务案例

java
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void someTransactionalMethod() {
    // 业务逻辑
}

@TransactionAttribute

6.1 注解作用介绍

@TransactionAttribute 注解用于指定事务的具体属性,如传播行为和隔离级别,通常与@Transactional注解配合使用。

6.2 注解属性介绍

  • value: 指定事务属性的组合。

6.3 注解业务案例

@Transactional(
    transactionAttribute = @TransactionAttribute(
        readOnly = true, 
        propagation = Propagation.SUPPORTS
    )
)
public void someTransactionalMethod() {
    // 业务逻辑
}

@Propagation@Isolation@TransactionAttribute注解通常作为@Transactional注解的参数使用,而不是单独使用。@EnableTransactionManagement@TransactionManagementConfigurer注解用于配置Spring的事务管理能力。

4种隔离策略案例说明

Isolation.DEFAULT

@Transactional(isolation = Isolation.DEFAULT)
public void processUserOrder() {
    // 业务逻辑,使用数据库默认隔离级别
}
  • 使用条件: 使用数据库默认隔离级别,适用于大多数标准业务逻辑,不特别指定隔离级别以利用数据库的默认配置,适合于那些对隔离级别没有特别要求的场景。
  • 业务案例举例: 在处理用户订单时,如果业务逻辑不涉及复杂的并发操作,使用默认隔离级别可以确保与数据库配置的一致性,同时简化事务管理。

Isolation.READ_UNCOMMITTED

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void generateInventoryReport() {
    // 生成库存报告,允许读取未提交的数据
}
  • 使用条件: 在生成报告或统计数据时,可能不需要完全的数据一致性,此时可以牺牲一点一致性以换取更高的并发性能,适合于那些对数据实时性要求不高的报告生成场景。
  • 业务案例举例: 当生成月末库存报告时,由于报告的目的是提供概览信息,而不是用于交易处理,因此可以接受读取到未提交的事务数据。

Isolation.READ_COMMITTED

@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferFunds(AccountId from, AccountId to, BigDecimal amount) {
    // 资金从一个账户转移到另一个账户
}
  • 使用条件: 保证在事务中读取的数据是其他事务已经提交的数据,适用于需要避免读取到其他未提交事务影响的数据的金融交易场景。
  • 业务案例举例: 在执行银行账户之间的资金转账时,必须确保转账操作基于已提交的账户余额,以防止出现不一致的财务记录。

Isolation.REPEATABLE_READ

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void checkAndConfirmBooking(BookingId bookingId) {
    // 检查预订信息并确认预订
}
  • 使用条件: 在处理过程中需要多次读取同一数据集,保证这些数据在事务过程中的一致性,适用于库存检查和订单处理,确保库存数据不被其他事务修改。
  • 业务案例举例: 在旅游预订系统中确认客户预订时,需要多次检查房间的可用性,以确保在确认过程中房间不会被其他客户预订。

Isolation.SERIALIZABLE

@Transactional(isolation = Isolation.SERIALIZABLE)
public void updateCriticalSystemParameters() {
    // 更新关键系统参数
}
  • 使用条件: 对于更新关键配置或敏感数据,需要确保完全的隔离,避免并发访问导致的数据不一致,虽然这会牺牲一定的并发性能。
  • 业务案例举例: 当需要更新影响系统全局运行的关键参数(如交易费用率)时,使用最高隔离级别可以确保更新操作的原子性和一致性,防止配置错误。

事务7种传播机制说明

Propagation.REQUIRED

  • 案例:

    java
    public class OrderService {
        @Transactional(Propagation.REQUIRED)
        public void processOrder(Order order) {
            // 保存订单
            saveOrder(order);
            // 扣减库存
            reduceInventory(order);
        }
    }
    
  • 使用条件: Propagation.REQUIRED是默认设置,适用于大多数业务逻辑,确保方法在事务上下文中执行,如果当前存在事务,则加入该事务;如果不存在,则新建一个。

  • 方法场景: 当需要执行一系列数据库操作,这些操作需要作为一个单一的原子工作单元被提交或回滚,例如处理订单时既要保存订单信息又要扣减库存。

Propagation.REQUIRES_NEW

public class UserService {
    @Transactional(Propagation.REQUIRES_NEW)
    public void deleteUser(User user) {
        // 删除用户
        userRepository.delete(user);
        // 清除用户相关数据
        clearUserData(user);
    }
}
  • 使用条件: 当需要执行一个新事务,并且这个新事务必须独立于任何现有事务时使用,即使存在一个活动的事务,REQUIRES_NEW也会创建一个新的事务。
  • 方法场景: 在删除用户时,可能需要确保删除操作在一个新的事务中执行,以避免由于现有事务的回滚而影响到删除操作。

Propagation.SUPPORTS

public class ProductService {
    @Transactional(Propagation.SUPPORTS)
    public List<Product> listProducts() {
        // 返回产品列表
        return productService.findAll();
    }
}
  • 使用条件: 当事务方法被调用时,如果已经存在一个事务,它应该在该事务的上下文中执行;如果没有事务存在,它应该非事务性地执行。
  • 方法场景: 查询操作,如列出所有产品的列表,如果存在事务,则在事务中执行查询,否则进行普通的查询。

Propagation.MANDATORY

public class AccountService {
    @Transactional(Propagation.MANDATORY)
    public void transferFunds(Account from, Account to, BigDecimal amount) {
        // 从from账户扣款
        from.debit(amount);
        // 给to账户加款
        to.credit(amount);
    }
}
  • 使用条件: 事务方法是在一个必须存在事务的上下文中被调用,如果不存在事务,抛出异常。
  • 方法场景: 执行资金转账时,必须确保在事务的上下文中执行,以保证资金的准确性和一致性。

Propagation.NESTED

public class TransactionService {
    @Transactional
    public void executeTransactions() {
        // 开启一个事务
        performTransaction();
        // 嵌套事务,进行更细粒度的操作
        @Transactional(Propagation.NESTED)
        performNestedTransaction();
    }
}
  • 使用条件: 需要在现有的事务中执行一个或多个操作,这些操作需要自己的事务上下文,例如,保存点或额外的隔离级别。
  • 方法场景: 在执行一系列需要额外事务控制的事务时,如在主事务中执行额外的事务以处理更细粒度的业务逻辑。

Propagation.NOT_SUPPORTED

public class CacheService {
    @Transactional(Propagation.NOT_SUPPORTED)
    public void refreshCache() {
        // 刷新缓存,不需要事务
        cacheManager.clearCache();
    }
}
  • 使用条件: 事务方法不应该运行在任何事务上下文中,如果存在一个活动的事务,它应该被挂起。
  • 方法场景: 执行不需要事务的操作,如刷新缓存,以避免与可能存在的事务冲突。

Propagation.NEVER

public class AuditService {
    @Transactional(Propagation.NEVER)
    public void logAuditEvent(AuditEvent event) {
        // 记录审计事件,不允许在事务上下文中执行
        auditRepository.save(event);
    }
}
  • 使用条件: 事务方法不应该运行在任何事务上下文中,如果存在一个活动的事务,抛出异常。
  • 方法场景: 记录审计事件时,需要确保这些记录操作不会受到任何现有事务的影响,以保证审计数据的完整性。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,188评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,464评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,562评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,893评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,917评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,708评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,430评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,342评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,801评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,976评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,115评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,804评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,458评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,008评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,135评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,365评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,055评论 2 355

推荐阅读更多精彩内容