From 6f342358db3aa5618d60ec69f69f8c6cfb6eb611 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 21 Mar 2014 12:06:04 -0500 Subject: [PATCH] HHH-7971 - Enabling ENABLE_LAZY_LOAD_NO_TRANS results in trying to remove elements in collections --- .../AbstractPersistentCollection.java | 26 ++-- .../proxy/AbstractLazyInitializer.java | 23 ++-- .../org/hibernate/test/lazyload/Child.java | 23 ++++ .../test/lazyload/JtaLazyLoadingTest.java | 123 ++++++++++++++++++ .../test/lazyload/LazyLoadingTest.java | 56 +++++--- .../org/hibernate/test/lazyload/Parent.java | 38 +++++- 6 files changed, 246 insertions(+), 43 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/lazyload/JtaLazyLoadingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java index 8b347d8a91..2bb52f27bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java @@ -35,6 +35,7 @@ import java.util.ListIterator; import javax.naming.NamingException; import org.hibernate.AssertionFailure; +import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; import org.hibernate.Session; @@ -81,7 +82,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers private Serializable storedSnapshot; private String sessionFactoryUuid; - private boolean specjLazyLoad; + private boolean allowLoadOutsideTransaction; /** * Not called by Hibernate, but used by non-JDK serialization, @@ -205,7 +206,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers boolean isJTA = false; if ( session == null ) { - if ( specjLazyLoad ) { + if ( allowLoadOutsideTransaction ) { session = openTemporarySessionForLoading(); isTempSession = true; } @@ -214,7 +215,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } else if ( !session.isOpen() ) { - if ( specjLazyLoad ) { + if ( allowLoadOutsideTransaction ) { originalSession = session; session = openTemporarySessionForLoading(); isTempSession = true; @@ -224,7 +225,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } else if ( !session.isConnected() ) { - if ( specjLazyLoad ) { + if ( allowLoadOutsideTransaction ) { originalSession = session; session = openTemporarySessionForLoading(); isTempSession = true; @@ -235,8 +236,6 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } if ( isTempSession ) { - // TODO: On the next major release, add an - // 'isJTA' or 'getTransactionFactory' method to Session. isJTA = session.getTransactionCoordinator() .getTransactionContext().getTransactionEnvironment() .getTransactionFactory() @@ -250,7 +249,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers // multiple transactions. ( (Session) session ).beginTransaction(); } - + session.getPersistenceContext().addUninitializedDetachedCollection( session.getFactory().getCollectionPersister( getRole() ), this @@ -284,7 +283,10 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers final SessionFactoryImplementor sf = (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); - return (SessionImplementor) sf.openSession(); + final SessionImplementor session = (SessionImplementor) sf.openSession(); + session.getPersistenceContext().setDefaultReadOnly( true ); + session.setFlushMode( FlushMode.MANUAL ); + return session; } protected Boolean readIndexExistence(final Object index) { @@ -593,7 +595,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers @Override public final boolean unsetSession(SessionImplementor currentSession) { - prepareForPossibleSpecialSpecjInitialization(); + prepareForPossibleLoadingOutsideTransaction(); if ( currentSession == this.session ) { this.session = null; return true; @@ -603,11 +605,11 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } - protected void prepareForPossibleSpecialSpecjInitialization() { + protected void prepareForPossibleLoadingOutsideTransaction() { if ( session != null ) { - specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled(); + allowLoadOutsideTransaction = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled(); - if ( specjLazyLoad && sessionFactoryUuid == null ) { + if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) { try { sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent(); } diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java index 3e77376a64..e79b802165 100755 --- a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java @@ -26,6 +26,7 @@ package org.hibernate.proxy; import java.io.Serializable; import javax.naming.NamingException; +import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; import org.hibernate.Session; @@ -59,7 +60,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { private Boolean readOnlyBeforeAttachedToSession; private String sessionFactoryUuid; - private boolean specjLazyLoad; + private boolean allowLoadOutsideTransaction; /** * For serialization from the non-pojo initializers (HHH-3309) @@ -148,7 +149,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final void unsetSession() { - prepareForPossibleSpecialSpecjInitialization(); + prepareForPossibleLoadingOutsideTransaction(); session = null; readOnly = false; readOnlyBeforeAttachedToSession = null; @@ -157,8 +158,8 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final void initialize() throws HibernateException { if ( !initialized ) { - if ( specjLazyLoad ) { - specialSpecjInitialization(); + if ( allowLoadOutsideTransaction ) { + permissiveInitialization(); } else if ( session == null ) { throw new LazyInitializationException( "could not initialize proxy - no Session" ); @@ -180,7 +181,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { } } - protected void specialSpecjInitialization() { + protected void permissiveInitialization() { if ( session == null ) { //we have a detached collection thats set to null, reattach if ( sessionFactoryUuid == null ) { @@ -190,9 +191,9 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { SessionFactoryImplementor sf = (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); SessionImplementor session = (SessionImplementor) sf.openSession(); - - // TODO: On the next major release, add an - // 'isJTA' or 'getTransactionFactory' method to Session. + session.getPersistenceContext().setDefaultReadOnly( true ); + session.setFlushMode( FlushMode.MANUAL ); + boolean isJTA = session.getTransactionCoordinator() .getTransactionContext().getTransactionEnvironment() .getTransactionFactory() @@ -240,11 +241,11 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { } } - protected void prepareForPossibleSpecialSpecjInitialization() { + protected void prepareForPossibleLoadingOutsideTransaction() { if ( session != null ) { - specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled(); + allowLoadOutsideTransaction = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled(); - if ( specjLazyLoad && sessionFactoryUuid == null ) { + if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) { try { sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/lazyload/Child.java b/hibernate-core/src/test/java/org/hibernate/test/lazyload/Child.java index f45422bae6..8c7cec8de0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lazyload/Child.java +++ b/hibernate-core/src/test/java/org/hibernate/test/lazyload/Child.java @@ -1,3 +1,26 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ package org.hibernate.test.lazyload; import javax.persistence.CascadeType; diff --git a/hibernate-core/src/test/java/org/hibernate/test/lazyload/JtaLazyLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/lazyload/JtaLazyLoadingTest.java new file mode 100644 index 0000000000..06755ce935 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/lazyload/JtaLazyLoadingTest.java @@ -0,0 +1,123 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.lazyload; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.jta.TestingJtaBootstrap; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * @author Oleksander Dukhno + */ +public class JtaLazyLoadingTest + extends BaseCoreFunctionalTestCase { + + private static final int CHILDREN_SIZE = 3; + private Long parentID; + private Long lastChildID; + + protected void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" ); + + TestingJtaBootstrap.prepare( cfg.getProperties() ); + cfg.setProperty( Environment.TRANSACTION_STRATEGY, JtaTransactionFactory.class.getName() ); + } + + protected Class[] getAnnotatedClasses() { + return new Class[] { + Parent.class, + Child.class + }; + } + + protected void prepareTest() + throws Exception { + Session s = openSession(); + s.beginTransaction(); + + Parent p = new Parent(); + for ( int i = 0; i < CHILDREN_SIZE; i++ ) { + final Child child = p.makeChild(); + s.persist( child ); + lastChildID = child.getId(); + } + s.persist( p ); + parentID = p.getId(); + + s.getTransaction().commit(); + s.clear(); + s.close(); + } + + @Test + @TestForIssue(jiraKey = "HHH-7971") + public void testLazyCollectionLoadingAfterEndTransaction() { + Session s = openSession(); + s.beginTransaction(); + Parent loadedParent = (Parent) s.load( Parent.class, parentID ); + s.getTransaction().commit(); + s.close(); + + assertFalse( Hibernate.isInitialized( loadedParent.getChildren() ) ); + + int i = 0; + for ( Child child : loadedParent.getChildren() ) { + i++; + assertNotNull( child ); + } + + assertEquals( CHILDREN_SIZE, i ); + + s = openSession(); + s.beginTransaction(); + Child loadedChild = (Child) s.load( Child.class, lastChildID ); + s.getTransaction().commit(); + s.close(); + + Parent p = loadedChild.getParent(); + int j = 0; + for ( Child child : p.getChildren() ) { + j++; + assertNotNull( child ); + } + + assertEquals( CHILDREN_SIZE, j ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/lazyload/LazyLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/lazyload/LazyLoadingTest.java index 650357e907..3723269a5f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lazyload/LazyLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/lazyload/LazyLoadingTest.java @@ -1,18 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ package org.hibernate.test.lazyload; +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; - import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; /** * @author Oleksander Dukhno @@ -27,8 +52,10 @@ public class LazyLoadingTest protected void configure(Configuration cfg) { super.configure( cfg ); cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" ); + cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" ); } + protected Class[] getAnnotatedClasses() { return new Class[] { Parent.class, @@ -41,15 +68,12 @@ public class LazyLoadingTest Session s = openSession(); s.beginTransaction(); - List children = new ArrayList(); - for ( int i = 0; i < CHILDREN_SIZE; i++ ) { - Child c = new Child(); - s.persist( c ); - lastChildID = c.getId(); - children.add( c ); - } Parent p = new Parent(); - p.setChildren( children ); + for ( int i = 0; i < CHILDREN_SIZE; i++ ) { + final Child child = p.makeChild(); + s.persist( child ); + lastChildID = child.getId(); + } s.persist( p ); parentID = p.getId(); @@ -63,12 +87,14 @@ public class LazyLoadingTest public void testLazyCollectionLoadingAfterEndTransaction() { Session s = openSession(); s.beginTransaction(); - Parent loadedPatent = (Parent) s.load( Parent.class, parentID ); + Parent loadedParent = (Parent) s.load( Parent.class, parentID ); s.getTransaction().commit(); s.close(); + assertFalse( Hibernate.isInitialized( loadedParent.getChildren() ) ); + int i = 0; - for ( Child child : loadedPatent.getChildren() ) { + for ( Child child : loadedParent.getChildren() ) { i++; assertNotNull( child ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/lazyload/Parent.java b/hibernate-core/src/test/java/org/hibernate/test/lazyload/Parent.java index ce93224479..7f7d48ff3a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lazyload/Parent.java +++ b/hibernate-core/src/test/java/org/hibernate/test/lazyload/Parent.java @@ -1,5 +1,30 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ package org.hibernate.test.lazyload; +import java.util.ArrayList; +import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -7,8 +32,6 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; -import java.util.ArrayList; -import java.util.List; /** * @author Oleksander Dukhno @@ -36,8 +59,13 @@ public class Parent { public void setChildren(List children) { this.children = children; - for ( Child c : children ) { - c.setParent( this ); - } } + + Child makeChild() { + final Child c = new Child(); + c.setParent( this ); + this.children.add( c ); + return c; + } + }