【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

 Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)

8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码

9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)

前言

在前一篇文章

【源码】Spring Data JPA原理解析之Repository的自动注入(二)-CSDN博客

分享了Repository bean的创建。Respository的bean是一个通过ProxyFactory创建的动态代理对象,该代理对象实现了自定义的Repository接口、Repository.class以及TransactionProxy.class接口,且代理的target为SimpleJpaRepository对象。

在开发中,只需自定义实现JpaRepository接口,就可以实现CRUD、findBy+属性名称+条件查询等许多查询功能,核心在于使用了动态代理,其中的save、delete、find等功能的实现就在SimpleJpaRepository。

本篇分享一下SimpleJpaRepository的原理,在开始介绍之前,先了解一下动态代理是如何执行而后调用执行到SimpleJpaRepository。

动态代理方法拦截

Spring框架的两大核心概念:IOC(控制反转,把创建对象过程交给Spring进行管理)和AOP(面向切面,不修改源代码进行功能增强)。前面讲解的两篇Repository的自动注入使用的是Spring的IOC,而Repository的代理对象使用的是AOP。AOP是一种编程思想,是基于动态代理实现。

限于篇幅,此处只分享核心的实现。

Respository的bean是一个通过ProxyFactory创建的动态代理对象,ProxyFactory的父类的是ProxyCreatorSupport。在ProxyCreatorSupport的构造方法中,会new一个DefaultAopProxyFactory()。

2.1 DefaultAopProxyFactory

DefaultAopProxyFactory为默认的AOP代理创建工厂,代码如下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// config.isOptimize()与config.isProxyTargetClass()默认返回都是false,可以通过config进行配置修改// 需要优化 || 强制cglib || 没有实现接口if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}// 倘若目标Class本身就是个接口,或者它已经是个JDK的代理类(Proxy的子类。所有的JDK代理类都是此类的子类),// 用JDK的动态代理。Proxy.isProxyClass(targetClass)方法判断targetClass是否是Proxy通过// getProxyClass()或newProxyInstance()创建的if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// 实用CGLIB代理方式// ObjenesisCglibAopProxy是CglibAopProxy的子类。Spring4.0之后提供的return new ObjenesisCglibAopProxy(config);}else {// 否则采用JDK得动态代理。(一般都有实现接口)return new JdkDynamicAopProxy(config);}}/*** 判断该代理对象是否没有用户提供的代理接口,是返回true* * 如果它没有实现过接口(ifcs.length == 0)||* 仅仅实现了一个接口,但是这个接口却是SpringProxy类型的。返回false* 其中SpringProxy:一个标记接口。Spring AOP产生的所有的代理类都是它的子类*/private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] ifcs = config.getProxiedInterfaces();return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));}}

该类对外只有一个createAopProxy()方法,返回一个AopProxy。对于Repository,在创建ProxyFactory之后,调用setInterfaces(),添加实现的接口,所以上面的hasNoUserSuppliedProxyInterfaces()方法将返回false。所以createAopProxy()方法返回的是JdkDynamicAopProxy对象。

2.2 JdkDynamicAopProxy

JdkDynamicAopProxy的核心代码如下:

/*** 实现AopProxy和InvocationHandler,自己本身是代理回调的处理器*/
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {/*** 真正创建JDK动态代理实例*/@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());}// 寻找接口,到最终代理的接口就是这里返回的所有接口(除了自己的接口,还有Spring默认的一些接口)Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);//查找被代理的接口中是否定义了equals、hashCode方法findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);// 第三个参数传的this,处理器就是自己,表示当前类是InvocationHandler类型的,当调用代理对象的任何方法的时候// 都会被被当前类的 invoke 方法处理。new一个代理对象return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}/*** 当在程序中调用代理对象的任何方法,最终都会被invoke方法处理*/@Override@Nullablepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//旧的代理对象Object oldProxy = null;//用来标记是否需要将代理对象暴露在ThreadLocal中boolean setProxyContext = false;//获取目标源TargetSource targetSource = this.advised.targetSource;//目标对象Object target = null;try {// 处理equals方法if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {return equals(args[0]);}// 处理hashCode方法else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {return hashCode();}/*** 处理DecoratingProxy*/else if (method.getDeclaringClass() == DecoratingProxy.class) {return AopProxyUtils.ultimateTargetClass(this.advised);}// 方法来源于 Advised 接口,代理对象默认情况下会实现 Advised 接口,可以通过代理对象来动态向代理对象中添加通知等else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;//是否需要在threadLocal中暴露代理对象if (this.advised.exposeProxy) {oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);// 获取当前方法的拦截器链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 拦截器链为空的情况下,表示这个方法上面没有找到任何增强的通知,那么会直接通过反射调用目标对象if (chain.isEmpty()) {// 获取方法请求的参数(有时候方法中有可变参数,所谓可变参数就是带有省略号(...)这种格式的参数,// 传入的参数类型和这种类型不一样的时候,会通过下面的adaptArgumentsIfNecessary方法进行转换)Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);//通过反射直接调用目标方法retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {// 创建一个方法调用器(包含了代理对象、目标对象、调用的方法、参数、目标类型、方法拦截器链)MethodInvocation invocation =new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// 通过拦截器链一个个调用最终到目标方法的调用retVal = invocation.proceed();}// 根据方法返回值的类型,做一些处理,比如方法返回的类型为自己,则最后需要将返回值置为代理对象Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target &&returnType != Object.class && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {retVal = proxy;}// 方法的返回值类型returnType为原始类型(即int、byte、double等这种类型的) && retVal为null,// 此时如果将null转换为原始类型会报错,所以此处直接抛出异常else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {// 目标对象不为null && 目标源不是静态的//所谓静态的,你可以理解为是否是单例的// isStatic为true,表示目标对象是单例的,同一个代理对象中所有方法共享一个目标对象// isStatic为false的时候,通常每次调用代理的方法,target对象是不一样的,所以方法调用万之后需要进行释放,可能有些资源清理,连接的关闭等操作if (target != null && !targetSource.isStatic()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// 需要将旧的代理再放回到上线文中AopContext.setCurrentProxy(oldProxy);}}}// 省略其他}

2.2.1 使用ProxyFactory.getProxy()方法时,会先createAopProxy(),调用2.1中的DefaultAopProxyFactory.createAopProxy()方法,返回一个JdkDynamicAopProxy对象,然后调用JdkDynamicAopProxy.getProxy()方法。在上面的getProxy()方法中,执行Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)创建一个代理对象,并返回。其中第三个参数为this,即JdkDynamicAopProxy本身,因为JdkDynamicAopProxy实现了InvocationHandler接口;

2.2.2 当代理对象的方法被调用的时候,都会执行InvocationHandler.invoke()方法。结合2.2.1的分析,会执行JdkDynamicAopProxy.invoke()方法。该方法的前半部分用于处理特殊方法,如equals、hashCode等。

然后获取当前方法的拦截器链,如果没有拦截器,则反射执行原方法;如果有,则new一个ReflectiveMethodInvocation对象,并调用ReflectiveMethodInvocation.proceed()方法。

对于Repository接口类,在创建的时候添加了好几个拦截器,所以会new一个ReflectiveMethodInvocation对象,并调用ReflectiveMethodInvocation.proceed()方法。

2.3 ReflectiveMethodInvocation

ReflectiveMethodInvocation的核心代码如下:

/*** Joinpoint的唯一实现子类,维护代理对象、目标对象、执行方法、方法可用的所有拦截器。在proceed()方法中,遍历所有可用的拦截器,* 并挨个匹配执行,如果不能匹配,则递归执行proceed()方法,执行下一个拦截器*/
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {//代理对象protected final Object proxy;//目标对象@Nullableprotected final Object target;//调用的方法protected final Method method;//调用方法的参数protected Object[] arguments;//目标类的class对象@Nullableprivate final Class<?> targetClass;/*** Lazily initialized map of user-specific attributes for this invocation.*/@Nullableprivate Map<String, Object> userAttributes;//需要动态核对的MethodInterceptor和InterceptorAndDynamicMethodMatcher列表protected final List<?> interceptorsAndDynamicMethodMatchers;// 当前使用的拦截器在集合中的下标private int currentInterceptorIndex = -1;/*** 保存基本信息*/protected ReflectiveMethodInvocation(Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {this.proxy = proxy;this.target = target;this.targetClass = targetClass;//获取桥接方法this.method = BridgeMethodResolver.findBridgedMethod(method);this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;}/*** 执行下一个拦截器* @return* @throws Throwable*/@Override@Nullablepublic Object proceed() throws Throwable {// We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {// 开始时,currentInterceptorIndex等于-1。当切面通知无法处理该代理方法时,会调用proceed()方法,// 直到currentInterceptorIndex的值等于this.interceptorsAndDynamicMethodMatchers.size() - 1,// 此时会执行invokeJoinpoint()方法。进而执行子类CglibAopProxy.CglibMethodInvocation.invokeJoinpoint()方法return invokeJoinpoint();}// 挨个的执行切面通知的方法Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 判断拦截器是否是InterceptorAndDynamicMethodMatcher(动态拦截器),// 动态拦截器要根据方法的参数的值来判断拦截器是否需要执行if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;// 检查方法的动态匹配,如果能够匹配,则执行对应的方法拦截器,如果切面有参数值,则对参数值进行赋值if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);} else {// 如果方法不能动态匹配,则递归执行proceed()return proceed();}}else {// 如果拦截器是一个普通的方法拦截器,则执行调用拦截器的invoke()方法return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}/*** 通过反射,调用执行目标对象的方法*/@Nullableprotected Object invokeJoinpoint() throws Throwable {return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);}// 省略其他}

在2.2.2中分析,Repository的方法调用时,会执行ReflectiveMethodInvocation.proceed()的方法,在该方法中,循环遍历所有的拦截器。

在拦截器的invoke(MethodInvocation invocation)方法中,传入的参数为this,即ReflectiveMethodInvocation本身。在拦截器中,如果需要执行下一个拦截器,需要再次调用invocation.proceed()方法。否则currentInterceptorIndex不会等于拦截器的个数减1,invokeJoinpoint()方法也将不会执行。

对于SimpleJpaRepository中实现的方法,会在执行完所有的拦截器之后,再次执行ReflectiveMethodInvocation.proceed()方法时,执行invokeJoinpoint()。在该方法中,执行target的方法,此处的target为SimpleJpaRepository对象。

SimpleJpaRepository

package org.springframework.data.jpa.repository.support;@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 entityManager;private final PersistenceProvider provider;private @Nullable CrudMethodMetadata metadata;private @Nullable ProjectionFactory projectionFactory;private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;/*** 在RepositoryFactorySupport类的getTargetRepositoryViaReflection() -> instantiateClass()方法调用实例化* @param entityInformation Repository<T, ID>中的T的信息* @param entityManager 系统的EntityManager对象*/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.entityManager = entityManager;this.provider = PersistenceProvider.fromEntityManager(entityManager);}public SimpleJpaRepository(Class<T> domainClass, EntityManager entityManager) {this(JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager), entityManager);}@Overridepublic void setRepositoryMethodMetadata(CrudMethodMetadata metadata) {this.metadata = metadata;}@Overridepublic void setEscapeCharacter(EscapeCharacter escapeCharacter) {this.escapeCharacter = escapeCharacter;}@Overridepublic void setProjectionFactory(ProjectionFactory projectionFactory) {this.projectionFactory = projectionFactory;}@Nullableprotected CrudMethodMetadata getRepositoryMethodMetadata() {return metadata;}protected Class<T> getDomainClass() {return entityInformation.getJavaType();}/*** 返回删除语句的sql。delete from %s x,其中%s为当前实体类的名称* @return*/private String getDeleteAllQueryString() {return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName());}/*** 返回count语句的sql。select count(*) from %s x,其中%s为当前实体类的名称* @return*/private String getCountQueryString() {String countQuery = String.format(COUNT_QUERY_STRING, provider.getCountQueryPlaceholder(), "%s");return getQueryString(countQuery, entityInformation.getEntityName());}@Transactional@Overridepublic void deleteById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);// 删除之前,会先调用findById()查询记录,如果存在,再执行delete方法findById(id).ifPresent(this::delete);}@Override@Transactional@SuppressWarnings("unchecked")public void delete(T entity) {Assert.notNull(entity, "Entity must not be null");// 如果是新实体,则不能删除。有很多实现,比如判断对应的entity定义的Id属性是否有值等if (entityInformation.isNew(entity)) {return;}// 获取用户自定义的实体类Class<?> type = ProxyUtils.getUserClass(entity);// 通过id从EntityManager查询记录是否存在T existing = (T) entityManager.find(type, entityInformation.getId(entity));// if the entity to be deleted doesn't exist, delete is a NOOPif (existing == null) {return;}// 获取实体并删除entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity));}@Override@Transactionalpublic void deleteAllById(Iterable<? extends ID> ids) {Assert.notNull(ids, "Ids must not be null");// 循环执行deleteByIdfor (ID id : ids) {deleteById(id);}}@Override@Transactionalpublic void deleteAllByIdInBatch(Iterable<ID> ids) {Assert.notNull(ids, "Ids must not be null");if (!ids.iterator().hasNext()) {return;}// 如果是复合idif (entityInformation.hasCompositeId()) {List<T> entities = new ArrayList<>();// generate entity (proxies) without accessing the database.// 生成实体(代理)而不访问数据库ids.forEach(id -> entities.add(getReferenceById(id)));// 复合id删除时,拼接删除语句,并执行deleteAllInBatch(entities);} else {// 执行delete from %s x where %s in :idsString queryString = String.format(DELETE_ALL_QUERY_BY_ID_STRING, entityInformation.getEntityName(),entityInformation.getIdAttribute().getName());Query query = entityManager.createQuery(queryString);// 一些JPA提供程序要求ids是Collection类型,因此如果尚未转换,则必须进行转换// 查询语句的ids赋值if (Collection.class.isInstance(ids)) {query.setParameter("ids", ids);} else {Collection<ID> idsCollection = StreamSupport.stream(ids.spliterator(), false).collect(Collectors.toCollection(ArrayList::new));query.setParameter("ids", idsCollection);}//  应用Query的hint。将添加的元数据中的hint信息添加到query中applyQueryHints(query);// 执行删除操作query.executeUpdate();}}@Override@Transactionalpublic void deleteAll(Iterable<? extends T> entities) {Assert.notNull(entities, "Entities must not be null");for (T entity : entities) {delete(entity);}}/*** 复合id删除时,拼接删除语句,并执行*/@Override@Transactionalpublic void deleteAllInBatch(Iterable<T> entities) {Assert.notNull(entities, "Entities must not be null");if (!entities.iterator().hasNext()) {return;}// 拼接删除语句,并执行applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities,entityManager).executeUpdate();}@Override@Transactionalpublic void deleteAll() {for (T element : findAll()) {delete(element);}}@Override@Transactionalpublic void deleteAllInBatch() {// 获取delete from %s x的查询对象Query query = entityManager.createQuery(getDeleteAllQueryString());// 设置hint信息applyQueryHints(query);// 执行queryquery.executeUpdate();}/*** 通过主键查询对象*/@Overridepublic Optional<T> findById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);// 获取Repository<T, ID>中定义的T的类型Class<T> domainType = getDomainClass();if (metadata == null) {return Optional.ofNullable(entityManager.find(domainType, id));}// 获取元数据中设置的锁类型。在Repository中通过@Lock(LockModeType.READ)添加的信息LockModeType type = metadata.getLockModeType();// 获取配置的hint信息Map<String, Object> hints = getHints();// 执行EntityManager.find()方法return Optional.ofNullable(type == null ? entityManager.find(domainType, id, hints) : entityManager.find(domainType, id, type, hints));}@Deprecated@Overridepublic T getOne(ID id) {return getReferenceById(id);}@Deprecated@Overridepublic T getById(ID id) {return getReferenceById(id);}/*** 通过entityManager.getReference()获取实体*/@Overridepublic T getReferenceById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);return entityManager.getReference(getDomainClass(), id);}@Overridepublic boolean existsById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);if (entityInformation.getIdAttribute() == null) {return findById(id).isPresent();}// 返回用于简单计数查询的占位符。默认实现返回x。子类PersistenceProvider返回*。select count(*) from tb_table tString placeholder = provider.getCountQueryPlaceholder();String entityName = entityInformation.getEntityName();// 获取主键属性名称Iterable<String> idAttributeNames = entityInformation.getIdAttributeNames();// 回用于执行给定id属性的查询字符串。返回select count(*) from %s x where x.id1Name = :id1Name and x.id2Name = :id2NameString existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames);TypedQuery<Long> query = entityManager.createQuery(existsQuery, Long.class);// 设置hintapplyQueryHints(query);// 如果不是复合idif (!entityInformation.hasCompositeId()) {// 设置id的值query.setParameter(idAttributeNames.iterator().next(), id);// 如果有一条记录,说明存在对应id的记录,返回truereturn query.getSingleResult() == 1L;}// 如果是复合idfor (String idAttributeName : idAttributeNames) {// 获取对应id的值Object idAttributeValue = entityInformation.getCompositeIdAttributeValue(id, idAttributeName);boolean complexIdParameterValueDiscovered = idAttributeValue != null&& !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass());// 发现复杂Id参数值if (complexIdParameterValueDiscovered) {// fall-back to findById(id) which does the proper mapping for the parameter.// 返回到findById(id),它为参数执行正确的映射// 执行findById获取记录,记录存在,返回truereturn findById(id).isPresent();}// 设置查询语句的参数query.setParameter(idAttributeName, idAttributeValue);}// 如果有一条记录,说明存在对应id的记录,返回true,否则返回falsereturn query.getSingleResult() == 1L;}/*** 获取所有数据,通过Criteria接口进行查询*/@Overridepublic List<T> findAll() {return getQuery(null, Sort.unsorted()).getResultList();}@Overridepublic List<T> findAllById(Iterable<ID> ids) {Assert.notNull(ids, "Ids must not be null");if (!ids.iterator().hasNext()) {return Collections.emptyList();}// 如果是复合id,轮询使用findById()逐条获取记录if (entityInformation.hasCompositeId()) {List<T> results = new ArrayList<>();// 遍历执行findById(),获取对象,加入到List集合中for (ID id : ids) {findById(id).ifPresent(results::add);}return results;}// 如果不是复合id,则使用in查询语句获取多条记录Collection<ID> idCollection = Streamable.of(ids).toList();// 通过Criteria接口中的Path.in(),一次批量查询多条语句ByIdsSpecification<T> specification = new ByIdsSpecification<>(entityInformation);// 通过给定的Specification和排序规则,创建一个TypedQuery对象,并添加hint、锁等信息TypedQuery<T> query = getQuery(specification, Sort.unsorted());// query参数赋值,并执行return query.setParameter(specification.parameter, idCollection).getResultList();}@Overridepublic List<T> findAll(Sort sort) {return getQuery(null, sort).getResultList();}@Overridepublic Page<T> findAll(Pageable pageable) {if (pageable.isUnpaged()) {return new PageImpl<>(findAll());}return findAll((Specification<T>) null, pageable);}@Overridepublic Optional<T> findOne(Specification<T> spec) {try {// 最多获取两条记录,只获取其中的一条return Optional.of(getQuery(spec, Sort.unsorted()).setMaxResults(2).getSingleResult());} catch (NoResultException e) {return Optional.empty();}}@Overridepublic List<T> findAll(Specification<T> spec) {return getQuery(spec, Sort.unsorted()).getResultList();}@Overridepublic Page<T> findAll(Specification<T> spec, Pageable pageable) {TypedQuery<T> query = getQuery(spec, pageable);return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()): readPage(query, getDomainClass(), pageable, spec);}@Overridepublic List<T> findAll(Specification<T> spec, Sort sort) {return getQuery(spec, sort).getResultList();}/*** 判断是否有符合Specification的记录* @param spec the {@link Specification} to use for the existence check, ust not be {@literal null}.* @return*/@Overridepublic boolean exists(Specification<T> spec) {// 创建返回Integer类型的queryCriteriaQuery<Integer> cq = this.entityManager.getCriteriaBuilder() //.createQuery(Integer.class) // 返回Integer类型.select(this.entityManager.getCriteriaBuilder().literal(1));// 从给定的Specification应用到给定的CriteriaQuery。如果有Specification,// 执行Specification.toPredicate(),并作为查询的whereapplySpecificationToCriteria(spec, getDomainClass(), cq);// 应用Repository中的方法元数据。将元数据中的@Lock、@QueryHint等信息添加到query中TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.entityManager.createQuery(cq));// 执行查询,查询结果记录数量为1,则返回true,否则返回falsereturn query.setMaxResults(1).getResultList().size() == 1;}/*** 通过Specification作为条件,执行删除操作*/@Overridepublic long delete(Specification<T> spec) {CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();CriteriaDelete<T> delete = builder.createCriteriaDelete(getDomainClass());if (spec != null) {Predicate predicate = spec.toPredicate(delete.from(getDomainClass()), null, builder);if (predicate != null) {delete.where(predicate);}}return this.entityManager.createQuery(delete).executeUpdate();}@Overridepublic <S extends T, R> R findBy(Specification<T> spec, Function<FetchableFluentQuery<S>, R> queryFunction) {Assert.notNull(spec, "Specification must not be null");Assert.notNull(queryFunction, "Query function must not be null");return doFindBy(spec, getDomainClass(), queryFunction);}private <S extends T, R> R doFindBy(Specification<T> spec, Class<T> domainClass,Function<FetchableFluentQuery<S>, R> queryFunction) {Assert.notNull(spec, "Specification must not be null");Assert.notNull(queryFunction, "Query function must not be null");ScrollQueryFactory scrollFunction = (sort, scrollPosition) -> {Specification<T> specToUse = spec;if (scrollPosition instanceof KeysetScrollPosition keyset) {KeysetScrollSpecification<T> keysetSpec = new KeysetScrollSpecification<>(keyset, sort, entityInformation);sort = keysetSpec.sort();specToUse = specToUse.and(keysetSpec);}TypedQuery<T> query = getQuery(specToUse, domainClass, sort);if (scrollPosition instanceof OffsetScrollPosition offset) {if(!offset.isInitial()) {query.setFirstResult(Math.toIntExact(offset.getOffset()) + 1);}}return query;};Function<Sort, TypedQuery<T>> finder = sort -> getQuery(spec, domainClass, sort);SpecificationScrollDelegate<T> scrollDelegate = new SpecificationScrollDelegate<>(scrollFunction,entityInformation);FetchableFluentQueryBySpecification<?, T> fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder,scrollDelegate, this::count, this::exists, this.entityManager, getProjectionFactory());return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);}@Overridepublic <S extends T> Optional<S> findOne(Example<S> example) {try {return Optional.of(getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()).setMaxResults(2).getSingleResult());} catch (NoResultException e) {return Optional.empty();}}@Overridepublic <S extends T> long count(Example<S> example) {return executeCountQuery(getCountQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType()));}@Overridepublic <S extends T> boolean exists(Example<S> example) {Specification<S> spec = new ExampleSpecification<>(example, this.escapeCharacter);CriteriaQuery<Integer> cq = this.entityManager.getCriteriaBuilder() //.createQuery(Integer.class) //.select(this.entityManager.getCriteriaBuilder().literal(1));applySpecificationToCriteria(spec, example.getProbeType(), cq);TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.entityManager.createQuery(cq));return query.setMaxResults(1).getResultList().size() == 1;}@Overridepublic <S extends T> List<S> findAll(Example<S> example) {return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), Sort.unsorted()).getResultList();}@Overridepublic <S extends T> List<S> findAll(Example<S> example, Sort sort) {return getQuery(new ExampleSpecification<>(example, escapeCharacter), example.getProbeType(), sort).getResultList();}@Overridepublic <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {ExampleSpecification<S> spec = new ExampleSpecification<>(example, escapeCharacter);Class<S> probeType = example.getProbeType();TypedQuery<S> query = getQuery(new ExampleSpecification<>(example, escapeCharacter), probeType, pageable);return pageable.isUnpaged() ? new PageImpl<>(query.getResultList()) : readPage(query, probeType, pageable, spec);}@Overridepublic <S extends T, R> R findBy(Example<S> example, Function<FetchableFluentQuery<S>, R> queryFunction) {Assert.notNull(example, "Sample must not be null");Assert.notNull(queryFunction, "Query function must not be null");ExampleSpecification<S> spec = new ExampleSpecification<>(example, escapeCharacter);Class<S> probeType = example.getProbeType();return doFindBy((Specification<T>) spec, (Class<T>) probeType, queryFunction);}@Overridepublic long count() {TypedQuery<Long> query = entityManager.createQuery(getCountQueryString(), Long.class);applyQueryHintsForCount(query);return query.getSingleResult();}@Overridepublic long count(@Nullable Specification<T> spec) {return executeCountQuery(getCountQuery(spec, getDomainClass()));}@Transactional@Overridepublic <S extends T> S save(S entity) {Assert.notNull(entity, "Entity must not be null");if (entityInformation.isNew(entity)) {entityManager.persist(entity);return entity;} else {return entityManager.merge(entity);}}@Transactional@Overridepublic <S extends T> S saveAndFlush(S entity) {S result = save(entity);flush();return result;}@Transactional@Overridepublic <S extends T> List<S> saveAll(Iterable<S> entities) {Assert.notNull(entities, "Entities must not be null");List<S> result = new ArrayList<>();for (S entity : entities) {result.add(save(entity));}return result;}@Transactional@Overridepublic <S extends T> List<S> saveAllAndFlush(Iterable<S> entities) {List<S> result = saveAll(entities);flush();return result;}@Transactional@Overridepublic void flush() {entityManager.flush();}@Deprecatedprotected Page<T> readPage(TypedQuery<T> query, Pageable pageable, @Nullable Specification<T> spec) {return readPage(query, getDomainClass(), pageable, spec);}protected <S extends T> Page<S> readPage(TypedQuery<S> query, final Class<S> domainClass, Pageable pageable,@Nullable Specification<S> spec) {if (pageable.isPaged()) {query.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));query.setMaxResults(pageable.getPageSize());}return PageableExecutionUtils.getPage(query.getResultList(), pageable,() -> executeCountQuery(getCountQuery(spec, domainClass)));}protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Pageable pageable) {Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();return getQuery(spec, getDomainClass(), sort);}protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass,Pageable pageable) {Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();return getQuery(spec, domainClass, sort);}/*** 通过给定的Specification和排序规则,创建一个TypedQuery对象*/protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Sort sort) {return getQuery(spec, getDomainClass(), sort);}/*** 通过给定的Specification和排序规则,创建一个TypedQuery对象*/protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort) {// 获取一个CriteriaBuilderCriteriaBuilder builder = entityManager.getCriteriaBuilder();// 创建CriteriaQueryCriteriaQuery<S> query = builder.createQuery(domainClass);// 获取一个Root,定义Criteria查询的根对象Root<S> root = applySpecificationToCriteria(spec, domainClass, query);// 查询rootquery.select(root);// 添加排序if (sort.isSorted()) {query.orderBy(toOrders(sort, root, builder));}// 创建Query,并添加hint、锁等信息return applyRepositoryMethodMetadata(entityManager.createQuery(query));}@Deprecatedprotected TypedQuery<Long> getCountQuery(@Nullable Specification<T> spec) {return getCountQuery(spec, getDomainClass());}protected <S extends T> TypedQuery<Long> getCountQuery(@Nullable Specification<S> spec, Class<S> domainClass) {CriteriaBuilder builder = entityManager.getCriteriaBuilder();CriteriaQuery<Long> query = builder.createQuery(Long.class);Root<S> root = applySpecificationToCriteria(spec, domainClass, query);if (query.isDistinct()) {query.select(builder.countDistinct(root));} else {query.select(builder.count(root));}// Remove all Orders the Specifications might have appliedquery.orderBy(Collections.emptyList());return applyRepositoryMethodMetadataForCount(entityManager.createQuery(query));}protected QueryHints getQueryHints() {return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata);}protected QueryHints getQueryHintsForCount() {return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata).forCounts();}/*** 从给定的Specification应用到给定的CriteriaQuery。如果有Specification,* 执行Specification.toPredicate(),并作为查询的where*/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。定义Criteria查询的根对象,Criteria查询的根定义为实体类型,它与SQL查询中的FROM子句类似Root<U> root = query.from(domainClass);// 如果没有Specification,直接返回rootif (spec == null) {return root;}// 创建CriteriaBuilder,用于构造条件查询、复合选择、表达式、谓词和排序CriteriaBuilder builder = entityManager.getCriteriaBuilder();// 执行Specification的toPredicate()方法,如果有返回值,则作为where语句,加入到query中Predicate predicate = spec.toPredicate(root, query, builder);if (predicate != null) {query.where(predicate);}// 返回rootreturn root;}/*** 应用Repository中的方法元数据。将元数据中的@Lock、@QueryHint等信息添加到query中*/private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {if (metadata == null) {return query;}// 获取元数据中的锁信息LockModeType type = metadata.getLockModeType();TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);// 设置hintapplyQueryHints(toReturn);// 返回queryreturn toReturn;}/*** 应用Query的hint。将添加的元数据中的hint信息添加到query中。* Spring Data JPA 中的 Query Hint 是一种强大的工具,可帮助优化数据库查询并提高应用性能。* 与直接控制执行不同,Query Hint 会影响优化器(Optimizer)的决策过程。* 详见:https://springdoc.cn/spring-data-jpa-query-hints/*/private void applyQueryHints(Query query) {if (metadata == null) {return;}// 获取一个DefaultQueryHints对象,执行query的setHintgetQueryHints().withFetchGraphs(entityManager).forEach(query::setHint);// 从metadata元数据中获取设置的hint,并调用query::setHint,设置对应的hint名称及hint值applyComment(metadata, query::setHint);}private <S> TypedQuery<S> applyRepositoryMethodMetadataForCount(TypedQuery<S> query) {if (metadata == null) {return query;}applyQueryHintsForCount(query);return query;}private void applyQueryHintsForCount(Query query) {if (metadata == null) {return;}getQueryHintsForCount().forEach(query::setHint);applyComment(metadata, query::setHint);}/*** 获取hint信息*/private Map<String, Object> getHints() {Map<String, Object> hints = new HashMap<>();// 获取系统设置的hintgetQueryHints().withFetchGraphs(entityManager).forEach(hints::put);// 获取类元数据中定义的hintif (metadata != null) {applyComment(metadata, hints::put);}return hints;}/*** 从metadata元数据中获取设置的hint,并调用BiConsumer,设置对应的hint名称及hint值*/private void applyComment(CrudMethodMetadata metadata, BiConsumer<String, Object> consumer) {if (metadata.getComment() != null && provider.getCommentHintKey() != null) {consumer.accept(provider.getCommentHintKey(), provider.getCommentHintValue(this.metadata.getComment()));}}private ProjectionFactory getProjectionFactory() {if (projectionFactory == null) {projectionFactory = new SpelAwareProxyProjectionFactory();}return projectionFactory;}private static long executeCountQuery(TypedQuery<Long> query) {Assert.notNull(query, "TypedQuery must not be null");List<Long> totals = query.getResultList();long total = 0L;for (Long element : totals) {total += element == null ? 0 : element;}return total;}@SuppressWarnings("rawtypes")/*** 通过Criteria接口中的Path.in(),一次批量查询多条语句*/private static final class ByIdsSpecification<T> implements Specification<T> {private static final long serialVersionUID = 1L;private final JpaEntityInformation<T, ?> entityInformation;@Nullable ParameterExpression<Collection<?>> parameter;ByIdsSpecification(JpaEntityInformation<T, ?> entityInformation) {this.entityInformation = entityInformation;}@Overridepublic Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {// 获取主键的PathPath<?> path = root.get(entityInformation.getIdAttribute());parameter = (ParameterExpression<Collection<?>>) (ParameterExpression) cb.parameter(Collection.class);// 使用in语句批量查询return path.in(parameter);}}private static class ExampleSpecification<T> implements Specification<T> {private static final long serialVersionUID = 1L;private final Example<T> example;private final EscapeCharacter 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;}@Overridepublic Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {return QueryByExamplePredicateBuilder.getPredicate(root, cb, example, escapeCharacter);}}
}

SimpleJpaRepository实现了JpaRepositoryImplementation接口,它是CrudRepository的默认实现。

在自定义的Repository中,如果不实现JpaRepositoryImplementation接口,只是不能直接访问JpaRepositoryImplementation中提供的方法,而实际上是可以使用JpaRepositoryImplementation中的方法。如通过反射机制调用等。

SimpleJpaRepository中提供的CRUD的方法,要么是通过拼接SQL语句,要么是通过Criteria的查询,执行EntityManager对应方法实现。大部分核心代码都做了注释,对Criteria查询不清楚的,可以看

Spring Data JPA Criteria查询、部分字段查询_jpa查询某个字段-CSDN博客

小结

限于篇幅,本篇就分享到这里,这里做一个小结:

1)通过上一篇源码,确认了Repository是代理对象,其中的target为SimpleJpaRepository对象;

2)介绍了访问JpaRepositoryImplementation中的方法时,如何通过动态代理执行到SimpleJpaRepository;

3)分析SimpleJpaRepository源码,核心是通过拼接SQL语句、Criteria的查询,执行EntityManager对应方法实现对数据库表的操作;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/17580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器数据恢复—EVA存储异常断电重启后虚拟机无法启动如何恢复数据?

服务器存储数据恢复环境&#xff1a; 某品牌EVA8400&#xff0c;服务器上安装VMware ESXi虚拟化平台&#xff0c;虚拟机的虚拟磁盘包括数据盘&#xff08;精简模式&#xff09;快照数据盘&#xff0c;部分虚拟机中运行oracle数据库和mysql数据库。 服务器存储故障&检测&…

c/c++内存操作函数

函数原型 void* memcpy( void* _Dst, void const* _Src, size_t _Size); // 内存拷贝 void* memmove( void* _Dst, void const* _Src, size_t _Size); // 内存移动 void* memset(void* _Dst, int _Val, size_t _Size); …

iOS内购欺诈漏洞

iOS内购欺诈漏洞 1.iOS内购欺诈漏洞概述2.伪造的凭证3.漏洞修复方案 1.iOS内购欺诈漏洞概述 黑产别的App上低价充值&#xff08;比如1元&#xff09;换取苹果真实凭证&#xff0c;再在目标App上下单高价&#xff08;648元&#xff09;商品&#xff0c;传入该凭证&#xff0c;如…

操作系统 - 输入/输出(I/O)管理

输入/输出(I/O)管理 考纲内容 I/O管理基础 设备&#xff1a;设备的基本概念&#xff0c;设备的分类&#xff0c;I/O接口 I/O控制方式&#xff1a;轮询方式&#xff0c;中断方式&#xff0c;DMA方式 I/O软件层次结构&#xff1a;中断处理程序&#xff0c;驱动程序&#xff0c;…

代码随想录算法训练营第四天| 24.两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

24.两两交换链表中的节点 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 解题思路 很麻烦的一道题目&#xff0c;不是很理解。还是看视频文章才AC的。 解法1 …

一些硬件知识【九】

STM32CUBEMX配置教程&#xff08;六&#xff09;高级定时器单通道输出互补PWM波&#xff08;带死区和刹车&#xff09;_高级定时器死区刹车怎么与电机模块连接?-CSDN博客 MOS选型关键参数&#xff1a; 额定电压&#xff0c;额定电流&#xff0c;导通电阻&#xff0c;阀值电压…

新型高性能数据记录仪ETHOS 2

| 具有强大CPU性能的数据记录仪 IPETRONIK推出了一款新型高性能数据记录仪——ETHOS 2&#xff0c;作为ETHOS的第二代&#xff0c;它借助新型英特尔i7-9850HE处理器&#xff0c;实现了11,572的性能指数&#xff0c;从而能够快速有效应对CAN FD、LIN和以太网总线测量方面的日益…

Double数据精度导致的问题

早知道double的数据精度有限&#xff0c;所以在计算金钱等精细数据的时候&#xff0c;要用bigdecimal&#xff1b; 但是最近在计算一些客流量数据数据的时候&#xff0c;也发现&#xff0c;这种数据精度不足的情况&#xff0c;会不仅仅导致小数点级的计算结果出错 实在是&#…

假暴力,cf1168B. Good Triple

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1168B - Codeforces 二、解题报告 1、思路分析 一眼没思路&#xff0c;打个暴力试试 因为如果 s[l, r] 是一个好字符串&#xff0c;那么s[i, r]一定也是好字符串&#xff0c;其中i < l 那么…

【生产管理】生产过程中的常见问题及解决方案

在生产管理中&#xff0c;会遇到多种问题&#xff0c;特别是与生产计划、生产进度控制以及品质控制方面&#xff0c;以下是生产过程中常见的问题及解决方案&#xff1a; 问题一&#xff1a;计划不清或无计划导致的物料进度无法保障 解决方案&#xff1a; 建立详细的生产计划&…

webserver服务器从零搭建到上线(八)|EpollPoller事件分发器类

文章目录 EpollPoller事件分发器类成员变量和成员函数解释私有的成员函数和成员变量成员函数 具体实现常量的作用构造函数和析构函数⭐️poll函数updateChannel函数removeChannel 函数removeChannel 和updateChannel⭐️fillActiveChannels 函数⭐️update 函数 总结 终于要开始…

[数据集][目标检测]喝水检测数据集VOC+YOLO格式995张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;995 标注数量(xml文件个数)&#xff1a;995 标注数量(txt文件个数)&#xff1a;995 标注类别…

jeecgboot 同一账号只允许一个人登录

1.需求分析 jeecgboot 框架要实现同一个账号只允许一个人登录&#xff0c;就跟游戏账号类似&#xff0c;“我登录了就把你踢下去&#xff0c;你登录了就把我踢下去”&#xff1b;jwt 原理是生成 token 后一段时间内登录都有效&#xff0c;jeecgboot 中 jwt 和 redis 联合使用后…

OpenCV学习(2.1) 初识图像

1.图像对象 图像是由一个个像素组成的&#xff0c;像素越多&#xff0c;体现到图像就是更加清晰&#xff0c;有更多的细节。举个例子&#xff0c;通常来说的分辨率&#xff0c;1080P&#xff0c;720P&#xff0c;480P就是指像素的数量&#xff0c;数量越多就越清晰。 2.打印图…

第四周:心态和角色

1. 关注自己到关注他人 关注自己到关注他人&#xff0c;就是利己到利他&#xff0c;基本上就是从全局的角度去看待事情&#xff0c;而不单单是自己一亩三分地里耕耘&#xff0c;团队出的任何事情&#xff0c;首要责任就在管理者身上&#xff0c;不再是单打独斗了&#xff0c;你…

在LINQ中,如何使用Include方法加载关联的实体或集合?

Include 方法允许你在查询数据时一并加载关联的实体或集合。这有助于减少数据库访问次数&#xff0c;因为你可以一次性获取所有需要的数据&#xff0c;而不是分别查询每个关联的实体。 一、以下是如何在 Entity Framework 中使用 Include 方法来加载关联实体或集合的步骤&…

NV-LIO:一种基于法向量的激光雷达-惯性系统(LIO)

论文&#xff1a;NV-LIO: LiDAR-Inertial Odometry using Normal Vectors Towards Robust SLAM in Multifloor Environments 作者&#xff1a;Dongha Chung, Jinwhan Kim NV-LIO&#xff1a;一种基于法向量的激光雷达-惯性系统&#xff08;LIO&#xff09;NV-LIO利用从激光雷…

vue3主题切换按钮与功能实现

代码: <template><div class"slideThree"><label class"theme-switch"><inputtype"checkbox"class"checkbox"v-model"isChecked"change"setTheme"id"slideThree"name"check…

Day08:CSS 高级

目标&#xff1a;掌握定位的作用及特点&#xff1b;掌握 CSS 高级技巧 一、定位 作用&#xff1a;灵活的改变盒子在网页中的位置 实现&#xff1a; 1.定位模式&#xff1a;position 2.边偏移&#xff1a;设置盒子的位置 leftrighttopbottom 水平方向偏移&#xff1a;left、…