HHH-18832 Don't skip bytecode enhancement just because an entity has a `@Transient` getter

This commit is contained in:
Yoann Rodière 2024-11-08 16:06:15 +01:00
parent 9c96e84a58
commit 74c5c844c0
2 changed files with 100 additions and 10 deletions

View File

@ -462,6 +462,11 @@ public class EnhancerImpl implements Enhancer {
// convert field letter to lower case // convert field letter to lower case
methodFieldName = methodFieldName.substring(0, 1).toLowerCase() + methodFieldName.substring(1); methodFieldName = methodFieldName.substring(0, 1).toLowerCase() + methodFieldName.substring(1);
TypeList typeList = methodDescription.getDeclaredAnnotations().asTypeList(); 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 -> if (typeList.stream().anyMatch(typeDefinitions ->
(typeDefinitions.getName().contains("jakarta.persistence")))) { (typeDefinitions.getName().contains("jakarta.persistence")))) {
propertyHasAnnotation = true; propertyHasAnnotation = true;

View File

@ -4,9 +4,19 @@
*/ */
package org.hibernate.orm.test.bytecode.enhancement.access; 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.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.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -15,16 +25,17 @@ import static org.assertj.core.api.Assertions.assertThat;
@DomainModel( @DomainModel(
annotatedClasses = { annotatedClasses = {
InvalidPropertyNameTest.SomeEntity.class, InvalidPropertyNameTest.SomeEntity.class,
InvalidPropertyNameTest.SomeEntityWithFalsePositive.class
} }
) )
@SessionFactory @SessionFactory
@JiraKey("HHH-16572")
@BytecodeEnhanced @BytecodeEnhanced
public class InvalidPropertyNameTest { public class InvalidPropertyNameTest {
@Test @Test
@FailureExpected(jiraKey = "HHH-16572") @FailureExpected(jiraKey = "HHH-16572")
@JiraKey("HHH-16572")
public void test(SessionFactoryScope scope) { public void test(SessionFactoryScope scope) {
scope.inTransaction( session -> { scope.inTransaction( session -> {
session.persist( new SomeEntity( 1L, "field", "property" ) ); session.persist( new SomeEntity( 1L, "field", "property" ) );
@ -43,15 +54,40 @@ public class InvalidPropertyNameTest {
} ); } );
} }
@AfterEach @Test
public void cleanup(SessionFactoryScope scope) { @JiraKey("HHH-18832")
// uncomment the following when @FailureExpected is removed above public void testNoFalsePositive(SessionFactoryScope scope) {
// scope.inTransaction( session -> { scope.inTransaction( session -> {
// session.remove( session.get( SomeEntity.class, 1L ) ); session.persist( new SomeEntityWithFalsePositive( 1L, "property1-initial", "property2-initial" ) );
// PropertyAccessTest} ); } );
// 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") @Table(name = "SOME_ENTITY")
static class SomeEntity { static class SomeEntity {
@Id @Id
@ -88,4 +124,53 @@ public class InvalidPropertyNameTest {
this.property = 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;
}
}
} }