【源碼】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注解的執行原理

前言

上一篇限于篇幅,只分享了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.pswp.cn/bicheng/20054.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20054.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20054.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Oracle中TAF與SCANIP全面解析

TAF (Transparent Application Failover) 概念&#xff1a; TAF是Oracle數據庫提供的一個高級特性&#xff0c;旨在實現應用程序在數據庫連接中斷時的透明重連。它允許應用程序在數據庫故障發生時&#xff0c;無需修改代碼或手動干預&#xff0c;就能自動連接到新的數據庫實例…

Java垃圾回收_1

一、垃圾回收 1.如何判斷對象可以回收 &#xff08;1&#xff09;引用計數法 存在循環引用問題&#xff0c; Java未使用這種算法 在引用計數法中&#xff0c;每個對象都有一個引用計數器&#xff0c;記錄著指向該對象的引用數量。當引用計數器為零時&#xff0c;表示沒有任…

JavaSE:SE知識整體總結

1、引言 歷時一個多月的學習&#xff0c;已經掌握了JavaSE的知識&#xff0c;這篇博客就來做一下SE知識的總結~ 2、數據類型和變量 Java中的數據類型分為基本數據類型和引用數據類型。 2.1 基本數據類型 基本數據類型共有四類八種&#xff1a; 四類&#xff1a;整形、浮點…

在phpstorm2024版里如何使用Jetbrains ai assistant 插件 ?

ai assistant激活成功后&#xff0c;如圖 ai assistant渠道&#xff1a;https://web.52shizhan.cn/activity/ai-assistant 在去年五月份的 Google I/O 2023 上&#xff0c;Google 為 Android Studio 推出了 Studio Bot 功能&#xff0c;使用了谷歌編碼基礎模型 Codey,Codey 是…

SpringBoot HelloWorld 之 實現注冊功能

SpringBoot HelloWorld 之 實現注冊功能 一.配置 創建數據庫big_event CREATE TABLE user (id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT ID,username varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT 用戶名,password varchar(32) COLLATE utf8_unicode_ci …

Vue3項目練習詳細步驟(第五部分:用戶模塊的功能)

頂部導航欄個人信息顯示 接口文檔 接口請求與綁定 導航欄下拉菜單功能 路由實現 退出登錄和路由跳轉實現 基本資料修改 頁面結構 接口文檔 接口請求與綁定 修改頭像 頁面結構 頭像回顯 頭像上傳 接口文檔 重置密碼 頁面結構 接口文檔 接口請求與綁定 頂部導航…

自然語言處理學習路線

學習目標 NLP 系統知識&#xff08;從入門到入土&#xff09; 學習內容 NLP的基本流程&#xff1a;&#xff08;待更&#xff09;文本預處理&#xff08;標點符號處理、繁體轉簡體、分詞Tokenizer&#xff09;&#xff1a;&#xff08;待更&#xff09;詞袋模型&#xff08;TF…

【T+】暢捷通T+軟件固定資產模塊反啟用

【問題描述】 暢捷通T軟件&#xff0c;固定資產模塊反啟用。 【解決方法】 針對賬套庫執行如下腳本清除資產的所有數據&#xff0c; 執行前請與客戶確認資產的所有數據都不要了&#xff0c;確認后備份賬套再執行腳本&#xff0c;切記&#xff01;&#xff01;&#xff01; 然后…

Flutter 中的 ChipTheme 小部件:全面指南

Flutter 中的 ChipTheme 小部件&#xff1a;全面指南 Flutter 是一個由 Google 開發的跨平臺 UI 框架&#xff0c;它提供了一套豐富的組件集合&#xff0c;用于構建現代化的、響應式的移動和 Web 應用。ChipTheme 是 Flutter 中一個專門用于統一設置應用中所有 Chip 組件樣式的…

紅外熱成像觀驅一體儀,夜間驅鳥新利器

夜間驅鳥是機場鳥防工作的重點和難點&#xff0c;但紅外熱成像觀驅一體儀的出現解決了這個問題&#xff0c;它結合了紅外熱成像技術和激光驅鳥技術&#xff0c;極大地提升了夜間驅鳥工作的效率和安全性。 驅鳥技術詳解&#xff1a; 在夜晚低能見度的環境下&#xff0c;紅外熱成…

基于開源項目ESP32 SVPWM驅動無刷電機開環速度測試

基于開源項目ESP32 SVPWM驅動無刷電機開環速度測試 ?本篇硬件電路和代碼來源于此開源項目&#xff1a;https://github.com/MengYang-x/STM3F401-FOC/tree/main&#x1f4cd;硬件電路和項目介紹&#xff0c;立創開源廣場&#xff1a;https://oshwhub.com/shadow27/tai-yang-nen…

【bug】在 Windows 上安裝 SDKMAN! 的完整指南

在 Windows 系統上&#xff0c;安裝 SDKMAN! 可能會遇到一些小問題。本文將詳細介紹如何解決這些問題并成功安裝 SDKMAN!。 問題描述 當在 PowerShell 中運行以下命令以安裝 SDKMAN! 時&#xff1a; curl -s get.sdkman.io | bash你可能會遇到以下錯誤&#xff1a; bash : …

前端面試題日常練-day45 【面試題】

題目 希望這些選擇題能夠幫助您進行前端面試的準備&#xff0c;答案在文末 1. 在Bootstrap中&#xff0c;以下哪個類用于創建一個具有響應式的導航欄&#xff1f; a) .navbar-responsive b) .responsive-nav c) .navbar-collapse d) .collapse-navbar 2. 哪個Bootstrap類用…

2024 HN CTF WebMisc 部分 wp

Web ez_tp 判斷是thinkphp 3.2 參考官方手冊:https://www.kancloud.cn/manual/thinkphp/1697 判斷路由模式 URL_CASE_INSENSITIVE > true, // 默認false 表示URL區分大小寫 true則表示不區分大小寫URL_MODEL > 1, // URL訪問模式,可選參數0、1、…

Python使用動態代理的多元應用

Python作為一種功能強大且易于學習的編程語言&#xff0c;在網絡編程領域具有廣泛的應用。當Python與動態代理技術結合時&#xff0c;便開啟了一扇通往更多可能性的大門。以下將深入探討Python使用動態代理可以實現的多種應用。 首先&#xff0c;Python結合動態代理在網絡爬蟲…

中文多模態InternVL-Chat-V1-5,中文理解能力強勁,8 項指標超越商業模型,性能媲美 GPT-4V

前言 近年來&#xff0c;多模態大型語言模型&#xff08;MLLM&#xff09;的快速發展&#xff0c;為人工智能在圖像、文本等多模態信息理解和處理方面帶來了前所未有的突破。然而&#xff0c;現有的主流多模態模型多以英文為訓練語言&#xff0c;在中文理解和處理方面存在著明…

可用于嵌入式的解釋器調研對比,及lua解釋器介紹

嵌入式不一定只能用C! ---------------------------------------------------------------------------------------手動分割線-------------------------------------------------------------------------------- 本文章參考了以下文章&#xff1a; 這里是引用 ------------…

1113 錢串子的加法

idea 測試點3&#xff1a;輸入的兩個整數都是0測試點4.5&#xff1a;大數&#xff0c;需要用大數加法 solution1(測試點4&#xff0c;5不通過) 直接相加再轉30進制 #include<iostream> #include<string> using namespace std; typedef long long ll; string a,…

linux sed命令替換文件端口

1、需求描述&#xff1a;因sed -i ‘s/舊端口/新端口/g’ 文件&#xff0c;替換會直接增加端口導致端口直接追加后面&#xff0c;因此需要修改 要求&#xff1a;2300替換為23003&#xff0c;23001替換為23004 <value>192.168.1.133</value></constructor-arg>…

RGMII接口--->(001)FPGA實現RGMII接口(一)

&#xff08;001&#xff09;FPGA實現RGMII接口(一) 1 目錄 &#xff08;a&#xff09;FPGA簡介 &#xff08;b&#xff09;IC簡介 &#xff08;c&#xff09;Verilog簡介 &#xff08;d&#xff09;FPGA實現RGMII接口(一) &#xff08;e&#xff09;結束 1 FPGA簡介 &…