zoukankan      html  css  js  c++  java
  • Spring Data JPA:解析JpaSpecificationExecutor & Specification

    源码

    在前面关于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方法即可。

    类图

    解读:

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

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

    解读:

    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之间的关系:

    解读:

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

    相关代码如下:

    <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");
  • 相关阅读:
    Prometheus学习系列(九)之Prometheus 存储
    Prometheus学习系列(八)之Prometheus API说明
    SSE图像算法优化系列七:基于SSE实现的极速的矩形核腐蚀和膨胀(最大值和最小值)算法。
    Crimm Imageshop 2.3。
    【短道速滑一】OpenCV中cvResize函数使用双线性插值缩小图像到长宽大小一半时速度飞快(比最近邻还快)之异象解析和自我实现。
    【算法随记七】巧用SIMD指令实现急速的字节流按位反转算法。
    【算法随记六】一段Matlab版本的Total Variation(TV)去噪算法的C语言翻译。
    SSE图像算法优化系列三十:GIMP中的Noise Reduction算法原理及快速实现。
    一种快速简便优秀的全局曲线调整与局部信息想结合的非线性彩色增强算法(多图深度分析和探索)
    【算法随记五】使用FFT变换自动去除图像中严重的网纹。
  • 原文地址:https://www.cnblogs.com/studyLog-share/p/15189769.html
Copyright © 2011-2022 走看看