diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 53be96b04c..4b72248665 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -14,7 +14,7 @@ ext { junit5Version = '5.3.1' h2Version = '1.4.199' - bytemanVersion = '4.0.8' //Compatible with JDK14 + bytemanVersion = '4.0.13' //Compatible with JDK14 jnpVersion = '5.0.6.CR1' hibernateCommonsVersion = '5.1.0.Final' diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java index 3c12e8bc16..e37b0936d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/AbstractEntityTuplizer.java @@ -16,11 +16,13 @@ import org.hibernate.EntityNameResolver; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; +import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributesMetadata; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.Assigned; @@ -701,13 +703,26 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer { @Override public final Object instantiate(Serializable id, SharedSessionContractImplementor session) { - Object result = getInstantiator().instantiate( session ); + Object result = getInstantiator().instantiate( id ); + linkToSession( result, session ); if ( id != null ) { setIdentifier( result, id, session ); } return result; } + protected void linkToSession(Object entity, SharedSessionContractImplementor session) { + if ( session == null ) { + return; + } + if ( entity instanceof PersistentAttributeInterceptable ) { + final BytecodeLazyAttributeInterceptor interceptor = getEntityMetamodel().getBytecodeEnhancementMetadata().extractLazyInterceptor( entity ); + if ( interceptor != null ) { + interceptor.setSession( session ); + } + } + } + @Override public final Object instantiate() throws HibernateException { return instantiate( null, null ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyOneToManyWithEqualsImplementationTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyOneToManyWithEqualsImplementationTest.java new file mode 100644 index 0000000000..68dc27fca5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyOneToManyWithEqualsImplementationTest.java @@ -0,0 +1,128 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.bytecode.enhancement.lazy; + +import org.hibernate.annotations.LazyToOne; +import org.hibernate.annotations.LazyToOneOption; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +@TestForIssue(jiraKey = "HHH-13380") +@RunWith( BytecodeEnhancerRunner.class ) +public class LazyOneToManyWithEqualsImplementationTest + extends BaseEntityManagerFunctionalTestCase { + + private Long personId; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class, Course.class }; + } + + @Before + public void setUp() { + doInJPA( this::entityManagerFactory, entityManager -> { + Person p = new Person(); + entityManager.persist(p); + personId = p.getId(); + + Course c1 = new Course( "First Course", p ); + p.getCourses().add( c1 ); + entityManager.persist( c1 ); + + Course c2 = new Course("Second Course", p ); + p.getCourses().add( c2 ); + entityManager.persist( c2 ); + }); + } + + + @Test + public void testRetrievalOfOneToMany() { + doInJPA(this::entityManagerFactory, entityManager -> { + Person p = entityManager.find( Person.class, personId ); + + Set courses = p.getCourses(); + assertEquals( courses.size(), 2 ); + }); + } + + @Entity(name = "Person") + public static class Person { + + @Id + @GeneratedValue() + private Long id; + public Long getId() { + return id; + } + + @OneToMany(mappedBy="person", fetch = FetchType.LAZY) + private Set courses = new HashSet<>(); + public Set getCourses() { return courses; } + + } + + @Entity(name = "Course") + public static class Course { + + @Id + @GeneratedValue() + private Long id; + public Long getId() { return id; } + + @Basic + private String title; + public String getTitle() { return title; } + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Person person; + public Person getPerson() { return person; } + + protected Course() {} + public Course(String title, Person person) { + this.title = title; + this.person = person; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( o == null || getClass() != o.getClass() ) return false; + Course course = (Course) o; + return title.equals( course.title ) && + person.equals( course.person ); + } + + @Override + public int hashCode() { + return Objects.hash( title, person ); + } + } + +}