Skip to content

Commit 120c228

Browse files
committed
Handle injection of several persistence units
This commit adapts the generated code so that each injection point has a dedicated namespace in the form of a private method. That prevents the same variable to be reused twice which lead to a compilation failure previously. Closes gh-30437
1 parent bd66c70 commit 120c228

File tree

2 files changed

+75
-11
lines changed

2 files changed

+75
-11
lines changed

spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.aot.generate.GeneratedMethod;
4444
import org.springframework.aot.generate.GeneratedMethods;
4545
import org.springframework.aot.generate.GenerationContext;
46+
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
4647
import org.springframework.aot.hint.RuntimeHints;
4748
import org.springframework.beans.BeanUtils;
4849
import org.springframework.beans.PropertyValues;
@@ -769,11 +770,11 @@ private static class AotContribution implements BeanRegistrationAotContribution
769770

770771
private final Class<?> target;
771772

772-
private final Collection<InjectedElement> injectedElements;
773+
private final List<InjectedElement> injectedElements;
773774

774775
AotContribution(Class<?> target, Collection<InjectedElement> injectedElements) {
775776
this.target = target;
776-
this.injectedElements = injectedElements;
777+
this.injectedElements = List.copyOf(injectedElements);
777778
}
778779

779780
@Override
@@ -797,19 +798,47 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be
797798

798799
private CodeBlock generateMethodCode(RuntimeHints hints, GeneratedClass generatedClass) {
799800
CodeBlock.Builder code = CodeBlock.builder();
800-
InjectionCodeGenerator injectionCodeGenerator =
801-
new InjectionCodeGenerator(generatedClass.getName(), hints);
802-
for (InjectedElement injectedElement : this.injectedElements) {
803-
CodeBlock resourceToInject = generateResourceToInjectCode(generatedClass.getMethods(),
804-
(PersistenceElement) injectedElement);
805-
code.add(injectionCodeGenerator.generateInjectionCode(
806-
injectedElement.getMember(), INSTANCE_PARAMETER,
807-
resourceToInject));
801+
if (this.injectedElements.size() == 1) {
802+
code.add(generateInjectedElementMethodCode(hints, generatedClass, this.injectedElements.get(0)));
803+
}
804+
else {
805+
for (InjectedElement injectedElement : this.injectedElements) {
806+
code.addStatement(applyInjectedElement(hints, generatedClass, injectedElement));
807+
}
808808
}
809809
code.addStatement("return $L", INSTANCE_PARAMETER);
810810
return code.build();
811811
}
812812

813+
private CodeBlock applyInjectedElement(RuntimeHints hints, GeneratedClass generatedClass, InjectedElement injectedElement) {
814+
String injectedElementName = injectedElement.getMember().getName();
815+
GeneratedMethod generatedMethod = generatedClass.getMethods().add(new String[] { "apply", injectedElementName }, method -> {
816+
method.addJavadoc("Apply the persistence injection for '$L'.", injectedElementName);
817+
method.addModifiers(javax.lang.model.element.Modifier.PRIVATE,
818+
javax.lang.model.element.Modifier.STATIC);
819+
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
820+
method.addParameter(this.target, INSTANCE_PARAMETER);
821+
method.addCode(generateInjectedElementMethodCode(hints, generatedClass, injectedElement));
822+
});
823+
ArgumentCodeGenerator argumentCodeGenerator = ArgumentCodeGenerator
824+
.of(RegisteredBean.class, REGISTERED_BEAN_PARAMETER).and(this.target, INSTANCE_PARAMETER);
825+
return generatedMethod.toMethodReference().toInvokeCodeBlock(argumentCodeGenerator, generatedClass.getName());
826+
}
827+
828+
private CodeBlock generateInjectedElementMethodCode(RuntimeHints hints, GeneratedClass generatedClass,
829+
InjectedElement injectedElement) {
830+
831+
CodeBlock.Builder code = CodeBlock.builder();
832+
InjectionCodeGenerator injectionCodeGenerator =
833+
new InjectionCodeGenerator(generatedClass.getName(), hints);
834+
CodeBlock resourceToInject = generateResourceToInjectCode(generatedClass.getMethods(),
835+
(PersistenceElement) injectedElement);
836+
code.add(injectionCodeGenerator.generateInjectionCode(
837+
injectedElement.getMember(), INSTANCE_PARAMETER,
838+
resourceToInject));
839+
return code.build();
840+
}
841+
813842
private CodeBlock generateResourceToInjectCode(
814843
GeneratedMethods generatedMethods, PersistenceElement injectedElement) {
815844

@@ -821,7 +850,7 @@ private CodeBlock generateResourceToInjectCode(
821850
EntityManagerFactoryUtils.class, ListableBeanFactory.class,
822851
REGISTERED_BEAN_PARAMETER, unitName);
823852
}
824-
String[] methodNameParts = {"get", unitName, "EntityManager"};
853+
String[] methodNameParts = { "get", unitName, "EntityManager" };
825854
GeneratedMethod generatedMethod = generatedMethods.add(methodNameParts, method ->
826855
generateGetEntityManagerMethod(method, injectedElement));
827856
return CodeBlock.of("$L($L)", generatedMethod.getName(), REGISTERED_BEAN_PARAMETER);

spring-orm/src/test/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessorAotContributionTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.junit.jupiter.api.BeforeEach;
3333
import org.junit.jupiter.api.Test;
3434

35+
import org.springframework.aot.hint.FieldHint;
3536
import org.springframework.aot.hint.TypeReference;
3637
import org.springframework.aot.test.generate.TestGenerationContext;
3738
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
@@ -160,6 +161,28 @@ void processAheadOfTimeWhenPersistenceContextWithCustomPropertiesOnMethod() {
160161
});
161162
}
162163

164+
@Test
165+
void processAheadOfTimeWhenPersistenceContextOnPrivateFields() {
166+
RegisteredBean registeredBean = registerBean(
167+
SeveralPersistenceContextField.class);
168+
testCompile(registeredBean, (actual, compiled) -> {
169+
EntityManagerFactory entityManagerFactory = mock();
170+
this.beanFactory.registerSingleton("custom", entityManagerFactory);
171+
this.beanFactory.registerAlias("custom", "another");
172+
SeveralPersistenceContextField instance = new SeveralPersistenceContextField();
173+
actual.accept(registeredBean, instance);
174+
assertThat(instance).extracting("customEntityManager").isNotNull();
175+
assertThat(instance).extracting("anotherEntityManager").isNotNull();
176+
assertThat(this.generationContext.getRuntimeHints().reflection().typeHints())
177+
.singleElement().satisfies(typeHint -> {
178+
assertThat(typeHint.getType()).isEqualTo(
179+
TypeReference.of(SeveralPersistenceContextField.class));
180+
assertThat(typeHint.fields().map(FieldHint::getName))
181+
.containsOnly("customEntityManager", "anotherEntityManager");
182+
});
183+
});
184+
}
185+
163186
private RegisteredBean registerBean(Class<?> beanClass) {
164187
String beanName = "testBean";
165188
this.beanFactory.registerBeanDefinition(beanName,
@@ -256,4 +279,16 @@ public void setEntityManager(EntityManager entityManager) {
256279

257280
}
258281

282+
static class SeveralPersistenceContextField {
283+
284+
@SuppressWarnings("unused")
285+
@PersistenceContext(name = "custom")
286+
private EntityManager customEntityManager;
287+
288+
@SuppressWarnings("unused")
289+
@PersistenceContext(name = "another")
290+
private EntityManager anotherEntityManager;
291+
292+
}
293+
259294
}

0 commit comments

Comments
 (0)