Java中结合反射与AOP实现JDBC乐观锁自动重试,核心是不侵入业务、统一拦截更新方法、动态识别版本字段、捕获冲突后查新重试;需数据库含version字段、实体提供getter/setter、DAO使用带version条件的SQL,并通过@RetryOnVersionConflict注解+AOP切面+反射提取主键/刷新实体/合并字段来实现。

Java 中结合反射与 AOP 实现 JDBC 乐观锁冲突的自动重试,核心在于:**不侵入业务逻辑、统一拦截更新方法、动态识别版本字段、捕获失败后自动查新+重试**。这不是简单加个注解就能跑通,而是要让框架“看懂”你的实体和 SQL 行为。
一、前提:确保数据库与实体支持乐观锁
必须满足以下三点,后续反射和 AOP 才有依据:
- 数据库表含 version 字段(INT/BIGINT),初始值为 0 或 1,UPDATE 时用
SET version = version + 1 - 实体类中明确声明 version 字段,如
private Integer version;,且提供 getter/setter - DAO 层的 UPDATE 方法使用带 version 条件的 SQL,例如:
UPDATE user SET name = ?, version = version + 1 WHERE id = ? AND version = ?
二、AOP 切面设计:识别可重试的更新方法
定义一个自定义注解 @RetryOnVersionConflict,标注在 Service 层的更新方法上:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOnVersionConflict {
int maxRetries() default 3;
long backoffMs() default 0; // 可选退避
}
切面匹配该注解,并开启事务(@Transactional):
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 环绕通知中获取目标方法参数,定位第一个参数——通常是待更新的实体对象
- 通过反射读取该实体的
version值(调用getVersion()) - 执行原方法;若抛出自定义异常(如
VersionConflictException),说明 UPDATE 影响行数为 0
三、反射解析与重试逻辑:动态重建更新上下文
重试不是盲目循环,每次都要“刷新数据”再提交。关键靠反射完成三件事:
-
提取主键:通过反射查找
getId()或标注@Id的字段,用于重新查询最新记录 -
重新加载最新实体:调用 DAO 的
selectById(id)方法(需约定命名或通过泛型推导) - 合并变更:对比旧实体与新实体,仅将业务字段(非 version/id)从原始参数复制到最新实体中,保留新 version 值
示例片段(简化):
Object latest = daoMethod.invoke(dao, id); // 反射调用查询
Field[] fields = target.getClass().getDeclaredFields();
for (Field f : fields) {
if (!"id".equals(f.getName()) && !"version".equals(f.getName())) {
f.setAccessible(true);
Object newVal = f.get(target); // 原始参数中的新值
if (newVal != null) f.set(latest, newVal); // 覆盖到最新实体
}
}
四、异常触发与重试控制
JDBC 层本身不抛出“乐观锁异常”,需由 DAO 显式判断:
- DAO 方法返回影响行数(int),若为 0,则手动抛出
VersionConflictException - AOP 切面捕获该异常,在 catch 块中递归调用自身(或用 for 循环),最多重试
maxRetries()次 - 每次重试前可 sleep(
backoffMs),避免瞬间打满数据库
注意:整个重试过程必须在同一个事务外进行(即切面不嵌套事务),否则会因事务已提交/回滚导致状态混乱;推荐将事务控制在最外层方法,AOP 内部不开启新事务。
这套机制把“查—改—校验—冲突处理”的流程收口到切面里,业务代码只管写 update 方法并加个注解,反射负责读懂对象结构,AOP 负责兜底重试。它轻量、可控,也不依赖 MyBatis-Plus 等框架的内置插件。

















