全局@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注解,通过控制过滤器的启用/禁用实现全局过滤和例外查询:
- 修改实体类:
@Entity @FilterDef(name = "excludePooled", parameters = @ParamDef(name = "isPooled", type = Boolean.class)) @Filter(name = "excludePooled", condition = "is_pooled = :isPooled") public class InventoryEntity { // 实体字段... }
- 在全局需要过滤的地方启用过滤器(比如通过AOP或者在Service层统一处理):
// 启用过滤器,只查询is_pooled=false的数据 Session session = entityManager.unwrap(Session.class); session.enableFilter("excludePooled").setParameter("isPooled", false);
- 在例外查询方法中禁用过滤器:
@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
相关产品推荐
相关产品推荐

