Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register arrays of Hibernate ORM's JDBC basic types for reflection #35319

Merged
merged 2 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}