From 4699c73243dc67e2a22c1dd2437a6cf35c581743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 4 Jul 2018 12:29:11 +0200 Subject: [PATCH] HHH-12720 Test proxy serialization with hibernate.enable_lazy_load_no_trans = true --- .../EntityProxySerializationTest.java | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/serialization/EntityProxySerializationTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/serialization/EntityProxySerializationTest.java b/hibernate-core/src/test/java/org/hibernate/serialization/EntityProxySerializationTest.java new file mode 100644 index 0000000000..afd39d9908 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/serialization/EntityProxySerializationTest.java @@ -0,0 +1,241 @@ +/* + * 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.serialization; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; +import org.hibernate.annotations.LazyToOne; +import org.hibernate.annotations.LazyToOneOption; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.internal.util.SerializationHelper; +import org.hibernate.proxy.AbstractLazyInitializer; +import org.hibernate.proxy.HibernateProxy; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Selaron + */ +public class EntityProxySerializationTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { SimpleEntity.class, ChildEntity.class }; + } + + @Override + protected void configure(final Configuration configuration) { + // enable LL without TX, which used to cause problems when serializing proxies (see HHH-12720) + configuration.setProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, Boolean.TRUE.toString() ); + } + + /** + * Prepare and persist a {@link SimpleEntity} with two {@link ChildEntity}. + */ + @Before + public void prepare() { + final Session s = openSession(); + + final Transaction t = s.beginTransaction(); + + try { + final Number count = (Number) s.createQuery("SELECT count(ID) FROM SimpleEntity").getSingleResult(); + if (count.longValue() > 0L) { + // entity already added previously + return; + } + + final SimpleEntity entity = new SimpleEntity(); + entity.setId( 1L ); + entity.setName( "TheParent" ); + + final ChildEntity c1 = new ChildEntity(); + c1.setId( 1L ); + c1.setParent( entity ); + + final ChildEntity c2 = new ChildEntity(); + c2.setId( 2L ); + c2.setParent( entity ); + + s.save( entity ); + s.save( c1 ); + s.save( c2 ); + } + finally { + t.commit(); + s.close(); + } + } + + /** + * Tests that lazy loading without transaction nor open session is generally + * working. The magic is done by {@link AbstractLazyInitializer} who opens a + * temporary session. + */ + @SuppressWarnings("unchecked") + @Test + public void testProxyInitializationWithoutTX() { + final Session s = openSession(); + + final Transaction t = s.beginTransaction(); + try { + final ChildEntity child = s.find( ChildEntity.class, 1L ); + + final SimpleEntity parent = child.getParent(); + + t.rollback(); + session.close(); + + // assert we have an uninitialized proxy + assertTrue( parent instanceof HibernateProxy ); + assertFalse( Hibernate.isInitialized( parent ) ); + + assertEquals( "TheParent", parent.getName() ); + + // assert we have an initialized proxy now + assertTrue( Hibernate.isInitialized( parent ) ); + } + finally { + if ( t.isActive() ) { + t.rollback(); + } + s.close(); + } + } + + /** + * Tests that lazy loading without transaction nor open session is generally + * working. The magic is done by {@link AbstractLazyInitializer} who opens a + * temporary session. + */ + @SuppressWarnings("unchecked") + @Test + @TestForIssue(jiraKey = "HHH-12720") + @FailureExpected(jiraKey = "HHH-12720") + public void testProxyInitializationWithoutTXAfterDeserialization() { + final Session s = openSession(); + + final Transaction t = s.beginTransaction(); + try { + final ChildEntity child = s.find( ChildEntity.class, 1L ); + + final SimpleEntity parent = child.getParent(); + + // destroy AbstractLazyInitializer internal state + final SimpleEntity deserializedParent = (SimpleEntity) SerializationHelper.clone( parent ); + + t.rollback(); + session.close(); + + // assert we have an uninitialized proxy + assertTrue( deserializedParent instanceof HibernateProxy ); + assertFalse( Hibernate.isInitialized( deserializedParent ) ); + + assertEquals( "TheParent", deserializedParent.getName() ); + + // assert we have an initialized proxy now + assertTrue( Hibernate.isInitialized( deserializedParent ) ); + } + finally { + if ( t.isActive() ) { + t.rollback(); + } + s.close(); + } + } + + @Entity(name = "SimpleEntity") + static class SimpleEntity implements Serializable { + + private Long id; + + private String name; + + Set children = new HashSet<>(); + + @Id + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + @OneToMany(targetEntity = ChildEntity.class, mappedBy = "parent") + @LazyCollection(LazyCollectionOption.EXTRA) + @Fetch(FetchMode.SELECT) + public Set getChildren() { + return children; + } + + public void setChildren(final Set children) { + this.children = children; + } + + } + + @Entity + static class ChildEntity { + private Long id; + + private SimpleEntity parent; + + @Id + public Long getId() { + return id; + } + + public void setId(final Long id) { + this.id = id; + } + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn + @LazyToOne(LazyToOneOption.PROXY) + public SimpleEntity getParent() { + return parent; + } + + public void setParent(final SimpleEntity parent) { + this.parent = parent; + } + + } +} \ No newline at end of file