QQA: Spring Data 如何查询属性是否在列表中

栏目: Java · 发布时间: 7年前

内容简介:每篇文章有多个标签,选中多个标签,要求找出含有该标签的文章。同时如果选中标签为空,则返回所有文章。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); // ⑤
            }
        });
    }
}

上面代码的注意点:

  1. 实现 JpaSpecificationExecutor 来启用 Specification,Repository 中会增加 findAll(Specification<T> spec) 等使用 Specification 的查询方法。
  2. 这里使用 Java 8 的 Lambda 表达式。等价于实现一个 Specification 实例。
  3. 返回结果为 List ,可能会出现重复的结果,加上 distinct 来去重。
  4. cb.conjunction() 等价于 where 1=1
  5. 重要:调用 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。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

谷歌时代的柏拉图

谷歌时代的柏拉图

[美] 丽贝卡·戈尔茨坦 / 李鹏程 / 中信出版集团·新思文化 / 2017-12-10 / 69.00元

我愿意用我所有的科技去换取和苏格拉底相处的一个下午。 ——史蒂夫•乔布斯 谷歌时代,科技昌明,众声喧哗,哲学提出的许多问题,科学似乎都已经给出了答案。若是如此,为什么我们今天还需要哲学?这个由古希腊城邦时代的哲人苏格拉底和柏拉图开创的学科,真的过时了吗? 已经2400岁 的柏拉图对此有话要说。哲学家兼小说家、美国国家人文奖章获得者戈尔茨坦史海钩沉,从经典著作中复活了柏拉图,让他来......一起来看看 《谷歌时代的柏拉图》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具