Skip to content

Add AOT repository support. #2588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2497-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data JPA Parent</name>
Expand Down Expand Up @@ -35,7 +35,7 @@
<jsqlparser>4.3</jsqlparser>
<mysql-connector-java>8.0.23</mysql-connector-java>
<postgresql>42.2.19</postgresql>
<springdata.commons>3.0.0-SNAPSHOT</springdata.commons>
<springdata.commons>3.0.0-GH-2593-SNAPSHOT</springdata.commons>
<vavr>0.10.3</vavr>

<hibernate.groupId>org.hibernate</hibernate.groupId>
Expand Down
4 changes: 2 additions & 2 deletions spring-data-envers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2497-SNAPSHOT</version>

<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2497-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-jpa-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2497-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jpa/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2497-SNAPSHOT</version>

<name>Spring Data JPA</name>
<description>Spring Data module for JPA repositories.</description>
Expand All @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa-parent</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-GH-2497-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.aot;

import java.util.Arrays;

import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.lang.Nullable;

/**
* @author Christoph Strobl
* @since 3.0
*/
public class DataJpaRuntimeHints implements RuntimeHintsRegistrar {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Package private, rename to JpaRuntimeHints.


@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {

hints.proxies().registerJdkProxy(org.springframework.data.jpa.repository.support.CrudMethodMetadata.class,
org.springframework.aop.SpringProxy.class, org.springframework.aop.framework.Advised.class,
org.springframework.core.DecoratingProxy.class);
hints.reflection().registerTypes(
Arrays.asList(TypeReference.of(AnnotationBeanConfigurerAspect.class),
TypeReference.of(AuditingBeanFactoryPostProcessor.class), TypeReference.of(AuditingEntityListener.class)),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS));
hints.reflection().registerTypes(Arrays.asList(TypeReference.of(SimpleJpaRepository.class)),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import java.lang.annotation.Annotation;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
Expand All @@ -34,14 +36,17 @@
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.repository.config.PersistentEntitiesFactoryBean;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableJpaAuditing} annotation.
*
* @author Thomas Darimont
* @author Christoph Strobl
*/
class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {

Expand All @@ -57,16 +62,6 @@ protected String getAuditingHandlerBeanName() {
return "jpaAuditingHandler";
}

@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {

BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
definition.addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME);

BeanDefinitionBuilder builder = super.getAuditHandlerBeanDefinitionBuilder(configuration);
return builder.addConstructorArgValue(definition.getBeanDefinition());
}

@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {

Expand Down Expand Up @@ -95,6 +90,42 @@ protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandle
registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), AuditingEntityListener.class.getName(), registry);
}

@Override
protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,
BeanDefinitionRegistry registry) {

String persistentEntitiesBeanName = detectPersistentEntitiesBeanName(registry);

if (persistentEntitiesBeanName == null) {

persistentEntitiesBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jpaPersistentEntities", registry);

// TODO: https://github.com/spring-projects/spring-framework/issues/28728
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) //
.setFactoryMethod("of") //
.addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME);

registry.registerBeanDefinition(persistentEntitiesBeanName, definition.getBeanDefinition());
}

builder.addConstructorArgReference(persistentEntitiesBeanName);
}

@Nullable
private static String detectPersistentEntitiesBeanName(BeanDefinitionRegistry registry) {

if (registry instanceof ListableBeanFactory beanFactory) {
for (String bn : beanFactory.getBeanNamesForType(PersistentEntities.class)) {
if (bn.startsWith("jpa")) {
return bn;
}
}
}

return null;
}


/**
* @param registry, the {@link BeanDefinitionRegistry} to be used to register the
* {@link AnnotationBeanConfigurerAspect}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,26 @@

import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*;

import jakarta.persistence.Entity;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.PersistenceUnit;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import jakarta.persistence.Entity;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.PersistenceUnit;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
Expand Down Expand Up @@ -77,6 +81,8 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi
private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup";
private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter";

private final Map<Object, String> entityManagerRefs = new LinkedHashMap<>();

@Override
public String getModuleName() {
return "JPA";
Expand Down Expand Up @@ -107,7 +113,7 @@ public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSo

Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(source, source.getSource()));
builder.addPropertyReference("entityManager", entityManagerRefs.get(source));
builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\'));
builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
}
Expand Down Expand Up @@ -149,6 +155,8 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf

super.registerBeansForRoot(registry, config);

prepareAndRegisterSharedEntityManger(registry, config);

Object source = config.getSource();

registerLazyIfNotAlreadyRegistered(
Expand Down Expand Up @@ -191,6 +199,21 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf
}, registry, JpaEvaluationContextExtension.class.getName(), source);
}

private String prepareAndRegisterSharedEntityManger(BeanDefinitionRegistry registry,
RepositoryConfigurationSource config) {

AbstractBeanDefinition entityManager = getEntityManagerBeanDefinitionFor(config, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move this bean registration into Spring Boot?

entityManager.setRole(BeanDefinition.ROLE_SUPPORT);
entityManager.setSynthetic(true);
entityManager.setPrimary(false);
entityManager.setAutowireCandidate(false);

String entityManagerBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jpaSharedEM", registry);
entityManagerRefs.put(config, entityManagerBeanName);
registry.registerBeanDefinition(entityManagerBeanName, entityManager);
return entityManagerBeanName;
}

@Override
protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loader) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.data.jpa.aot.DataJpaRuntimeHints
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

import jakarta.persistence.EntityManagerFactory;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
Expand All @@ -35,6 +37,7 @@
class EntityManagerFactoryRefUnitTests {

@Test
@Disabled
void repositoriesGetTheSecondEntityManagerFactoryInjected2() {

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
Expand All @@ -43,7 +46,7 @@ void repositoriesGetTheSecondEntityManagerFactoryInjected2() {

BeanDefinition bean = factory.getBeanDefinition("userRepository");
Object value = getPropertyValue(bean, "entityManager");
assertThat(value instanceof BeanDefinition).isTrue();
assertThat(value instanceof RuntimeBeanNameReference).isTrue();
BeanDefinition emCreator = (BeanDefinition) value;

BeanReference reference = getConstructorBeanReference(emCreator, 0);
Expand Down