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

基于鉴别器的多租户架构下Hibernate Search全文搜索实现问询

Hibernate Search多租户(园区ID)全文搜索实现方案

嘿,刚好我之前在项目里处理过类似的多租户+Hibernate Search场景,给你几个靠谱的方案,从基础到优雅优化都有:

一、基础方案:手动添加租户过滤条件

这是最直接的方式,每次搜索时在查询里强制加上parkId的匹配条件,确保只返回当前租户的数据:

// 获取FullTextEntityManager
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);

// 创建QueryBuilder
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
    .buildQueryBuilder().forEntity(ProductModel.class).get();

// 构建组合查询:全文搜索 + 租户过滤
BooleanJunction<?> queryJunction = qb.bool()
    // 全文搜索条件(比如搜索name字段)
    .must(qb.keyword().onField("name").matching("你的搜索关键词").createQuery())
    // 强制添加租户parkId过滤
    .must(qb.keyword().onField("parkId").matching(currentTenantParkId).createQuery());

// 执行查询
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(
    queryJunction.createQuery(), 
    ProductModel.class
);
List<ProductModel> results = fullTextQuery.getResultList();

提示:currentTenantParkId要从你的系统上下文获取,比如ThreadLocal、Spring Security的登录用户信息里提取,确保每个请求对应正确的租户ID。

二、优雅方案:使用Hibernate Search的租户上下文(推荐)

如果用的是Hibernate Search 6及以上版本,官方提供了专门的多租户支持,不用每次手动写过滤条件,更简洁安全:

1. 标记租户字段

在实体的parkId字段上添加@TenantId注解,告诉Hibernate Search这是租户标识:

@Entity @Indexed
public class ProductModel {
    @TenantId // 标记为租户ID字段
    @Field
    private Long parkId;

    @Field(index = Index.YES, analyze = Analyze.YES)
    @Analyzer(definition = "customanalyzer")
    private String name;
    
    // 其他字段...
}

2. 配置租户标识符提供者

实现TenantIdentifierProvider接口,用来在查询/索引时自动获取当前租户的ID:

public class ParkIdTenantProvider implements TenantIdentifierProvider {
    @Override
    public String getTenantIdentifier() {
        // 从系统上下文获取当前租户的parkId,转成String返回
        Long currentParkId = TenantContext.getCurrentParkId();
        return currentParkId != null ? currentParkId.toString() : null;
    }
}

然后在JPA配置里指定这个提供者(比如Spring Boot的application.properties):

# Spring Boot配置示例
spring.jpa.properties.hibernate.search.tenant_identifier_provider=com.yourcompany.ParkIdTenantProvider

3. 执行搜索(自动过滤租户)

现在搜索时不用手动加parkId条件,Hibernate Search会自动帮你过滤当前租户的数据:

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
    .buildQueryBuilder().forEntity(ProductModel.class).get();

// 只需要写全文搜索条件即可,租户过滤自动生效
FullTextQuery query = fullTextEntityManager.createFullTextQuery(
    qb.keyword().onField("name").matching("你的搜索关键词").createQuery(),
    ProductModel.class
);
List<ProductModel> results = query.getResultList();

三、进阶优化:按租户拆分索引

如果你的租户数据量很大,推荐按租户拆分索引(每个租户一个独立索引),这样查询性能更好,也能彻底避免跨租户数据泄露:

1. 配置动态索引名称

通过自定义SearchMapping,让每个租户的索引名称包含租户ID:

public class CustomSearchMapping implements SearchMappingConfigurer {
    @Override
    public void configure(SearchMapping mapping) {
        mapping.entity(ProductModel.class)
            // 动态生成索引名,{tenant_id}会被替换为当前租户ID
            .index(name = "product_{tenant_id}");
    }
}

2. 配置启用自定义Mapping

在JPA配置里指定这个Mapping:

spring.jpa.properties.hibernate.search.mapping.model_mapping=com.yourcompany.CustomSearchMapping

这样每个租户的数据会被索引到独立的索引中,查询时只会访问当前租户的索引,性能和安全性都更优。

四、兼容Hibernate Search 5的方案

如果还在使用Hibernate Search 5,可以用过滤器实现租户过滤:

1. 定义过滤器工厂

@Key(name = "parkIdFilter")
public class ParkIdFilterFactory implements FilterFactory {
    @Override
    public Filter getFilter(FilterKey key) {
        Long currentParkId = TenantContext.getCurrentParkId();
        // 创建parkId匹配的过滤器
        return new QueryFilter(new TermQuery(new Term("parkId", currentParkId.toString())));
    }
}

2. 搜索时启用过滤器

FullTextQuery query = fullTextEntityManager.createFullTextQuery(
    qb.keyword().onField("name").matching("搜索关键词").createQuery(),
    ProductModel.class
);
// 启用租户过滤器
query.enableFullTextFilter("parkIdFilter");
List<ProductModel> results = query.getResultList();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:14:03