我有以下实体:
@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;
.
.
.
}
这个想法是创建一个过滤器搜索来检索包含所提供的所有标签的所有广告,因此 IN 子句不起作用,因为它将检索至少具有一个标签的所有广告。我能够创建一个 sql 查询来检索我需要的内容,如下所示:
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)
所以我现在遇到的问题是我想为此查询创建一个 JPA 规范
我找到了一些文档来尝试构建部分查询,但问题是我有一个多对多关系,所以我不完全确定如何构建规范,这是我所拥有的,但它没有工作:
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);
};
}
正确的方法应该是什么?
维克多。
以下规格:
public static Specification<AdEntity> byTagsAllIn(Set<String> tags) {
return (root, query, builder) -> {
query.groupBy(root.get(AdEntity_.ID));
query.having(builder.equal(builder.count(root), tags.size()));
return root.joinSet(AdEntity_.TAGS, JoinType.INNER).get(TagEntity_.TAG_NAME).in(tags);
};
}
会生成这样的伪SQL:
Hibernate:
select
a1_0.id,
a1_0.advertisement_name
from
advertisements a1_0
join
(ad_tags t1_0
join
tags t1_1
on t1_1.id=t1_0.tag)
on a1_0.id=t1_0.ad
where
t1_1.tag_name in (?,?,?)
group by
a1_0.id
having
count(a1_0.id)=?
考虑使用 hibernate-jpamodelgen 在项目构建时生成静态元模型
AdEntity_
和 TagEntity_