前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊spring data jpa的SimpleJpaRepository

聊聊spring data jpa的SimpleJpaRepository

原创
作者头像
code4it
发布2019-04-14 20:16:23
4.4K0
发布2019-04-14 20:16:23
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究一下spring data jpa的SimpleJpaRepository

JpaRepositoryImplementation

spring-data-jpa-2.1.6.RELEASE-sources.jar!/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java

代码语言:javascript
复制
/**
 * SPI interface to be implemented by {@link JpaRepository} implementations.
 *
 * @author Oliver Gierke
 * @author Stefan Fussenegger
 */
@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);
}
  • JpaRepositoryImplementation接口继承了JpaRepository及JpaSpecificationExecutor,它是JpaRepository接口实现类的SPI interface;它定义了setRepositoryMethodMetadata方法

SimpleJpaRepository

spring-data-jpa-2.1.6.RELEASE-sources.jar!/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

代码语言:javascript
复制
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
​
    private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
​
    private final JpaEntityInformation<T, ?> entityInformation;
    private final EntityManager em;
    private final PersistenceProvider provider;
​
    private @Nullable CrudMethodMetadata metadata;
​
    /**
     * 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);
    }
​
    /**
     * Configures a custom {@link CrudMethodMetadata} to be used to detect {@link LockModeType}s and query hints to be
     * applied to queries.
     *
     * @param crudMethodMetadata
     */
    public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) {
        this.metadata = crudMethodMetadata;
    }
​
    @Nullable
    protected CrudMethodMetadata getRepositoryMethodMetadata() {
        return metadata;
    }
​
    //......
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
     */
    @Transactional
    public void deleteById(ID id) {
​
        Assert.notNull(id, ID_MUST_NOT_BE_NULL);
​
        delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(
                String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1)));
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
     */
    @Transactional
    public void delete(T entity) {
​
        Assert.notNull(entity, "The entity must not be null!");
        em.remove(em.contains(entity) ? entity : em.merge(entity));
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
     */
    @Transactional
    public void deleteAll(Iterable<? extends T> entities) {
​
        Assert.notNull(entities, "The given Iterable of entities not be null!");
​
        for (T entity : entities) {
            delete(entity);
        }
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java.lang.Iterable)
     */
    @Transactional
    public void deleteInBatch(Iterable<T> entities) {
​
        Assert.notNull(entities, "The given Iterable of entities not be null!");
​
        if (!entities.iterator().hasNext()) {
            return;
        }
​
        applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em)
                .executeUpdate();
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.Repository#deleteAll()
     */
    @Transactional
    public void deleteAll() {
​
        for (T element : findAll()) {
            delete(element);
        }
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch()
     */
    @Transactional
    public void deleteAllInBatch() {
        em.createQuery(getDeleteAllQueryString()).executeUpdate();
    }
​
    //......
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
     */
    @Transactional
    public <S extends T> S save(S entity) {
​
        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#saveAndFlush(java.lang.Object)
     */
    @Transactional
    public <S extends T> S saveAndFlush(S entity) {
​
        S result = save(entity);
        flush();
​
        return result;
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#save(java.lang.Iterable)
     */
    @Transactional
    public <S extends T> List<S> saveAll(Iterable<S> entities) {
​
        Assert.notNull(entities, "The given Iterable of entities not be null!");
​
        List<S> result = new ArrayList<S>();
​
        for (S entity : entities) {
            result.add(save(entity));
        }
​
        return result;
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#flush()
     */
    @Transactional
    public void flush() {
        em.flush();
    }
​
    //......
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#findById(java.io.Serializable)
     */
    public Optional<T> findById(ID id) {
​
        Assert.notNull(id, ID_MUST_NOT_BE_NULL);
​
        Class<T> domainType = getDomainClass();
​
        if (metadata == null) {
            return Optional.ofNullable(em.find(domainType, id));
        }
​
        LockModeType type = metadata.getLockModeType();
​
        Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
​
        return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
    }
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable)
     */
    @Override
    public T getOne(ID id) {
​
        Assert.notNull(id, ID_MUST_NOT_BE_NULL);
        return em.getReference(getDomainClass(), id);
    }
​
    /**
     * 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;
    }
    //......
}
  • SimpleJpaRepository实现了JpaRepositoryImplementation接口,它是CrudRepository的默认实现;它的构造器都要求传入EntityManager
  • 它的类上注解了@Transactional(readOnly = true);而对deleteById、delete、deleteAll、deleteInBatch、deleteAllInBatch、save、saveAndFlush、saveAll、flush都添加了@Transactional注解
  • 从各个方法的实现可以看到SimpleJpaRepository是使用EntityManager来完成具体的方法功能,对于查询功能很多都借助了applySpecificationToCriteria方法,将spring data的Specification转换为javax.persistence的CriteriaQuery

JpaRepositoryFactory

spring-data-jpa-2.1.6.RELEASE-sources.jar!/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java

代码语言:javascript
复制
public class JpaRepositoryFactory extends RepositoryFactorySupport {
​
    private final EntityManager entityManager;
    private final QueryExtractor extractor;
    private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor;
​
    private EntityPathResolver entityPathResolver;
    private EscapeCharacter escapeCharacter = EscapeCharacter.of('\\');
​
    /**
     * Creates a new {@link JpaRepositoryFactory}.
     *
     * @param entityManager must not be {@literal null}
     */
    public JpaRepositoryFactory(EntityManager entityManager) {
​
        Assert.notNull(entityManager, "EntityManager must not be null!");
​
        this.entityManager = entityManager;
        this.extractor = PersistenceProvider.fromEntityManager(entityManager);
        this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor();
        this.entityPathResolver = SimpleEntityPathResolver.INSTANCE;
​
        addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor);
​
        if (extractor.equals(PersistenceProvider.ECLIPSELINK)) {
            addQueryCreationListener(new EclipseLinkProjectionQueryCreationListener(entityManager));
        }
    }
​
    //......
​
    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata)
     */
    @Override
    protected final JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information) {
​
        JpaRepositoryImplementation<?, ?> repository = getTargetRepository(information, entityManager);
        repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata());
​
        return repository;
    }
​
    /**
     * Callback to create a {@link JpaRepository} instance with the given {@link EntityManager}
     *
     * @param information will never be {@literal null}.
     * @param entityManager will never be {@literal null}.
     * @return
     */
    protected JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information,
            EntityManager entityManager) {
​
        JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(information.getDomainType());
        Object repository = getTargetRepositoryViaReflection(information, entityInformation, entityManager);
​
        Assert.isInstanceOf(JpaRepositoryImplementation.class, repository);
​
        return (JpaRepositoryImplementation<?, ?>) repository;
    }
​
    //......
}
  • JpaRepositoryFactory的getTargetRepository会根据RepositoryInformation创建JpaRepositoryImplementation,这里默认创建的是SimpleJpaRepository实例

RepositoryFactorySupport

spring-data-commons-2.1.6.RELEASE-sources.jar!/org/springframework/data/repository/core/support/RepositoryFactorySupport.java

代码语言:javascript
复制
public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {
​
    //......
​
    /**
     * Returns a repository instance for the given interface backed by an instance providing implementation logic for
     * custom logic.
     *
     * @param repositoryInterface must not be {@literal null}.
     * @param fragments must not be {@literal null}.
     * @return
     * @since 2.0
     */
    @SuppressWarnings({ "unchecked" })
    public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
​
        if (LOG.isDebugEnabled()) {
            LOG.debug("Initializing repository instance for {}…", repositoryInterface.getName());
        }
​
        Assert.notNull(repositoryInterface, "Repository interface must not be null!");
        Assert.notNull(fragments, "RepositoryFragments must not be null!");
​
        RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
        RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
        RepositoryInformation information = getRepositoryInformation(metadata, composition);
​
        validate(information, composition);
​
        Object target = getTargetRepository(information);
​
        // Create proxy
        ProxyFactory result = new ProxyFactory();
        result.setTarget(target);
        result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
​
        if (MethodInvocationValidator.supports(repositoryInterface)) {
            result.addAdvice(new MethodInvocationValidator());
        }
​
        result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
        result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
​
        postProcessors.forEach(processor -> processor.postProcess(result, information));
​
        result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
​
        ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
        result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));
​
        composition = composition.append(RepositoryFragment.implemented(target));
        result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
​
        T repository = (T) result.getProxy(classLoader);
​
        if (LOG.isDebugEnabled()) {
            LOG.debug("Finished creation of repository instance for {}.", repositoryInterface.getName());
        }
​
        return repository;
    }
​
    //......
}
  • RepositoryFactorySupport的getRepository方法在调用子类的getTargetRepository创建SimpleJpaRepository实例之后,会对其进行proxy,设置其接口为用户定义的dao接口、Repository、TransactionalProxy,并添加了SurroundingTransactionDetectorMethodInterceptor、DefaultMethodInvokingMethodInterceptor、QueryExecutorMethodInterceptor、ImplementationMethodExecutionInterceptor等MethodInterceptor,最后生成最终的实现类

小结

  • JpaRepositoryImplementation接口继承了JpaRepository及JpaSpecificationExecutor,它是JpaRepository接口实现类的SPI interface;它定义了setRepositoryMethodMetadata方法
  • SimpleJpaRepository实现了JpaRepositoryImplementation接口,它是CrudRepository的默认实现;它的构造器都要求传入EntityManager;从各个方法的实现可以看到SimpleJpaRepository是使用EntityManager来完成具体的方法功能,对于查询功能很多都借助了applySpecificationToCriteria方法,将spring data的Specification转换为javax.persistence的CriteriaQuery
  • JpaRepositoryFactory的getTargetRepository会根据RepositoryInformation创建JpaRepositoryImplementation,这里默认创建的是SimpleJpaRepository实例;RepositoryFactorySupport的getRepository方法在调用子类的getTargetRepository创建SimpleJpaRepository实例之后,会对其进行proxy,设置其接口为用户定义的dao接口、Repository、TransactionalProxy,并添加了SurroundingTransactionDetectorMethodInterceptor、DefaultMethodInvokingMethodInterceptor、QueryExecutorMethodInterceptor、ImplementationMethodExecutionInterceptor等MethodInterceptor,最后生成最终的实现类

doc

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JpaRepositoryImplementation
  • SimpleJpaRepository
  • JpaRepositoryFactory
  • RepositoryFactorySupport
  • 小结
  • doc
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档