Quarkus迁移中如何给外部依赖Hibernate类的方法添加拦截器
Quarkus的CDI拦截器仅支持拦截CDI容器托管的Bean,Hibernate核心类不在CDI托管范围内,无法直接通过Interceptor实现原有切面逻辑。你原来的切面切的是org.hibernate.engine包下的Hibernate内部类,本身就存在版本升级不兼容的风险,直接用Hibernate官方提供的扩展SPI实现需求即可,稳定性更高,以下是三种可落地的方案:
方案1:使用
StatementInspector实现运行时SQL改写
这是Hibernate官方专门提供的SQL检查、改写扩展点,Quarkus会自动识别注册,无需额外配置,适合需要根据运行时上下文动态调整过滤条件的场景。
实现代码示例:import org.hibernate.resource.jdbc.spi.StatementInspector; import io.quarkus.hibernate.orm.PersistenceUnitExtension; @PersistenceUnitExtension public class ExtraFilterStatementInspector implements StatementInspector { @Override public String inspect(String sql) { // 匹配需要追加过滤条件的目标SQL,自行实现匹配逻辑即可 if (matchTargetNamedQuery(sql)) { // 注意判断SQL中是否已存在WHERE子句,避免语法错误 return sql + " AND your_extra_filter_column = :filterValue"; } return sql; } private boolean matchTargetNamedQuery(String sql) { // 按业务特征匹配需要处理的命名查询SQL即可 return true; } }方案2:使用Hibernate原生
@Filter注解实现自动过滤
如果你的需求是固定的数据权限类过滤,直接用Hibernate原生的过滤器功能即可,无需手动拼接SQL,由Hibernate自动处理语法适配,命名查询、Criteria查询、原生查询都能自动生效。
首先在对应实体类上定义过滤器:import org.hibernate.annotations.Filter; import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.ParamDef; @Entity @FilterDef(name = "extraDataFilter", parameters = @ParamDef(name = "filterVal", type = Long.class)) @Filter(name = "extraDataFilter", condition = "tenant_id = :filterVal") public class YourBizEntity { // 实体字段省略 }在执行查询前开启过滤器即可自动追加条件:
Session hibernateSession = entityManager.unwrap(Session.class); hibernateSession.enableFilter("extraDataFilter").setParameter("filterVal", currentTenantId); // 后续执行的所有相关查询都会自动带上过滤条件方案3:使用
MetadataContributor在启动阶段直接修改命名查询定义
如果你的过滤条件是固定的,不需要运行时动态调整,可以在Hibernate启动加载元数据阶段,直接修改命名查询的SQL定义,运行时无额外性能开销。
实现代码示例:import org.hibernate.boot.spi.MetadataContributor; import org.hibernate.engine.spi.NamedSQLQueryDefinition; import io.quarkus.hibernate.orm.PersistenceUnitExtension; @PersistenceUnitExtension public class NamedQueryFilterContributor implements MetadataContributor { @Override public void contribute(Metadata metadata, BootstrapContext bootstrapContext) { metadata.getNamedQueryDefinitions().forEach(queryDef -> { if (queryDef instanceof NamedSQLQueryDefinition targetDef) { String originSql = targetDef.getQueryString(); // 直接修改原有命名查询的SQL语句 targetDef.setQueryString(originSql + " AND fixed_filter_condition = 1"); } }); } }
注意:以上三种方案均基于Hibernate官方公开API实现,不会因为Hibernate版本迭代内部类逻辑变动失效,兼容Quarkus 1.13+所有正式版本。
内容的提问来源于stack exchange,提问作者Digsb

