Talk about SimpleJpaRepository of spring data jpa.

  jpa

Order

This article mainly studies SimpleJpaRepository of spring data jpa.

JpaRepositoryImplementation

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

/**
 * 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 interface inherits JpaRepository and JpaSpecificationExecutor, which is SPI interface; of JPA Repository interface implementation class; It defines the setRepositoryMethodMetadata method

SimpleJpaRepository

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

@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 implements the JpaRepositoryImplementation interface, which is the default implementation of CrudRepository. Its constructors all require to pass into EntityManager
  • It has @ Transactional (ReadOnly = True) annotated on its class; The @Transactional annotation is added to deleteById, delete, deleteAll, deleteInBatch, deleteAllInBatch, save, saveAndFlush, saveAll, flush.
  • From the implementation of each method, it can be seen that SimpleJpaRepository uses EntityManager to complete specific method functions. For many query functions, it uses applySpecificationToCriteria method to convert spring data’s Specification into javax.persistence’s CriteriaQuery.

JpaRepositoryFactory

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

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’s getTargetRepository will create JpaRepositoryImplementation according to RepositoryInformation, and SimpleJPARepositoryImplementation instance is created by default here.

RepositoryFactorySupport

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

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;
    }

    //......
}
  • The getRepository method of RepositoryFactorySupport will proxy SimpleJpaRepository instance after calling getTargetRepository of subclass, setting its interface as user-defined dao interface, Repository, TransactionalProxy, Methods Interceptors such as SurroundingTransactionDetectorFormeTermodinterceptor, DefaultMethodinvoking MethodInterceptor, QueryExecutorMethodInterceptor, ImplementationMethodExecutionInterceptor were added, and finally the final implementation class was generated.

Summary

  • JpaRepositoryImplementation interface inherits JpaRepository and JpaSpecificationExecutor, which is SPI interface; of JPA Repository interface implementation class; It defines the setRepositoryMethodMetadata method
  • SimpleJpaRepository implements the JpaRepositoryImplementation interface, which is the default implementation of CrudRepository. Its constructors all require to pass into EntityManager; ; From the implementation of each method, it can be seen that SimpleJpaRepository uses EntityManager to complete specific method functions. For many query functions, it uses applySpecificationToCriteria method to convert spring data’s Specification into javax.persistence’s CriteriaQuery.
  • JpaRepositoryFactory’s getTargetRepository will create JpaRepositoryImplementation according to RepositoryInformation, where SimpleJpaRepository instance is created by default; The getRepository method of RepositoryFactorySupport will proxy SimpleJpaRepository instance after calling getTargetRepository of subclass, setting its interface as user-defined dao interface, Repository, TransactionalProxy, Methods Interceptors such as SurroundingTransactionDetectorFormeTermodinterceptor, DefaultMethodinvoking MethodInterceptor, QueryExecutorMethodInterceptor, ImplementationMethodExecutionInterceptor were added, and finally the final implementation class was generated.

doc