Skip to content

Commit

Permalink
Merge pull request #35319 from yrodiere/i34071-uuid-array
Browse files Browse the repository at this point in the history
Register arrays of Hibernate ORM's JDBC basic types for reflection
  • Loading branch information
yrodiere committed Aug 14, 2023
2 parents 14763e3 + 70d7a6b commit 8009695
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,13 @@ 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(HibernateOrmTypes.JDBC_JAVA_TYPES.stream().map(d -> d.toString() + "[]").toArray(String[]::new))
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,10 @@ public void preGenAnnotationProxies(List<PersistenceUnitDescriptorBuildItem> per
// but there are plans to make deep changes to XML mapping in ORM (to rely on Jandex directly),
// so let's not waste our time on optimizations that won't be relevant in a few months.
List<String> annotationClassNames = new ArrayList<>();
for (DotName name : HibernateOrmAnnotations.JPA_MAPPING_ANNOTATIONS) {
for (DotName name : HibernateOrmTypes.JPA_MAPPING_ANNOTATIONS) {
annotationClassNames.add(name.toString());
}
for (DotName name : HibernateOrmAnnotations.HIBERNATE_MAPPING_ANNOTATIONS) {
for (DotName name : HibernateOrmTypes.HIBERNATE_MAPPING_ANNOTATIONS) {
annotationClassNames.add(name.toString());
}
reflective.produce(ReflectiveClassBuildItem.builder(annotationClassNames.toArray(new String[0]))
Expand Down Expand Up @@ -766,7 +766,7 @@ public void registerInjectServiceMethodsForReflection(CombinedIndexBuildItem ind
Set<String> classes = new HashSet<>();

// Built-in service classes; can't rely on Jandex as Hibernate ORM is not indexed by default.
HibernateOrmAnnotations.ANNOTATED_WITH_INJECT_SERVICE.stream()
HibernateOrmTypes.ANNOTATED_WITH_INJECT_SERVICE.stream()
.map(DotName::toString)
.forEach(classes::add);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

import org.jboss.jandex.DotName;

public final class HibernateOrmAnnotations {
public final class HibernateOrmTypes {

private HibernateOrmAnnotations() {
private HibernateOrmTypes() {
}

public static final List<DotName> PACKAGE_ANNOTATIONS = List.of(
Expand Down Expand Up @@ -316,4 +316,41 @@ private HibernateOrmAnnotations() {
DotName.createSimple("jakarta.persistence.PreRemove"),
DotName.createSimple("jakarta.persistence.PreUpdate"));

public static final List<DotName> JDBC_JAVA_TYPES = List.of(
DotName.createSimple("java.lang.Boolean"),
DotName.createSimple("java.lang.Byte"),
DotName.createSimple("java.lang.Character"),
DotName.createSimple("java.lang.Class"),
DotName.createSimple("java.lang.Double"),
DotName.createSimple("java.lang.Float"),
DotName.createSimple("java.lang.Integer"),
DotName.createSimple("java.lang.Long"),
DotName.createSimple("java.lang.Object"),
DotName.createSimple("java.lang.Short"),
DotName.createSimple("java.lang.String"),
DotName.createSimple("java.math.BigDecimal"),
DotName.createSimple("java.math.BigInteger"),
DotName.createSimple("java.net.InetAddress"),
DotName.createSimple("java.net.URL"),
DotName.createSimple("java.sql.Blob"),
DotName.createSimple("java.sql.Clob"),
DotName.createSimple("java.sql.NClob"),
DotName.createSimple("java.time.Duration"),
DotName.createSimple("java.time.Instant"),
DotName.createSimple("java.time.LocalDate"),
DotName.createSimple("java.time.LocalDateTime"),
DotName.createSimple("java.time.LocalTime"),
DotName.createSimple("java.time.OffsetDateTime"),
DotName.createSimple("java.time.OffsetTime"),
DotName.createSimple("java.time.Year"),
DotName.createSimple("java.time.ZoneId"),
DotName.createSimple("java.time.ZoneOffset"),
DotName.createSimple("java.time.ZonedDateTime"),
DotName.createSimple("java.util.Calendar"),
DotName.createSimple("java.util.Currency"),
DotName.createSimple("java.util.Date"),
DotName.createSimple("java.util.Locale"),
DotName.createSimple("java.util.Map$Entry"),
DotName.createSimple("java.util.TimeZone"),
DotName.createSimple("java.util.UUID"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public final class JpaJandexScavenger {
public JpaModelBuildItem discoverModelAndRegisterForReflection() {
Collector collector = new Collector();

for (DotName packageAnnotation : HibernateOrmAnnotations.PACKAGE_ANNOTATIONS) {
for (DotName packageAnnotation : HibernateOrmTypes.PACKAGE_ANNOTATIONS) {
enlistJPAModelAnnotatedPackages(collector, packageAnnotation);
}
enlistJPAModelClasses(collector, ClassNames.JPA_ENTITY);
Expand All @@ -95,7 +95,7 @@ public JpaModelBuildItem discoverModelAndRegisterForReflection() {
enlistEmbeddedsAndElementCollections(collector);

enlistPotentialCdiBeanClasses(collector, ClassNames.CONVERTER);
for (DotName annotation : HibernateOrmAnnotations.JPA_LISTENER_ANNOTATIONS) {
for (DotName annotation : HibernateOrmTypes.JPA_LISTENER_ANNOTATIONS) {
enlistPotentialCdiBeanClasses(collector, annotation);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
Expand All @@ -24,17 +25,22 @@
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Type;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import io.quarkus.deployment.index.IndexWrapper;
import io.quarkus.deployment.index.IndexingUtil;
import io.quarkus.deployment.index.PersistentClassIndex;
import io.quarkus.deployment.util.JandexUtil;
import io.quarkus.hibernate.orm.deployment.ClassNames;
import io.quarkus.hibernate.orm.deployment.HibernateOrmAnnotations;
import io.quarkus.hibernate.orm.deployment.HibernateOrmTypes;

/**
* Test that hardcoded lists of Hibernate ORM annotations stay up-to-date.
* Test that hardcoded lists of Hibernate ORM types stay up-to-date.
*/
public class HibernateOrmAnnotationsTest {
public class HibernateOrmTypesTest {

private static final DotName RETENTION = DotName.createSimple(Retention.class.getName());
private static final DotName TARGET = DotName.createSimple(Target.class.getName());
Expand All @@ -53,7 +59,7 @@ public void testNoMissingJpaAnnotation() {
Set<DotName> jpaMappingAnnotations = findRuntimeAnnotations(jpaIndex);
jpaMappingAnnotations.removeIf(name -> name.toString().startsWith("jakarta.persistence.metamodel."));

assertThat(HibernateOrmAnnotations.JPA_MAPPING_ANNOTATIONS)
assertThat(HibernateOrmTypes.JPA_MAPPING_ANNOTATIONS)
.containsExactlyInAnyOrderElementsOf(jpaMappingAnnotations);
}

Expand All @@ -65,7 +71,7 @@ public void testNoMissingJpaListenerAnnotation() {
.filter(name -> listenerAnnotationNamePattern.matcher(name.toString()).matches())
.collect(Collectors.toSet());

assertThat(HibernateOrmAnnotations.JPA_LISTENER_ANNOTATIONS)
assertThat(HibernateOrmTypes.JPA_LISTENER_ANNOTATIONS)
.containsExactlyInAnyOrderElementsOf(jpaMappingAnnotations);
}

Expand All @@ -76,7 +82,7 @@ public void testNoMissingHibernateAnnotation() {
hibernateMappingAnnotations.removeIf(name -> name.toString().contains(".spi."));
ignoreInternalAnnotations(hibernateMappingAnnotations);

assertThat(HibernateOrmAnnotations.HIBERNATE_MAPPING_ANNOTATIONS)
assertThat(HibernateOrmTypes.HIBERNATE_MAPPING_ANNOTATIONS)
.containsExactlyInAnyOrderElementsOf(hibernateMappingAnnotations);
}

Expand All @@ -93,7 +99,7 @@ public void testNoMissingPackageLevelAnnotation() {
packageLevelHibernateAnnotations.removeIf(name -> name.toString().contains(".internal."));
ignoreInternalAnnotations(packageLevelHibernateAnnotations);

assertThat(HibernateOrmAnnotations.PACKAGE_ANNOTATIONS)
assertThat(HibernateOrmTypes.PACKAGE_ANNOTATIONS)
.containsExactlyInAnyOrderElementsOf(packageLevelHibernateAnnotations);
}

Expand All @@ -102,10 +108,33 @@ public void testNoMissingInjectServiceAnnotatedClass() {
Set<DotName> injectServiceAnnotatedClasses = findClassesWithMethodsAnnotatedWith(hibernateIndex,
ClassNames.INJECT_SERVICE);

assertThat(HibernateOrmAnnotations.ANNOTATED_WITH_INJECT_SERVICE)
assertThat(HibernateOrmTypes.ANNOTATED_WITH_INJECT_SERVICE)
.containsExactlyInAnyOrderElementsOf(injectServiceAnnotatedClasses);
}

@Test
public void testNoMissingJdbcJavaTypeClass() {
Set<DotName> jdbcJavaTypeNames = new TreeSet<>();
DotName basicJavaTypeName = DotName.createSimple("org.hibernate.type.descriptor.java.BasicJavaType");
IndexView hibernateAndJdkIndex = new IndexWrapper(hibernateIndex, Thread.currentThread().getContextClassLoader(),
new PersistentClassIndex());

for (ClassInfo basicJavaTypeImplInfo : hibernateIndex.getAllKnownImplementors(basicJavaTypeName)) {
if (Modifier.isAbstract(basicJavaTypeImplInfo.flags())) {
continue;
}
List<Type> typeParams = JandexUtil.resolveTypeParameters(basicJavaTypeImplInfo.name(), basicJavaTypeName,
hibernateAndJdkIndex);
Type jdbcJavaType = typeParams.get(0);
if (jdbcJavaType.kind() == Type.Kind.CLASS) {
jdbcJavaTypeNames.add(jdbcJavaType.name());
}
}

assertThat(HibernateOrmTypes.JDBC_JAVA_TYPES)
.containsExactlyInAnyOrderElementsOf(jdbcJavaTypeNames);
}

private Set<DotName> findRuntimeAnnotations(Index index) {
Set<DotName> annotations = new HashSet<>();
for (AnnotationInstance retentionAnnotation : index.getAnnotations(RETENTION)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ private static void doStuffWithHibernate(EntityManagerFactory entityManagerFacto

deleteAllPerson(entityManagerFactory);

// Try an entity using a UUID
verifyUUIDEntity(entityManagerFactory);
}

private static void verifyJPANamedQuery(final EntityManagerFactory emf) {
Expand Down Expand Up @@ -144,6 +146,27 @@ private static String randomName() {
return UUID.randomUUID().toString();
}

private static void verifyUUIDEntity(final EntityManagerFactory emf) {
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
MyUUIDEntity myEntity = new MyUUIDEntity();
myEntity.setName("George");
em.persist(myEntity);
transaction.commit();
em.close();

em = emf.createEntityManager();
transaction = em.getTransaction();
transaction.begin();
myEntity = em.find(MyUUIDEntity.class, myEntity.getId());
if (myEntity == null || !"George".equals(myEntity.getName())) {
throw new RuntimeException("Incorrect loaded MyUUIDEntity " + myEntity);
}
transaction.commit();
em.close();
}

private void reportException(String errorMessage, final Exception e, final HttpServletResponse resp) throws IOException {
final PrintWriter writer = resp.getWriter();
if (errorMessage != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.quarkus.it.jpa.postgresql;

import java.util.UUID;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity(name = "uuidentity")
public class MyUUIDEntity {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String name;

public MyUUIDEntity() {
}

public MyUUIDEntity(UUID id, String name) {
this.id = id;
this.name = name;
}

public UUID getId() {
return id;
}

public void setId(UUID id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

0 comments on commit 8009695

Please sign in to comment.