diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java index 3f6f5c999e38..27d92325d230 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java @@ -464,6 +464,11 @@ else if (methodName.startsWith("get") || // convert field letter to lower case methodFieldName = methodFieldName.substring(0, 1).toLowerCase() + methodFieldName.substring(1); TypeList typeList = methodDescription.getDeclaredAnnotations().asTypeList(); + if (typeList.stream().anyMatch(typeDefinitions -> + (typeDefinitions.getName().equals("jakarta.persistence.Transient")))) { + // transient property so ignore it + continue; + } if (typeList.stream().anyMatch(typeDefinitions -> (typeDefinitions.getName().contains("jakarta.persistence")))) { propertyHasAnnotation = true; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/access/InvalidPropertyNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/access/InvalidPropertyNameTest.java index 3817c20230de..461ea8cb9861 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/access/InvalidPropertyNameTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/access/InvalidPropertyNameTest.java @@ -1,8 +1,18 @@ package org.hibernate.orm.test.bytecode.enhancement.access; -import jakarta.persistence.*; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; -import org.hibernate.testing.orm.junit.*; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -11,16 +21,17 @@ @DomainModel( annotatedClasses = { InvalidPropertyNameTest.SomeEntity.class, + InvalidPropertyNameTest.SomeEntityWithFalsePositive.class } ) @SessionFactory -@JiraKey("HHH-16572") @BytecodeEnhanced public class InvalidPropertyNameTest { @Test @FailureExpected(jiraKey = "HHH-16572") + @JiraKey("HHH-16572") public void test(SessionFactoryScope scope) { scope.inTransaction( session -> { session.persist( new SomeEntity( 1L, "field", "property" ) ); @@ -39,15 +50,40 @@ public void test(SessionFactoryScope scope) { } ); } + @Test + @JiraKey("HHH-18832") + public void testNoFalsePositive(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.persist( new SomeEntityWithFalsePositive( 1L, "property1-initial", "property2-initial" ) ); + } ); + + // Before HHH-18832 was fixed, lazy-loading enhancement was (incorrectly) skipped, + // resulting at best in `property1` being null in the code below, + // at worst in other errors such as java.lang.NoSuchMethodError: 'java.lang.String org.hibernate.orm.test.bytecode.enhancement.access.InvalidPropertyNameTest$SomeEntityWithFalsePositive.$$_hibernate_read_property1()' + // (see https://hibernate.zulipchat.com/#narrow/channel/132094-hibernate-orm-dev/topic/HHH-16572/near/481330806) + scope.inTransaction( session -> { + SomeEntityWithFalsePositive entity = session.getReference( SomeEntityWithFalsePositive.class, 1L ); + // Lazy-loading triggered by field access + // Proves bytecode enhancement is effective + assertThat( entity.property1 ).isEqualTo( "property1-initial" ); + } ); + + scope.inTransaction( session -> { + SomeEntityWithFalsePositive entity = session.getReference( SomeEntityWithFalsePositive.class, 1L ); + // Proves bytecode enhancement is effective even for the transient method + assertThat( entity.getProperty() ).isEqualTo( "property1-initial property2-initial" ); + } ); + } + @AfterEach public void cleanup(SessionFactoryScope scope) { - // uncomment the following when @FailureExpected is removed above - // scope.inTransaction( session -> { - // session.remove( session.get( SomeEntity.class, 1L ) ); - // PropertyAccessTest} ); + scope.inTransaction( session -> { + session.createQuery( "delete from SomeEntity" ).executeUpdate(); + session.createQuery( "delete from SomeEntityWithFalsePositive" ).executeUpdate(); + } ); } - @Entity + @Entity(name = "SomeEntity") @Table(name = "SOME_ENTITY") static class SomeEntity { @Id @@ -84,4 +120,53 @@ public void setPropertyMethod(String property) { this.property = property; } } + + @Entity(name = "SomeEntityWithFalsePositive") + static class SomeEntityWithFalsePositive { + + private Long id; + + private String property1; + + private String property2; + + public SomeEntityWithFalsePositive() { + } + + public SomeEntityWithFalsePositive(Long id, String property1, String property2) { + this.id = id; + this.property1 = property1; + this.property2 = property2; + } + + @Id + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getProperty1() { + return property1; + } + + public void setProperty1(String property1) { + this.property1 = property1; + } + + public String getProperty2() { + return property2; + } + + public void setProperty2(String property2) { + this.property2 = property2; + } + + @Transient + public String getProperty() { + return property1 + " " + property2; + } + } }