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 36310ab84e..d11f27d929 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 @@ -462,6 +462,11 @@ public class EnhancerImpl implements Enhancer { // 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 9d068b9fe9..4bb5e80c21 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 @@ -4,9 +4,19 @@ */ 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; @@ -15,16 +25,17 @@ import static org.assertj.core.api.Assertions.assertThat; @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" ) ); @@ -43,15 +54,40 @@ public class InvalidPropertyNameTest { } ); } - @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} ); + @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" ); + } ); } - @Entity + @AfterEach + public void cleanup(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.createQuery( "delete from SomeEntity" ).executeUpdate(); + session.createQuery( "delete from SomeEntityWithFalsePositive" ).executeUpdate(); + } ); + } + + @Entity(name = "SomeEntity") @Table(name = "SOME_ENTITY") static class SomeEntity { @Id @@ -88,4 +124,53 @@ public class InvalidPropertyNameTest { 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; + } + } }