基于 P6Spy 的数据库变更追踪框架,可捕获 INSERT/UPDATE/DELETE 操作,自动完成新旧值对比,并通过管道(Pipeline)将变更事件广播给监听器。
架构设计
框架采用分层架构,各层职责清晰:
ThinkBridgeP6spyEventListener ← P6Spy SQL 拦截层
│
▼
DefaultChangeHandler ← 变更捕获 + 新旧值对比引擎
│
▼
ChangePipeline ← 过滤器链 → 监听器广播
├─ ChangeFilter₁ (过滤)
├─ ChangeFilter₂ (过滤)
└─ ChangeListener₁,₂,₃... (消费)
-
变更捕获层:通过 P6Spy 拦截 SQL 执行,在 before/after 阶段分别查询变更前后的数据
-
对比引擎:自动计算新旧值差异,生成
ChangeSnapshot -
管道层:过滤器链 + 多监听器广播,支持同步/异步模式,线程安全
核心接口
DbChangeFactory
工厂接口,用于获取各组件实例:
public interface DbChangeFactory {
ChangePipeline getPipeline();
ChangeContextProvider getContextProvider();
}
ChangeListener
变更监听器,接收完整的变更事件:
@FunctionalInterface
public interface ChangeListener {
void onChange(ChangeEvent event);
default int getOrder() { return 0; }
}
ChangePipeline
变更管道,编排过滤器链和监听器广播:
public interface ChangePipeline {
void addListener(ChangeListener listener);
void removeListener(ChangeListener listener);
void addFilter(ChangeFilter filter);
void removeFilter(ChangeFilter filter);
void publish(ChangeEvent event);
}
数据模型
ChangeEvent
一次数据库变更的完整事件:
字段 |
类型 |
说明 |
context |
ChangeContext |
变更上下文(操作人、时间等) |
type |
ChangeType |
变更类型(INSERT/UPDATE/DELETE) |
tableName |
String |
变更的表名 |
sql |
String |
原始 SQL |
snapshots |
List<ChangeSnapshot> |
行级变更快照列表 |
success |
boolean |
变更是否成功 |
errorMessage |
String |
异常信息(如果有) |
ChangeSnapshot
单行数据的变更快照,包含变更前后完整状态:
字段 |
类型 |
说明 |
before |
Map<String,Object> |
变更前的字段值 |
after |
Map<String,Object> |
变更后的字段值 |
changes |
Map<String,ValueChange> |
字段级新旧值对比 |
primaryKey |
Map<String,Object> |
主键信息 |
ChangeContext
变更发生的环境上下文:
字段 |
类型 |
说明 |
operator |
String |
操作人标识 |
operatorIp |
String |
操作人 IP |
timestamp |
Instant |
变更时间 |
transactionId |
String |
事务 ID |
applicationName |
String |
应用名称 |
traceId |
String |
链路追踪 ID |
extra |
Map<String,Object> |
扩展属性 |
内置实现
LoggingChangeListener
开箱即用的日志监听器,默认已注册到 Pipeline,无需任何配置即可看到变更日志:
// 输出示例:
// [DB-CHANGE] table=user, type=UPDATE, snapshots=1, operator=admin
// [DB-CHANGE] Snapshot[0]: 2 field(s) changed
// [DB-CHANGE] name: 张三 -> 张三丰
// [DB-CHANGE] age: 25 -> 26
构造参数 verbose=true 时输出字段级变更详情。
TableNameFilter
按表名过滤变更事件:
// 白名单:只关注指定表
filter = TableNameFilter.whitelist(Set.of("user", "order"));
// 黑名单:排除指定表
filter = TableNameFilter.blacklist(Set.of("sys_log"));
使用方式
方式一:SPI 注册(推荐)
通过 Java SPI 机制注册自定义监听器,无需修改业务代码:
-
创建监听器实现
public class MyChangeListener implements ChangeListener {
@Override
public void onChange(ChangeEvent event) {
for (ChangeSnapshot snap : event.getSnapshots()) {
snap.getChanges().forEach((field, vc) -> {
log.info("{}: {} -> {}", field, vc.getOld(), vc.getCurrent());
});
}
}
}
-
在
META-INF/services/com.luamas.thinkbridge.common.dbchange.listener.ChangeListener中注册
com.example.MyChangeListener
方式二:编程式动态注册
运行时动态添加过滤器和监听器:
DbChangeFactory factory = DbChangeFactoryLoader.load();
// 添加过滤器
factory.getPipeline().addFilter(
TableNameFilter.whitelist(Set.of("user", "order"))
);
// 添加监听器
factory.getPipeline().addListener(new MyChangeListener());
变更对比原理
UPDATE 操作:
before() 阶段 → SELECT 被修改字段的旧值 → 存入 ThreadLocal
after() 阶段 → 再次 SELECT 新值 → computeChanges() 逐字段对比
→ 构建 ChangeSnapshot(before, after, changes)
INSERT 操作:
before() 阶段 → 记录表名和类型
after() 阶段 → 查询刚插入的行 → 构建快照(before=empty)
DELETE 操作:
before() 阶段 → SELECT * 完整行数据
after() 阶段 → 直接使用 before 数据 → 构建快照(after=empty)
此模块依赖 p6spy-spring-boot-starter,通过 ThinkBridgeP6spyEventListener 监听 SQL 执行事件并触发变更追踪。关于 P6Spy 的配置属性请参考 P6Spy 文档。
|