原创

如何为给定的具有多对多关系实体的 SQL 查询构建 JPA 规范?

温馨提示:
本文最后更新于 2024年04月12日,已超过 48 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

I have the following entities:

@Entity
@Table(name = "advertisement")
public class AdEntity {
   @Id
   @GeneratedValue(strategy = GenerationType.UUID)
   private String id;
   
   @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
   @JoinTable(name = "ad_tags", joinColumns = {@JoinColumn(name = "ad", foreignKey = @ForeignKey(name = "fk_at_ad")) }, inverseJoinColumns = {@JoinColumn(name = "tag", foreignKey = @ForeignKey(name = "fk_at_tag")) })
   private Set<TagEntity> tags;
.
.
.
}

@Entity
@Table(name = "tag")
public class TagEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;

    @ManyToMany(mappedBy = "tags")
    private Set<AdEntity> ads;
.
.
.
}

The idea is to create a filter search to retrieve all advertisements that contains all the tags provided, so an IN clause doesn't work since it will retrieve all advertisements that have at least one of the tags. I was able to create a sql query that retrieves what I need as follows:

select ad.id from advertisement ad
where ad.id in (select at.ad x
from ad_tags at
join tag t on t.id = at.tag
where t.name in ('tag1', 'tag2', 'tag3')
group by x
having count(tag) = 3)

So the problem that I have now is that I want to create a JPA Specification for this query

I have found some documentation to try to build part of the query, however the issue is that I have a ManyToMany relationship, so I'm not completely sure how to build the Specification, this is what I have, but it doesn't work:

public static Specification<AdEntity> filterTags(List<TagEntity> tags) {
    
  return (root, query, criteriaBuilder) -> {
            
    query.distinct(true);

            
    Subquery<TagEntity> tagSubquery = query.subquery(TagEntity.class);
    Root<TagEntity> tagRoot = tagSubquery.from(TagEntity.class);
            
    Expression<Collection<AdEntity>> adTags = tagRoot.get("ads");
            
    Expression<Long> countExpression = criteriaBuilder.count(root);
            
    query.multiselect(root.get("tags"), countExpression);
            
    query.groupBy(root.get("tags"));
            
    Predicate havingPredicate = criteriaBuilder.equal(countExpression, tags.size());
            
    query.having(havingPredicate);      
    tagSubquery.select(tagRoot);        
    tagSubquery.where(tagRoot.in(tags), criteriaBuilder.isMember(root, adTags));
    
            
    return criteriaBuilder.exists(tagSubquery);
  };
            
}

What should be the right way?

正文到此结束
热门推荐
本文目录