diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java index 6fc6de8e3af5..00b381bf9056 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java @@ -14,7 +14,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -75,6 +74,7 @@ public class BytecodeProviderImpl implements BytecodeProvider { private static final String INSTANTIATOR_PROXY_NAMING_SUFFIX = "HibernateInstantiator"; private static final String OPTIMIZER_PROXY_NAMING_SUFFIX = "HibernateAccessOptimizer"; + private static final String OPTIMIZER_PROXY_BRIDGE_NAMING_SUFFIX = "HibernateAccessOptimizerBridge"; private static final ElementMatcher.Junction newInstanceMethodName = ElementMatchers.named( "newInstance" ); private static final ElementMatcher.Junction getPropertyValuesMethodName = ElementMatchers.named( @@ -280,13 +280,13 @@ public ReflectionOptimizer getReflectionOptimizer( } } - private static class ForeignPackageClassInfo { + private static class BridgeMembersClassInfo { final Class clazz; final List propertyNames = new ArrayList<>(); final List getters = new ArrayList<>(); final List setters = new ArrayList<>(); - public ForeignPackageClassInfo(Class clazz) { + public BridgeMembersClassInfo(Class clazz) { this.clazz = clazz; } } @@ -295,99 +295,81 @@ private Class determineAccessOptimizerSuperClass(Class clazz, String[] pro if ( clazz.isInterface() ) { return Object.class; } - // generate access optimizer super classes for foreign package super classes that declare fields + // generate access optimizer super classes for super classes that declare members requiring bridge methods // each should declare protected static methods get_FIELDNAME(OWNER)/set_FIELDNAME(OWNER, TYPE) // which should be called then from within GetPropertyValues/SetPropertyValues // Since these super classes will be in the correct package, the package-private entity field access is fine - final List foreignPackageClassInfos = createForeignPackageClassInfos( clazz ); - for ( Iterator iterator = foreignPackageClassInfos.iterator(); iterator.hasNext(); ) { - final ForeignPackageClassInfo foreignPackageClassInfo = iterator.next(); - for ( int i = 0; i < getters.length; i++ ) { - final Member getter = getters[i]; - final Member setter = setters[i]; - boolean found = false; - if ( getter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( getter.getModifiers() ) ) { - foreignPackageClassInfo.getters.add( getter ); - found = true; - } - if ( setter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( setter.getModifiers() ) ) { - foreignPackageClassInfo.setters.add( setter ); - found = true; - } - if ( found ) { - foreignPackageClassInfo.propertyNames.add( propertyNames[i] ); - } - } - if ( foreignPackageClassInfo.getters.isEmpty() && foreignPackageClassInfo.setters.isEmpty() ) { - iterator.remove(); - } - } + final List bridgeMembersClassInfos = createBridgeMembersClassInfos( clazz, getters, setters, propertyNames ); Class superClass = Object.class; - for ( int i = foreignPackageClassInfos.size() - 1; i >= 0; i-- ) { - final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos.get( i ); + for ( int i = bridgeMembersClassInfos.size() - 1; i >= 0; i-- ) { + final BridgeMembersClassInfo bridgeMembersClassInfo = bridgeMembersClassInfos.get( i ); final Class newSuperClass = superClass; - final String className = foreignPackageClassInfo.clazz.getName() + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName( foreignPackageClassInfo.propertyNames, foreignPackageClassInfo.getters, foreignPackageClassInfo.setters ); + final String className = bridgeMembersClassInfo.clazz.getName() + "$" + OPTIMIZER_PROXY_BRIDGE_NAMING_SUFFIX + encodeName( bridgeMembersClassInfo.propertyNames, bridgeMembersClassInfo.getters, bridgeMembersClassInfo.setters ); superClass = byteBuddyState.load( - foreignPackageClassInfo.clazz, + bridgeMembersClassInfo.clazz, className, (byteBuddy, namingStrategy) -> { DynamicType.Builder builder = byteBuddy.with( namingStrategy ).subclass( newSuperClass ); - for ( Member getter : foreignPackageClassInfo.getters ) { - final Class getterType; - if ( getter instanceof Field field ) { - getterType = field.getType(); - } - else if ( getter instanceof Method method ) { - getterType = method.getReturnType(); - } + for ( Member getter : bridgeMembersClassInfo.getters ) { + if ( !Modifier.isPublic( getter.getModifiers() ) ) { + final Class getterType; + if ( getter instanceof Field field ) { + getterType = field.getType(); + } + else if ( getter instanceof Method method ) { + getterType = method.getReturnType(); + } else { throw new AssertionFailure( "Unexpected member" + getter ); } - builder = builder.defineMethod( - "get_" + getter.getName(), - TypeDescription.Generic.OfNonGenericType.ForLoadedType.of( - getterType - ), - Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC - ) - .withParameter( foreignPackageClassInfo.clazz ) - .intercept( - new Implementation.Simple( - new GetFieldOnArgument( - getter - ) - ) - ); - } - for ( Member setter : foreignPackageClassInfo.setters ) { - final Class setterType; - if ( setter instanceof Field field ) { - setterType = field.getType(); - } - else if ( setter instanceof Method method ) { - setterType = method.getParameterTypes()[0]; + builder = builder.defineMethod( + "get_" + getter.getName(), + TypeDescription.Generic.OfNonGenericType.ForLoadedType.of( + getterType + ), + Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC + ) + .withParameter( bridgeMembersClassInfo.clazz ) + .intercept( + new Implementation.Simple( + new GetFieldOnArgument( + getter + ) + ) + ); } + } + for ( Member setter : bridgeMembersClassInfo.setters ) { + if ( !Modifier.isPublic( setter.getModifiers() ) ) { + final Class setterType; + if ( setter instanceof Field field ) { + setterType = field.getType(); + } + else if ( setter instanceof Method method ) { + setterType = method.getParameterTypes()[0]; + } else { throw new AssertionFailure( "Unexpected member" + setter ); } - builder = builder.defineMethod( - "set_" + setter.getName(), - TypeDescription.Generic.VOID, - Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC - ) - .withParameter( foreignPackageClassInfo.clazz ) - .withParameter( setterType ) - .intercept( - new Implementation.Simple( - new SetFieldOnArgument( - setter - ) - ) - ); + builder = builder.defineMethod( + "set_" + setter.getName(), + TypeDescription.Generic.VOID, + Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC + ) + .withParameter( bridgeMembersClassInfo.clazz ) + .withParameter( setterType ) + .intercept( + new Implementation.Simple( + new SetFieldOnArgument( + setter + ) + ) + ); + } } return builder; @@ -397,10 +379,10 @@ else if ( setter instanceof Method method ) { for ( int j = 0; j < getters.length; j++ ) { final Member getter = getters[j]; final Member setter = setters[j]; - if ( foreignPackageClassInfo.getters.contains( getter ) ) { + if ( bridgeMembersClassInfo.getters.contains( getter ) && !Modifier.isPublic( getter.getModifiers() ) ) { getters[j] = new ForeignPackageMember( superClass, getter ); } - if ( foreignPackageClassInfo.setters.contains( setter ) ) { + if ( bridgeMembersClassInfo.setters.contains( setter ) && !Modifier.isPublic( setter.getModifiers() ) ) { setters[j] = new ForeignPackageMember( superClass, setter ); } } @@ -622,16 +604,31 @@ private boolean is64BitType(Class type) { } } - private List createForeignPackageClassInfos(Class clazz) { - final List foreignPackageClassInfos = new ArrayList<>(); + private List createBridgeMembersClassInfos( + Class clazz, + Member[] getters, + Member[] setters, + String[] propertyNames) { + final List bridgeMembersClassInfos = new ArrayList<>(); Class c = clazz.getSuperclass(); while (c != Object.class) { - if ( !c.getPackageName().equals( clazz.getPackageName() ) ) { - foreignPackageClassInfos.add( new ForeignPackageClassInfo( c ) ); + final BridgeMembersClassInfo bridgeMemberClassInfo = new BridgeMembersClassInfo( c ); + for ( int i = 0; i < getters.length; i++ ) { + final Member getter = getters[i]; + final Member setter = setters[i]; + if ( getter.getDeclaringClass() == c && !Modifier.isPublic( getter.getModifiers() ) + || setter.getDeclaringClass() == c && !Modifier.isPublic( setter.getModifiers() ) ) { + bridgeMemberClassInfo.getters.add( getter ); + bridgeMemberClassInfo.setters.add( setter ); + bridgeMemberClassInfo.propertyNames.add( propertyNames[i] ); + } + } + if ( !bridgeMemberClassInfo.propertyNames.isEmpty() ) { + bridgeMembersClassInfos.add( bridgeMemberClassInfo ); } c = c.getSuperclass(); } - return foreignPackageClassInfos; + return bridgeMembersClassInfos; } public ByteBuddyProxyHelper getByteBuddyProxyHelper() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/ForeignPackageSuperclassAccessorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/ForeignPackageSuperclassAccessorTest.java new file mode 100644 index 000000000000..a2fae4213692 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/ForeignPackageSuperclassAccessorTest.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode; + +import org.hibernate.orm.test.bytecode.foreignpackage.ConcreteEntity; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(annotatedClasses = { + ConcreteEntity.class, + SuperclassEntity.class +}) +@Jira("https://hibernate.atlassian.net/browse/HHH-19369") +public class ForeignPackageSuperclassAccessorTest { + + @Test + public void test(SessionFactoryScope scope) { + scope.inTransaction( session -> { + session.find( SuperclassEntity.class, 1L ); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/SuperclassEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/SuperclassEntity.java new file mode 100644 index 000000000000..c77a09e92b46 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/SuperclassEntity.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +public class SuperclassEntity { + @Id + protected long id; + protected String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/AncestorEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/AncestorEntity.java new file mode 100644 index 000000000000..ab7cdffb8a4d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/AncestorEntity.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import org.hibernate.orm.test.bytecode.enhancement.optimizer.parent.Ancestor; + +@Entity(name = "AncestorEntity") +@Inheritance(strategy = InheritanceType.JOINED) +public class AncestorEntity extends Ancestor { + + private Long id; + + private String field; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity3.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity3.java new file mode 100644 index 000000000000..100191b43b98 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity3.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity3") +public class ChildEntity3 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity4.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity4.java new file mode 100644 index 000000000000..a8ed90f3ef5b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity4.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity4") +public class ChildEntity4 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity5.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity5.java new file mode 100644 index 000000000000..ba7540fec1fa --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity5.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity5") +public class ChildEntity5 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity6.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity6.java new file mode 100644 index 000000000000..a996eb9089ed --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity6.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity6") +public class ChildEntity6 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity7.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity7.java new file mode 100644 index 000000000000..9a9775a53cd3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity7.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity7") +public class ChildEntity7 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChildField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity8.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity8.java new file mode 100644 index 000000000000..f6bdd29a2a29 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity8.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity8") +public class ChildEntity8 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChildField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity9.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity9.java new file mode 100644 index 000000000000..b734abdd91ee --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ChildEntity9.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity9") +public class ChildEntity9 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChildField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerMethodVisibilityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerMethodVisibilityTest.java new file mode 100644 index 000000000000..e6e9dd57eed4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerMethodVisibilityTest.java @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import org.hibernate.orm.test.bytecode.enhancement.optimizer.child.ChildEntity10; +import org.hibernate.orm.test.bytecode.enhancement.optimizer.parent.Ancestor; +import org.hibernate.orm.test.bytecode.enhancement.optimizer.parent.ChildEntity2; +import org.hibernate.query.Query; +import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel(annotatedClasses = { + Ancestor.class, + AncestorEntity.class, + ChildEntity2.class, + ChildEntity10.class +}) +@SessionFactory +@Jira("https://hibernate.atlassian.net/browse/HHH-19372") +@BytecodeEnhanced +public class HierarchyBytecodeOptimizerMethodVisibilityTest { + + @Test + public void testOptimizerSetPropertyValues(SessionFactoryScope scope) { + ChildEntity2 childEntity2 = new ChildEntity2(); + childEntity2.setId( 1L ); + childEntity2.setField( "field" ); + childEntity2.setChieldField( "childField" ); + + ChildEntity10 childEntity10 = new ChildEntity10(); + childEntity10.setId( 3L ); + childEntity10.setField( "field10" ); + childEntity10.setChieldField( "childField3" ); + + scope.inTransaction( session -> { + session.persist( childEntity2 ); + session.persist( childEntity10 ); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity2 c where c.field = :field", + ChildEntity2.class ); + query.setParameter( "field", "field" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity10 c where c.field = :field", + ChildEntity10.class ); + query.setParameter( "field", "field10" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + } + + @AfterAll + public void cleanup(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerOrderingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerOrderingTest.java new file mode 100644 index 000000000000..1493c992c53e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerOrderingTest.java @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import org.hibernate.orm.test.bytecode.enhancement.optimizer.parent.Ancestor; +import org.hibernate.query.Query; +import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel(annotatedClasses = { + Ancestor.class, + ParentEntity.class, + ChildEntity3.class, + ChildEntity4.class, + ChildEntity5.class, + ChildEntity6.class, + ChildEntity7.class, + ChildEntity8.class, + ChildEntity9.class +}) +@SessionFactory +@Jira("https://hibernate.atlassian.net/browse/HHH-19369") +@BytecodeEnhanced +public class HierarchyBytecodeOptimizerOrderingTest { + + @Test + public void testOptimizerSetPropertyValues(SessionFactoryScope scope) { + ChildEntity3 childEntity3 = new ChildEntity3(); + childEntity3.setId( 3L ); + childEntity3.setName( "child3" ); + childEntity3.setField( "field3" ); + childEntity3.setChieldField( "childField3" ); + + ChildEntity4 childEntity4 = new ChildEntity4(); + childEntity4.setId( 4L ); + childEntity4.setName( "child4" ); + childEntity4.setField( "field4" ); + childEntity4.setChieldField( "childField4" ); + + ChildEntity5 childEntity5 = new ChildEntity5(); + childEntity5.setId( 5L ); + childEntity5.setName( "child5" ); + childEntity5.setField( "field5" ); + childEntity5.setChieldField( "childField5" ); + + ChildEntity6 childEntity6 = new ChildEntity6(); + childEntity6.setId( 6L ); + childEntity6.setName( "child6" ); + childEntity6.setField( "field6" ); + childEntity6.setChieldField( "childField6" ); + + ChildEntity7 childEntity7 = new ChildEntity7(); + childEntity7.setId( 7L ); + childEntity7.setName( "child7" ); + childEntity7.setField( "field7" ); + childEntity7.setChildField( "childField7" ); + + scope.inTransaction( session -> { + session.persist( childEntity3 ); + session.persist( childEntity4 ); + session.persist( childEntity5 ); + session.persist( childEntity6 ); + session.persist( childEntity7 ); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity3 c where c.field = :field", + ChildEntity3.class ); + query.setParameter( "field", "field3" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity4 c where c.field = :field", + ChildEntity4.class ); + query.setParameter( "field", "field4" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity5 c where c.field = :field", + ChildEntity5.class ); + query.setParameter( "field", "field5" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity6 c where c.field = :field", + ChildEntity6.class ); + query.setParameter( "field", "field6" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( + "select c from ChildEntity7 c where c.field = :field", ChildEntity7.class ); + query.setParameter( "field", "field7" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + } + + @AfterAll + public void cleanup(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerTest.java new file mode 100644 index 000000000000..a90b07190ef3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/HierarchyBytecodeOptimizerTest.java @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import org.hibernate.orm.test.bytecode.enhancement.optimizer.child.ChildEntity; +import org.hibernate.query.Query; +import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel(annotatedClasses = { + ParentEntity.class, + ChildEntity.class, +}) +@SessionFactory +@Jira("https://hibernate.atlassian.net/browse/HHH-19372") +@BytecodeEnhanced +public class HierarchyBytecodeOptimizerTest { + + @Test + public void testOptimizerSetPropertyValues(SessionFactoryScope scope) { + ChildEntity childEntity = new ChildEntity(); + childEntity.setId( 1L ); + childEntity.setField( "field" ); + childEntity.setChieldField( "childField" ); + + scope.inTransaction( session -> { + session.persist( childEntity ); + } ); + + scope.inTransaction( session -> { + Query query = session.createQuery( "select c from ChildEntity c where c.field = :field", + ChildEntity.class ); + query.setParameter( "field", "field" ); + assertThat( query.uniqueResult() ).isNotNull(); + } ); + + } + + @AfterAll + public void cleanup(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().truncateMappedObjects(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ParentEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ParentEntity.java new file mode 100644 index 000000000000..ee933b160ed0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/ParentEntity.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +@Entity(name = "ParentEntity") +@Inheritance(strategy = InheritanceType.JOINED) +public class ParentEntity { + + @Id + private Long id; + + private String field; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/child/ChildEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/child/ChildEntity.java new file mode 100644 index 000000000000..d189fad30c9b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/child/ChildEntity.java @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer.child; + +import org.hibernate.orm.test.bytecode.enhancement.optimizer.ParentEntity; + +import jakarta.persistence.Entity; + +@Entity(name = "ChildEntity") +public class ChildEntity extends ParentEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/child/ChildEntity10.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/child/ChildEntity10.java new file mode 100644 index 000000000000..f1a6756a8c20 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/child/ChildEntity10.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer.child; + +import jakarta.persistence.Entity; +import org.hibernate.orm.test.bytecode.enhancement.optimizer.AncestorEntity; + +@Entity(name = "ChildEntity10") +public class ChildEntity10 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/parent/Ancestor.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/parent/Ancestor.java new file mode 100644 index 000000000000..15389f9b22fb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/parent/Ancestor.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer.parent; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +@Entity(name = "Parent") +@Inheritance(strategy = InheritanceType.JOINED) +public class Ancestor { + @Id + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/parent/ChildEntity2.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/parent/ChildEntity2.java new file mode 100644 index 000000000000..05e4917d8c9e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/optimizer/parent/ChildEntity2.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.enhancement.optimizer.parent; + +import jakarta.persistence.Entity; +import org.hibernate.orm.test.bytecode.enhancement.optimizer.AncestorEntity; + +@Entity(name = "ChildEntity2") +public class ChildEntity2 extends AncestorEntity { + private String childField; + + public String getChildField() { + return childField; + } + + public void setChieldField(String childField) { + this.childField = childField; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/foreignpackage/ConcreteEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/foreignpackage/ConcreteEntity.java new file mode 100644 index 000000000000..977c9b4529df --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/foreignpackage/ConcreteEntity.java @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bytecode.foreignpackage; + +import org.hibernate.orm.test.bytecode.SuperclassEntity; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class ConcreteEntity extends SuperclassEntity { + @Id + protected long id; + protected String bname; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getBname() { + return bname; + } + + public void setBname(String bname) { + this.bname = bname; + } +}