内容简介:每篇文章有多个标签,选中多个标签,要求找出含有该标签的文章。同时如果选中标签为空,则返回所有文章。Spring Data/JPA 如何实现?可以使用 Spring Data Specification 动态创建查询语句,最终结果如下:上面代码的注意点:
每篇文章有多个标签,选中多个标签,要求找出含有该标签的文章。同时如果选中标签为空,则返回所有文章。Spring Data/JPA 如何实现?
可以使用 Spring Data Specification 动态创建查询语句,最终结果如下:
public interface PostRepository extends JpaRepository<Post, Long>,
JpaSpecificationExecutor<Post> { // ①
default List<Post> query(List<Tag> tags) {
return findAll((root, cq, cb) -> { // ②
cq.distinct(true); // ③
if (tags == null || tags.isEmpty()) {
return cb.conjunction(); // ④
} else {
return root.join("tags").in(tags); // ⑤
}
});
}
}
上面代码的注意点:
-
实现
JpaSpecificationExecutor来启用 Specification,Repository 中会增加findAll(Specification<T> spec)等使用 Specification 的查询方法。 -
这里使用 Java 8 的 Lambda 表达式。等价于实现一个
Specification实例。 -
返回结果为
List,可能会出现重复的结果,加上distinct来去重。 -
cb.conjunction()等价于where 1=1。 -
重要:调用
join来定位多对多的(或其它的关联)属性。
文章类,其中一篇文章可以有多个标签 Tag
:
@Entity
public class Post {
@Id
@GeneratedValue
private long id;
private String name;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
private Set<Tag> tags = new HashSet<>();
// ...
}
标签类,一个标签可以被赋给多篇文章:
@Entity
public class Tag {
@Id
@GeneratedValue
private int id;
private String name;
private String createdBy;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
// ...
}
实际查询时生成的 SQL 如下:
select
distinct post0_.id as id1_0_,
post0_.name as name2_0_
from
post post0_
inner join
post_tags tags1_
on post0_.id=tags1_.posts_id
inner join
tag tag2_
on tags1_.tags_id=tag2_.id
where
tag2_.id in (
? , ?
)
为什么用 Specification
上面介绍的 Spring Specification 看起来比较复杂,其实如果只需要查询一个属性,可以直接定义 Spring Data 的 Query Method:
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findDistinctByTagsIn(List<Tag> tags); // ①
default List<Post> query(List<Tag> tags) { // ②
if (tags == null || tags.isEmpty()) {
return findAll();
} else {
return findDistinctByTagsIn(tags);
}
}
}
当然,① 处的方法不能处理 tags
为空时返回所有文章的需求,所以需要 ② 处的方法进行包装。
那么 Specification 还有什么用呢?考虑多个查询条件的组合,例如文章有多名作者,要根据作者和标签共同查询,则需要像这样实现:
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findDistinctByTagsIn(List<Tag> tags);
List<Post> findDistinctByAuthorsIn(List<Author> authors);
List<Post> findDistinctByAuthorsInAndTagsIn(List<Author> authors, List<Tag> tags);
default List<Post> query(List<Author> authors, List<Tag> tags) {
if ((authors == null || authors.isEmpty()) && (tags == null || tags.isEmpty())) {
return findAll();
} else if (authors == null || authors.isEmpty()) {
return findDistinctByTagsIn(tags);
} else if (tags == null || tags.isEmpty()) {
return findDistinctByAuthorsIn(authors);
} else {
return findDistinctByTagsIn(tags);
}
}
}
这种实现方式需要增加指数级的方法数量,因此更合适用 Specification 动态生成 Query。
- Spring Data repository with empty IN clause 跟本文探讨的问题相同,有更细致的分析。不过它处理的属性没有关联关系。
- https://www.jianshu.com/p/659e9715d01d Spring Data Specification 的一些使用示例。
- https://docs.oracle.com/javaee/6/tutorial/doc/gjivm.html Criteria API,说明挺详细,然而还是看不太懂。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- javascript 遍历json对象检查是否不存在某些属性/字段
- 将所有类定义为Scala中的情况,只是让所有参数自动生成属性是否正确?
- 是否,是否,总是富肥穷瘦?
- 容器是否取代了虚拟机,这四大理由是否打动你?
- BERT是否完美,语言模型又是否真正地「理解了语言」呢?
- CSS 属性篇(七):Display属性
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。