基于 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);
}

ChangeFilter

变更过滤器,在事件到达监听器前进行拦截:

@FunctionalInterface
public interface ChangeFilter {
    boolean accept(ChangeEvent event);
    default int getOrder() { return 0; }
}

ChangeContextProvider

变更上下文提供者,获取当前操作的环境信息:

@FunctionalInterface
public interface ChangeContextProvider {
    ChangeContext getContext();
}

数据模型

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>

扩展属性

ValueChange

单个字段的新旧值对比:

public class ValueChange {
    private String old;     // 旧值
    private String current; // 新值
}

ChangeType 枚举

枚举值

说明

INSERT

插入操作

DELETE

删除操作

UPDATE

更新操作

SELECT

查询操作

OTHERS

其他操作

内置实现

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"));

ChangeTypeFilter

按变更类型过滤:

// 只关注 UPDATE 操作
filter = new ChangeTypeFilter(Set.of(ChangeType.UPDATE));

DefaultChangeContextProvider

默认上下文提供者,自动通过反射获取 Spring Security 当前用户,无需硬依赖 Spring Security。

DefaultChangePipeline

默认管道实现,支持同步/异步两种模式,使用 CopyOnWriteArrayList 保证线程安全,每个监听器独立 try-catch 隔离异常。

使用方式

方式一:SPI 注册(推荐)

通过 Java SPI 机制注册自定义监听器,无需修改业务代码:

  1. 创建监听器实现

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());
            });
        }
    }
}
  1. 在 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());

方式三:自定义 DbChangeFactory

在 Spring Boot 中注册自定义工厂 Bean:

@Component
public class MyDbChangeFactory extends DefaultDbChangeFactory {
    public MyDbChangeFactory() {
        getPipeline().addFilter(
            TableNameFilter.whitelist(Set.of("user"))
        );
        getPipeline().addListener(new MyChangeListener());
    }
}

方式四:自定义上下文提供者

@Component
public class MyContextProvider implements ChangeContextProvider {
    @Override
    public ChangeContext getContext() {
        return ChangeContext.builder()
            .operator(SecurityUtils.getCurrentUser())
            .applicationName("my-app")
            .build();
    }
}

变更对比原理

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 文档。