From 436060008b1ded4025a31b68385d5ec1312a7e2d Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 13 Feb 2020 15:32:02 +0000 Subject: [PATCH] Fix empty collection not initialized --- .../internal/PersistentArrayHolder.java | 7 ++++ .../collection/internal/PersistentBag.java | 8 ++++- .../internal/PersistentIdentifierBag.java | 8 +++++ .../collection/internal/PersistentList.java | 7 ++++ .../collection/internal/PersistentMap.java | 7 ++++ .../collection/internal/PersistentSet.java | 7 ++++ .../collection/spi/PersistentCollection.java | 2 ++ ...aultInitializeCollectionEventListener.java | 16 ++++++++++ .../sql/results/internal/Helper.java | 32 +++++++++++++++++++ .../internal/LoadingCollectionEntryImpl.java | 25 +-------------- .../results/internal/StandardRowReader.java | 9 ------ 11 files changed, 94 insertions(+), 34 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentArrayHolder.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentArrayHolder.java index 6d0b5dbd26..5cb626f221 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentArrayHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentArrayHolder.java @@ -129,6 +129,13 @@ public class PersistentArrayHolder extends AbstractPersistentCollection { return result; } + @Override + public void initializeEmptyCollection(CollectionPersister persister) { + assert array == null; + array = Array.newInstance( persister.getElementClass(), 0 ); + endRead(); + } + @Override public void injectLoadedState(PluralAttributeMapping attributeMapping, List loadingState) { assert isInitializing(); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentBag.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentBag.java index e5b4578fa2..d44efb3bb6 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentBag.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentBag.java @@ -260,6 +260,13 @@ public class PersistentBag extends AbstractPersistentCollection implements List return getOrphans( sn, bag, entityName, getSession() ); } + @Override + public void initializeEmptyCollection(CollectionPersister persister) { + assert bag == null; + bag = (List) persister.getCollectionType().instantiate( 0 ); + endRead(); + } + @Override public Object disassemble(CollectionPersister persister) { final int length = bag.size(); @@ -294,7 +301,6 @@ public class PersistentBag extends AbstractPersistentCollection implements List return !persister.isOneToMany(); } - // For a one-to-many, a is not really a bag; // it is *really* a set, since it can't contain the // same element twice. It could be considered a bug diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentIdentifierBag.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentIdentifierBag.java index 8537ca3f78..c24080afc7 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentIdentifierBag.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentIdentifierBag.java @@ -372,6 +372,14 @@ public class PersistentIdentifierBag extends AbstractPersistentCollection implem return getOrphans( sn.values(), values, entityName, getSession() ); } + @Override + public void initializeEmptyCollection(CollectionPersister persister) { + assert identifiers == null; + identifiers = new HashMap<>(); + values = new ArrayList<>(); + endRead(); + } + @Override public void preInsert(CollectionPersister persister) throws HibernateException { final Iterator itr = values.iterator(); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentList.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentList.java index 9bf31ebb8e..abeb538bda 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentList.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentList.java @@ -98,6 +98,13 @@ public class PersistentList extends AbstractPersistentCollection implements List return getOrphans( sn, list, entityName, getSession() ); } + @Override + public void initializeEmptyCollection(CollectionPersister persister) { + assert list == null; + list = (List) persister.getCollectionType().instantiate( 0 ); + endRead(); + } + @Override public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException { final Type elementType = persister.getElementType(); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentMap.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentMap.java index 2c9958e250..3fb15a3a4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentMap.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentMap.java @@ -109,6 +109,13 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { return getOrphans( sn.values(), map.values(), entityName, getSession() ); } + @Override + public void initializeEmptyCollection(CollectionPersister persister) { + assert map == null; + map = (Map) persister.getCollectionType().instantiate( 0 ); + endRead(); + } + @Override public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException { final Type elementType = persister.getElementType(); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentSet.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentSet.java index aec16f0dd9..3310ffbee5 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentSet.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/PersistentSet.java @@ -113,6 +113,13 @@ public class PersistentSet extends AbstractPersistentCollection implements java. return getOrphans( sn.keySet(), set, entityName, getSession() ); } + @Override + public void initializeEmptyCollection(CollectionPersister persister) { + assert set == null; + set = (Set) persister.getCollectionType().instantiate( 0 ); + endRead(); + } + @Override public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException { final Type elementType = persister.getElementType(); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java index 2d7fa37481..fa6defe56e 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentCollection.java @@ -447,4 +447,6 @@ public interface PersistentCollection { * @return The orphans */ Collection getOrphans(Serializable snapshot, String entityName); + + void initializeEmptyCollection(CollectionPersister persister); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java index 78e4a62050..df2f4fd1b5 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultInitializeCollectionEventListener.java @@ -72,6 +72,7 @@ public class DefaultInitializeCollectionEventListener implements InitializeColle LOG.trace( "Collection not cached" ); } ceLoadedPersister.initialize( ce.getLoadedKey(), source ); + handlePotentiallyEmptyCollection( collection, source, ce, ceLoadedPersister ); if ( LOG.isTraceEnabled() ) { LOG.trace( "Collection initialized" ); } @@ -86,6 +87,21 @@ public class DefaultInitializeCollectionEventListener implements InitializeColle } } + private void handlePotentiallyEmptyCollection( + PersistentCollection collection, + SessionImplementor source, + CollectionEntry ce, CollectionPersister ceLoadedPersister) { + if ( !collection.wasInitialized() ) { + collection.initializeEmptyCollection( ceLoadedPersister ); + org.hibernate.sql.results.internal.Helper.finalizeCollectionLoading( + source.getPersistenceContext(), + ceLoadedPersister, + collection, + ce.getLoadedKey() + ); + } + } + /** * Try to initialize a collection from the cache * diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/Helper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/Helper.java index 75dbba1da9..b09ad45c40 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/Helper.java @@ -10,7 +10,12 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.BatchFetchQueue; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.results.ResultsLogger; import org.hibernate.sql.results.graph.DomainResultAssembler; @@ -58,4 +63,31 @@ public class Helper { } } + public static void finalizeCollectionLoading( + PersistenceContext persistenceContext, + CollectionPersister collectionDescriptor, + PersistentCollection collectionInstance, + Object key) { + CollectionEntry collectionEntry = persistenceContext.getCollectionEntry( collectionInstance ); + if ( collectionEntry == null ) { + collectionEntry = persistenceContext.addInitializedCollection( + collectionDescriptor, + collectionInstance, + key + ); + } + else { + collectionEntry.postInitialize( collectionInstance ); + } + + if ( collectionDescriptor.getCollectionType().hasHolder() ) { + persistenceContext.addCollectionHolder( collectionInstance ); + } + + final BatchFetchQueue batchFetchQueue = persistenceContext.getBatchFetchQueue(); + batchFetchQueue.removeBatchLoadableCollection( collectionEntry ); + + // todo (6.0) : there is other logic still needing to be implemented here. caching, etc + // see org.hibernate.engine.loading.internal.CollectionLoadContext#endLoadingCollection in 5.x + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/LoadingCollectionEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/LoadingCollectionEntryImpl.java index 7c3c2c9372..4a3112bb22 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/LoadingCollectionEntryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/LoadingCollectionEntryImpl.java @@ -11,10 +11,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import org.hibernate.collection.internal.PersistentArrayHolder; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.BatchFetchQueue; -import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.collection.CollectionPersister; @@ -90,27 +87,7 @@ public class LoadingCollectionEntryImpl implements LoadingCollectionEntry { final PersistenceContext persistenceContext = session.getPersistenceContext(); final CollectionPersister collectionDescriptor = getCollectionDescriptor(); - CollectionEntry collectionEntry = persistenceContext.getCollectionEntry( collectionInstance ); - if ( collectionEntry == null ) { - collectionEntry = persistenceContext.addInitializedCollection( - collectionDescriptor, - getCollectionInstance(), - getKey() - ); - } - else { - collectionEntry.postInitialize( collectionInstance ); - } - - if ( collectionDescriptor.getCollectionType().hasHolder() ) { - persistenceContext.addCollectionHolder( collectionInstance ); - } - - final BatchFetchQueue batchFetchQueue = persistenceContext.getBatchFetchQueue(); - batchFetchQueue.removeBatchLoadableCollection( collectionEntry ); - - // todo (6.0) : there is other logic still needing to be implemented here. caching, etc - // see org.hibernate.engine.loading.internal.CollectionLoadContext#endLoadingCollection in 5.x + Helper.finalizeCollectionLoading( persistenceContext, collectionDescriptor, collectionInstance, getKey() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java index 90bf5c0e70..2a549eb54b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java @@ -13,7 +13,6 @@ import org.hibernate.query.named.RowReaderMemento; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.results.graph.collection.CollectionInitializer; import org.hibernate.sql.results.graph.DomainResultAssembler; -import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; @@ -52,14 +51,6 @@ public class StandardRowReader implements RowReader { this.callback = callback; this.resultRow = new Object[assemblerCount]; - - for ( int i = 0; i < initializers.size(); i++ ) { - final Initializer initializer = initializers.get( i ); - if ( initializer instanceof EntityInitializer ) { - final EntityInitializer entityInitializer = (EntityInitializer) initializer; - - } - } } @Override