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

如何在Hibernate Search 6.x投影查询中高效获取Hibernate Envers版本号

实现思路

1. 将Envers修订号纳入Hibernate Search索引

要实现版本校验,核心是把实体对应的Envers最新修订号存入Hibernate Search索引,以下两种高效方案可选:

方式一:数据库公式映射+索引

在实体类中添加基于数据库查询的计算字段,直接映射该实体的最新Envers修订号,同时标记为可索引字段:

@Entity
@Audited
public class YourEntity {
    @Id
    private Long id;

    // 替换YourEntity_AUD为实际的审计表名,自动计算最新修订号
    @Formula("(SELECT MAX(e.REV) FROM YourEntity_AUD e WHERE e.id = id)")
    @IndexedField(name = "revisionNumber")
    private Integer revisionNumber;

    // 其他实体字段...
}

这种方式无需额外代码侵入,Hibernate会自动计算字段值,Hibernate Search索引时直接读取存储。注意需给审计表的id和REV字段建立联合索引,避免大表场景下的查询性能问题。

方式二:Envers事件监听主动设置修订号

若担心@Formula的查询开销,可通过Envers监听器在实体变更时主动获取修订号,存入临时字段后索引:

  1. 实现Envers的RevisionListener,用ThreadLocal绑定当前事务的修订号:
public class CustomRevisionListener implements RevisionListener {
    private static final ThreadLocal<Integer> CURRENT_REV = new ThreadLocal<>();

    @Override
    public void newRevision(Object revisionEntity) {
        RevInfo revInfo = (RevInfo) revisionEntity;
        CURRENT_REV.set(revInfo.getId());
    }

    public static Integer getCurrentRevision() {
        return CURRENT_REV.get();
    }

    public static void clear() {
        CURRENT_REV.remove();
    }
}
  1. 在Hibernate配置中指定监听器(以persistence.xml为例):
<property name="org.hibernate.envers.revision_listener" value="com.yourpackage.CustomRevisionListener"/>
  1. 实体类中添加临时索引字段,通过生命周期回调赋值:
@Entity
@Audited
public class YourEntity {
    @Id
    private Long id;

    @Transient
    @IndexedField(name = "revisionNumber")
    private Integer revisionNumber;

    @PostPersist
    @PostUpdate
    public void setRevisionNumber() {
        this.revisionNumber = CustomRevisionListener.getCurrentRevision();
        CustomRevisionListener.clear();
    }

    // 其他实体字段...
}

该方案避免了索引时的额外数据库查询,性能更优,ThreadLocal确保了修订号与当前事务的绑定安全。

2. 投影查询时校验版本一致性

查询时同时投影索引中的修订号,与数据库中实体的最新修订号对比,判断是否同步:

// 1. 执行投影查询,获取数据和索引中的修订号
List<Tuple> results = searchSession.search(YourEntity.class)
        .where(f -> f.matchAll())
        .select(f -> f.composite(
                f.field("id", Long.class),
                f.field("name", String.class),
                f.field("revisionNumber", Integer.class)
        ))
        .fetchHits(20);

// 2. 校验版本,按需加载数据
AuditReader auditReader = AuditReaderFactory.get(entityManager);
for (Tuple tuple : results) {
    Long id = tuple.get(0, Long.class);
    String projectedName = tuple.get(1, String.class);
    Integer indexedRev = tuple.get(2, Integer.class);

    // 查询数据库中实体的最新修订号
    Number latestDbRev = auditReader.getRevisionNumberForEntity(id, YourEntity.class);

    if (indexedRev.equals(latestDbRev.intValue())) {
        // 索引与数据库同步,直接使用投影数据
        useProjectedData(projectedName);
    } else {
        // 索引过时,从数据库加载最新数据
        YourEntity latestEntity = entityManager.find(YourEntity.class, id);
        useLatestData(latestEntity);
    }
}

3. 额外优化建议

  • 增量索引配合:保持Hibernate Search默认的增量索引开启状态,仅在实体变更时更新索引,降低版本不一致概率。
  • 批量校验:处理大量数据时,通过IN子句批量查询数据库中的修订号,减少单次查询开销。
  • 修订号缓存:对频繁访问的实体,缓存其最新修订号,进一步减少数据库查询次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 07:24:19