一、代码
1. maven 依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<scope>provided</scope>
</dependency>
</dependencies>
2. java 代码
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/temp
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
@ToString
@Entity(name = "student")
public class StudentDO {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
}
@Repository
public interface StudentDao extends JpaRepository<StudentDO, Long> {
StudentDO findByIdAndName(Long id, String name);
@Query("select s from student s WHERE s.id = ?1")
StudentDO findNativeSql1(Long id);
@Query(value = "select * from student WHERE id = ?1", nativeQuery = true)
StudentDO findNativeSql2(Long id);
}
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Autowired
private StudentDao studentDao;
@Override
public void run(ApplicationArguments args) throws Exception {
List<StudentDO> result1 = studentDao.findAll();
StudentDO result2 = studentDao.findByIdAndName(1L, "zhangsan");
StudentDO result3 = studentDao.findNativeSql1(1L);
StudentDO result4 = studentDao.findNativeSql2(1L);
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
System.out.println(result4);
}
}
@SpringBootApplication
public class Boot {
public static void main(String[] args) {
SpringApplication.run(Boot.class, args);
}
}
3. 日志
Hibernate: select studentdo0_.id as id1_0_, studentdo0_.name as name2_0_ from student studentdo0_
Hibernate: select studentdo0_.id as id1_0_, studentdo0_.name as name2_0_ from student studentdo0_ where studentdo0_.id=? and studentdo0_.name=?
Hibernate: select studentdo0_.id as id1_0_, studentdo0_.name as name2_0_ from student studentdo0_ where studentdo0_.id=?
Hibernate: select * from student WHERE id = ?
[StudentDO(id=1, name=zhangsan)]
StudentDO(id=1, name=zhangsan)
StudentDO(id=1, name=zhangsan)
StudentDO(id=1, name=zhangsan)
二、原理
1.加载 JpaRepositoriesAutoConfiguration
org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件下有:
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration
2.加载 JpaRepositoriesRegistrar
JpaRepositoriesAutoConfiguration 加载 JpaRepositoriesImportSelector,JpaRepositoriesImportSelector 加载 JpaRepositoriesRegistrar
JpaRepositoriesRegistrar 又继承了抽象类 AbstractRepositoryConfigurationSourceSupport 类。这是一个 ImportBeanDefinitionRegistrar,设计目的就是在 SpringBoot 自动发现机制中发现用户自定义的 JpaRepository。在 Spring 容器启动中,该 ImportBeanDefinitionRegistrar 就会执行。
// JpaRepositoriesAutoConfiguration.java
@AutoConfiguration(after = { HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
matchIfMissing = true)
@Import(JpaRepositoriesImportSelector.class)
public class JpaRepositoriesAutoConfiguration {
...
static class JpaRepositoriesImportSelector implements ImportSelector {
private static final boolean ENVERS_AVAILABLE = ClassUtils.isPresent(
"org.springframework.data.envers.repository.config.EnableEnversRepositories",
JpaRepositoriesImportSelector.class.getClassLoader());
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] { determineImport() };
}
private String determineImport() {
return ENVERS_AVAILABLE ? EnversRevisionRepositoriesRegistrar.class.getName()
: JpaRepositoriesRegistrar.class.getName();
}
}
}
JpaRepositoriesRegistrar 执行 registerBeanDefinitions 方法注册 bean
// JpaRepositoriesRegistrar.java
class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(
getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment);
delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension());
}
private AnnotationRepositoryConfigurationSource getConfigurationSource(BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
AnnotationMetadata metadata = AnnotationMetadata.introspect(getConfiguration());
return new AutoConfiguredAnnotationRepositoryConfigurationSource(metadata, getAnnotation(), this.resourceLoader,
this.environment, registry, importBeanNameGenerator) {
};
}
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableJpaRepositories.class;
}
@Override
protected Class<?> getConfiguration() {
return EnableJpaRepositoriesConfiguration.class;
}
@Override
protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
return new JpaRepositoryConfigExtension();
}
...
@EnableJpaRepositories
private static class EnableJpaRepositoriesConfiguration {
}
}
// EnableJpaRepositories.java
public @interface EnableJpaRepositories {
...
String entityManagerFactoryRef() default "entityManagerFactory";
String transactionManagerRef() default "transactionManager";
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
String repositoryImplementationPostfix() default "Impl";
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
...
}
3.加载自定义 JpaRepository 的 BeanDefinition
对于自定义的 JpaRepository,例如 studentDao,BeanDefinition 注册到 spring 容器,beanClass 是 org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean
RepositoryConfigurationExtension.registerBeansForRoot 会注册 EntityManagerBeanDefinitionRegistrarPostProcessor、JpaMetamodelMappingContextFactoryBean、PersistenceAnnotationBeanPostProcessor、DefaultJpaContext、JpaMetamodelCacheCleanup、JpaEvaluationContextExtension
RepositoryComponentProvider 负责找出 找出实现 Repository 接口,或有 RepositoryDefinition 注解的类
// RepositoryConfigurationDelegate.java
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
...
extension.registerBeansForRoot(registry, configurationSource);
RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension,
configurationSource, resourceLoader, environment);
List<BeanComponentDefinition> definitions = new ArrayList<>();
...
Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension
.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode);
Map<String, RepositoryConfiguration<?>> configurationsByRepositoryName = new HashMap<>(configurations.size());
for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : configurations) {
configurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration);
BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
extension.postProcess(definitionBuilder, configurationSource);
if (isXml) {
extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
} else {
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
}
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
beanDefinition.setResourceDescription(configuration.getResourceDescription());
String beanName = configurationSource.generateBeanName(beanDefinition);
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName, configuration.getRepositoryInterface(),
configuration.getRepositoryFactoryBeanClassName()));
}
beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
registry.registerBeanDefinition(beanName, beanDefinition);
definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
}
potentiallyLazifyRepositories(configurationsByRepositoryName, registry, configurationSource.getBootstrapMode());
...
return definitions;
}
// JpaRepositoryConfigExtension.java
@Override
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) {
super.registerBeansForRoot(registry, config);
Object source = config.getSource();
registerLazyIfNotAlreadyRegistered(
() -> new RootBeanDefinition(EntityManagerBeanDefinitionRegistrarPostProcessor.class), registry,
EM_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME, source);
registerLazyIfNotAlreadyRegistered(() -> new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class),
registry, JPA_MAPPING_CONTEXT_BEAN_NAME, source);
registerLazyIfNotAlreadyRegistered(() -> new RootBeanDefinition(PAB_POST_PROCESSOR), registry,
AnnotationConfigUtils.PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME, source);
// Register bean definition for DefaultJpaContext
registerLazyIfNotAlreadyRegistered(() -> {
RootBeanDefinition contextDefinition = new RootBeanDefinition(DefaultJpaContext.class);
contextDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
return contextDefinition;
}, registry, JPA_CONTEXT_BEAN_NAME, source);
registerIfNotAlreadyRegistered(() -> new RootBeanDefinition(JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME), registry,
JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME, source);
// EvaluationContextExtension for JPA specific SpEL functions
registerIfNotAlreadyRegistered(() -> {
Object value = AnnotationRepositoryConfigurationSource.class.isInstance(config) //
? config.getRequiredAttribute(ESCAPE_CHARACTER_PROPERTY, Character.class) //
: config.getAttribute(ESCAPE_CHARACTER_PROPERTY).orElse("\\");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JpaEvaluationContextExtension.class);
builder.addConstructorArgValue(value);
return builder.getBeanDefinition();
}, registry, JpaEvaluationContextExtension.class.getName(), source);
}
getBasePackages() 得到扫描为启动类目录,例如 org.example
RepositoryComponentProvider 负责找出 找出实现 Repository 接口,或有 RepositoryDefinition 注解的类
// AbstractRepositoryConfigurationSourceSupport$1.java
@Override
public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
scanner.setEnvironment(environment);
scanner.setResourceLoader(loader);
getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it));
return Streamable.of(() -> getBasePackages().stream()//
.flatMap(it -> scanner.findCandidateComponents(it).stream()));
}
protected Streamable<String> getBasePackages() {
return Streamable.of(AutoConfigurationPackages.get(this.beanFactory));
}
4.注册 JpaRepositoryFactoryBean 实例
前面已把自定义的 JpaRepository,例如 studentDao,生成 BeanDefinition,其 name 是 studentDao,beanClass 是 org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean,注册到 spring 容器
5.生成代理类 SimpleJpaRepository
repository 是 JpaRepositoryFactory
// JpaRepositoryFactoryBean.java
public JpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
@NonNull
public T getObject() {
return this.repository.get();
}
public void afterPropertiesSet() {
this.factory = createRepositoryFactory();
...
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
...
if (!lazyInit) {
this.repository.get();
}
}
创建代理对象,target 是 SimpleJpaRepository,repositoryInterface 是 StudentDao
postProcessors 有 5 个:
- CrudMethodMetadataPostProcessor
- JpaRepositoryFactory$lambda
- PersistenceExceptionTranslationRepositoryProxyPostProcessor
- TransactionalRepositoryProxyPostProcessor
- EventPublishingRepositoryProxyPostProcessor
- CrudMethodMetadataPostProcessor 会 addAdvice 为 CrudMethodMetadataPopulatingMethodInterceptor
- paRepositoryFactory$lambda 会 addAdvice 为 SurroundingTransactionDetectorMethodInterceptor.INSTANCE
- PersistenceExceptionTranslationRepositoryProxyPostProcessor 会 addAdvice 为 PersistenceExceptionTranslationInterceptor
- TransactionalRepositoryProxyPostProcessor 会 addAdvice 为 TransactionInterceptor
- EventPublishingRepositoryProxyPostProcessor 会 addAdvice 为 EventPublishingMethodInterceptor
// JpaRepositoryFactory.java
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments)
...
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
if (MethodInvocationValidator.supports(repositoryInterface)) {
result.addAdvice(new MethodInvocationValidator());
}
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
if (!postProcessors.isEmpty()) {
postProcessors.forEach(processor -> {
...
processor.postProcess(result, information);
...
});
}
if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
}
...
result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy,
namedQueries, queryPostProcessors, methodInvocationListeners));
result.addAdvice(
new ImplementationMethodExecutionInterceptor(information, compositionToUse, methodInvocationListeners));
T repository = (T) result.getProxy(classLoader);
...
return repository;
}
6.执行方法
advisors 有:
- advisor 为 ExposeInvocationInterceptor$1,pointcut 为 Pointcut.TRUE,advice 为 ExposeInvocationInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 Pointcut.TRUE,advice 为 CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 Pointcut.TRUE,advice 为 PersistenceExceptionTranslationInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 Pointcut.TRUE,advice 为 TransactionInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 Pointcut.TRUE,advice 为 DefaultMethodInvokingMethodInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 Pointcut.TRUE,advice 为 QueryExecutorMethodInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 Pointcut.TRUE,advice 为 RepositoryFactorySupport$ImplementationMethodExecutionInterceptor
- advisor 为 DefaultPointcutAdvisor,pointcut 为 AnnotationMatchingPointcut,匹配被 @Repository 标注的类,advice 为 PersistenceExceptionTranslationInterceptor
TransactionInterceptor 用于开始事务和关闭事务
// JdkDynamicAopProxy.java
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
TargetSource targetSource = this.advised.targetSource;
Object target = null;
...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
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();
}
...
return retVal;
}
6.1.执行 baseClass 方法
studentDao.findAll() 会执行 RepositoryFactorySupport$ImplementationMethodExecutionInterceptor,会调用 SimpleJpaRepository 的 findAll() 方法
// QueryExecutorMethodInterceptor.java
@Nullable
private Object doInvoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (hasQueryFor(method)) {
RepositoryMethodInvoker invocationMetadata = invocationMetadataCache.get(method);
if (invocationMetadata == null) {
invocationMetadata = RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method));
invocationMetadataCache.put(method, invocationMetadata);
}
return invocationMetadata.invoke(repositoryInformation.getRepositoryInterface(), invocationMulticaster,
invocation.getArguments());
}
return invocation.proceed();
}
// RepositoryFactorySupport$ImplementationMethodExecutionInterceptor
@Nullable
@Override
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();
...
return composition.invoke(invocationMulticaster, method, arguments);
}
// RepositoryComposition.java
Object invoke(RepositoryInvocationMulticaster listener, Method method, Object[] args) throws Throwable {
...
return fragments.invoke(metadata != null ? metadata.getRepositoryInterface() : method.getDeclaringClass(), listener,
method, methodToCall, argumentConverter.apply(methodToCall, args));
}
// RepositoryComposition$RepositoryFragments.java
@Nullable
Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster listener, Method invokedMethod,
Method methodToCall, Object[] args) throws Throwable {
...
return repositoryMethodInvoker.invoke(repositoryInterface, listener, args);
}
invokable 为 RepositoryMethodInvoker$RepositoryFragmentMethodInvoker$$Lambda$795/773765215
// RepositoryMethodInvoker$RepositoryQueryMethodInvoker.java
@Nullable
public Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
throws Exception {
return shouldAdaptReactiveToSuspended() ? doInvokeReactiveToSuspended(repositoryInterface, multicaster, args)
: doInvoke(repositoryInterface, multicaster, args);
}
@Nullable
private Object doInvoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
throws Exception {
...
Object result = invokable.invoke(args);
...
return result;
}
baseClassMethod.invoke(instance, args) 调用 SimpleJpaRepository 的方法
// RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.java
public RepositoryFragmentMethodInvoker(CoroutineAdapterInformation adapterInformation, Method declaredMethod,
Object instance, Method baseClassMethod) {
super(declaredMethod, args -> {
if (adapterInformation.isAdapterMethod()) {
Object[] invocationArguments = new Object[args.length - 1];
System.arraycopy(args, 0, invocationArguments, 0, invocationArguments.length);
return baseClassMethod.invoke(instance, invocationArguments);
}
return baseClassMethod.invoke(instance, args);
});
this.adapterInformation = adapterInformation;
}
protected RepositoryMethodInvoker(Method method, Invokable invokable) {
this.method = method;
this.invokable = invokable;
...
}
@Nullable
public Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
throws Exception {
return shouldAdaptReactiveToSuspended() ? doInvokeReactiveToSuspended(repositoryInterface, multicaster, args)
: doInvoke(repositoryInterface, multicaster, args);
}
@Nullable
private Object doInvoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
throws Exception {
...
try {
Object result = invokable.invoke(args);
...
return result;
} catch (Exception e) {
...
}
}
// SimpleJpaRepository.java
@Override
public List<T> findAll() {
return getQuery(null, Sort.unsorted()).getResultList();
}
6.2.执行自定义方法
执行 studentDao.findByIdAndName(1L, "zhangsan"),
RepositoryQuery 为 PartTreeJpaQuery
// QueryExecutorMethodInterceptor.java
@Nullable
private Object doInvoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (hasQueryFor(method)) {
RepositoryMethodInvoker invocationMetadata = invocationMetadataCache.get(method);
if (invocationMetadata == null) {
invocationMetadata = RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method));
invocationMetadataCache.put(method, invocationMetadata);
}
return invocationMetadata.invoke(repositoryInformation.getRepositoryInterface(), invocationMulticaster,
invocation.getArguments());
}
return invocation.proceed();
}
// RepositoryMethodInvoker.java
static RepositoryQueryMethodInvoker forRepositoryQuery(Method declaredMethod, RepositoryQuery query) {
return new RepositoryQueryMethodInvoker(declaredMethod, query);
}
private static class RepositoryQueryMethodInvoker extends RepositoryMethodInvoker {
public RepositoryQueryMethodInvoker(Method method, RepositoryQuery repositoryQuery) {
super(method, repositoryQuery::execute);
}
}
// RepositoryMethodInvoker.java
protected RepositoryMethodInvoker(Method method, Invokable invokable) {
this.method = method;
this.invokable = invokable;
...
}
invokable 是 RepositoryMethodInvoker$RepositoryQueryMethodInvoker$$Lambda$810/2053481312
// RepositoryMethodInvoker$RepositoryQueryMethodInvoker.java
@Nullable
public Object invoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
throws Exception {
return shouldAdaptReactiveToSuspended() ? doInvokeReactiveToSuspended(repositoryInterface, multicaster, args)
: doInvoke(repositoryInterface, multicaster, args);
}
@Nullable
private Object doInvoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
throws Exception {
...
Object result = invokable.invoke(args);
...
return result;
}
// PartTreeJpaQuery,java
@Nullable
@Override
public Object execute(Object[] parameters) {
return doExecute(getExecution(), parameters);
}
@Nullable
private Object doExecute(JpaQueryExecution execution, Object[] values) {
JpaParametersParameterAccessor accessor = obtainParameterAccessor(values);
Object result = execution.execute(this, accessor);
ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor);
return withDynamicProjection.processResult(result, new TupleConverter(withDynamicProjection.getReturnedType()));
}
// JpaQueryExecution$SingleEntityExecution.java
@Nullable
public Object execute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) {
Object result;
try {
result = doExecute(query, accessor);
} catch (NoResultException e) {
return null;
}
...
}
@Override
protected Object doExecute(AbstractJpaQuery query, JpaParametersParameterAccessor accessor) {
return query.createQuery(accessor).getSingleResult();
}