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

全局@Where过滤后,如何通过JPA查询is_pooled=true的库存数据?

解决全局@Where过滤下查询例外数据的问题

问题原因

你在InventoryEntity上添加的@Where(clause = "is_pooled = false")会被Hibernate自动追加到所有针对该实体的查询语句末尾,包括派生查询和自定义HQL查询。这就导致你之前的两种方法最终生成的SQL都包含冲突条件:

  • 方法1的派生查询最终条件:inventory_id IN (...) AND is_pooled = true AND is_pooled = false
  • 方法2的HQL查询最终条件:WHERE i.inventoryId IN (?) AND i.isPooled = true AND is_pooled = false
    自然无法查询到任何数据。

可行解决方案

方案1:使用原生SQL查询

直接通过原生SQL绕过Hibernate的@Where自动追加逻辑,在Repository中定义如下方法:

@Query(value = "SELECT * FROM inventory WHERE inventory_id IN (?1) AND is_pooled = true", nativeQuery = true)
List<InventoryEntity> findPooledInventoriesByIds(Collection<Long> inventoryIds);

注意替换SQL中的表名inventory和字段名inventory_id、is_pooled为你数据库中的实际名称。

方案2:创建无@Where的视图实体

新建一个和InventoryEntity映射同一张表的实体类,不添加@Where注解,专门用于查询包含is_pooled=true的数据:

@Entity
@Table(name = "inventory")
public class InventoryWithPooledEntity {
    // 复制InventoryEntity的所有字段、注解和映射关系,去掉@Where
    private Long inventoryId;
    private Boolean isPooled;
    // 其他字段、getter/setter...
}

然后为这个实体创建对应的Repository:

public interface InventoryWithPooledRepository extends JpaRepository<InventoryWithPooledEntity, Long> {
    List<InventoryWithPooledEntity> findAllByInventoryIdInAndIsPooledTrue(Collection<Long> inventoryIds);
}

在需要查询的地方注入这个Repository即可获取目标数据。

方案3:通过EntityManager手动构建查询并禁用过滤器

利用JPA的EntityManager构建查询,并通过查询提示禁用实体上的@Where过滤:

@Override
public ResponseEntity<?> getPooledParentInfo(Long inventoryId) {
    PooledBloodInventoryEntity pooledBloodInventoryEntity = pooledInventoryRepository.findDistinctFirstByInventoryId(inventoryId);
    List<Long> pooledInventoryIdList = pooledBloodInventoryEntity.getParentInventoryIds();

    // 注入EntityManager
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<InventoryEntity> cq = cb.createQuery(InventoryEntity.class);
    Root<InventoryEntity> root = cq.from(InventoryEntity.class);

    cq.where(
        cb.and(
            root.get("inventoryId").in(pooledInventoryIdList),
            cb.equal(root.get("isPooled"), true)
        )
    );

    TypedQuery<InventoryEntity> query = entityManager.createQuery(cq);
    // 关键:禁用实体上的@Where过滤条件
    query.setHint("org.hibernate.annotations.QueryHints.FILTERS_DISABLED", true);

    List<InventoryEntity> inventoryEntityList = query.getResultList();
    return new ResponseEntity<>(inventoryEntityList, new HttpHeaders(), HttpStatus.OK);
}

方案4:替换@Where为@Filter(需全局管理过滤器状态)

将实体上的@Where替换为Hibernate的@Filter注解,通过控制过滤器的启用/禁用实现全局过滤和例外查询:

  1. 修改实体类:
@Entity
@FilterDef(name = "excludePooled", parameters = @ParamDef(name = "isPooled", type = Boolean.class))
@Filter(name = "excludePooled", condition = "is_pooled = :isPooled")
public class InventoryEntity {
    // 实体字段...
}
  1. 在全局需要过滤的地方启用过滤器(比如通过AOP或者在Service层统一处理):
// 启用过滤器,只查询is_pooled=false的数据
Session session = entityManager.unwrap(Session.class);
session.enableFilter("excludePooled").setParameter("isPooled", false);
  1. 在例外查询方法中禁用过滤器:
@Override
public ResponseEntity<?> getPooledParentInfo(Long inventoryId) {
    PooledBloodInventoryEntity pooledBloodInventoryEntity = pooledInventoryRepository.findDistinctFirstByInventoryId(inventoryId);
    List<Long> pooledInventoryIdList = pooledBloodInventoryEntity.getParentInventoryIds();

    // 禁用过滤器,允许查询所有数据
    Session session = entityManager.unwrap(Session.class);
    session.disableFilter("excludePooled");

    List<InventoryEntity> inventoryEntityList = inventoryRepository.findAllByInventoryIdInAndIsPooledTrue(pooledInventoryIdList);

    // 记得恢复过滤器状态(可选,根据业务需求)
    session.enableFilter("excludePooled").setParameter("isPooled", false);

    return new ResponseEntity<>(inventoryEntityList, new HttpHeaders(), HttpStatus.OK);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:33:15