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

 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自定义方法命名规则执行原理(一)

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

11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理

12、【源码】SpringBoot事务注册原理

13、【源码】Spring Data JPA原理解析之事务注册原理

前言

上一篇限于篇幅,只分享了Repository自定义方法命名规则的方法在QueryExecutorMethodInterceptor的构造方法中,通过查询查找策略CreateIfNotFoundQueryLookupStrategy创建一个PartTreeJpaQuery对象。该对象解析方法名称的关键字、查询属性、查询关键字,封装成PartTree。而后将Method和PartTreeJpaQuery组合存放在QueryExecutorMethodInterceptor的Map<Method, RepositoryQuery> queries中。

本篇继续往下分享Repository自定义方法命名规则的方法是如何调用执行的。

方法调用拦截

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

上面博文分享了Repository bean的创建。Respository的bean是一个通过ProxyFactory创建的动态代理对象,该代理对象添加了QueryExecutorMethodInterceptor拦截器。

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

博客中介绍了动态代理拦截,当Repository中的接口被调用的时候,会执行ReflectiveMethodInvocation.proceed()的方法,在该方法中,循环遍历所有的拦截器,执行拦截器的invoke(MethodInvocation invocation)方法。

所以会执行QueryExecutorMethodInterceptor.invoke()方法。QueryExecutorMethodInterceptor的相关代码如下:

package org.springframework.data.repository.core.support;/*** 此MethodInterceptor拦截对自定义实现的方法的调用,当自定义的方法被调用时,会被该类拦截。* 此外,它还解析对finder的方法调用,并触发它们的执行。如果返回true,则可以依赖于设置自定义存储库实现实例。*/
class QueryExecutorMethodInterceptor implements MethodInterceptor {// Repository信息,为DefaultRepositoryInformation对象。获取Repository信息,getRepositoryInformation()返回一个RepositoryInformation对象。// 如子类JpaRepositoryFactory,指定baseClass为SimpleJpaRepository.classprivate final RepositoryInformation repositoryInformation;// 方法缓存,key为方法,value为对应方法的查询解析信息private final Map<Method, RepositoryQuery> queries;// 方法调用缓存,key为方法,value为对应方法调用时要执行的执行器private final Map<Method, RepositoryMethodInvoker> invocationMetadataCache = new ConcurrentReferenceHashMap<>();// 查询执行结果处理器private final QueryExecutionResultHandler resultHandler;// 在实体类中添加@NamedQueries注解,配置相关查询信息,默认为空private final NamedQueries namedQueries;private final List<QueryCreationListener<?>> queryPostProcessors;private final RepositoryInvocationMulticaster invocationMulticaster;// 省略其他@Override@Nullablepublic Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();// 通过返回的返回值,获取执行适配器,默认都为nullQueryExecutionConverters.ExecutionAdapter executionAdapter = QueryExecutionConverters //.getExecutionAdapter(method.getReturnType());if (executionAdapter == null) {return resultHandler.postProcessInvocationResult(doInvoke(invocation), method);}return executionAdapter //.apply(() -> resultHandler.postProcessInvocationResult(doInvoke(invocation), method));}@Nullableprivate Object doInvoke(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();// 判断方法是否存在RepositoryQuery。在构造函数中,会解析Repository中的查询方法,并缓存到Mapif (hasQueryFor(method)) {RepositoryMethodInvoker invocationMetadata = invocationMetadataCache.get(method);if (invocationMetadata == null) {// 首次执行对应方法,先创建一个RepositoryQueryMethodInvoker对象,保存方法即方法对应的RepositoryQueryinvocationMetadata = RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method));// 加入缓存invocationMetadataCache.put(method, invocationMetadata);}// 获取方法所在的Repository类名、方法的参数值【invocation.getArguments()】,执行RepositoryQueryMethodInvoker.invoke()方法return invocationMetadata.invoke(repositoryInformation.getRepositoryInterface(), invocationMulticaster,invocation.getArguments());}// 如果能够处理该查询方法,则不执行invocation.proceed(),即结束拦截器链return invocation.proceed();}/*** 判断是否为给定方法执行查询*/private boolean hasQueryFor(Method method) {return queries.containsKey(method);}}

1.1 在QueryExecutorMethodInterceptor.invoke()中,核心功能如下:

1)执行doInvoke()方法,执行数据库相关操作,获取返回信息;

2)执行resultHandler.postProcessInvocationResult(),进行返回值类型转换;

1.2 在doInvoke()方法中,执行如下:

1)调用hasQueryFor()方法,判断当前方法是否有对应的RepositoryQuery对象。在上一篇博文中以及做了详细介绍,该对象是在QueryExecutorMethodInterceptor的构造方法中解析方法信息后封装的和查询相关的信息对象;

2)如果存在RepositoryQuery对象,则执行RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method)),创建一个RepositoryQueryMethodInvoker对象,然后执行RepositoryQueryMethodInvoker.invoke()方法;

3)如果不存在RepositoryQuery对象,则执行invocation.proceed(),执行ReflectiveMethodInvocation.proceed()方法,继续执行下一个拦截器或执行target的对应方法;

RepositoryQueryMethodInvoker

RepositoryQueryMethodInvoker查询方法回调类的核心代码如下:

abstract class RepositoryMethodInvoker {private final Method method;private final Class<?> returnedType;private final Invokable invokable;private final boolean suspendedDeclaredMethod;@SuppressWarnings("ReactiveStreamsUnusedPublisher")protected RepositoryMethodInvoker(Method method, Invokable invokable) {this.method = method;if (KotlinDetector.isKotlinReflectPresent()) {// 省略其他} else {this.suspendedDeclaredMethod = false;this.returnedType = method.getReturnType();this.invokable = invokable;}}static RepositoryQueryMethodInvoker forRepositoryQuery(Method declaredMethod, RepositoryQuery query) {return new RepositoryQueryMethodInvoker(declaredMethod, query);}@Nullablepublic Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)throws Exception {return doInvoke(repositoryInterface, multicaster, args);}@Nullableprivate Object doInvoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)throws Exception {// 创建一个RepositoryMethodInvocationCaptor对象RepositoryMethodInvocationCaptor invocationResultCaptor = RepositoryMethodInvocationCaptor.captureInvocationOn(repositoryInterface);try {// 执行对应方法的RepositoryQuery的execute方法Object result = invokable.invoke(args);if (result != null && ReactiveWrappers.supports(result.getClass())) {return new ReactiveInvocationListenerDecorator().decorate(repositoryInterface, multicaster, args, result);}if (result instanceof Stream) {return ((Stream<?>) result).onClose(() -> multicaster.notifyListeners(method, args, computeInvocationResult(invocationResultCaptor.success())));}// 执行结果通知。回调RepositoryMethodInvocationListener.afterInvocation()multicaster.notifyListeners(method, args, computeInvocationResult(invocationResultCaptor.success()));return result;} catch (Exception e) {multicaster.notifyListeners(method, args, computeInvocationResult(invocationResultCaptor.error(e)));throw e;}}private RepositoryMethodInvocation computeInvocationResult(RepositoryMethodInvocationCaptor captured) {return new RepositoryMethodInvocation(captured.getRepositoryInterface(), method, captured.getCapturedResult(),captured.getDuration());}interface Invokable {@NullableObject invoke(Object[] args) throws Exception;}private static class RepositoryQueryMethodInvoker extends RepositoryMethodInvoker {public RepositoryQueryMethodInvoker(Method method, RepositoryQuery repositoryQuery) {// repositoryQuery::execute方法回调声明作为参数赋值给invokable,当执行invokable.invoke()时,// 执行repositoryQuery.execute()方法super(method, repositoryQuery::execute);}}// 省略其他
}

RepositoryQueryMethodInvoker是私有静态内部类,父类为RepositoryMethodInvoker。

在上面讲解的1.2的2)中,通过RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method)),创建一个RepositoryQueryMethodInvoker对象,将repositoryQuery::execute方法回调声明作为参数赋值给invokable。

当执行RepositoryQueryMethodInvoker.invoke()时,执行doInvoke()方法,该方法执行如下:

1)创建一个RepositoryMethodInvocationCaptor对象;

2)执行invokable.invoke(),即执行对应方法的RepositoryQuery的execute方法,执行数据库操作;

RepositoryQuery.execute() -> AbstractJpaQuery.execute() -> AbstractJpaQuery.doExecute() -> JpaQueryExecution.execute() -> JpaQueryExecution.doExecute()。

3)执行结果通知。回调RepositoryMethodInvocationListener.afterInvocation();

4)返回2)中的返回值;

第2)中调用的JpaQueryExecution.doExecute()是一个抽象方法,实现类如下:

针对数据库表操作后不同的返回值信息,使用不同的实现类,且实现类都是JpaQueryExecution的内部类。以下以SingleEntityExecution为例。

public abstract class JpaQueryExecution {static class SingleEntityExecution extends JpaQueryExecution {@Overrideprotected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) {return query.createQuery(accessor).getSingleResult();}}
}

执行AbstractJpaQuery.createQuery(),获取一个Query对象,最后调用Query.getSingleResult(),返回一个查询执行结果。其他的实现类处理类似,只是最后调用Query的不同方法,从数据库中查询不同的结果值。

AbstractJpaQuery

AbstractJpaQuery的相关代码如下:

/*** 记录Repository中每个方法解析后的信息*/
public abstract class AbstractJpaQuery implements RepositoryQuery {private final JpaQueryMethod method;private final EntityManager em;private final JpaMetamodel metamodel;// 根据EntityManager,返回PersistenceProvider。PersistenceProvider是枚举类型,有HIBERNATE、ECLIPSELINK、GENERIC_JPA。// 不同的PersistenceProvider,extractQueryString、getIdentifierFrom等方式不一样private final PersistenceProvider provider;// 根据查询方法的返回值,使用不同的执行器private final Lazy<JpaQueryExecution> execution;// 参数绑定器final Lazy<ParameterBinder> parameterBinder = Lazy.of(this::createBinder);@Nullable@Overridepublic Object execute(Object[] parameters) {return doExecute(getExecution(), parameters);}/*** 执行查询* @param execution 执行器。主要根据方法的返回值确定的执行器* @param values 方法执行时的参数值* @return*/@Nullableprivate Object doExecute(JpaQueryExecution execution, Object[] values) {// 创建一个JpaParametersParameterAccessor对象,保存方法的参数信息及本次查询的参数值JpaParametersParameterAccessor accessor = obtainParameterAccessor(values);// 执行数据库查询,获取返回值Object result = execution.execute(this, accessor);ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor);return withDynamicProjection.processResult(result, new TupleConverter(withDynamicProjection.getReturnedType()));}private JpaParametersParameterAccessor obtainParameterAccessor(Object[] values) {if (method.isNativeQuery() && PersistenceProvider.HIBERNATE.equals(provider)) {return new HibernateJpaParametersParameterAccessor(method.getParameters(), values, em);}return new JpaParametersParameterAccessor(method.getParameters(), values);}/*** 获取方法对应的查询执行器*/protected JpaQueryExecution getExecution() {// 获取根据返回值确定的查询的执行器JpaQueryExecution execution = this.execution.getNullable();if (execution != null) {return execution;}// 如果添加了@Modify注解,则返回if (method.isModifyingQuery()) {return new ModifyingExecution(method, em);}// 否则返回单个实体类的执行器return new SingleEntityExecution();}/*** 为query添加定义的查询hint信息。方法中添加@QueryHints注解*/protected <T extends Query> T applyHints(T query, JpaQueryMethod method) {List<QueryHint> hints = method.getHints();if (!hints.isEmpty()) {for (QueryHint hint : hints) {applyQueryHint(query, hint);}}// Apply any meta-attributes that existif (method.hasQueryMetaAttributes()) {if (provider.getCommentHintKey() != null) {query.setHint( //provider.getCommentHintKey(), provider.getCommentHintValue(method.getQueryMetaAttributes().getComment()));}}return query;}protected <T extends Query> void applyQueryHint(T query, QueryHint hint) {Assert.notNull(query, "Query must not be null");Assert.notNull(hint, "QueryHint must not be null");query.setHint(hint.name(), hint.value());}/*** 为query应用锁模式*/private Query applyLockMode(Query query, JpaQueryMethod method) {LockModeType lockModeType = method.getLockModeType();return lockModeType == null ? query : query.setLockMode(lockModeType);}protected Query createQuery(JpaParametersParameterAccessor parameters) {return applyLockMode(applyEntityGraphConfiguration(applyHints(doCreateQuery(parameters), method), method), method);}/*** 如果方法添加@EntityGraph注解,在query中添加对应的Hint* @param query* @param method* @return*/private Query applyEntityGraphConfiguration(Query query, JpaQueryMethod method) {JpaEntityGraph entityGraph = method.getEntityGraph();if (entityGraph != null) {QueryHints hints = Jpa21Utils.getFetchGraphHint(em, method.getEntityGraph(),getQueryMethod().getEntityInformation().getJavaType());hints.forEach(query::setHint);}return query;}/*** 为查询创建一个Query,并调用query.setParameter()设置参数值及分页信息*/protected abstract Query doCreateQuery(JpaParametersParameterAccessor accessor);// 省略其他
}

在createQuery()方法中,执行如下:

1)调用抽象方法doCreateQuery(),获取一个Query;

对于自定义方法命名规则的方法,实现在PartTreeJpaQuery类。

2)执行applyHints(),在query中添加对应的Hint;

3)执行applyEntityGraphConfiguration(),如果方法添加@EntityGraph注解,在query中添加对应的Hint;

4)执行applyLockMode(),为query应用锁模式;

PartTreeJpaQuery

PartTreeJpaQuery的相关代码如下:

package org.springframework.data.jpa.repository.query;/*** 保存了方法信息,包括方法、方法参数、方法名称解析后的Part树、对应的查询query、查询计数countQuery等信息*/
public class PartTreeJpaQuery extends AbstractJpaQuery {private final PartTree tree;private final JpaParameters parameters;private final QueryPreparer query;private final QueryPreparer countQuery;private final EntityManager em;private final EscapeCharacter escape;private final JpaMetamodelEntityInformation<?, Object> entityInformation;/*** 为查询创建一个Query,并调用query.setParameter()设置参数值及分页信息*/@Overridepublic Query doCreateQuery(JpaParametersParameterAccessor accessor) {return query.createQuery(accessor);}@Override@SuppressWarnings("unchecked")public TypedQuery<Long> doCreateCountQuery(JpaParametersParameterAccessor accessor) {return (TypedQuery<Long>) countQuery.createQuery(accessor);}private class QueryPreparer {// 缓存创建的对象private final @Nullable CriteriaQuery<?> cachedCriteriaQuery;private final @Nullable ParameterBinder cachedParameterBinder;private final QueryParameterSetter.QueryMetadataCache metadataCache = new QueryParameterSetter.QueryMetadataCache();QueryPreparer(boolean recreateQueries) {JpaQueryCreator creator = createCreator(null);if (recreateQueries) {this.cachedCriteriaQuery = null;this.cachedParameterBinder = null;} else {// 子类CountQueryPreparer的createQuery(),执行JpaCountQueryCreator重写的complete()方法,// 执行query.select(),select为builder.count(),并加上predicate条件信息this.cachedCriteriaQuery = creator.createQuery();this.cachedParameterBinder = getBinder(creator.getParameterExpressions());}}/*** 为查询创建一个Query,并调用query.setParameter()设置参数值及分页信息*/public Query createQuery(JpaParametersParameterAccessor accessor) {CriteriaQuery<?> criteriaQuery = cachedCriteriaQuery;ParameterBinder parameterBinder = cachedParameterBinder;if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) {JpaQueryCreator creator = createCreator(accessor);criteriaQuery = creator.createQuery(getDynamicSort(accessor));List<ParameterMetadata<?>> expressions = creator.getParameterExpressions();parameterBinder = getBinder(expressions);}if (parameterBinder == null) {throw new IllegalStateException("ParameterBinder is null");}// 通过EntityManager.createQuery(criteriaQuery),返回TypedQueryTypedQuery<?> query = createQuery(criteriaQuery);ScrollPosition scrollPosition = accessor.getParameters().hasScrollPositionParameter()? accessor.getScrollPosition(): null;// 调用invokeBinding()执行query.setParameter()方法,设置查询的条件参数值,如果有分页,设置分页信息// 如果有需要,设置返回最大值信息return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, query, accessor, this.metadataCache),scrollPosition);}@SuppressWarnings("ConstantConditions")private Query restrictMaxResultsIfNecessary(Query query, @Nullable ScrollPosition scrollPosition) {if (scrollPosition instanceof OffsetScrollPosition offset && !offset.isInitial()) {query.setFirstResult(Math.toIntExact(offset.getOffset()) + 1);}if (tree.isLimiting()) {if (query.getMaxResults() != Integer.MAX_VALUE) {if (query.getMaxResults() > tree.getMaxResults() && query.getFirstResult() > 0) {query.setFirstResult(query.getFirstResult() - (query.getMaxResults() - tree.getMaxResults()));}}query.setMaxResults(tree.getMaxResults());}if (tree.isExistsProjection()) {query.setMaxResults(1);}return query;}/*** 通过EntityManager.createQuery(criteriaQuery),返回TypedQuery*/private TypedQuery<?> createQuery(CriteriaQuery<?> criteriaQuery) {if (this.cachedCriteriaQuery != null) {synchronized (this.cachedCriteriaQuery) {return getEntityManager().createQuery(criteriaQuery);}}return getEntityManager().createQuery(criteriaQuery);}protected JpaQueryCreator createCreator(@Nullable JpaParametersParameterAccessor accessor) {EntityManager entityManager = getEntityManager();CriteriaBuilder builder = entityManager.getCriteriaBuilder();ResultProcessor processor = getQueryMethod().getResultProcessor();ParameterMetadataProvider provider;ReturnedType returnedType;if (accessor != null) {provider = new ParameterMetadataProvider(builder, accessor, escape);returnedType = processor.withDynamicProjection(accessor).getReturnedType();} else {provider = new ParameterMetadataProvider(builder, parameters, escape);returnedType = processor.getReturnedType();}if (accessor != null && accessor.getScrollPosition() instanceof KeysetScrollPosition keyset) {return new JpaKeysetScrollQueryCreator(tree, returnedType, builder, provider, entityInformation, keyset);}return new JpaQueryCreator(tree, returnedType, builder, provider);}/*** 调用query.setParameter()方法,设置查询的条件参数值,如果有分页,设置分页信息*/protected Query invokeBinding(ParameterBinder binder, TypedQuery<?> query, JpaParametersParameterAccessor accessor,QueryParameterSetter.QueryMetadataCache metadataCache) {// 将query查询添加到缓存QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata("query", query);return binder.bindAndPrepare(query, metadata, accessor);}private ParameterBinder getBinder(List<ParameterMetadata<?>> expressions) {return ParameterBinderFactory.createCriteriaBinder(parameters, expressions);}private Sort getDynamicSort(JpaParametersParameterAccessor accessor) {return parameters.potentiallySortsDynamically() //? accessor.getSort() //: Sort.unsorted();}}private class CountQueryPreparer extends QueryPreparer {CountQueryPreparer(boolean recreateQueries) {super(recreateQueries);}/*** 创建一个JpaCountQueryCreator*/@Overrideprotected JpaQueryCreator createCreator(@Nullable JpaParametersParameterAccessor accessor) {EntityManager entityManager = getEntityManager();CriteriaBuilder builder = entityManager.getCriteriaBuilder();ParameterMetadataProvider provider;if (accessor != null) {provider = new ParameterMetadataProvider(builder, accessor, escape);} else {provider = new ParameterMetadataProvider(builder, parameters, escape);}return new JpaCountQueryCreator(tree, getQueryMethod().getResultProcessor().getReturnedType(), builder, provider);}@Overrideprotected Query invokeBinding(ParameterBinder binder, TypedQuery<?> query, JpaParametersParameterAccessor accessor,QueryParameterSetter.QueryMetadataCache metadataCache) {QueryParameterSetter.QueryMetadata metadata = metadataCache.getMetadata("countquery", query);return binder.bind(query, metadata, accessor);}}
}

1)在PartTreeJpaQuery.doCreateQuery()方法,执行QueryPreparer.createQuery()方法。

2)QueryPreparer.createQuery()方法先调用createQuery(),执行如下:

2.1)通过EntityManager.createQuery(criteriaQuery),返回TypedQuery;

2.2)执行invokeBinding(),在TypedQuery对象中,调用query.setParameter()绑定查询条件的参数值,如果有分页,设置分页信息;

通过执行ParameterBinder.bindAndPrepare()方法,调用query.setParameter()绑定查询条件的参数值,如果有分页,设置分页信息。

2.3)执行restrictMaxResultsIfNecessary(),如果有需要,设置返回最大值信息;

ParameterBinder

ParameterBinder的代码如下:

package org.springframework.data.jpa.repository.query;/*** ParameterBinder用于将方法参数绑定到Query。通常在执行AbstractJpaQuery时执行。*/
public class ParameterBinder {static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters";private final JpaParameters parameters;// 查询方法对应的参数设置器private final Iterable<QueryParameterSetter> parameterSetters;private final boolean useJpaForPaging;ParameterBinder(JpaParameters parameters, Iterable<QueryParameterSetter> parameterSetters) {this(parameters, parameterSetters, true);}public ParameterBinder(JpaParameters parameters, Iterable<QueryParameterSetter> parameterSetters,boolean useJpaForPaging) {Assert.notNull(parameters, "JpaParameters must not be null");Assert.notNull(parameterSetters, "Parameter setters must not be null");this.parameters = parameters;this.parameterSetters = parameterSetters;this.useJpaForPaging = useJpaForPaging;}public <T extends Query> T bind(T jpaQuery, QueryParameterSetter.QueryMetadata metadata,JpaParametersParameterAccessor accessor) {// 绑定参数值bind(metadata.withQuery(jpaQuery), accessor, ErrorHandling.STRICT);return jpaQuery;}public void bind(QueryParameterSetter.BindableQuery query, JpaParametersParameterAccessor accessor,ErrorHandling errorHandling) {// 遍历方法的参数设置器,调用QueryParameterSetter.setParameter() -> query.setParameter()为查询语句赋值for (QueryParameterSetter setter : parameterSetters) {setter.setParameter(query, accessor, errorHandling);}}Query bindAndPrepare(Query query, QueryParameterSetter.QueryMetadata metadata,JpaParametersParameterAccessor accessor) {// 绑定参数。调用query.setParameter(),为查询赋值bind(query, metadata, accessor);// 如果没有分页,直接返回if (!useJpaForPaging || !parameters.hasLimitingParameters() || accessor.getPageable().isUnpaged()) {return query;}// 设置分页信息query.setFirstResult(PageableUtils.getOffsetAsInteger(accessor.getPageable()));query.setMaxResults(accessor.getPageable().getPageSize());return query;}
}

小结

限于篇幅,本篇先分享到这里。结合上一篇【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)一起做一个小结:

1)Repository的代理类中,会添加QueryExecutorMethodInterceptor方法拦截器;

2)QueryExecutorMethodInterceptor方法拦截器的构造方法中,会根据查询查找策略CreateIfNotFoundQueryLookupStrategy,获得RepositoryQuery对象,解析方法。对于按方法命名规则实现的方法,使用的RepositoryQuery对象为PartTreeJpaQuery;

3)在PartTreeJpaQuery构造方法中,创建一个PartTree对象,解析方法名称中的起始关键字【如:findBy、readBy、deleteBy等】、条件属性【实体类中的属性】、查询关键字【Between、In、Equals等】;

4)创建对应方法的countQuery和query,将解析出的查询的基础信息封装在QueryPreparer对象中,根据解析出的查询信息,创建CriteriaQuery对象;

5)解析完方法信息,保存在PartTreeJpaQuery后,保存到QueryExecutorMethodInterceptor的Map<Method, RepositoryQuery> queries中;

6)当Repository的接口被调用的时候,在ReflectiveMethodInvocation.proceed()中,先执行QueryExecutorMethodInterceptor.invoke()方法;

6.1)调用doInvoke()方法,获取数据库执行后的数据;

6.1.1)调用RepositoryQueryMethodInvoker.invoke() -> RepositoryQuery.execute() -> AbstractJpaQuery.execute() -> AbstractJpaQuery.doExecute() -> JpaQueryExecution.execute() -> JpaQueryExecution.doExecute();

6.1.2)doExecute()是一个抽象方法,针对不同的数据库查询返回值信息,使用不同的实现类。所有的实现类都会先调用AbstractJpaQuery.createQuery(),获取一个Query对象;

6.1.3)在AbstractJpaQuery.createQuery()中,调用抽象方法doCreateQuery()。对于按方法命名规则的Repository接口,实现类为PartTreeJpaQuery;

6.1.4)在PartTreeJpaQuery.coCreateQuery()方法中,通过EntityManager.createQuery(criteriaQuery)返回TypedQuery,然后执行invokeBinding(),在TypedQuery对象中,调用query.setParameter()绑定查询条件的参数值,如果有分页,设置分页信息;

6.1.5)参数完参数,在6.1.3中设置hint等。然后执行6.1.2中的具体实现类,执行数据库查询。如SingleEntityExecution实现类,执行TypeQuery.getSingleResult(),然后单个结果;

6.2)调用resultHandler.postProcessInvocationResult(),对数据库查询后的值进行返回值类型转换;

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

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

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

相关文章

位运算算法

位运算是计算机中常用的一种运算方法&#xff0c;它直接对二进制数的位进行操作。位运算主要包括按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09;、按位取反&#xff08;~&#xff09;、左移&#xff08;<<&a…

【云原生】Kubernetes----Ingress对外服务

目录 引言 一、K8S对外方式 &#xff08;一&#xff09;NodePort 1.作用 2.弊端 3.示例 &#xff08;二&#xff09;externalIPs 1.作用 2.弊端 3.示例 &#xff08;三&#xff09;LoadBalancer 1.作用 2.弊端 &#xff08;四&#xff09;Ingress 二、Ingress的…

ChatGPT-4o抢先体验

速度很快&#xff0c;结果很智能&#xff0c;支持多模态输入输出&#xff0c;感兴趣联系作者。 windows/linux/mac 客户端下载参考&#xff1a;https://github.com/lencx/Noi

dns域名解析服务和bond网卡

目录 dns域名解析服务 一、DNS 1、定义 2、以www.baidu.com为例 3、域名体系结构 4、DNS解析使用的协议和端口 5、dns域名解析的过程 6、dns解析的优先级 二、如何实现域名解析 1、域名解析 2、bind配置文件位置 &#xff08;一&#xff09;正向解析 &#xff08;…

IP协议1.0

基本概念&#xff1a; • 主机: 配有IP地址, 但是不进⾏路由控制的设备; • 路由器: 即配有IP地址, ⼜能进⾏路由控制; • 节点: 主机和路由器的统称; IP协议的报头 • 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4. • 4位头部⻓度(header length): IP头部的⻓…

高并发系统限流原理

短时间内巨大的访问流量&#xff0c;我们如何让系统在处理高并发的同时还能保证自身系统的稳定性&#xff1f;估计有人会说&#xff0c;增加机器就可以了&#xff0c;因为我的系统架构设计就是按照分布式思想进行架构设计的&#xff0c;所以可以只需要增加机器就可以解决问题了…

计算机组成原理·考点知识点整理

根据往年考试题&#xff0c;对考点和知识点的一个整理。 校验编码 码距 一种编码的最小码距&#xff0c;其实就是指这种编码的码距。码距有两种定义&#xff1a; 码距所描述的对象含义 2 2 2 个特定的码其二进制表示中不同位的个数一种编码这种编码中任意 2 2 2 个合法编码的…

QT+FFmpeg+Windows开发环境搭建(加薪点)

01、Windows 环境搭建 FFMPEG官网:http://ffmpeg.org/ 02、下载4.2.1版本源码 源码:https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2 03、下载4.2.1编译好的文件 下载已经编译好的FFMPEG)(迅雷下载很快) 网址:https://ffmpeg.zeranoe.com/builds/ 32位下载地址:(迅雷…

【wiki知识库】05.分类管理模块--后端SpringBoot模块

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 一、&#x1f525;今日目标 二、☀SpringBoot代码修改 1.使用逆向工程生成Category表结构 2. 新增CategoryQueryParam 3.新增CategorySaveParam 4.新增CategotyQueryVo 三、&#x1f916;新增分类管理的相关接口…

8. C#多线程基础概念

文章目录 一. 目标二. 技能介绍① 进程和线程② 为什么需要多线程③ C#实现多线程的方式④ 线程的操作(创建_终止_挂起_恢复) 一. 目标 进程和线程基本概念为什么需要多线程?C#实现多线程的方式?线程Thread的创建,终止,挂起和恢复? 二. 技能介绍 ① 进程和线程 什么是进程…

IO流,文件操作

参考 Java IO 基础知识总结 | JavaGuide 史上最骚最全最详细的IO流教程&#xff0c;没有之一&#xff01; - 宜春 - 博客园 零、io-流简介 IO 即 Input/Output&#xff0c;输入和输出。数据输入到计算机内存的过程即输入&#xff0c;反之输出到外部存储&#xff08;比如数据…

Microsoft Outlook Lite 引入短信功能

随着科技的不断进步&#xff0c;我们的沟通方式也在不断演变。微软最新推出的 Outlook Lite 应用&#xff0c;不仅为我们提供了一个轻量级的电子邮件管理工具&#xff0c;现在更是带来了一项令人兴奋的新功能——短信服务。 Outlook Lite&#xff1a;轻量级&#xff0c;功能全…

手眼标定学习笔记

目录 标定代码&#xff1a; 手眼标定原理学习 什么是手眼标定 手眼标定的目的 eye in hand eye to hand AXXB问题的求解 标定代码&#xff1a; GitHub - pumpkin-ws/HandEyeCalib 推荐博文&#xff1a; https://zhuanlan.zhihu.com/p/486592374 手眼标定原理学习 参…

带DSP音效处理D类数字功放TAS5805M中文资料

国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …

Vue3中的常见组件通信之v-model

Vue3中的常见组件通信之v-model 概述 ​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。 组件关系传递方式父传子1. props2. v-model3. $r…

智能视频监控技术为游泳馆安全护航,助力安全管理新升级

随着社会的进步和科技的发展&#xff0c;视频监控技术在各行各业的应用越来越广泛。游泳馆作为公共场所&#xff0c;每天都会有大量的游泳者进出。在这样的环境中&#xff0c;有时难免会发生一些意外事故&#xff0c;如溺水、摔倒等。因此&#xff0c;视频监控建设的必要性尤为…

使用 MDC 实现日志链路跟踪,包教包会!

在微服务环境中&#xff0c;我们经常使用 Skywalking、Spring Cloud Sleut 等去实现整体请求链路的追踪&#xff0c;但是这个整体运维成本高&#xff0c;架构复杂&#xff0c;本次我们来使用 MDC 通过 Log 来实现一个轻量级的会话事务跟踪功能&#xff0c;需要的朋友可以参考一…

TCP 协议的相关特性

一些TCP协议的基础标志位&#xff1a; URG:紧急指针是否有效 ACK:确认号是否有效 PSH:提示接收端应用程序立刻把数据读走 RST:要求重新建立连接&#xff0c;也叫复位报文段 SYN:请求建立连接&#xff0c;同步报文段 FIN:通知要断开连接了我这里&#xff0c;结束报文段 一&#…

C++缺省参数函数重载

缺省参数 大家知道什么是备胎吗&#xff1f; C中函数的参数也可以配备胎。 3.1缺省参数概念 缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时&#xff0c;如果没有指定实参则采用该默认值&#xff0c;否则使用指定的实参。 void TestFunc(int a 0…

引擎:Shader

一、原理 创建Shader脚本&#xff0c;创建材质球&#xff0c;将物体的渲染效果Shader脚本挂载到材质球&#xff0c;最后把材质球挂到3d物体上面从而实现渲染。 二、模型边缘发光 原理&#xff1a;正对着摄像机的模型三角面边缘光最弱&#xff0c;垂直于摄像机的模型三角面边缘光…