Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
yrodiere committed Dec 20, 2024
1 parent 3ac7045 commit c640246
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ private static DotName createConstant(String fqcn) {
public static final DotName ID_CLASS = createConstant("jakarta.persistence.IdClass");
public static final DotName CONVERTER = createConstant("jakarta.persistence.Converter");
public static final DotName EMBEDDED = createConstant("jakarta.persistence.Embedded");
public static final DotName ID = createConstant("jakarta.persistence.Id");
public static final DotName EMBEDDED_ID = createConstant("jakarta.persistence.EmbeddedId");
public static final DotName ELEMENT_COLLECTION = createConstant("jakarta.persistence.ElementCollection");
public static final DotName PROXY = createConstant("org.hibernate.annotations.Proxy");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,6 @@ ReflectiveClassBuildItem registerGeneratorClassesForReflections() {
.build();
}

// Workaround for https://hibernate.atlassian.net/browse/HHH-16809
// See https://github.com/hibernate/hibernate-orm/pull/6815#issuecomment-1662197545
@BuildStep
ReflectiveClassBuildItem registerJdbcArrayTypesForReflection() {
return ReflectiveClassBuildItem
.builder(ClassNames.JDBC_JAVA_TYPES.stream().map(d -> d.toString() + "[]").toArray(String[]::new))
.reason(ClassNames.GRAAL_VM_FEATURES.toString())
.build();
}

// Workaround for https://hibernate.atlassian.net/browse/HHH-18875
// See https://hibernate.zulipchat.com/#narrow/channel/132094-hibernate-orm-dev/topic/StandardStack.20and.20reflection
@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,27 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import io.quarkus.deployment.bean.JavaBeanUtil;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmCompositeAttributeType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmDiscriminatorSubclassEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmEntityBaseDefinition;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmJoinedSubclassEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmRootEntityType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmUnionSubclassEntityType;
import org.hibernate.boot.jaxb.mapping.AttributesContainer;
import org.hibernate.boot.jaxb.mapping.JaxbAttributes;
import org.hibernate.boot.jaxb.mapping.EntityOrMappedSuperclass;
import org.hibernate.boot.jaxb.mapping.JaxbConverter;
import org.hibernate.boot.jaxb.mapping.JaxbEmbeddable;
import org.hibernate.boot.jaxb.mapping.JaxbEntity;
import org.hibernate.boot.jaxb.mapping.JaxbEntityListener;
import org.hibernate.boot.jaxb.mapping.JaxbEntityListeners;
import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings;
import org.hibernate.boot.jaxb.mapping.JaxbId;
import org.hibernate.boot.jaxb.mapping.JaxbMappedSuperclass;
import org.hibernate.boot.jaxb.mapping.JaxbPersistenceUnitDefaults;
import org.hibernate.boot.jaxb.mapping.JaxbPersistenceUnitMetadata;
Expand Down Expand Up @@ -60,6 +65,7 @@
*/
public final class JpaJandexScavenger {

public static final List<DotName> ID_ATTRIBUTE_ANNOTATIONS = Arrays.asList(ClassNames.ID, ClassNames.EMBEDDED_ID);
public static final List<DotName> EMBEDDED_ANNOTATIONS = Arrays.asList(ClassNames.EMBEDDED_ID, ClassNames.EMBEDDED);

private static final String XML_MAPPING_DEFAULT_ORM_XML = "META-INF/orm.xml";
Expand Down Expand Up @@ -110,6 +116,27 @@ public JpaModelBuildItem discoverModelAndRegisterForReflection() throws BuildExc
reflectiveClass.produce(ReflectiveClassBuildItem.builder(className).methods().fields().build());
}

// Creating an array of IDs doesn't seem far-fetched; Hibernate ORM seems to be doing it in a few places.
// Ideally it'd use Object[] in these cases, but we're not quite there yet.
// See https://hibernate.atlassian.net/browse/HHH-16809
// and https://hibernate.atlassian.net/browse/HHH-18976,
// which upon fixing may or may not make this unnecessary.
for (String className : collector.idTypes) {
reflectiveClass.produce(ReflectiveClassBuildItem.builder(className + "[]").constructors().build());
}

// Workaround for https://hibernate.atlassian.net/browse/HHH-16809
// Workaround for https://hibernate.atlassian.net/browse/HHH-18976
// Interestingly, when using @IdClass,
// the follow code seems to result in creating an array whose element type is the entity type... ?
// https://github.com/hibernate/hibernate-orm/blob/1aac8c356ecbd07adad035cb402bd97e8025e43c/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java#L49
// So, we'll assume in the case of IdClass, the entity type is sometimes used as the ID type.
// Note a relevant test in Quarkus is the one involving io.quarkus.it.jpa.generics.SnapshotEventEntry:
// it fails without this piece of code
for (String className : collector.entityTypes) {
reflectiveClass.produce(ReflectiveClassBuildItem.builder(className + "[]").constructors().build());
}

if (!collector.enumTypes.isEmpty()) {
reflectiveClass.produce(ReflectiveClassBuildItem.builder("java.lang.Enum").methods().build());
for (String className : collector.enumTypes) {
Expand Down Expand Up @@ -225,9 +252,31 @@ private void enlistOrmXmlMappingManagedClass(Collector collector, String package
enlistIdClass(collector, DotName.createSimple(qualifyIfNecessary(packagePrefix, idClass.getClazz())));
}

enlistOrmXmlMappingAttributes(collector, packagePrefix, entityClassName, managed.getAttributes());

enlistOrmXmlMappingListeners(collector, packagePrefix, managed.getEntityListeners());
}

private void enlistOrmXmlMappingAttributes(Collector collector, String packagePrefix, String entityClassName,
AttributesContainer attributes) {
if (attributes instanceof JaxbAttributes) {
for (JaxbId idAttribute : ((JaxbAttributes) attributes).getId()) {
ClassInfo classInfo = index.getClassByName(entityClassName);
collectAttributeTypes(collector.idTypes::add, classInfo, idAttribute.getName());
}
}
}

private void collectAttributeTypes(Consumer<String> typeCollector, ClassInfo classInfo, String name) {
// We'll look for all possible sources of information;
// most likely only one will match, and worst case adding too many types won't change much to image size.
var method = classInfo.method(JavaBeanUtil.getGetterName(name, "Z"));
if (method != null) {
typeCollector.accept(getRawTypeName(method.returnType()));
}

}

private void enlistOrmXmlMappingListeners(Collector collector, String packagePrefix, JaxbEntityListeners entityListeners) {
if (entityListeners == null) {
return;
Expand Down Expand Up @@ -372,6 +421,36 @@ private void enlistEmbeddedsAndElementCollections(Collector collector) throws Bu
}
}

private void enlistIdTypes(Collector collector) throws BuildException {
Set<DotName> embeddedTypes = new HashSet<>();

for (DotName idAnnotation : ID_ATTRIBUTE_ANNOTATIONS) {
for (AnnotationInstance annotation : index.getAnnotations(idAnnotation)) {
AnnotationTarget target = annotation.target();

switch (target.kind()) {
case FIELD:
var field = target.asField();
collectIdAttributeType(embeddedTypes, field.type());
break;
case METHOD:
var method = target.asMethod();
if (method.isBridge()) {
// Generated by javac for covariant return type override.
// There's another method with a more specific return type, ignore this one.
continue;
}
collectIdAttributeType(embeddedTypes, method.returnType());
break;
default:
throw new IllegalStateException(
"[internal error] " + idAnnotation + " placed on a unknown element: " + target);
}

}
}
}

private void enlistJPAModelAnnotatedPackages(Collector collector, DotName dotName) {
Collection<AnnotationInstance> jpaAnnotations = index.getAnnotations(dotName);

Expand Down Expand Up @@ -422,6 +501,7 @@ private void enlistJPAModelIdClasses(Collector collector, DotName dotName) {
private void enlistIdClass(Collector collector, DotName idClass) {
addClassHierarchyToReflectiveList(collector, idClass);
collector.modelTypes.add(idClass.toString());
collector.idTypes.add(idClass.toString());
}

private void enlistPotentialCdiBeanClasses(Collector collector, DotName dotName) {
Expand Down Expand Up @@ -519,19 +599,23 @@ private static void collectModelType(Collector collector, ClassInfo modelClass)
}
}

private void collectEmbeddedType(Set<DotName> embeddedTypes, ClassInfo declaringClass,
Declaration attribute, Type attributeType, boolean validate)
throws BuildException {
DotName className;
private void collectIdAttributeType(Set<DotName> idTypes, Type attributeType) {
switch (attributeType.kind()) {
case CLASS:
className = attributeType.asClassType().name();
break;
case PARAMETERIZED_TYPE:
className = attributeType.name();
idTypes.add(attributeType.name());
break;
default:
// do nothing
}
}

private void collectEmbeddedType(Set<DotName> embeddedTypes, ClassInfo declaringClass,
Declaration attribute, Type attributeType, boolean validate)
throws BuildException {
DotName className = getRawTypeName(attributeType);
if (className == null) {
// do nothing
return;
}
if (validate && !index.getClassByName(className).hasAnnotation(ClassNames.EMBEDDABLE)) {
Expand Down Expand Up @@ -568,6 +652,19 @@ private void collectElementCollectionTypes(Set<DotName> embeddedTypes, ClassInfo
}
}

private String getRawTypeName(Type type) {
switch (attributeType.kind()) {
case CLASS:
className = attributeType.asClassType().name();
break;
case PARAMETERIZED_TYPE:
className = attributeType.name();
break;
default:
return null;
}
}

private static boolean isIgnored(DotName classDotName) {
String className = classDotName.toString();
if (className.startsWith("java.util.") || className.startsWith("java.lang.")
Expand All @@ -590,6 +687,7 @@ private static boolean isInJavaPackage(DotName classDotName) {
private static class Collector {
final Set<String> packages = new HashSet<>();
final Set<String> entityTypes = new HashSet<>();
final Set<String> idTypes = new HashSet<>();
final Set<DotName> potentialCdiBeanTypes = new HashSet<>();
final Set<String> modelTypes = new HashSet<>();
final Set<String> enumTypes = new HashSet<>();
Expand Down

0 comments on commit c640246

Please sign in to comment.