You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中能否让Liquibase的<insert>语句支持Hibernate Envers的@Audit?

问题描述

我们的应用此前通过Liquibase的<insert>或<update>语句填充config表,该表是JPA实体并采用Hibernate Envers的@Audit审计功能,对应存在config_audit表。

为了让Liquibase的插入/更新操作也能触发审计,我们切换为<customChange>变更集方案,示例代码如下:

<changeSet id="my_id" author="author">
    <customChange class="com.app.MyCustomTaskChange">
        <param name="field1" value="val1" />
        <param name="field2" value="val2" />
    </customChange>
</changeSet>
public class MyCustomTaskChange implements CustomTaskChange, CustomTaskRollback {
    @Setter
    private String field1;

    @Setter
    private String field2;

    @Setter
    private static ConfigJpaRepository configJpaRepository;

    @Override
    public void execute(Database database) throws CustomChangeException {
        configJpaRepository.save(new ConfigEntity(field1, field2));
    }
    
    // 其他方法省略以简化展示
}

目前该方案运行正常,config和config_audit表均能正常添加条目,但我们想了解是否有更优雅且具前瞻性的方式,让Liquibase的<insert>或<update>(或类似)语句直接适配@Audit审计能力?


更优雅的解决方案

1. 通用JPA变更类型(推荐)

自定义一个通用的Liquibase变更类型,通过JPA EntityManager操作实体,自动触发Envers审计逻辑,避免为每个实体单独编写CustomTaskChange:

public class JpaEntityChange extends AbstractChange {
    private String entityClassName;
    private Map<String, Object> parameters = new HashMap<>();

    // 从Spring上下文获取EntityManager(需确保Liquibase运行在Spring环境中)
    private EntityManager getEntityManager() {
        return ApplicationContextProvider.getApplicationContext().getBean(EntityManager.class);
    }

    @Override
    public void execute(Database database) throws CustomChangeException {
        EntityManager em = getEntityManager();
        try {
            Class<?> entityClass = Class.forName(entityClassName);
            Object entity = entityClass.getDeclaredConstructor().newInstance();
            
            // 通过反射设置实体字段值
            for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                BeanUtils.setProperty(entity, entry.getKey(), entry.getValue());
            }
            
            em.persist(entity);
            em.flush();
        } catch (Exception e) {
            throw new CustomChangeException("JPA实体操作失败", e);
        }
    }

    // 省略必要的getter/setter、getConfirmationMessage、getChangeMetaData等方法
}

在Liquibase配置中使用该通用变更:

<changeSet id="insert-config-jpa" author="author">
    <customChange class="com.app.JpaEntityChange">
        <param name="entityClassName" value="com.app.ConfigEntity"/>
        <param name="field1" value="val1"/>
        <param name="field2" value="val2"/>
    </customChange>
</changeSet>

这种方式的优势是通用性强,所有带@Audit的实体都可以复用该变更类型,无需重复编写自定义任务,维护成本更低。

2. 结合Spring Bean调用(贴合Spring生态)

如果是Spring Boot项目,直接利用Liquibase的Spring集成能力,调用Spring管理的业务Bean方法,通过业务逻辑触发JPA保存和审计:

首先定义业务服务:

@Service
public class ConfigService {
    private final ConfigJpaRepository configRepo;

    public ConfigService(ConfigJpaRepository configRepo) {
        this.configRepo = configRepo;
    }

    public void saveConfig(String field1, String field2) {
        configRepo.save(new ConfigEntity(field1, field2));
    }
}

然后在Liquibase变更集中调用该Bean:

<changeSet id="insert-config-spring" author="author">
    <customChange class="org.springframework.boot.liquibase.SpringLiquibaseCustomChange">
        <param name="beanName" value="configService"/>
        <param name="methodName" value="saveConfig"/>
        <param name="arguments" value="val1,val2"/>
    </customChange>
</changeSet>

这种方式避免了静态依赖注入的问题,完全贴合Spring的依赖注入原则,适合已经深度使用Spring生态的项目。

3. 手动插入审计表(不推荐长期使用)

如果只是临时需求,可以在Liquibase的<insert>/<update>后手动插入审计表记录,但这种方式耦合度极高,实体字段变更后必须同步修改审计表的插入语句:

<changeSet id="insert-config-with-audit" author="author">
    <insert tableName="config">
        <column name="id" value="1"/>
        <column name="field1" value="val1"/>
        <column name="field2" value="val2"/>
    </insert>
    <insert tableName="config_audit">
        <column name="id" value="1"/>
        <column name="field1" value="val1"/>
        <column name="field2" value="val2"/>
        <column name="rev" value="${next-rev-id}"/> <!-- 需要确保revision ID正确 -->
        <column name="revtype" value="0"/> <!-- 0=插入,1=更新,2=删除 -->
    </insert>
</changeSet>

总结

你当前的<customChange>方案是可行的,但通用JPA变更类型和Spring Bean调用的方式更优雅、更具前瞻性:

  • 通用JPA变更类型适合多实体场景,减少重复代码;
  • Spring Bean调用更贴合Spring生态,依赖管理更规范;
  • 手动插入审计表的方式仅适合临时快速实现,长期维护成本高。

内容的提问来源于stack exchange,提问作者mjj1409

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 14:17:53