From e0129a36318333825bd43bf91c5a52b7e2da2b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 18 Oct 2022 22:51:39 +0200 Subject: [PATCH] Register reflection hints for JPA persistence callbacks Closes gh-29348 --- ...agedTypesBeanRegistrationAotProcessor.java | 22 +++++++++++++++++++ .../orm/jpa/domain/Employee.java | 5 +++++ ...ypesBeanRegistrationAotProcessorTests.java | 2 ++ 3 files changed, 29 insertions(+) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessor.java index 5f4a99b4f15..c7b260daa27 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessor.java @@ -16,7 +16,9 @@ package org.springframework.orm.jpa.persistenceunit; +import java.lang.annotation.Annotation; import java.lang.reflect.Executable; +import java.util.Arrays; import java.util.List; import javax.lang.model.element.Modifier; @@ -24,11 +26,20 @@ import javax.lang.model.element.Modifier; import jakarta.persistence.Converter; import jakarta.persistence.EntityListeners; import jakarta.persistence.IdClass; +import jakarta.persistence.PostLoad; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostRemove; +import jakarta.persistence.PostUpdate; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.BindingReflectionHintsRegistrar; +import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; @@ -41,6 +52,7 @@ import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.ParameterizedTypeName; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; /** * {@link BeanRegistrationAotProcessor} implementations for persistence managed @@ -55,6 +67,9 @@ import org.springframework.util.ClassUtils; */ class PersistenceManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor { + private static final List> CALLBACK_TYPES = Arrays.asList(PreUpdate.class, + PostUpdate.class, PrePersist.class, PostPersist.class, PreRemove.class, PostRemove.class, PostLoad.class); + @Nullable @Override public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { @@ -115,6 +130,7 @@ class PersistenceManagedTypesBeanRegistrationAotProcessor implements BeanRegistr contributeEntityListenersHints(hints, managedClass); contributeIdClassHints(hints, managedClass); contributeConverterHints(hints, managedClass); + contributeCallbackHints(hints, managedClass); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException("Failed to instantiate the managed class: " + managedClassName, ex); @@ -145,5 +161,11 @@ class PersistenceManagedTypesBeanRegistrationAotProcessor implements BeanRegistr } } + private void contributeCallbackHints(RuntimeHints hints, Class managedClass) { + ReflectionHints reflection = hints.reflection(); + ReflectionUtils.doWithMethods(managedClass, method -> + reflection.registerMethod(method, ExecutableMode.INVOKE), + method -> CALLBACK_TYPES.stream().anyMatch(method::isAnnotationPresent)); + } } } diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/domain/Employee.java b/spring-orm/src/test/java/org/springframework/orm/jpa/domain/Employee.java index 496b14e3152..602fb096c60 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/domain/Employee.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/domain/Employee.java @@ -20,6 +20,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.IdClass; +import jakarta.persistence.PreRemove; @Entity @IdClass(EmployeeId.class) @@ -59,4 +60,8 @@ public class Employee { public void setLocation(EmployeeLocation location) { this.location = location; } + + @PreRemove + public void preRemove() { + } } diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessorTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessorTests.java index cb9d5264acf..c40cb8cdfa0 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessorTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/persistenceunit/PersistenceManagedTypesBeanRegistrationAotProcessorTests.java @@ -91,6 +91,8 @@ class PersistenceManagedTypesBeanRegistrationAotProcessorTests { .accepts(hints); assertThat(RuntimeHintsPredicates.reflection().onType(Employee.class) .withMemberCategories(MemberCategory.DECLARED_FIELDS)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onMethod(Employee.class, "preRemove")) + .accepts(hints); assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeId.class) .withMemberCategories(MemberCategory.DECLARED_FIELDS)).accepts(hints); assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeLocationConverter.class)