JPA查询:匹配无关联实体多对多属性查询指定租户关联的所有Item
实体定义
你提供的三个JPA实体代码如下:
@Entity public class Item { [...] @ManyToOne(optional = false) @JoinColumn(nullable = false) private ItemDescriptor asset; [...] } @Entity public class ItemDescriptor { [...] @ManyToMany(fetch = FetchType.LAZY) private Set<ContentPlan> contentPlans; [...] } @Entity public class Tenant { [...] @ManyToMany private Set<ContentPlan> contentPlans; [...] }
查询需求
给定指定租户Tenant t,查询所有符合以下条件的Item i:i关联的ItemDescriptor的contentPlans集合中,至少有一个元素属于t的contentPlans范围。
实现方案
方案1:Spring Data JPA 派生查询(最简单,无需手动写JPQL)
直接在Item对应的Repository接口中添加如下方法,传入租户的contentPlans集合即可:
// 自动去重,避免多对多关联返回重复Item List<Item> findDistinctByAsset_ContentPlansIn(Set<ContentPlan> tenantContentPlans);
调用示例:
Tenant t = // 你获取到的指定租户实例 List<Item> items = itemRepository.findDistinctByAsset_ContentPlansIn(t.getContentPlans());
方案2:自定义JPQL查询
如果需要更灵活的查询逻辑,可以用@Query注解写JPQL:
@Query("SELECT DISTINCT i FROM Item i " + "JOIN i.asset a " + "JOIN a.contentPlans cp " + "WHERE cp IN :tenantContentPlans") List<Item> findItemsByTenantContentPlans(@Param("tenantContentPlans") Set<ContentPlan> tenantContentPlans);
如果直接传入Tenant实例作为参数,也可以结合SpEL简化写法:
@Query("SELECT DISTINCT i FROM Item i " + "JOIN i.asset a " + "JOIN a.contentPlans cp " + "WHERE cp IN :#{#tenant.contentPlans}") List<Item> findItemsByTenant(@Param("tenant") Tenant tenant);
方案3:Criteria API 实现(适用于动态查询场景)
如果查询条件是动态拼接的,可以用Criteria API实现:
public List<Item> findItemsByTenant(Tenant tenant) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Item> query = cb.createQuery(Item.class); Root<Item> itemRoot = query.from(Item.class); // 关联ItemDescriptor Join<Item, ItemDescriptor> assetJoin = itemRoot.join("asset"); // 关联contentPlans Join<ItemDescriptor, ContentPlan> cpJoin = assetJoin.join("contentPlans"); // 筛选条件:contentPlan在租户的contentPlans范围内 query.where(cpJoin.in(tenant.getContentPlans())) .distinct(true); // 去重 return entityManager.createQuery(query).getResultList(); }
注意事项
- 所有方案都加了
DISTINCT,是因为多对多关联查询时,如果一个Item对应的ItemDescriptor有多个符合条件的ContentPlan,会返回多条重复的Item记录,去重后可以避免这个问题。 - 如果你的需求是Item关联的ItemDescriptor的所有contentPlans都属于租户的contentPlans,只需要把查询逻辑调整为
NOT EXISTS (SELECT cp FROM a.contentPlans cp WHERE cp NOT IN :tenantContentPlans)即可。
内容的提问来源于stack exchange,提问作者Marcus
相关产品推荐
相关产品推荐

