From 151a6899a7bd5ad53a7826e6ef7c3f0958d466df Mon Sep 17 00:00:00 2001 From: Minjae Seon Date: Sat, 1 Nov 2025 01:57:28 +0900 Subject: [PATCH 1/2] HHH-18691 Add Testcase for Get Property from Embedded Record --- .../data/RecordFieldEntityTest.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/data/RecordFieldEntityTest.java diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/data/RecordFieldEntityTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/data/RecordFieldEntityTest.java new file mode 100644 index 000000000000..b96e731e6aed --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/data/RecordFieldEntityTest.java @@ -0,0 +1,146 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.envers.test.integration.data; + + +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Id; +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.Audited; +import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; +import org.hibernate.orm.test.envers.Priority; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +/** + * Tests that {@link jakarta.persistence.Embedded} works correctly when combined with Java record classes + * in the context of Envers auditing. + * + * @author Minjae Seon + */ +@JiraKey(value = "HHH-18691") +public class RecordFieldEntityTest extends BaseEnversJPAFunctionalTestCase { + record TestRecord(String foo, String bar) {} + + static class TestEmbeddedClass { + public TestEmbeddedClass() {} + public TestEmbeddedClass(String foo, String bar) { + this.foo = foo; + this.bar = bar; + } + + private String foo; + private String bar; + + public String getFoo() { + return foo; + } + + public String getBar() { + return bar; + } + + public void setFoo(String foo) { + this.foo = foo; + } + } + + @Entity + @Audited + static class WithRecord { + @Id + private Integer id; + + @Embedded + private TestRecord testRecord; + + public Integer getId() { + return id; + } + + static WithRecord of(int id, String foo, String bar) { + WithRecord withRecord = new WithRecord(); + + withRecord.id = id; + withRecord.testRecord = new TestRecord(foo, bar); + return withRecord; + } + } + + @Entity + @Audited + static class WithoutRecord { + @Id + private Integer id; + + @Embedded + private TestEmbeddedClass testEmbeddedClass; + + public Integer getId() { + return id; + } + + public TestEmbeddedClass getTestEmbeddedClass() { + return testEmbeddedClass; + } + + static WithoutRecord of(int id, String foo, String bar) { + WithoutRecord withoutRecord = new WithoutRecord(); + + withoutRecord.id = id; + withoutRecord.testEmbeddedClass = new TestEmbeddedClass(foo, bar); + + return withoutRecord; + } + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ WithRecord.class, WithoutRecord.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + em.getTransaction().begin(); + + // Create WithRecord Entity + WithRecord withRecord = WithRecord.of( 1, "foo", "bar" ); + em.persist( withRecord ); + + // Create WithoutRecord + WithoutRecord withoutRecord = WithoutRecord.of( 1, "foo", "bar" ); + em.persist( withoutRecord ); + + em.getTransaction().commit(); + + em.close(); + } + + @Test + public void testLoadRecordData() { + AuditReader auditReader = getAuditReader(); + WithRecord recordRev = auditReader.find( WithRecord.class, 1, 1 ); + + assertEquals("WithRecord.TestRecord.foo equals foo", "foo", recordRev.testRecord.foo()); + assertEquals("WithRecord.TestRecord.bar equals bar", "bar", recordRev.testRecord.bar()); + } + + @Test + public void testLoadWithoutRecordData() { + AuditReader auditReader = getAuditReader(); + WithoutRecord withoutRecordRev = auditReader.find( WithoutRecord.class, 1, 1 ); + + assertEquals("WithoutRecord.TestEmbeddedClass.foo equals foo", "foo", withoutRecordRev.getTestEmbeddedClass().getFoo()); + assertEquals("WithoutRecord.TestEmbeddedClass.bar equals bar", "bar", withoutRecordRev.getTestEmbeddedClass().getBar()); + } +} From 433fc1447710d9eae35f635c645e8d6e7f3fb02f Mon Sep 17 00:00:00 2001 From: Minjae Seon Date: Sat, 1 Nov 2025 02:09:40 +0900 Subject: [PATCH 2/2] HHH-18691 Add Record Classes Support to ComponentMetadataGenerator --- .../metadata/ComponentMetadataGenerator.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java index c9174f00e4fc..19d30558e586 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/ComponentMetadataGenerator.java @@ -18,6 +18,8 @@ import org.hibernate.mapping.Value; import org.hibernate.metamodel.internal.EmbeddableCompositeUserTypeInstantiator; import org.hibernate.metamodel.internal.EmbeddableInstantiatorPojoIndirecting; +import org.hibernate.metamodel.internal.EmbeddableInstantiatorRecordIndirecting; +import org.hibernate.metamodel.internal.EmbeddableInstantiatorRecordStandard; import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.usertype.CompositeUserType; @@ -84,6 +86,18 @@ else if ( propComponent.getInstantiator() != null ) { propComponent.getInstantiatorPropertyNames() ); } + else if ( propComponent.getComponentClass() != null && + propComponent.getComponentClass().isRecord() ) { + if ( propComponent.sortProperties() == null ) { + instantiator = new EmbeddableInstantiatorRecordStandard( propComponent.getComponentClass() ); + } + else { + instantiator = EmbeddableInstantiatorRecordIndirecting.of( + propComponent.getComponentClass(), + propComponent.getPropertyNames() + ); + } + } else { instantiator = null; }