当前位置: 首页 > news >正文

Java-140 深入浅出 MySQL Spring事务失效的常见场景与解决方案详解(2)

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!“快的模型 + 深度思考模型 + 实时路由”,持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年09月29日更新到:
Java-136 深入浅出 MySQL Spring Boot @Transactional 使用指南:事务传播、隔离级别与异常回滚策略
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

请添加图片描述

事务失效场景(2)

参考来源

● https://heapdump.cn/article/5542790

续接上节

访问权限不是 public

在 Spring 框架中,事务失效是一个常见的问题,特别是在使用基于注解的声明式事务管理时。这种情况通常是由于 Spring 的事务机制底层实现原理导致的。具体来说:

  1. 实现机制分析:

    • Spring 的事务管理是通过 AOP(面向切面编程)机制实现的
    • AOP 的核心实现方式是使用动态代理(Dynamic Proxy)技术
    • 对于 JDK 动态代理,要求目标方法必须是 public 的才能被代理
  2. 具体失效原因:

    • 当方法不是 public 时,在 AbstractFallbackTransactionAttributeSource 类的 computeTransactionAttribute() 方法中
    • 会直接返回 null,表示不应用事务属性
    • 从而导致事务功能失效
  3. 源码分析路径:

    AbstractFallbackTransactionAttributeSource
    └── computeTransactionAttribute()└── 检查方法修饰符是否为 public├── 是:继续处理事务属性└── 否:返回 null
    
  4. 实际应用中的典型场景:

    • 开发者在 private 或 protected 方法上添加 @Transactional 注解
    • 在同一个类内部调用带有 @Transactional 注解的方法(这种自调用也会导致事务失效)
    • 在非 public 方法上使用 Spring 的事务注解
  5. 解决方案:

    • 确保所有需要事务管理的方法都是 public 的
    • 避免在同一个类中进行自调用
    • 考虑使用 AspectJ 模式代替动态代理模式(需要额外配置)
    • 对于内部调用,可以通过 AopContext.currentProxy() 获取代理对象

建议开发者在遇到事务失效问题时,首先检查方法的访问修饰符是否符合要求,这是最常见的事务失效原因之一。在 Spring 框架中,事务失效是一个常见的问题,特别是在使用基于注解的声明式事务管理时。这种情况通常是由于 Spring 的事务机制底层实现原理导致的。具体来说:

  1. 实现机制分析:

    • Spring 的事务管理是通过 AOP(面向切面编程)机制实现的
    • AOP 的核心实现方式是使用动态代理(Dynamic Proxy)技术
    • 对于 JDK 动态代理,要求目标方法必须是 public 的才能被代理
  2. 具体失效原因:

    • 当方法不是 public 时,在 AbstractFallbackTransactionAttributeSource 类的 computeTransactionAttribute() 方法中
    • 会直接返回 null,表示不应用事务属性
    • 从而导致事务功能失效
  3. 源码分析路径:

    AbstractFallbackTransactionAttributeSource
    └── computeTransactionAttribute()└── 检查方法修饰符是否为 public├── 是:继续处理事务属性└── 否:返回 null
    
  4. 实际应用中的典型场景:

    • 开发者在 private 或 protected 方法上添加 @Transactional 注解
    • 在同一个类内部调用带有 @Transactional 注解的方法(这种自调用也会导致事务失效)
    • 在非 public 方法上使用 Spring 的事务注解
  5. 解决方案:

    • 确保所有需要事务管理的方法都是 public 的
    • 避免在同一个类中进行自调用
    • 考虑使用 AspectJ 模式代替动态代理模式(需要额外配置)
    • 对于内部调用,可以通过 AopContext.currentProxy() 获取代理对象

建议开发者在遇到事务失效问题时,首先检查方法的访问修饰符是否符合要求,这是最常见的事务失效原因之一。

数据库存储引擎不支持事务

Spring 事务失效的原因分析:存储引擎选择

事务失效的根本原因

Spring 事务的底层实现实际上是依赖于数据库本身的事务支持机制。当我们在代码中使用@Transactional注解时,Spring 只是通过AOP代理对数据库操作进行了事务管理的封装,其本质仍然是调用数据库提供的事务功能。

MySQL存储引擎的事务支持差异

在MySQL数据库中,不同的存储引擎对事务的支持程度存在显著差异:

  1. MyISAM存储引擎

    • 不支持事务处理
    • 不支持行级锁(只有表锁)
    • 不支持外键约束
    • 性能较高,适合读多写少的场景
    • 示例:CREATE TABLE my_table (...) ENGINE=MyISAM;
  2. InnoDB存储引擎

    • 完全支持ACID事务特性
    • 支持行级锁
    • 支持外键约束
    • 提供崩溃恢复能力
    • 示例:CREATE TABLE my_table (...) ENGINE=InnoDB;

开发实践建议

  1. 表设计阶段检查

    • 在创建表时显式指定存储引擎类型
    • 示例SQL:SHOW TABLE STATUS LIKE 'table_name'\G可查看现有表的存储引擎
  2. 数据库全局配置

    • 可以在my.cnf/my.ini配置文件中设置默认存储引擎
   [mysqld]default-storage-engine=InnoDB
  1. 转换现有表
    • 对于已存在的MyISAM表,可以使用ALTER TABLE命令转换:
   ALTER TABLE table_name ENGINE=InnoDB;
  1. Spring配置验证
    • 在应用启动时添加检查逻辑,确保关键业务表使用InnoDB引擎
    • 可以通过JDBC元数据API编程检查表属性

其他可能导致事务失效的因素

除了存储引擎选择外,还应注意:

  • 事务传播行为设置不当
  • 异常捕获处理方式不正确
  • 方法访问权限问题(如非public方法)
  • 同一个类内方法调用导致的代理失效# Spring 事务失效的原因分析:存储引擎选择

事务失效的根本原因

Spring 事务的底层实现实际上是依赖于数据库本身的事务支持机制。当我们在代码中使用@Transactional注解时,Spring 只是通过AOP代理对数据库操作进行了事务管理的封装,其本质仍然是调用数据库提供的事务功能。

MySQL存储引擎的事务支持差异

在MySQL数据库中,不同的存储引擎对事务的支持程度存在显著差异:

  1. MyISAM存储引擎

    • 不支持事务处理
    • 不支持行级锁(只有表锁)
    • 不支持外键约束
    • 性能较高,适合读多写少的场景
    • 示例:CREATE TABLE my_table (...) ENGINE=MyISAM;
  2. InnoDB存储引擎

    • 完全支持ACID事务特性
    • 支持行级锁
    • 支持外键约束
    • 提供崩溃恢复能力
    • 示例:CREATE TABLE my_table (...) ENGINE=InnoDB;

开发实践建议

  1. 表设计阶段检查

    • 在创建表时显式指定存储引擎类型
    • 示例SQL:SHOW TABLE STATUS LIKE 'table_name'\G可查看现有表的存储引擎
  2. 数据库全局配置

    • 可以在my.cnf/my.ini配置文件中设置默认存储引擎
   [mysqld]default-storage-engine=InnoDB
  1. 转换现有表
    • 对于已存在的MyISAM表,可以使用ALTER TABLE命令转换:
   ALTER TABLE table_name ENGINE=InnoDB;
  1. Spring配置验证
    • 在应用启动时添加检查逻辑,确保关键业务表使用InnoDB引擎
    • 可以通过JDBC元数据API编程检查表属性

其他可能导致事务失效的因素

除了存储引擎选择外,还应注意:

  • 事务传播行为设置不当
  • 异常捕获处理方式不正确
  • 方法访问权限问题(如非public方法)
  • 同一个类内方法调用导致的代理失效

注解配置错误

事务失效的常见场景之一是当方法参数中设置了readOnly = True属性。这种情况下,Spring会将事务标记为只读事务(read-only transaction),其主要表现和影响包括:

  1. 只读事务特性

    • 数据库会针对只读事务进行优化
    • 通常会将事务隔离级别设置为最低(如READ_UNCOMMITTED)
    • Hibernate等ORM框架会禁用脏检查机制
  2. 增删改操作限制

    • 当尝试执行INSERT、UPDATE或DELETE等写操作时
    • 底层会抛出异常,如"Read-only transaction cannot perform write operations"
    • 具体错误信息可能因数据库驱动或ORM框架而异
  3. 典型应用场景

   @Transactional(readOnly = true)public User getUserById(Long id) {// 只允许查询操作return userRepository.findById(id);}
  1. 常见错误配置

    • 误将写操作方法标记为只读
    • 在Service层方法上全局设置readOnly=true
    • 没有根据实际业务需求区分读写事务
  2. 解决方案

    • 严格区分读写方法的事务配置
    • 写操作方法应该去掉readOnly属性或显式设置为false
    • 可以使用@Transactional的默认配置(不指定readOnly)来允许写操作

注意:不同持久化框架对只读事务的实现可能略有差异,但基本原理相同,即只读事务不能执行任何修改数据库状态的操作。事务失效的常见场景之一是当方法参数中设置了readOnly = True属性。这种情况下,Spring会将事务标记为只读事务(read-only transaction),其主要表现和影响包括:

  1. 只读事务特性

    • 数据库会针对只读事务进行优化
    • 通常会将事务隔离级别设置为最低(如READ_UNCOMMITTED)
    • Hibernate等ORM框架会禁用脏检查机制
  2. 增删改操作限制

    • 当尝试执行INSERT、UPDATE或DELETE等写操作时
    • 底层会抛出异常,如"Read-only transaction cannot perform write operations"
    • 具体错误信息可能因数据库驱动或ORM框架而异
  3. 典型应用场景

   @Transactional(readOnly = true)public User getUserById(Long id) {// 只允许查询操作return userRepository.findById(id);}
  1. 常见错误配置

    • 误将写操作方法标记为只读
    • 在Service层方法上全局设置readOnly=true
    • 没有根据实际业务需求区分读写事务
  2. 解决方案

    • 严格区分读写方法的事务配置
    • 写操作方法应该去掉readOnly属性或显式设置为false
    • 可以使用@Transactional的默认配置(不指定readOnly)来允许写操作

注意:不同持久化框架对只读事务的实现可能略有差异,但基本原理相同,即只读事务不能执行任何修改数据库状态的操作。

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {private final UnitInfoMapper unitInfoMapper;@Transactional(readOnly = true)@Overridepublic String test01() {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);return "ok";}
}

直接运行报错了:

java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowedat com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) ~[mysql-connector-java-8.0.19.jar:8.0.19]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63) ~[mysql-connector-java-8.0.19.jar:8.0.19]

事务超时时间过短

timeout 参数设置的过短,比如 0.1 秒,导致事务直接超时。

错误的传播机制

比如使用了 NOT_SUPPORTED 的传播机制,虽然代码中报了异常,但是数据库的数据还是插入进去了。

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {private final UnitInfoMapper unitInfoMapper;@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic String test01() {UnitInfo unitInfo = UnitInfo.builder().unitName("wzk").unitCode("icu").build();unitInfoMapper.insertUnitInfo(unitInfo);if (1 == 1) {throw new RuntimeException("ERROR");}return "ok";}
}

数据库的结果:
在这里插入图片描述

http://www.dtcms.com/a/438559.html

相关文章:

  • AI心理类APP测评:产品设计、技术实现及对独立开发者的启示
  • AI编程开发系统013-基于Vue+SpringBoot的“知语”花卉销售网站(源码+演示视频+讲解+lw)
  • 广州有几个区分别叫什么南京做网站优化哪家好
  • 【Jitsi Meet】阿里云Docker安装Jitsi Meet后的调整
  • 企业网站颜色手机做网站软件
  • php入门
  • 深圳网站搭建哪里好奎文建设局网站
  • 用织梦建设网站杭州公司官方网站制作
  • 数仓各层级设计总结
  • 8.设计模式-两阶段终止(优雅停机)
  • 那里网站建设好广东建设信息网三类人
  • 网站建设百度小程序手机版网站怎么做
  • 上海机械网站建设工程网站开发
  • 网站功能说明怎么做为什么我的网站备案通过还是显示未备案
  • 校园门户网站系统建设白帽seo公司
  • nodejs中http模块搭建web服务器
  • 电子书管理与阅读平台BookHeaven
  • 网站建设有哪些分类做网站图片大小不合适怎么调
  • 【LeetCode - 每日1题】接雨水问题1
  • 太原cms建站先做网站还是先申请域名
  • 京东网站设计代码做淘宝客可以有高佣金的网站
  • wdcp创建网站网站建设如何增加二级页面
  • 阿联酋网站后缀星空无限传媒免费观看电视剧
  • 网站建设营销策划方案二次元动漫网站设计方案
  • 专做母婴食品的网站学网站建设要什么
  • Python基础入门例程90-NP90 修正错误的字母
  • 【C++】使用红黑树封装map和set
  • Java 高并发多线程 “基础”面试清单(含超通俗生活案例与深度理解)
  • 什么网站可以做兼职销售群晖可以做网站服务器吗
  • 【LaTeX】 8 LaTeX 表格制作