Spring Data JPA:解析JpaSpecificationExecutor & Specification

摘要:
可以看出,规范是JpaSpecificationExecutor接口的核心。
源码

在前面关于SimpleJpaRepository的文章[地址]中可以得知,SimpleJpaRepository间接实现了JpaSpecificationExecutor接口,本文就详细探究一下该接口。

JpaSpecificationExecutor的定义如下:

/**
 * Interface to allow execution of {@link Specification}s based on the JPA criteria API.
 *
 * @author Oliver Gierke
 * @author Christoph Strobl
 */
public interface JpaSpecificationExecutor<T> {

    /**
     * Returns a single entity matching the given {@link Specification} or {@link Optional#empty()} if none found.
     *
     * @param spec can be {@literal null}.
     * @return never {@literal null}.
     * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found.
     */
    Optional<T> findOne(@Nullable Specification<T> spec);

    /**
     * Returns all entities matching the given {@link Specification}.
     *
     * @param spec can be {@literal null}.
     * @return never {@literal null}.
     */
    List<T> findAll(@Nullable Specification<T> spec);

    /**
     * Returns a {@link Page} of entities matching the given {@link Specification}.
     *
     * @param spec can be {@literal null}.
     * @param pageable must not be {@literal null}.
     * @return never {@literal null}.
     */
    Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

    /**
     * Returns all entities matching the given {@link Specification} and {@link Sort}.
     *
     * @param spec can be {@literal null}.
     * @param sort must not be {@literal null}.
     * @return never {@literal null}.
     */
    List<T> findAll(@Nullable Specification<T> spec, Sort sort);

    /**
     * Returns the number of instances that the given {@link Specification} will return.
     *
     * @param spec the {@link Specification} to count instances for. Can be {@literal null}.
     * @return the number of instances.
     */
    long count(@Nullable Specification<T> spec);
}

解读:

上述接口提供了一个findOne方法以及三个接受不同参数的findAll方法,这几个方法接受Specification类型的参数

示例

在实际开发中,通常按如下示例中展示的方式使用JpaSpecificationExecutor接口

Repository层:

@Repository
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {

}

Service层:

  public Page<User> getUsers(Integer id, Integer pageNum, Integer pageSize) {
    Sort sort = Sort.by(Sort.Direction.DESC, "id");
    Pageable pageable = PageRequest.of(pageNum, pageSize, sort);

    Specification<User> specification = (Specification<User>) (root, query, cb) -> {
      Path<Integer> path = root.get("id");
      return cb.lt(path, id);
    };
    return userRepository.findAll(specification, pageable);
  }

解读:

此处Service调用了userRepository的findAll方法,参数为Specification的实例以及Pageable的实例,该findAll方法实质上是JpaSpecificationExecutor提供的findAll方法:

Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
Specification

从本文前面的描述得知,在调用JpaSpecificationExecutor接口提供的几个方法时需要构造Specification类型的实例作为参数。

由此可知,Specification是JpaSpecificationExecutor接口的核心。

在前面关于SimpleJpaRepository的文章[地址]中提到了构造Specification类型参数的方式:匿名内部类或者ExampleSpecification的实例,本小节来剖析一下Specification的细节。

源码

Specification定义在包路径org.springframework.data.jpa.domain下,其定义如下:

/**
 * Specification in the sense of Domain Driven Design.
 *
 * @author Oliver Gierke
 * @author Thomas Darimont
 * @author Krzysztof Rzymkowski
 * @author Sebastian Staudt
 * @author Mark Paluch
 * @author Jens Schauder
 */
public interface Specification<T> extends Serializable {

    long serialVersionUID = 1L;

    /**
     * Negates the given {@link Specification}.
     *
     * @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
     * @param spec can be {@literal null}.
     * @return guaranteed to be not {@literal null}.
     * @since 2.0
     */
    static <T> Specification<T> not(@Nullable Specification<T> spec) {

        return spec == null //
                ? (root, query, builder) -> null //
                : (root, query, builder) -> builder.not(spec.toPredicate(root, query, builder));
    }

    /**
     * Simple static factory method to add some syntactic sugar around a {@link Specification}.
     *
     * @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
     * @param spec can be {@literal null}.
     * @return guaranteed to be not {@literal null}.
     * @since 2.0
     */
    static <T> Specification<T> where(@Nullable Specification<T> spec) {
        return spec == null ? (root, query, builder) -> null : spec;
    }

    /**
     * ANDs the given {@link Specification} to the current one.
     *
     * @param other can be {@literal null}.
     * @return The conjunction of the specifications
     * @since 2.0
     */
    default Specification<T> and(@Nullable Specification<T> other) {
        return SpecificationComposition.composed(this, other, CriteriaBuilder::and);
    }

    /**
     * ORs the given specification to the current one.
     *
     * @param other can be {@literal null}.
     * @return The disjunction of the specifications
     * @since 2.0
     */
    default Specification<T> or(@Nullable Specification<T> other) {
        return SpecificationComposition.composed(this, other, CriteriaBuilder::or);
    }

    /**
     * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
     * {@link Root} and {@link CriteriaQuery}.
     *
     * @param root must not be {@literal null}.
     * @param query must not be {@literal null}.
     * @param criteriaBuilder must not be {@literal null}.
     * @return a {@link Predicate}, may be {@literal null}.
     */
    @Nullable
    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
}

解读:

其中只有toPredicate方法抽象方法,所以通过匿名内部类的形式构造Specification的实例时只需实现toPredicate方法即可。

类图

Spring Data JPA:解析JpaSpecificationExecutor &amp; Specification第1张

解读:

从类图可知,ByIdsSpecification、ExampleSpecification实现了Specification接口

进一步发掘,可以发现ByIdsSpecification、ExampleSpecification都是SimpleJpaRepository的内部类,如下图所示:

Spring Data JPA:解析JpaSpecificationExecutor &amp; Specification第2张

解读:

ByIdsSpecification、ExampleSpecification按照各自的应用场景实现了toPredicate方法方法。

扩展阅读

Use Criteria Queries in a Spring Data Application[地址]


Root、Path

Specification中toPredicate方法的的第一个参数为Root<T> root,前面示例的Service层在实现toPredicate方法时通过调用如下语句获得Path类型的变量

Path<Integer> path = root.get("id");

解读:

此处的Path表示来自绑定类型或集合的简单属性(或复合属性)的路径,即user.id。

Root、Path均定义在包路径javax.persistence.criteria下。

下图展示了Root、Path之间的关系:

Spring Data JPA:解析JpaSpecificationExecutor &amp; Specification第3张

解读:

从上图可知,Root接口间接继承了Path接口,前述调用语句中的get方法由Path接口定义

Spring Data JPA:解析JpaSpecificationExecutor &amp; Specification第4张

相关代码如下:

<Y> Path<Y> get(String attributeName);

参加[官方Doc]

Note:

如果需要获取类似user.address.city的属性路径,则相关实现代码如下:

Path<Object> address = root.get("address");
Path<Object> city = address.get("city");
Predicate predicate = cb.equal(city, "WH");

免责声明:文章转载自《Spring Data JPA:解析JpaSpecificationExecutor &amp;amp; Specification》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Photoshop CS4破解方法Java_Hbase优化下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

补习系列(19)-springboot JPA + PostGreSQL

目录 SpringBoot 整合 PostGreSQL 一、PostGreSQL简介 二、关于 SpringDataJPA 三、整合 PostGreSQL A. 依赖包 B. 配置文件 C. 模型定义 D. 持久层 E. Service 层 四、高级操作 1. 自定义查询 2. 聚合 3. 视图 4. 连接池 5. 事务 小结 Spr...

【转载】时域信号的频谱、功率谱和功率谱密度计算

原文地址:http://blog.chinaunix.net/uid-11829250-id-4992257.html 以高斯信号为例,计算幅度谱、相位谱、双边功率谱、双边功率谱密度、单边功率谱、单边功率谱密度。(转载请注明出处) MATLAB程序代码: %================================================...

学习笔记_Jpa

SpringDataJpa 原文链接 JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现...

利用pyinstaller打包加密Python项目

  最近用Python给媳妇写了两个小项目,给解决了她的每天重复的一些人工操作。媳妇很开心,但是问题来了,她是个Python小白,对她来说,需要安装配置Python环境和一大堆第三方模块是个麻烦事儿。而且后续把这些工作交接给别人的话,一是又需要重新安装Python环境,二是我辛苦给她写的源码就这样暴露了。   为了解决这个问题,于是就开始百度。果然Pyth...

utittest和pytest中mock的使用详细介绍

头号玩家 模拟世界 单元测试库介绍 mock Mock是Python中一个用于支持单元测试的库,它的主要功能是使用mock对象替代掉指定的Python对象,以达到模拟对象的行为。python3.3 以前,mock是第三方库,需要安装之后才能使用。python3.3之后,mock作为标准库内置到 unittest。 unittest: unittest是Py...

记录k8s安装es

apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: elasticsearch-pdb namespace: test spec: selector: matchLabels: app: elasticsearch maxUnavail...