Skip to content

Commit 5bf4f4c

Browse files
committed
Automatically register custom value generators for reflection
1 parent f975bbc commit 5bf4f4c

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ClassNames.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ private static DotName createConstant(String fqcn) {
6262
public static final DotName HIBERNATE_CACHE = createConstant("org.hibernate.Cache");
6363
public static final DotName PERSISTENCE_UNIT_UTIL = createConstant("jakarta.persistence.PersistenceUnitUtil");
6464

65+
public static final DotName GENERIC_GENERATOR = createConstant("org.hibernate.annotations.GenericGenerator");
66+
public static final DotName ID_GENERATOR_TYPE = createConstant("org.hibernate.annotations.IdGeneratorType");
67+
public static final DotName VALUE_GENERATION_TYPE = createConstant("org.hibernate.annotations.ValueGenerationType");
68+
6569
public static final DotName INTERCEPTOR = createConstant("org.hibernate.Interceptor");
6670
public static final DotName STATEMENT_INSPECTOR = createConstant("org.hibernate.resource.jdbc.spi.StatementInspector");
6771
public static final DotName FORMAT_MAPPER = createConstant("org.hibernate.type.format.FormatMapper");

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ NativeImageFeatureBuildItem staticNativeImageFeature() {
2626

2727
// TODO try to limit registration to those that are actually needed, based on configuration + mapping.
2828
// https://github.com/quarkusio/quarkus/pull/32433#issuecomment-1497615958
29+
// See also io.quarkus.hibernate.orm.deployment.JpaJandexScavenger.enlistClassReferences for
30+
// the beginning of a solution (which only handles custom types, not references by name such as 'sequence').
2931
@BuildStep
3032
ReflectiveClassBuildItem registerGeneratorAndOptimizerClassesForReflections() {
3133
return ReflectiveClassBuildItem

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/JpaJandexScavenger.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildExc
9494
enlistPotentialCdiBeanClasses(collector, annotation);
9595
}
9696

97+
enlistPotentialClassReferences(collector, ClassNames.GENERIC_GENERATOR, "type", "strategy");
98+
enlistPotentialClassReferences(collector, ClassNames.ID_GENERATOR_TYPE, "value");
99+
enlistPotentialClassReferences(collector, ClassNames.VALUE_GENERATION_TYPE, "generatedBy");
100+
97101
for (JpaModelPersistenceUnitContributionBuildItem persistenceUnitContribution : persistenceUnitContributions) {
98102
enlistExplicitMappings(collector, persistenceUnitContribution);
99103
}
@@ -214,10 +218,16 @@ private void enlistOrmXmlMappingManagedClass(Collector collector, String package
214218
String nodeName) {
215219
String name = safeGetClassName(packagePrefix, managed, nodeName);
216220
enlistExplicitClass(collector, name);
217-
if (managed instanceof JaxbEntity) {
221+
if (managed instanceof JaxbEntity entity) {
218222
// The call to 'enlistExplicitClass' above may not
219223
// detect that this class is an entity if it is not annotated
220224
collector.entityTypes.add(name);
225+
226+
// Generators may be instantiated reflectively
227+
if (entity.getGenericGenerator() != null) {
228+
var generator = entity.getGenericGenerator();
229+
enlistPotentialClassReference(collector, generator == null ? null : generator.getClazz());
230+
}
221231
}
222232

223233
enlistOrmXmlMappingListeners(collector, packagePrefix, managed.getEntityListenerContainer());
@@ -437,6 +447,47 @@ private void enlistPotentialCdiBeanClasses(Collector collector, DotName dotName)
437447
}
438448
}
439449

450+
private void enlistPotentialClassReferences(Collector collector, DotName dotName, String... referenceAttributes) {
451+
Collection<AnnotationInstance> jpaAnnotations = index.getAnnotations(dotName);
452+
453+
if (jpaAnnotations == null) {
454+
return;
455+
}
456+
457+
for (AnnotationInstance annotation : jpaAnnotations) {
458+
for (String referenceAttribute : referenceAttributes) {
459+
var referenceValue = annotation.value(referenceAttribute);
460+
if (referenceValue == null) {
461+
continue;
462+
}
463+
String reference = switch (referenceValue.kind()) {
464+
case CLASS -> referenceValue.asClass().name().toString();
465+
case STRING -> {
466+
String stringRef = referenceValue.asString();
467+
if (stringRef.isEmpty() || index.getClassByName(stringRef) == null) {
468+
// No reference, or reference to a built-in strategy name like 'sequence'
469+
// (which we can't resolve here and handle through GraalVMFeatures.registerGeneratorAndOptimizerClassesForReflections)
470+
yield null;
471+
}
472+
yield stringRef;
473+
}
474+
default -> null;
475+
};
476+
enlistPotentialClassReference(collector, reference);
477+
}
478+
}
479+
}
480+
481+
/**
482+
* Add the class to the reflective list with only constructor and method access.
483+
*/
484+
private void enlistPotentialClassReference(Collector collector, String reference) {
485+
if (reference == null) {
486+
return;
487+
}
488+
collector.javaTypes.add(reference);
489+
}
490+
440491
/**
441492
* Add the class to the reflective list with full method and field access.
442493
* Add the superclasses recursively as well as the interfaces.

0 commit comments

Comments
 (0)