searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Transactional的原理及注意事项

2025-06-06 08:25:35
1
0
Trasactional在java中作为事务的注解被广泛使用,本文将简单介绍它的原理以及注意事项,某些情况下会失效,以更合理的使用该注解。

1. 原理

1.1 事务的基本概念与 ACID 特性

事务是数据库操作的最小逻辑单元,确保一组操作要么全部成功,要么全部失败。其核心特性 ACID 包括:
 
  • 原子性(Atomicity):事务中的操作不可分割,要么全做,要么全不做。
  • 一致性(Consistency):事务执行前后,数据从一个合法状态转换到另一个合法状态。
  • 隔离性(Isolation):多个事务并发执行时,彼此互不干扰。
  • 持久性(Durability):事务提交后,数据修改永久保存到数据库。
 
在 Java 开发中,Spring 框架通过 @Transactional 注解简化了事务管理,其底层依赖数据库提供的事务机制(如 MySQL 的 InnoDB 引擎支持事务)和 Spring AOP 实现逻辑增强。

1.2 @Transactional 的实现机制

1.2.1 代理模式与 AOP

Spring 通过 AOP(面向切面编程)实现事务增强,核心流程如下:
  1. 代理创建:当 Spring 容器scan到 @Transactional 注解时,会为目标 Bean 生成代理对象(JDK 动态代理或 CGLIB 代理)。
  2. 切面逻辑:在代理对象的方法调用前后,织入事务开启、提交、回滚等逻辑。
  3. 调用链:客户端通过代理对象调用目标方法时,实际执行的是被事务逻辑包装的增强方法。

1.2.2 关键类与接口

  • PlatformTransactionManager:事务管理器接口,定义了事务操作的核心方法(getTransactioncommitrollback)。
    • DataSourceTransactionManager:基于数据源的事务管理器,适用于 JDBC 或 MyBatis 操作。
    • JpaTransactionManager:适用于 JPA 或 Hibernate 等 ORM 框架。
  • TransactionAttribute:事务属性接口,封装了事务的传播行为、隔离级别、超时时间等配置。
  • TransactionStatus:事务状态接口,用于记录事务的执行状态(是否新事务、是否已提交等)。

2.失效场景

2.1 spring未管理到所在类

目标类未使用 @Component@Service 等注解,未纳入 Spring 容器。
解决方法:对目标类正确的添加相应的Spring容器注解

2.2 方法非public

Transactional的生效要求是public方法
解决方法:对该事务方法添加public;

2.3 作用在私有方法上

@Transactional 修饰的方法不能是 private,因为Spring无法对私有方法生成动态代理;
解决方法:修改方法类型为类的public方法。

2.4 作用在static/final 方法上

CGLIB是通过生成目标类子类的方式生成代理类的,被final、static修饰后,无法继承父类与父类的方法。
解决方法:对需要事务的逻辑抽成类的方法,不使用final关键字。

2.5 方法被所在类其它方法引用

事务的管理是通过代理执行的方式生效的,如果是方法内部调用,将不会走代理逻辑,也就调用不到了。
解决方法:
  1. 将事务方法单独抽到新的类中,在原来的class里进行引用,可以被代理;
  2. 在原类中注入自身,使用该事务方法的地方,不是直接声明方法名,而是调用自身的该方法,以此完成代理。

2.6 抛错异常不符合约定

默认情况下事务仅回滚RuntimeException和Error,不回滚受检异常(例如IOException),因此如果抛出业务自定义的异常,可能无法事务回滚。
解决方法:配置rollback参数,将需要回滚的异常声明在注解中。

2.7 异常被内部捕获未抛出

同前一情况相反,出错而不抛出异常,则对注解来说是正常执行的逻辑,不会进行事务回滚。
解决方法:重新梳理业务逻辑,将需要事务回滚的异常抛出,并声明为rollback的类型;

2.8 逻辑中使用了多线程

因为Transactional是和线程绑定的,因此逻辑内部如果新开了线程,则原子性失效。
解决方法:梳理事务与多线程之间的关系,尽量规避在多线程中处理事务。
0条评论
作者已关闭评论
c****k
4文章数
0粉丝数
c****k
4 文章 | 0 粉丝
c****k
4文章数
0粉丝数
c****k
4 文章 | 0 粉丝
原创

Transactional的原理及注意事项

2025-06-06 08:25:35
1
0
Trasactional在java中作为事务的注解被广泛使用,本文将简单介绍它的原理以及注意事项,某些情况下会失效,以更合理的使用该注解。

1. 原理

1.1 事务的基本概念与 ACID 特性

事务是数据库操作的最小逻辑单元,确保一组操作要么全部成功,要么全部失败。其核心特性 ACID 包括:
 
  • 原子性(Atomicity):事务中的操作不可分割,要么全做,要么全不做。
  • 一致性(Consistency):事务执行前后,数据从一个合法状态转换到另一个合法状态。
  • 隔离性(Isolation):多个事务并发执行时,彼此互不干扰。
  • 持久性(Durability):事务提交后,数据修改永久保存到数据库。
 
在 Java 开发中,Spring 框架通过 @Transactional 注解简化了事务管理,其底层依赖数据库提供的事务机制(如 MySQL 的 InnoDB 引擎支持事务)和 Spring AOP 实现逻辑增强。

1.2 @Transactional 的实现机制

1.2.1 代理模式与 AOP

Spring 通过 AOP(面向切面编程)实现事务增强,核心流程如下:
  1. 代理创建:当 Spring 容器scan到 @Transactional 注解时,会为目标 Bean 生成代理对象(JDK 动态代理或 CGLIB 代理)。
  2. 切面逻辑:在代理对象的方法调用前后,织入事务开启、提交、回滚等逻辑。
  3. 调用链:客户端通过代理对象调用目标方法时,实际执行的是被事务逻辑包装的增强方法。

1.2.2 关键类与接口

  • PlatformTransactionManager:事务管理器接口,定义了事务操作的核心方法(getTransactioncommitrollback)。
    • DataSourceTransactionManager:基于数据源的事务管理器,适用于 JDBC 或 MyBatis 操作。
    • JpaTransactionManager:适用于 JPA 或 Hibernate 等 ORM 框架。
  • TransactionAttribute:事务属性接口,封装了事务的传播行为、隔离级别、超时时间等配置。
  • TransactionStatus:事务状态接口,用于记录事务的执行状态(是否新事务、是否已提交等)。

2.失效场景

2.1 spring未管理到所在类

目标类未使用 @Component@Service 等注解,未纳入 Spring 容器。
解决方法:对目标类正确的添加相应的Spring容器注解

2.2 方法非public

Transactional的生效要求是public方法
解决方法:对该事务方法添加public;

2.3 作用在私有方法上

@Transactional 修饰的方法不能是 private,因为Spring无法对私有方法生成动态代理;
解决方法:修改方法类型为类的public方法。

2.4 作用在static/final 方法上

CGLIB是通过生成目标类子类的方式生成代理类的,被final、static修饰后,无法继承父类与父类的方法。
解决方法:对需要事务的逻辑抽成类的方法,不使用final关键字。

2.5 方法被所在类其它方法引用

事务的管理是通过代理执行的方式生效的,如果是方法内部调用,将不会走代理逻辑,也就调用不到了。
解决方法:
  1. 将事务方法单独抽到新的类中,在原来的class里进行引用,可以被代理;
  2. 在原类中注入自身,使用该事务方法的地方,不是直接声明方法名,而是调用自身的该方法,以此完成代理。

2.6 抛错异常不符合约定

默认情况下事务仅回滚RuntimeException和Error,不回滚受检异常(例如IOException),因此如果抛出业务自定义的异常,可能无法事务回滚。
解决方法:配置rollback参数,将需要回滚的异常声明在注解中。

2.7 异常被内部捕获未抛出

同前一情况相反,出错而不抛出异常,则对注解来说是正常执行的逻辑,不会进行事务回滚。
解决方法:重新梳理业务逻辑,将需要事务回滚的异常抛出,并声明为rollback的类型;

2.8 逻辑中使用了多线程

因为Transactional是和线程绑定的,因此逻辑内部如果新开了线程,则原子性失效。
解决方法:梳理事务与多线程之间的关系,尽量规避在多线程中处理事务。
文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0