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

news2024/11/21 1:42:20

 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.class
	private 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
	@Nullable
	public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {

		Method method = invocation.getMethod();
		// 通过返回的返回值,获取执行适配器,默认都为null
		QueryExecutionConverters.ExecutionAdapter executionAdapter = QueryExecutionConverters //
				.getExecutionAdapter(method.getReturnType());

		if (executionAdapter == null) {
			return resultHandler.postProcessInvocationResult(doInvoke(invocation), method);
		}

		return executionAdapter //
				.apply(() -> resultHandler.postProcessInvocationResult(doInvoke(invocation), method));
	}

	@Nullable
	private Object doInvoke(MethodInvocation invocation) throws Throwable {

		Method method = invocation.getMethod();
		// 判断方法是否存在RepositoryQuery。在构造函数中,会解析Repository中的查询方法,并缓存到Map
		if (hasQueryFor(method)) {

			RepositoryMethodInvoker invocationMetadata = invocationMetadataCache.get(method);

			if (invocationMetadata == null) {
				// 首次执行对应方法,先创建一个RepositoryQueryMethodInvoker对象,保存方法即方法对应的RepositoryQuery
				invocationMetadata = 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);
	}
	
	@Nullable
	public Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
			throws Exception {
		return doInvoke(repositoryInterface, multicaster, args);
	}

	@Nullable
	private 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 {

		@Nullable
		Object 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 {

		@Override
		protected 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
	@Override
	public Object execute(Object[] parameters) {
		return doExecute(getExecution(), parameters);
	}

	/**
	 * 执行查询
	 * @param execution 执行器。主要根据方法的返回值确定的执行器
	 * @param values 方法执行时的参数值
	 * @return
	 */
	@Nullable
	private 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 exist
		if (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()设置参数值及分页信息
	 */
	@Override
	public 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),返回TypedQuery
			TypedQuery<?> 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
		 */
		@Override
		protected 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);
		}

		@Override
		protected 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.coloradmin.cn/o/1720115.html

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

相关文章

苍穹外卖数据可视化

文章目录 1、用户统计2、订单统计3、销量排名Top10 1、用户统计 所谓用户统计&#xff0c;实际上统计的是用户的数量。通过折线图来展示&#xff0c;上面这根蓝色线代表的是用户总量&#xff0c;下边这根绿色线代表的是新增用户数量&#xff0c;是具体到每一天。所以说用户统计…

C++系列-STL简介

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 什么是STL STL是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 STL的版本 原始版本 Alexander Stepa…

浅谈安科瑞ASJ10-LD1A智能漏电继电器的设计与应用-安科瑞 蒋静

一 产品简介 功能 ASJ10-LD1A安科瑞智能电力继电器 剩余电流保护可与低压断路器或低压接触器等组成组合式的剩余电流动作保护器&#xff0c;主要适用于交流50Hz&#xff0c;额定电压为400V及以下的TT或TN系统配电线路&#xff0c;防止接地故障电流引起的设备和电气火灾事故&a…

香橙派OriengePi AiPro 华为昇腾芯片开发板开箱测评

香橙派OriengePi AiPro 华为昇腾芯片开发板开箱测评 文章目录 前言OrangePi AIpro硬件相关及配置外观接口配置虚拟桌面网络配置拓展swap内存 软件相关及配置docker基础镜像搭建pytorch安装及匹配 软件测试使用yolo v8测试使用模型转换 总结 前言 博主有幸受邀CSDN测评香橙派与…

智能小家电风潮渗透美国市场,哪些产品适合入驻沃尔玛?

智能小家电风潮在美国市场持续渗透&#xff0c;这为众多品牌提供了良好的商机。沃尔玛作为全球零售巨头&#xff0c;自然成为许多品牌进驻美国市场的首选平台。 针对智能小家电产品&#xff0c;以下是一些适合入驻沃尔玛的产品类型&#xff1a; 一、智能厨房电器 美国市场接受…

如何基于知行之桥V2024快速搭建一个EDI工作流?

本文将基于知行之桥EDI系统V2024版本展开介绍&#xff0c;与之前的版本相比&#xff0c;UI界面的变化较为明显。 创建工作区 首先登录知行之桥EDI系统&#xff0c;输入用户名密码后将会看到 概览 页面。 点击 概览 右侧的 工作流&#xff0c;即可进入到 工作流 页面&#xff…

K8S 证书过期不能使用kubectl之后,kubeadm 重新生成证书

查询证书过期时间 kubeadm certs check-expiration重新生成证书 # 重新生成所有证书 kubeadm certs renew all # 重新生成某个组件的证书 kubeadm certs renew 组件名称 如&#xff1a;apiserver生成新的配置 # 重新生成kubeconfig配置 kubeadm init phase kubeconfig # 重…

12 FreeRTOS 调试与优化

1、调试 1.1 打印 在FreeRTOS工程中使用了microlib&#xff0c;里面实现了printf函数。 只需要实现一下以下函数即可使用printf。 int fputc(int ch; FILE *f); 假如要从串口实现打印函数&#xff1a; int fputc( int ch, FILE *f ) {//指定串口USART_TypeDef* USARTx USAR…

语音深度鉴伪识别项目实战:基于深度学习的语音深度鉴伪识别算法模型(二)音频数据预处理及去噪算法+Python源码应用

前言 深度学习技术在当今技术市场上面尚有余力和开发空间的&#xff0c;主流落地领域主要有&#xff1a;视觉&#xff0c;听觉&#xff0c;AIGC这三大板块。 目前视觉板块的框架和主流技术在我上一篇基于Yolov7-LPRNet的动态车牌目标识别算法模型已有较为详细的解说。与AIGC相…

【火炬打宝策略】

打宝策略刷遗物&#xff1a; 时可4 只刷奇诊加稀有度&#xff0c;没有奇诊可以直接不打。

当新媒体运营开始说真话,这些道理你真的懂么?沈阳新媒体运营培训

运营新人&#xff0c;尤其是刚毕业、啥都不会的大学生&#xff0c;一定要认清的现实就是&#xff1a;虽然新媒体运营这个岗位门槛比较低&#xff0c;薪资也比较香&#xff0c;但绝不是养老型的工作。 平时大家还是很忙的&#xff0c;所以一定要摒弃学生思维&#xff0c;千万别…

vivo鄢楠:基于OceanBase 的降本增效实践

在3 月 20 日的2024 OceanBase 数据库城市行中&#xff0c;vivo的 体系与流程 IT 部 DBA 组总监鄢楠就“vivo 基于 OceanBase 的降本增效实践”进行了主题演讲。本文为该演讲的精彩回顾。 vivo 在1995年于中国东莞成立&#xff0c;作为一家全球领先的移动互联网智能终端公司&am…

【HarmonyOS - ArkTS - 状态管理】

概述 本文主要是从页面和应用级两个方面介绍了ArkTS中的状态管理的一些概念以及如何使用。可能本文比较粗略&#xff0c;细节化请前往官网(【状态管理】)学习&#xff0c;若有理解错误的地方&#xff0c;欢迎评论指正。 装饰器总览 由于下面会频繁提及到装饰器&#xff0c;所…

将三个字符串通过strcat连接起来并打印输出

将三个字符串通过strcat连接起来并打印输出 #include <stdio.h> #include <string.h> int main () { char a[10]"I", b[10]" am",c[10]" happy"; strcat(a,b); strcat(a,c); printf("%s",a); printf("\n"); re…

win10键盘按乱了,如何恢复?

今天键盘被宝宝给按乱了&#xff0c;好不容易给重新调整回来&#xff0c;记录备忘&#xff1a; 1、win10的asdf和方向键互换了&#xff1a; 使用Fnw键来回切换&#xff0c;OK&#xff01; 2、键盘的win键失效&#xff0c;例如&#xff1a;按winD无法显示桌面。此时&#xf…

Django企业招聘后台管理系统开发实战四

前言 首先我们看一下产品的需求背景&#xff0c;这个产品为了解决招聘面试的过程中&#xff0c;线下面试管理效率低&#xff0c;面试过程和结果不方便跟踪的痛点 招聘管理的系统几乎是每一家中小公司都需要的产品 我们以校园招聘的面试为例子来做 MVP 产品迭代 首先我们来看一下…

【计算机毕设】基于Spring Boot的课程作业管理系统 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 课程作业管理系统旨在为教师和学生提供一个便捷的平台&#xff0c;用于发布、提交和评定课程作业。本系统旨在提高作业管理的效率&#xff0c;促进教…

【免费Web系列】JavaWeb实战项目案例四

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 多表操作&员工列表查询 1. 多表关系 关于单表的操作(单表的设计、单表的增删改查)我们就已经学习完了。接下来我们就要来学习多表的操作&#xff0c;首先来学习多表的设计。 项目开发中&#xff0…

如何使用 Connector API 将数据提取到 Elasticsearch Serverless 中

作者&#xff1a;来自 Elastic Jedr Blaszyk Elasticsearch 支持一系列摄取方法。 其中之一是 Elastic Connectors&#xff0c;它将 SQL 数据库或 SharePoint Online 等外部数据源与 Elasticsearch 索引同步。 连接器对于在现有数据之上构建强大的搜索体验特别有用。 例如&…

618 购物指南:简单点 618 捡便宜,用这个利器就行

直接干货&#xff0c;看效果&#xff0c;安装脚本直接显示商家额外优惠券&#xff1a; 1、安装好脚本后&#xff0c;打开京东、淘宝(天猫) 商品详情页面&#xff0c;脚本会自动获取优惠券进行展示。 2、如果当前商品 处于 30 天最低价&#xff0c;脚本将自动进行标记 提醒&…