zoukankan      html  css  js  c++  java
  • Spring Data JPA:解析SimpleJpaRepository

    源码

    SimpleJpaRepository的定义如下:

    /**
     * Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer
     * you a more sophisticated interface than the plain {@link EntityManager} .
     *
     * @author Oliver Gierke
     * @author Eberhard Wolff
     * @author Thomas Darimont
     * @author Mark Paluch
     * @author Christoph Strobl
     * @author Stefan Fussenegger
     * @author Jens Schauder
     * @author David Madden
     * @author Moritz Becker
     * @param <T> the type of the entity to handle
     * @param <ID> the type of the entity's identifier
     */
    @Repository
    @Transactional(readOnly = true)
    public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

    解读:

    SimpleJpaRepository实现了JpaRepositoryImplementation接口。

    JpaRepositoryImplementation的定义如下:

    /**
     * SPI interface to be implemented by {@link JpaRepository} implementations.
     *
     * @author Oliver Gierke
     * @author Stefan Fussenegger
     * @author Jens Schauder
     */
    @NoRepositoryBean
    public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
    
        /**
         * Configures the {@link CrudMethodMetadata} to be used with the repository.
         *
         * @param crudMethodMetadata must not be {@literal null}.
         */
        void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata);
    
        /**
         * Configures the {@link EscapeCharacter} to be used with the repository.
         *
         * @param escapeCharacter Must not be {@literal null}.
         */
        default void setEscapeCharacter(EscapeCharacter escapeCharacter) {
    
        }
    }

    解读:

    JpaRepositoryImplementation接口继承了JpaSpecificationExecutor。

    类图

    调用链路

    观察SimpleJpaRepository中findOne(Example<S> example)方法的实现,代码如下:

        /*
         * (non-Javadoc)
         * @see org.springframework.data.repository.query.QueryByExampleExecutor#findOne(org.springframework.data.domain.Example)
         */
        @Override
        public <S extends T> Optional<S> findOne(Example<S> example) {
    
            try {
                return Optional
                        .of(getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), Sort.unsorted())
                                .getSingleResult());
            } catch (NoResultException e) {
                return Optional.empty();
            }
        }

    解读:

    此方法由QueryByExampleExecutor接口定义。

    观察SimpleJpaRepository中findOne(@Nullable Specification<T> spec)方法的实现,代码如下:

        /*
         * (non-Javadoc)
         * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findOne(org.springframework.data.jpa.domain.Specification)
         */
        @Override
        public Optional<T> findOne(@Nullable Specification<T> spec) {
    
            try {
                return Optional.of(getQuery(spec, Sort.unsorted()).getSingleResult());
            } catch (NoResultException e) {
                return Optional.empty();
            }
        }

    解读:

    此方法由JpaSpecificationExecutor接口定义。

    小结:

    上述两个findOne方法最终都是调用了如下getQuery方法:

        /**
         * Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}.
         *
         * @param spec can be {@literal null}.
         * @param domainClass must not be {@literal null}.
         * @param sort must not be {@literal null}.
         * @return
         */
        protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort) {
    
            CriteriaBuilder builder = em.getCriteriaBuilder();
            CriteriaQuery<S> query = builder.createQuery(domainClass);
    
            Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
            query.select(root);
    
            if (sort.isSorted()) {
                query.orderBy(toOrders(sort, root, builder));
            }
    
            return applyRepositoryMethodMetadata(em.createQuery(query));
        }

    解读:

    getQuery方法调用了applySpecificationToCriteria方法,该方法的实现如下:

        /**
         * Applies the given {@link Specification} to the given {@link CriteriaQuery}.
         *
         * @param spec can be {@literal null}.
         * @param domainClass must not be {@literal null}.
         * @param query must not be {@literal null}.
         * @return
         */
        private <S, U extends T> Root<U> applySpecificationToCriteria(@Nullable Specification<U> spec, Class<U> domainClass,
                CriteriaQuery<S> query) {
    
            Assert.notNull(domainClass, "Domain class must not be null!");
            Assert.notNull(query, "CriteriaQuery must not be null!");
    
            Root<U> root = query.from(domainClass);
    
            if (spec == null) {
                return root;
            }
    
            CriteriaBuilder builder = em.getCriteriaBuilder();
            Predicate predicate = spec.toPredicate(root, query, builder);
    
            if (predicate != null) {
                query.where(predicate);
            }
    
            return root;
        }

    解读:

    applySpecificationToCriteria方法调用了Specification的toPredicate方法,该方法是一个抽象方法,其定义如下:

        /**
         * 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);

    解读:

    纵观前述调用过程可知,此处Specification的toPredicate方法由findOne方法传递给getQuery方法的参数对应的类实现。

    从findOne(Example<S> example)的实现可知,toPredicate方法由参数new ExampleSpecification<S>(example, escapeCharacter)对应ExampleSpecification类实现。

    ExampleSpecification

    由前面的类图可知,ExampleSpecification是SimpleJpaRepository的内部类,其定义如下:

        /**
         * {@link Specification} that gives access to the {@link Predicate} instance representing the values contained in the
         * {@link Example}.
         *
         * @author Christoph Strobl
         * @since 1.10
         * @param <T>
         */
        private static class ExampleSpecification<T> implements Specification<T> {
    
            private static final long serialVersionUID = 1L;
    
            private final Example<T> example;
            private final EscapeCharacter escapeCharacter;
    
            /**
             * Creates new {@link ExampleSpecification}.
             *
             * @param example
             * @param escapeCharacter
             */
            ExampleSpecification(Example<T> example, EscapeCharacter escapeCharacter) {
    
                Assert.notNull(example, "Example must not be null!");
                Assert.notNull(escapeCharacter, "EscapeCharacter must not be null!");
    
                this.example = example;
                this.escapeCharacter = escapeCharacter;
            }
    
            /*
             * (non-Javadoc)
             * @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder)
             */
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return QueryByExamplePredicateBuilder.getPredicate(root, cb, example, escapeCharacter);
            }
        }

    解读:

    ExampleSpecification实现了Specification接口中的toPredicate方法。

    小结:

    如果需要调用SimpleJpaRepository的findOne方法,需要构造Specification或者Example对应的实例。

    示例:

    以匿名内部类的形式构造Specification对应的实例

        Specification<User> specification = new Specification<>() {
          @Override
          public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            Path<Integer> path = root.get("id");
            return cb.lt(path, id);
          }
        };

    从前面的描述可知,getQuery方法是findOne方法的核心,而getQuery中query的构造主要由成员变量em来完成,所以此处观察成员变量是在何处赋值的。

    构造函数

    对应的定义如下:

        /**
         * Creates a new {@link SimpleJpaRepository} to manage objects of the given {@link JpaEntityInformation}.
         *
         * @param entityInformation must not be {@literal null}.
         * @param entityManager must not be {@literal null}.
         */
        public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
    
            Assert.notNull(entityInformation, "JpaEntityInformation must not be null!");
            Assert.notNull(entityManager, "EntityManager must not be null!");
    
            this.entityInformation = entityInformation;
            this.em = entityManager;
            this.provider = PersistenceProvider.fromEntityManager(entityManager);
        }
    
        /**
         * Creates a new {@link SimpleJpaRepository} to manage objects of the given domain type.
         *
         * @param domainClass must not be {@literal null}.
         * @param em must not be {@literal null}.
         */
        public SimpleJpaRepository(Class<T> domainClass, EntityManager em) {
            this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em);
        }

    解读:

    SimpleJpaRepository中成员变量是在构造SimpleJpaRepository对象时传递进来的。


    Note:

    有如下一些方法调用了getQuery方法

    扩展阅读

    Jakarta Persistence[地址]

  • 相关阅读:
    C++练习--实现客户机(CLIENT)类
    C++创建一个名为Ellipse的椭圆类--练习
    C++创建People类--练习
    C++定义一个简单的Computer类
    C++创建学生类练习
    第十四届浙江财经大学程序设计竞赛重现赛--A-A Sad Story
    第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--I-填空题
    第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--K-密码
    第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--G-旋转矩阵
    Hadoop学习笔记(2) 关于MapReduce
  • 原文地址:https://www.cnblogs.com/studyLog-share/p/15187770.html
Copyright © 2011-2022 走看看