diff --git a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle index 540135826674..4de95ebbc31c 100644 --- a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle +++ b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle @@ -43,6 +43,40 @@ compileTestJava { } +// Tests with records +if ( gradle.ext.javaVersions.test.release.asInt() >= 17 && gradle.ext.javaToolchainEnabled ) { + // We need to configure the source and target version to 17 + //compileTestJava17Java { + compileTestJava { + javaCompiler = javaToolchains.compilerFor { + languageVersion = gradle.ext.javaVersions.test.compiler + } + sourceCompatibility = 17 + targetCompatibility = 17 + } + + test { + javaLauncher = javaToolchains.launcherFor { + languageVersion = gradle.ext.javaVersions.test.launcher + } + + if ( gradle.ext.javaVersions.test.launcher.asInt() >= 19 ) { + logger.warn( "The version of Java bytecode that will be tested is not supported by Bytebuddy by default. " + + " Setting 'net.bytebuddy.experimental=true'." ) + systemProperty 'net.bytebuddy.experimental', true + } + } +} else { + sourceSets { + test { + java { + exclude '**/records/*.java' + } + } + } +} + + task jaxb { // configure Gradle up-to-date checking inputs.dir( xsdDir ).withPropertyName("xsdDir" ).withPathSensitivity( PathSensitivity.RELATIVE ) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java index cb713288a3eb..60d2a3a97479 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java @@ -221,7 +221,7 @@ private boolean isJPAEntity(Element element) { private void handleRootElementAnnotationMirrors(final Element element) { List annotationMirrors = element.getAnnotationMirrors(); for ( AnnotationMirror mirror : annotationMirrors ) { - if ( !ElementKind.CLASS.equals( element.getKind() ) ) { + if ( !element.getKind().isClass() || ElementKind.ENUM.equals( element.getKind() ) ) { continue; } @@ -252,7 +252,7 @@ private void handleRootElementAnnotationMirrors(final Element element) { private MetaEntity tryGettingExistingEntityFromContext(AnnotationMirror mirror, String fqn) { MetaEntity alreadyExistingMetaEntity = null; if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ENTITY ) - || TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS )) { + || TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS ) ) { alreadyExistingMetaEntity = context.getMetaEntity( fqn ); } else if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.EMBEDDABLE ) ) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java index d9f04df947ea..8e2f64bc6365 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java @@ -313,7 +313,8 @@ public Boolean visitDeclared(DeclaredType declaredType, Element element) { return Boolean.TRUE; } - if ( ElementKind.CLASS.equals( element.getKind() ) || ElementKind.INTERFACE.equals( element.getKind() ) ) { + if ( ( element.getKind().isClass() && !ElementKind.ENUM.equals( element.getKind() ) ) || + ElementKind.INTERFACE.equals( element.getKind() ) ) { TypeElement typeElement = ( (TypeElement) element ); String typeName = typeElement.getQualifiedName().toString(); if ( Constants.BASIC_TYPES.contains( typeName ) ) { diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Address.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Address.java new file mode 100644 index 000000000000..89853206cc35 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Address.java @@ -0,0 +1,7 @@ +package org.hibernate.jpamodelgen.test.records; + +import jakarta.persistence.Embeddable; + +@Embeddable +public record Address(String street, String city, String postalCode) { +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Author.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Author.java new file mode 100644 index 000000000000..2a95f3855864 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Author.java @@ -0,0 +1,53 @@ +package org.hibernate.jpamodelgen.test.records; + +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity +public class Author { + + @Id + @GeneratedValue + private Long id; + + @Embedded + private Address address; + + private String firstName; + + private String lastName; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Java14RecordsTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Java14RecordsTest.java new file mode 100644 index 000000000000..4bccbd5d73d6 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/records/Java14RecordsTest.java @@ -0,0 +1,52 @@ +package org.hibernate.jpamodelgen.test.records; + +import jakarta.persistence.metamodel.SingularAttribute; +import org.hibernate.jpamodelgen.test.util.CompilationTest; +import org.hibernate.jpamodelgen.test.util.TestForIssue; +import org.hibernate.jpamodelgen.test.util.WithClasses; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; + +import static java.lang.reflect.Modifier.isStatic; +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor; +import static org.hibernate.jpamodelgen.test.util.TestUtil.getFieldFromMetamodelFor; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class Java14RecordsTest extends CompilationTest { + + @Test + @TestForIssue(jiraKey = "HHH-16261") + @WithClasses({Address.class, Author.class}) + public void testEmbeddableRecordProperty() { + assertMetamodelClassGeneratedFor(Address.class); + for (final String fieldName : List.of("street", "city", "postalCode")) { + assertNotNull("Address must contain '" + fieldName + "' field", getFieldFromMetamodelFor(Address.class, fieldName)); + } + assertMetamodelClassGeneratedFor(Author.class); + + final Field addressField = getFieldFromMetamodelFor(Author.class, "address"); + assertNotNull("Author must contain 'address' field", addressField); + assertTrue(isStatic(addressField.getModifiers())); + if (addressField.getGenericType() instanceof ParameterizedType parameterizedType) { + assertEquals(SingularAttribute.class, parameterizedType.getRawType()); + final Type[] typeArguments = parameterizedType.getActualTypeArguments(); + assertEquals(2, typeArguments.length); + assertEquals(Author.class, typeArguments[0]); + assertEquals(Address.class, typeArguments[1]); + } else { + fail("Address field must be instance of ParameterizedType"); + } + + final Field addressNameField = getFieldFromMetamodelFor(Author.class, "address".toUpperCase()); + assertNotNull("Author must contain 'ADDRESS' field", addressNameField); + assertTrue(isStatic(addressNameField.getModifiers())); + assertEquals(String.class, addressNameField.getGenericType()); + } +}