diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/EnumeratedValueResolution.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/EnumeratedValueResolution.java index 83352b9f2a..99e453d996 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/EnumeratedValueResolution.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/EnumeratedValueResolution.java @@ -36,7 +36,8 @@ public class EnumeratedValueResolution> implements BasicValue. this.domainJtd = domainJtd; this.jdbcJtd = jdbcJtd; this.std = std; - this.valueConverter = valueConverter; +// this.valueConverter = valueConverter; + this.valueConverter = null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java index e4e4aa8faa..7bd6a96c05 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java @@ -25,6 +25,7 @@ import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.MetadataSourceType; +import org.hibernate.collection.internal.StandardCollectionSemanticsResolver; import org.hibernate.collection.spi.CollectionSemanticsResolver; import org.hibernate.dialect.function.SQLFunction; import org.hibernate.metamodel.internal.StandardManagedTypeRepresentationResolver; @@ -61,7 +62,8 @@ public interface MetadataBuildingOptions { } default CollectionSemanticsResolver getPersistentCollectionRepresentationResolver() { - throw new NotYetImplementedFor6Exception( getClass() ); + // for now always return the standard one + return StandardCollectionSemanticsResolver.INSTANCE; } /** 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 866766afef..d72e0d1f00 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 @@ -21,6 +21,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; import org.jboss.logging.Logger; @@ -190,6 +192,31 @@ public class PersistentArrayHolder extends AbstractPersistentCollection { return element; } + @Override + public Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException { + assert elementAssembler != null; + assert indexAssembler != null; + assert identifierAssembler == null; + + final Object element = elementAssembler.assemble( rowProcessingState ); + final int index = (int) indexAssembler.assemble( rowProcessingState ); + + for ( int i = tempList.size(); i<=index; i++) { + //noinspection unchecked + tempList.add( i, null ); + } + + //noinspection unchecked + tempList.set( index, element ); + + return element; + } + @Override @SuppressWarnings("unchecked") public Iterator entries(CollectionPersister persister) { 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 d455770a08..b0a7c5d3ae 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 @@ -22,6 +22,8 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; /** @@ -133,6 +135,24 @@ public class PersistentBag extends AbstractPersistentCollection implements List return element; } + @Override + public Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException { + assert indexAssembler == null; + assert identifierAssembler == null; + + final Object element = elementAssembler.assemble( rowProcessingState ); + if ( element != null ) { + //noinspection unchecked + bag.add( element ); + } + return element; + } + @Override public void beforeInitialize(CollectionPersister persister, int anticipatedSize) { this.bag = (List) persister.getCollectionType().instantiate( anticipatedSize ); 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 ddd1227efa..13893a24c8 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 @@ -22,6 +22,8 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; /** @@ -372,6 +374,30 @@ public class PersistentIdentifierBag extends AbstractPersistentCollection implem return element; } + @Override + public Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException { + assert indexAssembler == null; + final Object element = elementAssembler.assemble( rowProcessingState ); + final Object identifier = identifierAssembler.assemble( rowProcessingState ); + + final Object old = identifiers.put( + values.size(), + identifier + ); + + if ( old == null ) { + //maintain correct duplication if loaded in a cartesian product + values.add( element ); + } + + return element; + } + @Override @SuppressWarnings("unchecked") public Serializable getSnapshot(CollectionPersister persister) throws HibernateException { 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 1a8cc4804c..39bd56c40b 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 @@ -20,6 +20,8 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; /** @@ -206,9 +208,9 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public boolean containsAll(Collection coll) { read(); + //noinspection unchecked return list.containsAll( coll ); } @@ -262,9 +264,9 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public boolean retainAll(Collection coll) { initialize( true ); + //noinspection unchecked if ( list.retainAll( coll ) ) { dirty(); return true; @@ -275,7 +277,6 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public void clear() { if ( isClearQueueEnabled() ) { queueOperation( new Clear() ); @@ -290,7 +291,6 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public Object get(int index) { if ( index < 0 ) { throw new ArrayIndexOutOfBoundsException( "negative index" ); @@ -300,7 +300,6 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public Object set(int index, Object value) { if (index<0) { throw new ArrayIndexOutOfBoundsException("negative index"); @@ -310,6 +309,7 @@ public class PersistentList extends AbstractPersistentCollection implements List if ( old==UNKNOWN ) { write(); + //noinspection unchecked return list.set( index, value ); } else { @@ -319,7 +319,6 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public Object remove(int index) { if ( index < 0 ) { throw new ArrayIndexOutOfBoundsException( "negative index" ); @@ -338,45 +337,40 @@ public class PersistentList extends AbstractPersistentCollection implements List } @Override - @SuppressWarnings("unchecked") public void add(int index, Object value) { if ( index < 0 ) { throw new ArrayIndexOutOfBoundsException( "negative index" ); } write(); + //noinspection unchecked list.add( index, value ); } @Override - @SuppressWarnings("unchecked") public int indexOf(Object value) { read(); return list.indexOf( value ); } @Override - @SuppressWarnings("unchecked") public int lastIndexOf(Object value) { read(); return list.lastIndexOf( value ); } @Override - @SuppressWarnings("unchecked") public ListIterator listIterator() { read(); return new ListIteratorProxy( list.listIterator() ); } @Override - @SuppressWarnings("unchecked") public ListIterator listIterator(int index) { read(); return new ListIteratorProxy( list.listIterator( index ) ); } @Override - @SuppressWarnings("unchecked") public java.util.List subList(int from, int to) { read(); return new ListProxy( list.subList( from, to ) ); @@ -409,6 +403,32 @@ public class PersistentList extends AbstractPersistentCollection implements List return element; } + @Override + public Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException { + assert elementAssembler != null; + assert indexAssembler != null; + assert identifierAssembler == null; + + final Object element = elementAssembler.assemble( rowProcessingState ); + final int index = (int) indexAssembler.assemble( rowProcessingState ); + + //pad with nulls from the current last element up to the new index + for ( int i = list.size(); i<=index; i++) { + //noinspection unchecked + list.add( i, null ); + } + + //noinspection unchecked + list.set( index, element ); + + return element; + } + @Override @SuppressWarnings("unchecked") public Iterator entries(CollectionPersister persister) { 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 0976a3cf7e..5506ac2a1b 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 @@ -22,6 +22,8 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; @@ -176,7 +178,6 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { } @Override - @SuppressWarnings("unchecked") public Object put(Object key, Object value) { if ( isPutQueueEnabled() ) { final Object old = readElementByIndex( key ); @@ -186,6 +187,7 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { } } initialize( true ); + //noinspection unchecked final Object old = map.put( key, value ); // would be better to use the element-type to determine // whether the old and the new are equal here; the problem being @@ -198,7 +200,6 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { } @Override - @SuppressWarnings("unchecked") public Object remove(Object key) { if ( isPutQueueEnabled() ) { final Object old = readElementByIndex( key ); @@ -218,7 +219,6 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { } @Override - @SuppressWarnings("unchecked") public void putAll(Map puts) { if ( puts.size() > 0 ) { initialize( true ); @@ -230,7 +230,6 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { } @Override - @SuppressWarnings("unchecked") public void clear() { if ( isClearQueueEnabled() ) { queueOperation( new Clear() ); @@ -245,34 +244,29 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { } @Override - @SuppressWarnings("unchecked") public Set keySet() { read(); return new SetProxy( map.keySet() ); } @Override - @SuppressWarnings("unchecked") public Collection values() { read(); return new SetProxy( map.values() ); } @Override - @SuppressWarnings("unchecked") public Set entrySet() { read(); return new EntrySetProxy( map.entrySet() ); } @Override - @SuppressWarnings("unchecked") public boolean empty() { return map.isEmpty(); } @Override - @SuppressWarnings("unchecked") public String toString() { read(); return map.toString(); @@ -281,7 +275,6 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { private transient List loadingEntries; @Override - @SuppressWarnings("unchecked") public Object readFrom( ResultSet rs, CollectionPersister persister, @@ -298,6 +291,30 @@ public class PersistentMap extends AbstractPersistentCollection implements Map { return element; } + @Override + public Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException { + assert elementAssembler != null; + assert indexAssembler != null; + assert identifierAssembler == null; + + final Object element = elementAssembler.assemble( rowProcessingState ); + + if ( element != null ) { + final Object index = indexAssembler.assemble( rowProcessingState ); + if ( loadingEntries == null ) { + loadingEntries = new ArrayList<>(); + } + loadingEntries.add( new Object[] { index, element } ); + } + + return element; + } + @Override @SuppressWarnings("unchecked") public boolean endRead() { 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 0f9a77f72f..753ce80af5 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 @@ -21,6 +21,8 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; @@ -342,6 +344,27 @@ public class PersistentSet extends AbstractPersistentCollection implements java. return element; } + @Override + public Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException { + assert elementAssembler != null; + assert indexAssembler == null; + assert identifierAssembler == null; + + final Object element = elementAssembler.assemble( rowProcessingState ); + + if ( element != null ) { + //noinspection unchecked + tempList.add( element ); + } + + return element; + } + @Override @SuppressWarnings("unchecked") public void beginRead() { diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardCollectionSemanticsResolver.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardCollectionSemanticsResolver.java new file mode 100644 index 0000000000..6b5119a3ee --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardCollectionSemanticsResolver.java @@ -0,0 +1,84 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.collection.internal; + +import org.hibernate.MappingException; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.collection.spi.CollectionSemantics; +import org.hibernate.collection.spi.CollectionSemanticsResolver; +import org.hibernate.mapping.Array; +import org.hibernate.mapping.Bag; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.IdentifierBag; +import org.hibernate.mapping.List; +import org.hibernate.mapping.Map; +import org.hibernate.mapping.PrimitiveArray; +import org.hibernate.mapping.Set; + +/** + * Standard implementation of CollectionSemanticsResolver + * + * @author Steve Ebersole + */ +public class StandardCollectionSemanticsResolver implements CollectionSemanticsResolver { + /** + * Singleton access + */ + public static final StandardCollectionSemanticsResolver INSTANCE = new StandardCollectionSemanticsResolver(); + + @Override + public CollectionSemantics resolveRepresentation(Collection bootDescriptor) { + if ( bootDescriptor instanceof PrimitiveArray ) { + throw new NotYetImplementedFor6Exception(); + } + + if ( bootDescriptor instanceof Array ) { + return StandardArraySemantics.INSTANCE; + } + + if ( bootDescriptor instanceof Bag ) { + return StandardBagSemantics.INSTANCE; + } + + if ( bootDescriptor instanceof IdentifierBag ) { + return StandardIdentifierBagSemantics.INSTANCE; + } + + if ( bootDescriptor instanceof List ) { + return StandardListSemantics.INSTANCE; + } + + if ( bootDescriptor instanceof Map ) { + if ( bootDescriptor.isSorted() ) { + return StandardSortedMapSemantics.INSTANCE; + } + + if ( bootDescriptor.hasOrder() ) { + return StandardOrderedMapSemantics.INSTANCE; + } + + return StandardMapSemantics.INSTANCE; + } + + if ( bootDescriptor instanceof Set ) { + if ( bootDescriptor.isSorted() ) { + return StandardSortedSetSemantics.INSTANCE; + } + + if ( bootDescriptor.hasOrder() ) { + return StandardOrderedSetSemantics.INSTANCE; + } + + return StandardSetSemantics.INSTANCE; + } + + throw new MappingException( + "Unexpected org.hibernate.mapping.Collection impl [" + + bootDescriptor + "]; unknown CollectionSemantics" + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java index 33078e0dd4..b3b2e7f4aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java @@ -9,6 +9,7 @@ package org.hibernate.collection.spi; import java.util.Iterator; import java.util.function.Consumer; +import org.hibernate.Incubating; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.CollectionClassification; import org.hibernate.persister.collection.CollectionPersister; @@ -22,6 +23,7 @@ import org.hibernate.persister.collection.CollectionPersister; * @author Steve Ebersole * @author Gavin King */ +@Incubating public interface CollectionSemantics { /** * Get the classification of collections described by this semantic diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemanticsResolver.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemanticsResolver.java index 69913b3354..4a62eea102 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemanticsResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemanticsResolver.java @@ -6,9 +6,12 @@ */ package org.hibernate.collection.spi; +import org.hibernate.Incubating; import org.hibernate.mapping.Collection; /** + * Resolve the collection semantics for the given mapped collection + * * todo (6.0) ... * * Ideally would act as the contract that allows pluggable resolution of @@ -17,6 +20,7 @@ import org.hibernate.mapping.Collection; * * @author Steve Ebersole */ +@Incubating public interface CollectionSemanticsResolver { // really need some form of access to the attribute site CollectionSemantics resolveRepresentation(Collection bootDescriptor); 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 24032cee22..0c3c0d2f12 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 @@ -16,6 +16,8 @@ import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.CollectionAliases; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.RowProcessingState; import org.hibernate.type.Type; /** @@ -173,6 +175,13 @@ public interface PersistentCollection { Object readFrom(ResultSet rs, CollectionPersister role, CollectionAliases descriptor, Object owner) throws HibernateException, SQLException; + Object readFrom( + RowProcessingState rowProcessingState, + DomainResultAssembler elementAssembler, + DomainResultAssembler indexAssembler, + DomainResultAssembler identifierAssembler, + Object owner) throws HibernateException; + /** * Get the identifier of the given collection entry. This refers to the collection identifier, not the * identifier of the (possibly) entity elements. This is only valid for invocation on the diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionIdentifierDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionIdentifierDescriptor.java new file mode 100644 index 0000000000..a33f269d6f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionIdentifierDescriptor.java @@ -0,0 +1,25 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping; + +import org.hibernate.metamodel.CollectionClassification; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.spi.DomainResult; +import org.hibernate.sql.results.spi.DomainResultCreationState; + +/** + * Descriptor for the collection identifier. Only used with {@link CollectionClassification#IDBAG} collections + * + * @author Steve Ebersole + */ +public interface CollectionIdentifierDescriptor { + DomainResult createDomainResult( + NavigablePath collectionPath, + TableGroup tableGroup, + DomainResultCreationState creationState); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionMappingType.java index 0197886154..08775d3001 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionMappingType.java @@ -15,5 +15,5 @@ import org.hibernate.collection.spi.CollectionSemantics; * @author Steve Ebersole */ public interface CollectionMappingType extends MappingType { - CollectionSemantics getCCollectionSemantics(); + CollectionSemantics getCollectionSemantics(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionPart.java new file mode 100644 index 0000000000..3e29b639b7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/CollectionPart.java @@ -0,0 +1,48 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping; + +import org.hibernate.sql.results.spi.Fetchable; + +/** + * @author Steve Ebersole + */ +public interface CollectionPart extends ModelPart, Fetchable { + enum Nature { + ELEMENT( "{element}" ), + INDEX( "{index}" ); + + private final String name; + + Nature(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Nature fromName(String name) { + if ( ELEMENT.name.equals( name ) ) { + return ELEMENT; + } + else if ( INDEX.name.equals( name ) ) { + return INDEX; + } + + throw new IllegalArgumentException( + "Unrecognized CollectionPart Nature name [" + name + + "]; expecting `" + ELEMENT.name + "` or `" + + INDEX.name + "`" + ); + } + } + + Nature getNature(); + + ModelPart getPartTypeDescriptor(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java index ed89209fcc..c5c6e7e057 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java @@ -102,6 +102,7 @@ public class EmbeddableMappingType implements ManagedMappingType { CompositeType compositeType, MappingModelCreationProcess creationProcess) { final String containingTableExpression = valueMapping.getContainingTableExpression(); + final List mappedColumnExpressions = valueMapping.getMappedColumnExpressions(); final Type[] subtypes = compositeType.getSubtypes(); @@ -196,6 +197,11 @@ public class EmbeddableMappingType implements ManagedMappingType { throw new NotYetImplementedFor6Exception( getClass() ); } + @Override + public int getNumberOfFetchables() { + return attributeMappings.size(); + } + @Override public void visitFetchables( Consumer fetchableConsumer, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java index 2aae1e9f29..c7c0898f85 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java @@ -14,12 +14,13 @@ import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer; +import org.hibernate.sql.results.spi.Fetchable; import org.hibernate.sql.results.spi.FetchableContainer; /** * @author Steve Ebersole */ -public interface EmbeddableValuedModelPart extends ModelPart, FetchableContainer, TableGroupJoinProducer { +public interface EmbeddableValuedModelPart extends ModelPart, Fetchable, FetchableContainer, TableGroupJoinProducer { EmbeddableMappingType getEmbeddableTypeDescriptor(); /** diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java new file mode 100644 index 0000000000..723a08e0df --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java @@ -0,0 +1,19 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping; + +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.spi.DomainResult; +import org.hibernate.sql.results.spi.DomainResultCreationState; + +/** + * @author Steve Ebersole + */ +public interface ForeignKeyDescriptor { + DomainResult createDomainResult(NavigablePath collectionPath, TableGroup tableGroup, DomainResultCreationState creationState); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java index 27e53b5283..36325884a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java @@ -6,15 +6,39 @@ */ package org.hibernate.metamodel.mapping; +import java.util.function.Consumer; + import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer; +import org.hibernate.sql.results.spi.Fetchable; +import org.hibernate.sql.results.spi.FetchableContainer; /** * @author Steve Ebersole */ -public interface PluralAttributeMapping extends AttributeMapping, StateArrayContributorMapping { +public interface PluralAttributeMapping + extends AttributeMapping, StateArrayContributorMapping, TableGroupJoinProducer, FetchableContainer { + CollectionPersister getCollectionDescriptor(); - ModelPart getValueDescriptor(); + ForeignKeyDescriptor getKeyDescriptor(); - ModelPart getIndexDescriptor(); + CollectionPart getIndexDescriptor(); + + CollectionPart getElementDescriptor(); + + CollectionIdentifierDescriptor getIdentifierDescriptor(); + + @Override + default void visitKeyFetchables(Consumer fetchableConsumer, EntityMappingType treatTargetType) { + final CollectionPart indexDescriptor = getIndexDescriptor(); + if ( indexDescriptor != null ) { + fetchableConsumer.accept( indexDescriptor ); + } + } + + @Override + default void visitFetchables(Consumer fetchableConsumer, EntityMappingType treatTargetType) { + fetchableConsumer.accept( getElementDescriptor() ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java index 91c65dc53f..4473f339e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java @@ -13,14 +13,17 @@ import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; +import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.SqlResultsLogger; import org.hibernate.sql.results.internal.domain.basic.BasicFetch; import org.hibernate.sql.results.internal.domain.basic.BasicResult; import org.hibernate.sql.results.spi.DomainResult; @@ -36,13 +39,24 @@ import org.hibernate.type.spi.TypeConfiguration; * * @author Steve Ebersole */ -public class BasicValuedCollectionPart implements BasicValuedModelPart { +public class BasicValuedCollectionPart implements CollectionPart, BasicValuedModelPart { + + private final CollectionPersister collectionDescriptor; + private final Nature nature; + private final BasicType mapper; + private final BasicValueConverter valueConverter; + + private final String tableExpression; + private final String columnExpression; + public BasicValuedCollectionPart( + CollectionPersister collectionDescriptor, Nature nature, BasicType mapper, BasicValueConverter valueConverter, String tableExpression, String columnExpression) { + this.collectionDescriptor = collectionDescriptor; this.nature = nature; this.mapper = mapper; this.valueConverter = valueConverter; @@ -50,14 +64,15 @@ public class BasicValuedCollectionPart implements BasicValuedModelPart { this.columnExpression = columnExpression; } - enum Nature { ELEMENT, INDEX } + @Override + public Nature getNature() { + return nature; + } - private final Nature nature; - private final BasicType mapper; - private final BasicValueConverter valueConverter; - - private final String tableExpression; - private final String columnExpression; + @Override + public BasicType getPartTypeDescriptor() { + return mapper; + } @Override public String getContainingTableExpression() { @@ -91,7 +106,9 @@ public class BasicValuedCollectionPart implements BasicValuedModelPart { return new BasicResult( sqlSelection.getValuesArrayPosition(), resultVariable, - getJavaTypeDescriptor() + getJavaTypeDescriptor(), + valueConverter, + navigablePath ); } @@ -100,9 +117,9 @@ public class BasicValuedCollectionPart implements BasicValuedModelPart { return exprResolver.resolveSqlSelection( exprResolver.resolveSqlExpression( - SqlExpressionResolver.createColumnReferenceKey( tableExpression, columnExpression ), + SqlExpressionResolver.createColumnReferenceKey( tableGroup.getPrimaryTableReference(), columnExpression ), sqlAstProcessingState -> new ColumnReference( - tableGroup.resolveTableReference( tableExpression ).getIdentificationVariable(), + tableGroup.getPrimaryTableReference().getIdentificationVariable(), columnExpression, mapper, creationState.getSqlAstCreationState().getCreationContext().getSessionFactory() @@ -148,6 +165,12 @@ public class BasicValuedCollectionPart implements BasicValuedModelPart { LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { + SqlResultsLogger.INSTANCE.debugf( + "Generating Fetch for collection-part : `%s` -> `%s`", + collectionDescriptor.getRole(), + nature.getName() + ); + final SqlSelection sqlSelection = resolveSqlSelection( creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( fetchablePath.getParent() ), creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java new file mode 100644 index 0000000000..64bf6898f0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java @@ -0,0 +1,83 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping.internal; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.sql.SqlAstCreationState; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.spi.SqlSelection; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.internal.domain.basic.BasicResult; +import org.hibernate.sql.results.spi.DomainResult; +import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.type.BasicType; + +/** + * @author Steve Ebersole + */ +public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierDescriptor { + private final CollectionPersister collectionDescriptor; + private final String columnName; + private final BasicType type; + + public CollectionIdentifierDescriptorImpl( + CollectionPersister collectionDescriptor, + String columnName, + BasicType type) { + this.collectionDescriptor = collectionDescriptor; + this.columnName = columnName; + this.type = type; + } + + @Override + public DomainResult createDomainResult( + NavigablePath collectionPath, + TableGroup tableGroup, + DomainResultCreationState creationState) { + + + final SqlAstCreationState astCreationState = creationState.getSqlAstCreationState(); + final SqlAstCreationContext astCreationContext = astCreationState.getCreationContext(); + final SessionFactoryImplementor sessionFactory = astCreationContext.getSessionFactory(); + final SqlExpressionResolver sqlExpressionResolver = astCreationState.getSqlExpressionResolver(); + + final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection( + sqlExpressionResolver.resolveSqlExpression( + SqlExpressionResolver.createColumnReferenceKey( + tableGroup.getPrimaryTableReference(), + columnName + ), + p -> new ColumnReference( + tableGroup.getPrimaryTableReference().getIdentificationVariable(), + columnName, + type, + sessionFactory + ) + ), + type.getJavaTypeDescriptor(), + sessionFactory.getTypeConfiguration() + ); + + //noinspection unchecked + return new BasicResult( + sqlSelection.getValuesArrayPosition(), + null, + type.getJavaTypeDescriptor(), + collectionPath + ); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + collectionDescriptor.getRole() + ")"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index bb82f8f401..a0df6b9dcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -31,6 +31,7 @@ import org.hibernate.query.sqm.sql.SqlExpressionResolver; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.tree.expression.ColumnReference; @@ -39,7 +40,9 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.CompositeTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.sql.results.internal.domain.composite.CompositeFetch; import org.hibernate.sql.results.internal.domain.composite.CompositeResult; import org.hibernate.sql.results.spi.DomainResult; @@ -162,6 +165,7 @@ public class EmbeddedAttributeMapping this, fetchParent, fetchTiming, + getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), creationState ); } @@ -245,8 +249,44 @@ public class EmbeddedAttributeMapping ); } + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + getEmbeddableTypeDescriptor().visitAttributeMappings( + attrMapping -> { + if ( attrMapping instanceof TableGroupProducer ) { + ( (TableGroupProducer) attrMapping ).applyTableReferences( + sqlAliasBase, + baseJoinType, + collector, + sqlExpressionResolver, + creationContext + ); + } + else if ( attrMapping.getMappedTypeDescriptor() instanceof TableGroupProducer ) { + ( (TableGroupProducer) attrMapping.getMappedTypeDescriptor() ).applyTableReferences( + sqlAliasBase, + baseJoinType, + collector, + sqlExpressionResolver, + creationContext + ); + } + } + ); + } + @Override public String getSqlAliasStem() { return getAttributeName(); } + + @Override + public int getNumberOfFetchables() { + return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java new file mode 100644 index 0000000000..5536337942 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -0,0 +1,189 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping.internal; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.LockMode; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchTiming; +import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.SingularAttributeMapping; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.sql.SqlAstCreationState; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; +import org.hibernate.sql.results.internal.domain.composite.CompositeFetch; +import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Steve Ebersole + */ +public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedModelPart { + private final CollectionPersister collectionDescriptor; + private final Nature nature; + private final EmbeddableMappingType embeddableMappingType; + + private final String containingTableExpression; + + private final SingularAttributeMapping parentInjectionAttribute; + private final List columnExpressions; + private final String sqlAliasStem; + + @SuppressWarnings("WeakerAccess") + public EmbeddedCollectionPart( + CollectionPersister collectionDescriptor, + Nature nature, + EmbeddableMappingType embeddableMappingType, + SingularAttributeMapping parentInjectionAttribute, + String containingTableExpression, + List columnExpressions, + String sqlAliasStem) { + this.collectionDescriptor = collectionDescriptor; + this.nature = nature; + this.embeddableMappingType = embeddableMappingType; + this.parentInjectionAttribute = parentInjectionAttribute; + this.containingTableExpression = containingTableExpression; + this.columnExpressions = columnExpressions; + this.sqlAliasStem = sqlAliasStem; + } + + @Override + public Nature getNature() { + return nature; + } + + @Override + public EmbeddableMappingType getEmbeddableTypeDescriptor() { + return embeddableMappingType; + } + + @Override + public EmbeddableMappingType getPartTypeDescriptor() { + return getEmbeddableTypeDescriptor(); + } + + @Override + public String getContainingTableExpression() { + return containingTableExpression; + } + + @Override + public List getMappedColumnExpressions() { + return columnExpressions; + } + + @Override + public SingularAttributeMapping getParentInjectionAttributeMapping() { + return parentInjectionAttribute; + } + + @Override + public String getFetchableName() { + return getNature().getName(); + } + + @Override + public FetchStrategy getMappedFetchStrategy() { + return FetchStrategy.IMMEDIATE_JOIN; + } + + @Override + public Fetch generateFetch( + FetchParent fetchParent, + NavigablePath fetchablePath, + FetchTiming fetchTiming, + boolean selected, + LockMode lockMode, + String resultVariable, + DomainResultCreationState creationState) { + return new CompositeFetch( + fetchablePath, + this, + fetchParent, + FetchTiming.IMMEDIATE, + false, + creationState + ); + } + + @Override + public Expression toSqlExpression( + TableGroup tableGroup, + Clause clause, + SqmToSqlAstConverter walker, + SqlAstCreationState sqlAstCreationState) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public TableGroupJoin createTableGroupJoin( + NavigablePath navigablePath, + TableGroup lhs, + String explicitSourceAlias, + JoinType joinType, + LockMode lockMode, + SqlAliasBaseGenerator aliasBaseGenerator, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public String getSqlAliasStem() { + return sqlAliasStem; + } + + @Override + public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { + return getEmbeddableTypeDescriptor().findSubPart( name, treatTargetType ); + } + + @Override + public void visitSubParts(Consumer consumer, EntityMappingType treatTargetType) { + getEmbeddableTypeDescriptor().visitSubParts( consumer, treatTargetType ); + } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return getEmbeddableTypeDescriptor().getJavaTypeDescriptor(); + } + + @Override + public int getNumberOfFetchables() { + return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java new file mode 100644 index 0000000000..3696c80167 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java @@ -0,0 +1,81 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping.internal; + +import org.hibernate.LockMode; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.FetchStrategy; +import org.hibernate.engine.FetchTiming; +import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityValuedModelPart; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Steve Ebersole + */ +public class EntityCollectionPart implements CollectionPart, EntityValuedModelPart { + private final Nature nature; + private final EntityMappingType entityMappingType; + + @SuppressWarnings("WeakerAccess") + public EntityCollectionPart(Nature nature, EntityMappingType entityMappingType) { + this.nature = nature; + this.entityMappingType = entityMappingType; + } + + @Override + public Nature getNature() { + return nature; + } + + @Override + public EntityMappingType getPartTypeDescriptor() { + return getEntityMappingType(); + } + + @Override + public EntityMappingType getEntityMappingType() { + return entityMappingType; + } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return getEntityMappingType().getJavaTypeDescriptor(); + } + + @Override + public String getFetchableName() { + return nature.getName(); + } + + @Override + public FetchStrategy getMappedFetchStrategy() { + return FetchStrategy.IMMEDIATE_JOIN; + } + + @Override + public Fetch generateFetch( + FetchParent fetchParent, + NavigablePath fetchablePath, + FetchTiming fetchTiming, + boolean selected, + LockMode lockMode, + String resultVariable, + DomainResultCreationState creationState) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public int getNumberOfFetchables() { + return entityMappingType.getNumberOfFetchables(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index 9302e1fed8..84ec6ad2cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -7,6 +7,10 @@ package org.hibernate.metamodel.mapping.internal; import java.io.Serializable; +import java.util.Iterator; +import java.util.List; +import java.util.SortedMap; +import java.util.SortedSet; import java.util.function.Consumer; import org.hibernate.LockMode; @@ -16,36 +20,35 @@ import org.hibernate.collection.internal.StandardArraySemantics; import org.hibernate.collection.internal.StandardBagSemantics; import org.hibernate.collection.internal.StandardIdentifierBagSemantics; import org.hibernate.collection.internal.StandardListSemantics; -import org.hibernate.collection.internal.StandardMapSemantics; -import org.hibernate.collection.internal.StandardOrderedMapSemantics; -import org.hibernate.collection.internal.StandardOrderedSetSemantics; -import org.hibernate.collection.internal.StandardSetSemantics; -import org.hibernate.collection.internal.StandardSortedMapSemantics; -import org.hibernate.collection.internal.StandardSortedSetSemantics; import org.hibernate.collection.spi.CollectionSemantics; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.mapping.Array; -import org.hibernate.mapping.Bag; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Component; -import org.hibernate.mapping.IdentifierBag; -import org.hibernate.mapping.List; -import org.hibernate.mapping.Map; +import org.hibernate.mapping.IndexedCollection; +import org.hibernate.mapping.OneToMany; import org.hibernate.mapping.PersistentClass; -import org.hibernate.mapping.PrimitiveArray; import org.hibernate.mapping.Property; -import org.hibernate.mapping.Set; +import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.ToOne; +import org.hibernate.mapping.Value; +import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionMappingType; +import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.MappingType; @@ -54,15 +57,19 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMetadata; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; +import org.hibernate.metamodel.spi.DomainMetamodel; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.SQLLoadableCollection; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.persister.walking.internal.FetchStrategyHelper; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqlExpressionResolver; import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.spi.SqlAliasStemHelper; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; @@ -76,6 +83,7 @@ import org.hibernate.sql.results.spi.Fetch; import org.hibernate.sql.results.spi.FetchParent; import org.hibernate.type.BasicType; import org.hibernate.type.CompositeType; +import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.MutabilityPlan; @@ -622,75 +630,152 @@ public class MappingModelCreationHelper { final Collection bootValueMapping = (Collection) bootProperty.getValue(); final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext(); - final CollectionPersister collectionDescriptor = creationContext.getDomainModel().findCollectionDescriptor( bootValueMapping.getRole() ); + final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory(); + final Dialect dialect = sessionFactory.getJdbcServices().getJdbcEnvironment().getDialect(); + final DomainMetamodel domainModel = creationContext.getDomainModel(); + + final CollectionPersister collectionDescriptor = domainModel.findCollectionDescriptor( bootValueMapping.getRole() ); assert collectionDescriptor != null; + tableExpression = ( (Joinable) collectionDescriptor ).getTableName(); + + final String sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( bootProperty.getName() ); + final CollectionMappingType collectionMappingType; final JavaTypeDescriptorRegistry jtdRegistry = creationContext.getJavaTypeDescriptorRegistry(); - if ( bootValueMapping instanceof Array ) { - if ( bootValueMapping instanceof PrimitiveArray ) { - throw new NotYetImplementedFor6Exception(); - } - else { + final ForeignKeyDescriptor keyDescriptor = interpretKeyDescriptor( + bootProperty, + bootValueMapping, + dialect, + creationProcess + ); + + final CollectionPart elementDescriptor = interpretElement( + bootValueMapping, + tableExpression, + collectionDescriptor, + sqlAliasStem, + dialect, + creationProcess + ); + final CollectionPart indexDescriptor; + CollectionIdentifierDescriptor identifierDescriptor = null; + + final CollectionSemantics collectionSemantics = collectionDescriptor.getCollectionSemantics(); + switch ( collectionSemantics.getCollectionClassification() ) { + case ARRAY: { collectionMappingType = new CollectionMappingTypeImpl( jtdRegistry.getDescriptor( Object[].class ), StandardArraySemantics.INSTANCE ); + + final BasicValue index = (BasicValue) ( (IndexedCollection) bootValueMapping ).getIndex(); + indexDescriptor = new BasicValuedCollectionPart( + collectionDescriptor, + CollectionPart.Nature.INDEX, + creationContext.getTypeConfiguration().getBasicTypeForJavaType( Integer.class ), + // no converter + null, + tableExpression, + index.getColumnIterator().next().getText( dialect ) + ); + + break; } - } - else if ( bootValueMapping instanceof Bag ) { - collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.Collection.class ), - StandardBagSemantics.INSTANCE - ); - } - else if ( bootValueMapping instanceof IdentifierBag ) { - collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.Collection.class ), - StandardIdentifierBagSemantics.INSTANCE - ); - } - else if ( bootValueMapping instanceof List ) { - collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.List.class ), - StandardListSemantics.INSTANCE - ); - } - else if ( bootValueMapping instanceof Map ) { - if ( bootValueMapping.isSorted() ) { + case BAG: { collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.SortedMap.class ), - StandardSortedMapSemantics.INSTANCE + jtdRegistry.getDescriptor( java.util.Collection.class ), + StandardBagSemantics.INSTANCE + ); + + indexDescriptor = null; + + break; + } + case IDBAG: { + collectionMappingType = new CollectionMappingTypeImpl( + jtdRegistry.getDescriptor( java.util.Collection.class ), + StandardIdentifierBagSemantics.INSTANCE + ); + + indexDescriptor = null; + + assert collectionDescriptor instanceof SQLLoadableCollection; + final SQLLoadableCollection loadableCollection = (SQLLoadableCollection) collectionDescriptor; + final String identifierColumnName = loadableCollection.getIdentifierColumnName(); + assert identifierColumnName != null; + + identifierDescriptor = new CollectionIdentifierDescriptorImpl( + collectionDescriptor, + identifierColumnName, + (BasicType) loadableCollection.getIdentifierType() + ); + + break; + } + case LIST: { + final BasicValue index = (BasicValue) ( (IndexedCollection) bootValueMapping ).getIndex(); + + indexDescriptor = new BasicValuedCollectionPart( + collectionDescriptor, + CollectionPart.Nature.INDEX, + creationContext.getTypeConfiguration().getBasicTypeForJavaType( Integer.class ), + // no converter + null, + tableExpression, + index.getColumnIterator().next().getText( dialect ) + ); + + collectionMappingType = new CollectionMappingTypeImpl( + jtdRegistry.getDescriptor( java.util.List.class ), + StandardListSemantics.INSTANCE + ); + + break; + } + case MAP: + case ORDERED_MAP: + case SORTED_MAP: { + final Class mapJavaType = collectionSemantics.getCollectionClassification() == CollectionClassification.SORTED_MAP + ? SortedMap.class + : java.util.Map.class; + collectionMappingType = new CollectionMappingTypeImpl( + jtdRegistry.getDescriptor( mapJavaType ), + collectionSemantics + ); + + indexDescriptor = interpretMapKey( + bootValueMapping, + collectionDescriptor, + tableExpression, + sqlAliasStem, + dialect, + creationProcess + ); + + break; + } + case SET: + case ORDERED_SET: + case SORTED_SET: { + final Class setJavaType = collectionSemantics.getCollectionClassification() == CollectionClassification.SORTED_MAP + ? SortedSet.class + : java.util.Set.class; + collectionMappingType = new CollectionMappingTypeImpl( + jtdRegistry.getDescriptor( setJavaType ), + collectionSemantics + ); + + indexDescriptor = null; + + break; + } + default: { + throw new MappingException( + "Unexpected CollectionClassification : " + collectionSemantics.getCollectionClassification() ); } - else { - collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.Map.class ), - bootValueMapping.hasOrder() - ? StandardOrderedMapSemantics.INSTANCE - : StandardMapSemantics.INSTANCE - ); - } - } - else if ( bootValueMapping instanceof Set ) { - if ( bootValueMapping.isSorted() ) { - collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.SortedSet.class ), - StandardSortedSetSemantics.INSTANCE - ); - } - else { - collectionMappingType = new CollectionMappingTypeImpl( - jtdRegistry.getDescriptor( java.util.Set.class ), - bootValueMapping.hasOrder() - ? StandardOrderedSetSemantics.INSTANCE - : StandardSetSemantics.INSTANCE - ); - } - } - else { - throw new MappingException( "Unexpected org.hibernate.mapping.Collection impl : " + bootValueMapping ); } final StateArrayContributorMetadata contributorMetadata = new StateArrayContributorMetadata() { @@ -733,7 +818,7 @@ public class MappingModelCreationHelper { final FetchStyle style = FetchStrategyHelper.determineFetchStyleByMetadata( ( (OuterJoinLoadable) declaringType ).getFetchMode( stateArrayPosition ), collectionDescriptor.getCollectionType(), - creationContext.getSessionFactory() + sessionFactory ); return new PluralAttributeMappingImpl( @@ -742,15 +827,15 @@ public class MappingModelCreationHelper { entityMappingType -> contributorMetadata, collectionMappingType, stateArrayPosition, - tableExpression, - attrColumnExpressions, - null, - null, + keyDescriptor, + elementDescriptor, + indexDescriptor, + identifierDescriptor, new FetchStrategy( FetchStrategyHelper.determineFetchTiming( style, collectionDescriptor.getCollectionType(), - creationContext.getSessionFactory() + sessionFactory ), style ), @@ -760,6 +845,156 @@ public class MappingModelCreationHelper { ); } + private static ForeignKeyDescriptor interpretKeyDescriptor( + Property bootProperty, + Collection bootValueMapping, + Dialect dialect, + MappingModelCreationProcess creationProcess) { + final Type keyType = bootValueMapping.getKey().getType(); + + if ( keyType instanceof BasicType ) { + assert bootValueMapping.getKey().getColumnSpan() == 1; + return new SimpleForeignKeyDescriptor( + bootValueMapping.getKey().getColumnIterator().next().getText( dialect ), + (BasicType) keyType + ); + } + + throw new NotYetImplementedFor6Exception( + "Support for composite collection foreign-keys not yet implemented: " + bootValueMapping.getRole() + ); + } + + private static CollectionPart interpretMapKey( + Collection bootValueMapping, + CollectionPersister collectionDescriptor, + String tableExpression, + String sqlAliasStem, + Dialect dialect, + MappingModelCreationProcess creationProcess) { + assert bootValueMapping instanceof IndexedCollection; + final IndexedCollection indexedCollection = (IndexedCollection) bootValueMapping; + final Value bootMapKeyDescriptor = indexedCollection.getIndex(); + + if ( bootMapKeyDescriptor instanceof BasicValue ) { + final BasicValue basicValue = (BasicValue) bootMapKeyDescriptor; + return new BasicValuedCollectionPart( + collectionDescriptor, + CollectionPart.Nature.INDEX, + basicValue.resolve().getResolvedBasicType(), + basicValue.resolve().getValueConverter(), + tableExpression, + basicValue.getColumnIterator().next().getText( dialect ) + ); + } + + if ( bootMapKeyDescriptor instanceof Component ) { + final Component component = (Component) bootMapKeyDescriptor; + final CompositeType compositeType = (CompositeType) component.getType(); + + final List columnExpressions = CollectionHelper.arrayList( component.getColumnSpan() ); + final Iterator columnIterator = component.getColumnIterator(); + while ( columnIterator.hasNext() ) { + columnExpressions.add( columnIterator.next().getText( dialect ) ); + } + + final EmbeddableMappingType mappingType = EmbeddableMappingType.from( + component, + compositeType, + inflightDescriptor -> new EmbeddedCollectionPart( + collectionDescriptor, + CollectionPart.Nature.INDEX, + inflightDescriptor, + // parent-injection + null, + tableExpression, + columnExpressions, + sqlAliasStem + ), + creationProcess + ); + + return (CollectionPart) mappingType.getEmbeddedValueMapping(); + } + + throw new NotYetImplementedFor6Exception( + "Support for plural attributes with index type [" + bootMapKeyDescriptor + "] not yet implemented" + ); + } + + private static CollectionPart interpretElement( + Collection bootDescriptor, + String tableExpression, + CollectionPersister collectionDescriptor, + String sqlAliasStem, + Dialect dialect, + MappingModelCreationProcess creationProcess) { + final Value element = bootDescriptor.getElement(); + + if ( element instanceof BasicValue ) { + final BasicValue basicElement = (BasicValue) element; + return new BasicValuedCollectionPart( + collectionDescriptor, + CollectionPart.Nature.ELEMENT, + basicElement.resolve().getResolvedBasicType(), + basicElement.resolve().getValueConverter(), + tableExpression, + basicElement.getColumnIterator().next().getText( dialect ) + ); + } + + if ( element instanceof Component ) { + final Component component = (Component) element; + final CompositeType compositeType = (CompositeType) collectionDescriptor.getElementType(); + + final List columnExpressions = CollectionHelper.arrayList( component.getColumnSpan() ); + final Iterator columnIterator = component.getColumnIterator(); + while ( columnIterator.hasNext() ) { + columnExpressions.add( columnIterator.next().getText( dialect ) ); + } + + final EmbeddableMappingType mappingType = EmbeddableMappingType.from( + component, + compositeType, + embeddableMappingType -> new EmbeddedCollectionPart( + collectionDescriptor, + CollectionPart.Nature.ELEMENT, + embeddableMappingType, + // parent-injection + null, + tableExpression, + columnExpressions, + sqlAliasStem + ), + creationProcess + ); + + return (CollectionPart) mappingType.getEmbeddedValueMapping(); + } + + if ( element instanceof OneToMany || element instanceof ToOne ) { + final EntityPersister associatedEntity; + + if ( element instanceof OneToMany ) { + associatedEntity = creationProcess.getEntityPersister( + ( (OneToMany) element ).getReferencedEntityName() + ); + } + else { + // many-to-many + associatedEntity = creationProcess.getEntityPersister( + ( (ToOne) element ).getReferencedEntityName() + ); + } + + return new EntityCollectionPart( CollectionPart.Nature.ELEMENT, associatedEntity ); + } + + throw new NotYetImplementedFor6Exception( + "Support for plural attributes with element type [" + element + "] not yet implemented" + ); + } + private static class CollectionMappingTypeImpl implements CollectionMappingType { private final JavaTypeDescriptor collectionJtd; private final CollectionSemantics semantics; @@ -773,7 +1008,7 @@ public class MappingModelCreationHelper { } @Override - public CollectionSemantics getCCollectionSemantics() { + public CollectionSemantics getCollectionSemantics() { return semantics; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index 4fab2d109c..84d1989924 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -6,12 +6,17 @@ */ package org.hibernate.metamodel.mapping.internal; +import java.util.function.Consumer; + import org.hibernate.LockMode; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionMappingType; +import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; @@ -20,7 +25,18 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqlAstCreationState; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; +import org.hibernate.sql.ast.spi.SqlAliasStemHelper; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupBuilder; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; +import org.hibernate.sql.results.internal.domain.collection.DelayedCollectionFetch; +import org.hibernate.sql.results.internal.domain.collection.EagerCollectionFetch; import org.hibernate.sql.results.spi.DomainResultCreationState; import org.hibernate.sql.results.spi.Fetch; import org.hibernate.sql.results.spi.FetchParent; @@ -33,17 +49,18 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme private final PropertyAccess propertyAccess; private final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess; + private final ForeignKeyDescriptor fkDescriptor; + private final CollectionPart elementDescriptor; + private final CollectionPart indexDescriptor; + private final CollectionIdentifierDescriptor identifierDescriptor; + private final FetchStrategy fetchStrategy; - - private final String tableExpression; - private final String[] attrColumnExpressions; - - private final ModelPart elementDescriptor; - private final ModelPart indexDescriptor; - private final CascadeStyle cascadeStyle; + private final CollectionPersister collectionDescriptor; + private final String sqlAliasStem; + @SuppressWarnings("WeakerAccess") public PluralAttributeMappingImpl( String attributeName, @@ -51,10 +68,10 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess, CollectionMappingType collectionMappingType, int stateArrayPosition, - String tableExpression, - String[] attrColumnExpressions, - ModelPart elementDescriptor, - ModelPart indexDescriptor, + ForeignKeyDescriptor fkDescriptor, + CollectionPart elementDescriptor, + CollectionPart indexDescriptor, + CollectionIdentifierDescriptor identifierDescriptor, FetchStrategy fetchStrategy, CascadeStyle cascadeStyle, ManagedMappingType declaringType, @@ -63,36 +80,47 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme this.propertyAccess = propertyAccess; this.stateArrayContributorMetadataAccess = stateArrayContributorMetadataAccess; this.stateArrayPosition = stateArrayPosition; - this.tableExpression = tableExpression; - this.attrColumnExpressions = attrColumnExpressions; + this.fkDescriptor = fkDescriptor; this.elementDescriptor = elementDescriptor; this.indexDescriptor = indexDescriptor; + this.identifierDescriptor = identifierDescriptor; this.fetchStrategy = fetchStrategy; this.cascadeStyle = cascadeStyle; this.collectionDescriptor = collectionDescriptor; - } + this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName ); + } @Override public CollectionMappingType getMappedTypeDescriptor() { return (CollectionMappingType) super.getMappedTypeDescriptor(); } + @Override + public ForeignKeyDescriptor getKeyDescriptor() { + return fkDescriptor; + } + @Override public CollectionPersister getCollectionDescriptor() { return collectionDescriptor; } @Override - public ModelPart getValueDescriptor() { + public CollectionPart getElementDescriptor() { return elementDescriptor; } @Override - public ModelPart getIndexDescriptor() { + public CollectionPart getIndexDescriptor() { return indexDescriptor; } + @Override + public CollectionIdentifierDescriptor getIdentifierDescriptor() { + return identifierDescriptor; + } + @Override public int getStateArrayPosition() { return stateArrayPosition; @@ -128,8 +156,119 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme String resultVariable, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); - final TableGroup collectionTableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( fetchablePath ); - throw new NotYetImplementedFor6Exception( getClass() ); + if ( fetchTiming == FetchTiming.IMMEDIATE || selected ) { + final TableGroup collectionTableGroup = sqlAstCreationState.getFromClauseAccess().resolveTableGroup( + fetchablePath, + p -> { + final TableGroupJoin tableGroupJoin = createTableGroupJoin( + fetchablePath, + sqlAstCreationState.getFromClauseAccess().getTableGroup( fetchParent.getNavigablePath() ), + null, + JoinType.LEFT, + lockMode, + creationState.getSqlAliasBaseManager(), + creationState.getSqlAstCreationState().getSqlExpressionResolver(), + creationState.getSqlAstCreationState().getCreationContext() + ); + return tableGroupJoin.getJoinedGroup(); + } + ); + + return new EagerCollectionFetch( + fetchablePath, + this, + fkDescriptor.createDomainResult( fetchablePath, collectionTableGroup, creationState ), + getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), + fetchParent, + creationState + ); + } + + + return new DelayedCollectionFetch( + fetchablePath, + this, + true, + fetchParent + ); + } + + @Override + public String getSqlAliasStem() { + return sqlAliasStem; + } + + @Override + public TableGroupJoin createTableGroupJoin( + NavigablePath navigablePath, + TableGroup lhs, + String explicitSourceAlias, + JoinType joinType, + LockMode lockMode, + SqlAliasBaseGenerator aliasBaseGenerator, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + final String aliasRoot = explicitSourceAlias == null ? sqlAliasStem : explicitSourceAlias; + final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( aliasRoot ); + + final TableGroupBuilder tableGroupBuilder = TableGroupBuilder.builder( + navigablePath, + this, + lockMode, + sqlAliasBase, + creationContext.getSessionFactory() + ); + + applyTableReferences( + sqlAliasBase, + joinType, + tableGroupBuilder, + sqlExpressionResolver, + creationContext + ); + + return new TableGroupJoin( navigablePath, joinType, tableGroupBuilder.build() ); + } + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + getCollectionDescriptor().applyTableReferences( sqlAliasBase, baseJoinType, collector, sqlExpressionResolver, creationContext ); + } + + @Override + public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { + final CollectionPart.Nature nature = CollectionPart.Nature.fromName( name ); + if ( nature == CollectionPart.Nature.ELEMENT ) { + return elementDescriptor; + } + else if ( nature == CollectionPart.Nature.INDEX ) { + return indexDescriptor; + } + + return null; + } + + @Override + public void visitSubParts(Consumer consumer, EntityMappingType treatTargetType) { + consumer.accept( elementDescriptor ); + if ( indexDescriptor != null ) { + consumer.accept( indexDescriptor ); + } + } + + @Override + public int getNumberOfFetchables() { + return indexDescriptor == null ? 1 : 2; + } + + @Override + public String toString() { + return "PluralAttribute(" + getCollectionDescriptor().getRole() + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java new file mode 100644 index 0000000000..82491b3f2a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java @@ -0,0 +1,64 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping.internal; + +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.sql.SqlAstCreationState; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.spi.SqlSelection; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.internal.domain.basic.BasicResult; +import org.hibernate.sql.results.spi.DomainResult; +import org.hibernate.sql.results.spi.DomainResultCreationState; + +/** + * @author Steve Ebersole + */ +public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor { + private final String keyColumnExpression; + private final JdbcMapping jdbcMapping; + + public SimpleForeignKeyDescriptor(String keyColumnExpression, JdbcMapping jdbcMapping) { + this.keyColumnExpression = keyColumnExpression; + this.jdbcMapping = jdbcMapping; + } + + @Override + public DomainResult createDomainResult( + NavigablePath collectionPath, + TableGroup tableGroup, + DomainResultCreationState creationState) { + final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); + final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); + final SqlSelection sqlSelection = sqlExpressionResolver.resolveSqlSelection( + sqlExpressionResolver.resolveSqlExpression( + SqlExpressionResolver.createColumnReferenceKey( + tableGroup.getPrimaryTableReference(), + keyColumnExpression + ), + s -> new ColumnReference( + tableGroup.getPrimaryTableReference().getIdentificationVariable(), + keyColumnExpression, + jdbcMapping, + creationState.getSqlAstCreationState().getCreationContext().getSessionFactory() + ) + ), + jdbcMapping.getJavaTypeDescriptor(), + sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration() + ); + + //noinspection unchecked + return new BasicResult( + sqlSelection.getValuesArrayPosition(), + null, + jdbcMapping.getJavaTypeDescriptor() + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingularAssociationAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingularAssociationAttributeMapping.java index 317433ad8b..15c58c554f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingularAssociationAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingularAssociationAttributeMapping.java @@ -7,12 +7,12 @@ package org.hibernate.metamodel.mapping.internal; import org.hibernate.LockMode; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.ManagedMappingType; -import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; @@ -29,7 +29,7 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu int stateArrayPosition, StateArrayContributorMetadataAccess attributeMetadataAccess, FetchStrategy mappedFetchStrategy, - MappingType type, + EntityMappingType type, ManagedMappingType declaringType, PropertyAccess propertyAccess) { super( @@ -43,9 +43,14 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu ); } + @Override + public EntityMappingType getMappedTypeDescriptor() { + return (EntityMappingType) super.getMappedTypeDescriptor(); + } + @Override public EntityMappingType getEntityMappingType() { - return null; + return getMappedTypeDescriptor(); } @Override @@ -57,6 +62,11 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { - return null; + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public int getNumberOfFetchables() { + return getEntityMappingType().getNumberOfFetchables(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 556f8742e2..04fcec82bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -16,7 +16,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.function.Consumer; import org.hibernate.AssertionFailure; import org.hibernate.FetchMode; @@ -33,6 +32,7 @@ import org.hibernate.cache.spi.entry.CacheEntryStructure; import org.hibernate.cache.spi.entry.StructuredCollectionCacheEntry; import org.hibernate.cache.spi.entry.StructuredMapCacheEntry; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; +import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; @@ -56,6 +56,7 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectations; import org.hibernate.loader.collection.CollectionInitializer; +import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Column; import org.hibernate.mapping.Formula; @@ -64,9 +65,9 @@ import org.hibernate.mapping.IndexedCollection; import org.hibernate.mapping.List; import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Table; +import org.hibernate.mapping.Value; import org.hibernate.metadata.CollectionMetadata; -import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.metamodel.model.domain.EmbeddableDomainType; +import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.PropertyMapping; @@ -84,18 +85,23 @@ import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition; import org.hibernate.persister.walking.spi.CompositionDefinition; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.pretty.MessageHelper; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; import org.hibernate.sql.Alias; import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.Template; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.type.AnyType; import org.hibernate.type.AssociationType; +import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; -import org.hibernate.type.descriptor.java.JavaTypeDescriptor; -import org.hibernate.type.spi.TypeConfiguration; import org.jboss.logging.Logger; @@ -234,10 +240,26 @@ public abstract class AbstractCollectionPersister private final Comparator comparator; + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // "mapping model" + + private final CollectionSemantics collectionSemantics; + + private final BasicValueConverter elementConverter; + private final BasicValueConverter indexConverter; + + // temprary + private final BasicType convertedElementType; + private final BasicType convertedIndexType; + public AbstractCollectionPersister( - Collection collectionBinding, + Collection collectionBootDescriptor, CollectionDataAccess cacheAccessStrategy, PersisterCreationContext creationContext) throws MappingException, CacheException { + final Value elementBootDescriptor = collectionBootDescriptor.getElement(); + final Value indexBootDescriptor = collectionBootDescriptor instanceof IndexedCollection + ? ( (IndexedCollection) collectionBootDescriptor ).getIndex() + : null; final Database database = creationContext.getMetadata().getDatabase(); final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment(); @@ -245,7 +267,7 @@ public abstract class AbstractCollectionPersister this.factory = creationContext.getSessionFactory(); this.cacheAccessStrategy = cacheAccessStrategy; if ( factory.getSessionFactoryOptions().isStructuredCacheEntriesEnabled() ) { - cacheEntryStructure = collectionBinding.isMap() + cacheEntryStructure = collectionBootDescriptor.isMap() ? StructuredMapCacheEntry.INSTANCE : StructuredCollectionCacheEntry.INSTANCE; } @@ -255,54 +277,54 @@ public abstract class AbstractCollectionPersister dialect = factory.getDialect(); sqlExceptionHelper = factory.getSQLExceptionHelper(); - collectionType = collectionBinding.getCollectionType(); - navigableRole = new NavigableRole( collectionBinding.getRole() ); - entityName = collectionBinding.getOwnerEntityName(); + collectionType = collectionBootDescriptor.getCollectionType(); + navigableRole = new NavigableRole( collectionBootDescriptor.getRole() ); + entityName = collectionBootDescriptor.getOwnerEntityName(); ownerPersister = factory.getEntityPersister( entityName ); - queryLoaderName = collectionBinding.getLoaderName(); - isMutable = collectionBinding.isMutable(); - mappedByProperty = collectionBinding.getMappedByProperty(); + queryLoaderName = collectionBootDescriptor.getLoaderName(); + isMutable = collectionBootDescriptor.isMutable(); + mappedByProperty = collectionBootDescriptor.getMappedByProperty(); - Table table = collectionBinding.getCollectionTable(); - fetchMode = collectionBinding.getElement().getFetchMode(); - elementType = collectionBinding.getElement().getType(); + Table table = collectionBootDescriptor.getCollectionTable(); + fetchMode = elementBootDescriptor.getFetchMode(); + elementType = elementBootDescriptor.getType(); // isSet = collectionBinding.isSet(); // isSorted = collectionBinding.isSorted(); - isPrimitiveArray = collectionBinding.isPrimitiveArray(); - isArray = collectionBinding.isArray(); - subselectLoadable = collectionBinding.isSubselectLoadable(); + isPrimitiveArray = collectionBootDescriptor.isPrimitiveArray(); + isArray = collectionBootDescriptor.isArray(); + subselectLoadable = collectionBootDescriptor.isSubselectLoadable(); qualifiedTableName = determineTableName( table, jdbcEnvironment ); - int spacesSize = 1 + collectionBinding.getSynchronizedTables().size(); + int spacesSize = 1 + collectionBootDescriptor.getSynchronizedTables().size(); spaces = new String[spacesSize]; spaces[0] = qualifiedTableName; - Iterator iter = collectionBinding.getSynchronizedTables().iterator(); + Iterator iter = collectionBootDescriptor.getSynchronizedTables().iterator(); for ( int i = 1; i < spacesSize; i++ ) { spaces[i] = (String) iter.next(); } - sqlWhereString = StringHelper.isNotEmpty( collectionBinding.getWhere() ) ? "( " + collectionBinding.getWhere() + ") " : null; + sqlWhereString = StringHelper.isNotEmpty( collectionBootDescriptor.getWhere() ) ? "( " + collectionBootDescriptor.getWhere() + ") " : null; hasWhere = sqlWhereString != null; sqlWhereStringTemplate = hasWhere ? Template.renderWhereStringTemplate( sqlWhereString, dialect, factory.getSqlFunctionRegistry() ) : null; - hasOrphanDelete = collectionBinding.hasOrphanDelete(); + hasOrphanDelete = collectionBootDescriptor.hasOrphanDelete(); - int batch = collectionBinding.getBatchSize(); + int batch = collectionBootDescriptor.getBatchSize(); if ( batch == -1 ) { batch = factory.getSessionFactoryOptions().getDefaultBatchFetchSize(); } batchSize = batch; - isVersioned = collectionBinding.isOptimisticLocked(); + isVersioned = collectionBootDescriptor.isOptimisticLocked(); // KEY - keyType = collectionBinding.getKey().getType(); - iter = collectionBinding.getKey().getColumnIterator(); - int keySpan = collectionBinding.getKey().getColumnSpan(); + keyType = collectionBootDescriptor.getKey().getType(); + iter = collectionBootDescriptor.getKey().getColumnIterator(); + int keySpan = collectionBootDescriptor.getKey().getColumnSpan(); keyColumnNames = new String[keySpan]; keyColumnAliases = new String[keySpan]; int k = 0; @@ -328,7 +350,7 @@ public abstract class AbstractCollectionPersister elementPersister = null; } - int elementSpan = collectionBinding.getElement().getColumnSpan(); + int elementSpan = elementBootDescriptor.getColumnSpan(); elementColumnAliases = new String[elementSpan]; elementColumnNames = new String[elementSpan]; elementColumnWriters = new String[elementSpan]; @@ -341,13 +363,13 @@ public abstract class AbstractCollectionPersister elementColumnIsInPrimaryKey = new boolean[elementSpan]; boolean isPureFormula = true; boolean hasNotNullableColumns = false; - boolean oneToMany = collectionBinding.isOneToMany(); + boolean oneToMany = collectionBootDescriptor.isOneToMany(); boolean[] columnInsertability = null; if ( !oneToMany ) { - columnInsertability = collectionBinding.getElement().getColumnInsertability(); + columnInsertability = elementBootDescriptor.getColumnInsertability(); } int j = 0; - iter = collectionBinding.getElement().getColumnIterator(); + iter = elementBootDescriptor.getColumnIterator(); while ( iter.hasNext() ) { Selectable selectable = (Selectable) iter.next(); elementColumnAliases[j] = selectable.getAlias( dialect, table ); @@ -390,10 +412,10 @@ public abstract class AbstractCollectionPersister // INDEX AND ROW SELECT - hasIndex = collectionBinding.isIndexed(); + hasIndex = collectionBootDescriptor.isIndexed(); if ( hasIndex ) { // NativeSQL: collect index column and auto-aliases - IndexedCollection indexedCollection = (IndexedCollection) collectionBinding; + IndexedCollection indexedCollection = (IndexedCollection) collectionBootDescriptor; indexType = indexedCollection.getIndex().getType(); int indexSpan = indexedCollection.getIndex().getColumnSpan(); boolean[] indexColumnInsertability = indexedCollection.getIndex().getColumnInsertability(); @@ -440,12 +462,12 @@ public abstract class AbstractCollectionPersister baseIndex = 0; } - hasIdentifier = collectionBinding.isIdentified(); + hasIdentifier = collectionBootDescriptor.isIdentified(); if ( hasIdentifier ) { - if ( collectionBinding.isOneToMany() ) { + if ( collectionBootDescriptor.isOneToMany() ) { throw new MappingException( "one-to-many collections with identifiers are not supported" ); } - IdentifierCollection idColl = (IdentifierCollection) collectionBinding; + IdentifierCollection idColl = (IdentifierCollection) collectionBootDescriptor; identifierType = idColl.getIdentifier().getType(); iter = idColl.getIdentifier().getColumnIterator(); Column col = (Column) iter.next(); @@ -473,68 +495,68 @@ public abstract class AbstractCollectionPersister // sqlSelectString = sqlSelectString(); // sqlSelectRowString = sqlSelectRowString(); - if ( collectionBinding.getCustomSQLInsert() == null ) { + if ( collectionBootDescriptor.getCustomSQLInsert() == null ) { sqlInsertRowString = generateInsertRowString(); insertCallable = false; insertCheckStyle = ExecuteUpdateResultCheckStyle.COUNT; } else { - sqlInsertRowString = collectionBinding.getCustomSQLInsert(); - insertCallable = collectionBinding.isCustomInsertCallable(); - insertCheckStyle = collectionBinding.getCustomSQLInsertCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( collectionBinding.getCustomSQLInsert(), insertCallable ) - : collectionBinding.getCustomSQLInsertCheckStyle(); + sqlInsertRowString = collectionBootDescriptor.getCustomSQLInsert(); + insertCallable = collectionBootDescriptor.isCustomInsertCallable(); + insertCheckStyle = collectionBootDescriptor.getCustomSQLInsertCheckStyle() == null + ? ExecuteUpdateResultCheckStyle.determineDefault( collectionBootDescriptor.getCustomSQLInsert(), insertCallable ) + : collectionBootDescriptor.getCustomSQLInsertCheckStyle(); } - if ( collectionBinding.getCustomSQLUpdate() == null ) { + if ( collectionBootDescriptor.getCustomSQLUpdate() == null ) { sqlUpdateRowString = generateUpdateRowString(); updateCallable = false; updateCheckStyle = ExecuteUpdateResultCheckStyle.COUNT; } else { - sqlUpdateRowString = collectionBinding.getCustomSQLUpdate(); - updateCallable = collectionBinding.isCustomUpdateCallable(); - updateCheckStyle = collectionBinding.getCustomSQLUpdateCheckStyle() == null - ? ExecuteUpdateResultCheckStyle.determineDefault( collectionBinding.getCustomSQLUpdate(), insertCallable ) - : collectionBinding.getCustomSQLUpdateCheckStyle(); + sqlUpdateRowString = collectionBootDescriptor.getCustomSQLUpdate(); + updateCallable = collectionBootDescriptor.isCustomUpdateCallable(); + updateCheckStyle = collectionBootDescriptor.getCustomSQLUpdateCheckStyle() == null + ? ExecuteUpdateResultCheckStyle.determineDefault( collectionBootDescriptor.getCustomSQLUpdate(), insertCallable ) + : collectionBootDescriptor.getCustomSQLUpdateCheckStyle(); } - if ( collectionBinding.getCustomSQLDelete() == null ) { + if ( collectionBootDescriptor.getCustomSQLDelete() == null ) { sqlDeleteRowString = generateDeleteRowString(); deleteCallable = false; deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE; } else { - sqlDeleteRowString = collectionBinding.getCustomSQLDelete(); - deleteCallable = collectionBinding.isCustomDeleteCallable(); + sqlDeleteRowString = collectionBootDescriptor.getCustomSQLDelete(); + deleteCallable = collectionBootDescriptor.isCustomDeleteCallable(); deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE; } - if ( collectionBinding.getCustomSQLDeleteAll() == null ) { + if ( collectionBootDescriptor.getCustomSQLDeleteAll() == null ) { sqlDeleteString = generateDeleteString(); deleteAllCallable = false; deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE; } else { - sqlDeleteString = collectionBinding.getCustomSQLDeleteAll(); - deleteAllCallable = collectionBinding.isCustomDeleteAllCallable(); + sqlDeleteString = collectionBootDescriptor.getCustomSQLDeleteAll(); + deleteAllCallable = collectionBootDescriptor.isCustomDeleteAllCallable(); deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE; } - sqlSelectSizeString = generateSelectSizeString( collectionBinding.isIndexed() && !collectionBinding.isMap() ); + sqlSelectSizeString = generateSelectSizeString( collectionBootDescriptor.isIndexed() && !collectionBootDescriptor.isMap() ); sqlDetectRowByIndexString = generateDetectRowByIndexString(); sqlDetectRowByElementString = generateDetectRowByElementString(); sqlSelectRowByIndexString = generateSelectRowByIndexString(); logStaticSQL(); - isLazy = collectionBinding.isLazy(); - isExtraLazy = collectionBinding.isExtraLazy(); + isLazy = collectionBootDescriptor.isLazy(); + isExtraLazy = collectionBootDescriptor.isExtraLazy(); - isInverse = collectionBinding.isInverse(); + isInverse = collectionBootDescriptor.isInverse(); - if ( collectionBinding.isArray() ) { - elementClass = ( (org.hibernate.mapping.Array) collectionBinding ).getElementClass(); + if ( collectionBootDescriptor.isArray() ) { + elementClass = ( (org.hibernate.mapping.Array) collectionBootDescriptor ).getElementClass(); } else { // for non-arrays, we don't need to know the element class @@ -569,9 +591,9 @@ public abstract class AbstractCollectionPersister } } - hasOrder = collectionBinding.getOrderBy() != null; + hasOrder = collectionBootDescriptor.getOrderBy() != null; if ( hasOrder ) { - LOG.debugf( "Translating order-by fragment [%s] for collection role : %s", collectionBinding.getOrderBy(), getRole() ); + LOG.debugf( "Translating order-by fragment [%s] for collection role : %s", collectionBootDescriptor.getOrderBy(), getRole() ); // orderByTranslation = Template.translateOrderBy( // collectionBinding.getOrderBy(), // new ColumnMapperImpl(), @@ -587,20 +609,20 @@ public abstract class AbstractCollectionPersister } // Handle any filters applied to this collectionBinding - filterHelper = new FilterHelper( collectionBinding.getFilters(), factory); + filterHelper = new FilterHelper( collectionBootDescriptor.getFilters(), factory); // Handle any filters applied to this collectionBinding for many-to-many - manyToManyFilterHelper = new FilterHelper( collectionBinding.getManyToManyFilters(), factory); - manyToManyWhereString = StringHelper.isNotEmpty( collectionBinding.getManyToManyWhere() ) ? - "( " + collectionBinding.getManyToManyWhere() + ")" : + manyToManyFilterHelper = new FilterHelper( collectionBootDescriptor.getManyToManyFilters(), factory); + manyToManyWhereString = StringHelper.isNotEmpty( collectionBootDescriptor.getManyToManyWhere() ) ? + "( " + collectionBootDescriptor.getManyToManyWhere() + ")" : null; manyToManyWhereTemplate = manyToManyWhereString == null ? null : Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() ); - hasManyToManyOrder = collectionBinding.getManyToManyOrdering() != null; + hasManyToManyOrder = collectionBootDescriptor.getManyToManyOrdering() != null; if ( hasManyToManyOrder ) { - LOG.debugf( "Translating many-to-many order-by fragment [%s] for collection role : %s", collectionBinding.getOrderBy(), getRole() ); + LOG.debugf( "Translating many-to-many order-by fragment [%s] for collection role : %s", collectionBootDescriptor.getOrderBy(), getRole() ); // manyToManyOrderByTranslation = Template.translateOrderBy( // collectionBinding.getManyToManyOrdering(), // new ColumnMapperImpl(), @@ -614,9 +636,37 @@ public abstract class AbstractCollectionPersister // manyToManyOrderByTranslation = null; } - comparator = collectionBinding.getComparator(); + comparator = collectionBootDescriptor.getComparator(); initCollectionPropertyMap(); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // "mapping model" + + this.collectionSemantics = creationContext.getBootstrapContext() + .getMetadataBuildingOptions() + .getPersistentCollectionRepresentationResolver() + .resolveRepresentation( collectionBootDescriptor ); + + if ( elementBootDescriptor instanceof BasicValue ) { + final BasicValue.Resolution basicTypeResolution = ( (BasicValue) elementBootDescriptor ).resolve(); + this.elementConverter = basicTypeResolution.getValueConverter(); + this.convertedElementType = basicTypeResolution.getResolvedBasicType(); + } + else { + this.elementConverter = null; + this.convertedElementType = null; + } + + if ( indexBootDescriptor instanceof BasicValue ) { + final BasicValue.Resolution basicTypeResolution = ( (BasicValue) indexBootDescriptor ).resolve(); + this.indexConverter = basicTypeResolution.getValueConverter(); + this.convertedIndexType = basicTypeResolution.getResolvedBasicType(); + } + else { + this.indexConverter = null; + this.convertedIndexType = null; + } } @Override @@ -872,6 +922,16 @@ public abstract class AbstractCollectionPersister return elementType; } + @Override + public BasicValueConverter getElementConverter() { + return elementConverter; + } + + @Override + public BasicValueConverter getIndexConverter() { + return indexConverter; + } + /** * Return the element class of an array, or null otherwise. needed by arrays */ @@ -947,7 +1007,14 @@ public abstract class AbstractCollectionPersister */ protected int writeElement(PreparedStatement st, Object elt, int i, SharedSessionContractImplementor session) throws HibernateException, SQLException { - getElementType().nullSafeSet( st, elt, i, elementColumnIsSettable, session ); + if ( elementConverter != null ) { + //noinspection unchecked + final Object converted = elementConverter.toRelationalValue( elt ); + convertedElementType.getJdbcValueBinder().bind( st, converted, i, session ); + } + else { + getElementType().nullSafeSet( st, elt, i, elementColumnIsSettable, session ); + } return i + ArrayHelper.countTrue( elementColumnIsSettable ); } @@ -957,7 +1024,16 @@ public abstract class AbstractCollectionPersister */ protected int writeIndex(PreparedStatement st, Object index, int i, SharedSessionContractImplementor session) throws HibernateException, SQLException { - getIndexType().nullSafeSet( st, incrementIndexByBase( index ), i, indexColumnIsSettable, session ); + if ( indexConverter != null ) { + //noinspection unchecked + final Object converted = indexConverter.toRelationalValue( index ); + //noinspection unchecked + convertedIndexType.getJdbcValueBinder().bind( st, converted, i, session ); + } + else { + getIndexType().nullSafeSet( st, incrementIndexByBase( index ), i, indexColumnIsSettable, session ); + } + return i + ArrayHelper.countTrue( indexColumnIsSettable ); } @@ -976,7 +1052,13 @@ public abstract class AbstractCollectionPersister if ( elementIsPureFormula ) { throw new AssertionFailure( "cannot use a formula-based element in the where condition" ); } - getElementType().nullSafeSet( st, elt, i, elementColumnIsInPrimaryKey, session ); + if ( elementConverter != null ) { + final Object converted = elementConverter.toRelationalValue( elt ); + convertedElementType.getJdbcValueBinder().bind( st, converted, i, session ); + } + else { + getElementType().nullSafeSet( st, elt, i, elementColumnIsInPrimaryKey, session ); + } return i + elementColumnAliases.length; } @@ -989,7 +1071,13 @@ public abstract class AbstractCollectionPersister if ( indexContainsFormula ) { throw new AssertionFailure( "cannot use a formula-based index in the where condition" ); } - getIndexType().nullSafeSet( st, incrementIndexByBase( index ), i, session ); + if ( indexConverter != null ) { + final Object converted = indexConverter.toRelationalValue( index ); + convertedIndexType.getJdbcValueBinder().bind( st, converted, i, session ); + } + else { + getIndexType().nullSafeSet( st, incrementIndexByBase( index ), i, session ); + } return i + indexColumnAliases.length; } @@ -2297,4 +2385,51 @@ public abstract class AbstractCollectionPersister } }; } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // "mapping model" + + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + if ( isOneToMany() ) { + // one-to-many does not define a "collection table" + assert elementPersister != null; + } + else { + // we do have a "collection table" - apply it first + collector.applySecondaryTableReferences( + new TableReference( qualifiedTableName, sqlAliasBase.generateNewAlias(), false, getFactory() ), + baseJoinType, + (lhs, rhs, joinType) -> { + // create the join-predicate between the owner table and the collection table + throw new NotYetImplementedFor6Exception( getClass() ); + } + ); + + } + + if ( elementPersister != null ) { + elementPersister.applyTableReferences( + sqlAliasBase, + // todo (6.0) : determine the proper join-type to use + JoinType.LEFT, + collector, + sqlExpressionResolver, + creationContext + ); + } + + } + + @Override + public CollectionSemantics getCollectionSemantics() { + return collectionSemantics; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java index d271051a69..76142b7a39 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java @@ -32,6 +32,7 @@ import org.hibernate.loader.collection.BatchingCollectionInitializerBuilder; import org.hibernate.loader.collection.CollectionInitializer; import org.hibernate.loader.collection.SubselectCollectionLoader; import org.hibernate.mapping.Collection; +import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.pretty.MessageHelper; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java index aa508b52de..92fd8240aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/CollectionPersister.java @@ -14,17 +14,26 @@ import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.cache.spi.access.CollectionDataAccess; import org.hibernate.cache.spi.entry.CacheEntryStructure; +import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.IdentifierGenerator; import org.hibernate.metadata.CollectionMetadata; import org.hibernate.metamodel.CollectionClassification; +import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.walking.spi.CollectionDefinition; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; +import org.hibernate.sql.ast.tree.from.TableReferenceContributor; import org.hibernate.type.CollectionType; import org.hibernate.type.Type; @@ -62,7 +71,7 @@ import org.hibernate.type.Type; * @see org.hibernate.collection.spi.PersistentCollection * @author Gavin King */ -public interface CollectionPersister extends CollectionDefinition { +public interface CollectionPersister extends CollectionDefinition, TableReferenceContributor { /** * Initialize the given collection with the given key * TODO: add owner argument!! @@ -103,6 +112,21 @@ public interface CollectionPersister extends CollectionDefinition { * Return the element class of an array, or null otherwise */ Class getElementClass(); + + /** + * The value converter for the element values of this collection + */ + default BasicValueConverter getElementConverter() { + return null; + } + + /** + * The value converter for index values of this collection (effectively map keys only) + */ + default BasicValueConverter getIndexConverter() { + return null; + } + /** * Read the key from a row of the JDBC ResultSet */ @@ -332,4 +356,30 @@ public interface CollectionPersister extends CollectionDefinition { * @see CollectionClassification#SORTED_SET */ Comparator getSortingComparator(); + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // mapping model + + @Override + default void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + throw new NotYetImplementedFor6Exception( + "The persister used for this collection [" + getNavigableRole() + + "] does not yet implement support for use in SQL AST creation;" + + " should implement `TableReferenceContributor#applyTableReferences`" + ); + } + + default CollectionSemantics getCollectionSemantics() { + throw new NotYetImplementedFor6Exception( + "The persister used for this collection [" + getNavigableRole() + + "] does not yet implement support for `" + + CollectionSemantics.class.getName() + "`" + ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 71e8c26e72..4b866947c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -184,7 +184,9 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupBuilder; import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; @@ -1187,20 +1189,23 @@ public abstract class AbstractEntityPersister SqlAstCreationContext creationContext) { final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ); - final TableReference primaryTableReference = resolvePrimaryTableReference( sqlAliasBase, sqlExpressionResolver ); - - final List joins = new ArrayList<>( ); - resolveTableReferenceJoins( primaryTableReference, sqlAliasBase, tableReferenceJoinType, joins::add, sqlExpressionResolver ); - - return new StandardTableGroup( + final TableGroupBuilder builder = TableGroupBuilder.builder( navigablePath, this, lockMode, - primaryTableReference, - joins, sqlAliasBase, - getFactory() + creationContext.getSessionFactory() ); + + applyTableReferences( + sqlAliasBase, + tableReferenceJoinType, + builder, + sqlExpressionResolver, + creationContext + ); + + return builder.build(); } protected TableReference resolvePrimaryTableReference( @@ -1228,6 +1233,33 @@ public abstract class AbstractEntityPersister } } + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + org.hibernate.sql.ast.JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + final TableReference primaryTableReference = resolvePrimaryTableReference( + sqlAliasBase, + sqlExpressionResolver + ); + + collector.applyPrimaryReference( primaryTableReference ); + + for ( int i = 1; i < getSubclassTableSpan(); i++ ) { + collector.addTableReferenceJoin( + createTableReferenceJoin( + i, + primaryTableReference, + baseJoinType, + sqlAliasBase, + sqlExpressionResolver + ) + ); + } + } + protected TableReferenceJoin createTableReferenceJoin( int subClassTablePosition, TableReference rootTableReference, @@ -6483,6 +6515,11 @@ public abstract class AbstractEntityPersister // otherwise, nothing to do } + @Override + public int getNumberOfFetchables() { + return attributeMappings.size(); + } + @Override public void visitFetchables( Consumer fetchableConsumer, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 7031218ec6..35a29ba131 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -38,8 +38,13 @@ import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.persister.walking.spi.EntityDefinition; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasStemHelper; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.tree.from.RootTableGroupProducer; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.Type; @@ -93,6 +98,11 @@ public interface EntityPersister extends EntityDefinition, EntityValuedModelPart @Deprecated void generateEntityDefinition(); + @Override + default int getNumberOfFetchables() { + return getNumberOfAttributeMappings(); + } + /** * Finish the initialization of this object. {@link #prepareMappingModel} * must be called for all entity persisters before calling this method. @@ -906,6 +916,4 @@ public interface EntityPersister extends EntityDefinition, EntityValuedModelPart default boolean canIdentityInsertBeDelayed() { return false; } - - } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 93d19b86fb..6a2570b681 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -499,6 +499,7 @@ public abstract class BaseSqmToSqlAstConverter creationContext ); + lhsTableGroup.addTableGroupJoin( tableGroupJoin ); fromClauseIndex.register( sqmJoin, tableGroupJoin.getJoinedGroup() ); // add any additional join restrictions diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java index 29e0b01f12..ad0113cf74 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/FromClauseIndex.java @@ -37,7 +37,6 @@ public class FromClauseIndex extends SimpleFromClauseAccessImpl { * Holds *explicitly* fetched joins */ private Map fetchesByPath; - private Map> fetchesByParentPath; private final Set affectedTableNames = new HashSet<>(); @@ -61,6 +60,20 @@ public class FromClauseIndex extends SimpleFromClauseAccessImpl { ); } } + + if ( sqmPath instanceof SqmAttributeJoin ) { + final SqmAttributeJoin sqmJoin = (SqmAttributeJoin) sqmPath; + if ( sqmJoin.isFetched() ) { + registerJoinFetch( sqmJoin ); + } + } + } + + private void registerJoinFetch(SqmAttributeJoin sqmJoin) { + if ( fetchesByPath == null ) { + fetchesByPath = new HashMap<>(); + } + fetchesByPath.put( sqmJoin.getNavigablePath(), sqmJoin ); } public boolean isResolved(SqmFrom fromElement) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java index e0264c9083..a48b8ff107 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java @@ -31,6 +31,7 @@ import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqlAstCreationState; +import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; @@ -67,7 +68,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; @SuppressWarnings("unchecked") public class StandardSqmSelectToSqlAstConverter extends BaseSqmToSqlAstConverter - implements DomainResultCreationState, org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter { + implements DomainResultCreationState, SqmSelectToSqlAstConverter { private final LoadQueryInfluencers fetchInfluencers; private final CircularFetchDetector circularFetchDetector = new CircularFetchDetector(); @@ -155,7 +156,7 @@ public class StandardSqmSelectToSqlAstConverter @Override public List visitFetches(FetchParent fetchParent) { - final List fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfAttributeMappings() ); + final List fetches = CollectionHelper.arrayList( fetchParent.getReferencedMappingType().getNumberOfFetchables() ); //noinspection Convert2Lambda final Consumer fetchableConsumer = new Consumer() { @@ -214,7 +215,7 @@ public class StandardSqmSelectToSqlAstConverter if ( fetchedJoin != null ) { // there was an explicit fetch in the SQM - // there should be a TableGroupJoin registered n SqmJoin registered for this `fetchablePath` already + // there should be a TableGroupJoin registered for this `fetchablePath` already // because it assert getFromClauseIndex().getTableGroup( fetchablePath ) != null; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java index bd51fe5f9d..f3a9c49aaf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java @@ -6,13 +6,18 @@ */ package org.hibernate.sql.ast.tree.from; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.function.BiFunction; import java.util.function.Consumer; import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.tree.predicate.Predicate; /** * @author Steve Ebersole @@ -23,7 +28,7 @@ public class StandardTableGroup extends AbstractTableGroup { public StandardTableGroup( NavigablePath navigablePath, - RootTableGroupProducer tableGroupProducer, + TableGroupProducer tableGroupProducer, LockMode lockMode, TableReference primaryTableReference, List tableJoins, @@ -34,11 +39,6 @@ public class StandardTableGroup extends AbstractTableGroup { this.tableJoins = tableJoins; } - @Override - public RootTableGroupProducer getModelPart() { - return (RootTableGroupProducer) super.getModelPart(); - } - @Override public void applyAffectedTableNames(Consumer nameCollector) { // todo (6.0) : if we implement dynamic TableReference creation, this still needs to return the expressions for all mapped tables not just the ones with a TableReference at this time diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupBuilder.java new file mode 100644 index 0000000000..825f50e456 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupBuilder.java @@ -0,0 +1,131 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.function.Function; + +import org.hibernate.LockMode; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; + +/** + * @author Steve Ebersole + */ +public class TableGroupBuilder implements TableReferenceCollector { + public static TableGroupBuilder builder( + NavigablePath path, + TableGroupProducer producer, + LockMode lockMode, + SqlAliasBase sqlAliasBase, + SessionFactoryImplementor sessionFactory) { + return new TableGroupBuilder( path, producer, lockMode, sqlAliasBase, sessionFactory ); + } + + private final NavigablePath path; + private final TableGroupProducer producer; + private final SessionFactoryImplementor sessionFactory; + + private final SqlAliasBase sqlAliasBase; + private final LockMode lockMode; + + private final Function primaryJoinProducer; + + private TableReference primaryTableReference; + private TableReference secondaryTableLhs; + + private java.util.List tableJoins; + + private TableGroupBuilder( + NavigablePath path, + TableGroupProducer producer, + LockMode lockMode, + SqlAliasBase sqlAliasBase, + SessionFactoryImplementor sessionFactory) { + this( path, producer, lockMode, sqlAliasBase, null, sessionFactory ); + } + + private TableGroupBuilder( + NavigablePath path, + TableGroupProducer producer, + LockMode lockMode, + SqlAliasBase sqlAliasBase, + Function primaryJoinProducer, + SessionFactoryImplementor sessionFactory) { + this.path = path; + this.producer = producer; + this.lockMode = lockMode; + this.sqlAliasBase = sqlAliasBase; + this.primaryJoinProducer = primaryJoinProducer; + this.sessionFactory = sessionFactory; + } + + public TableGroup build() { + if ( primaryTableReference == null ) { + throw new IllegalStateException( "Primary TableReference was not specified : " + path ); + } + + return new StandardTableGroup( + path, + producer, + lockMode, + primaryTableReference, + tableJoins == null ? Collections.emptyList() : tableJoins, + sqlAliasBase, + sessionFactory + ); + } + + @Override + public void applyPrimaryReference(TableReference tableReference) { + if ( primaryTableReference != null ) { + assert primaryJoinProducer != null; + + addTableReferenceJoin( primaryJoinProducer.apply( tableReference ) ); + + } + else { + primaryTableReference = tableReference; + } + + secondaryTableLhs = tableReference; + } + + @Override + public void applySecondaryTableReferences( + TableReference tableReference, + JoinType tableReferenceJoinType, + TableReferenceJoinPredicateProducer predicateProducer) { + if ( primaryTableReference == null ) { + primaryTableReference = tableReference; + secondaryTableLhs = primaryTableReference; + } + else { + addTableReferenceJoin( + new TableReferenceJoin( + tableReferenceJoinType, + tableReference, + predicateProducer.producePredicate( + secondaryTableLhs, + tableReference, + tableReferenceJoinType + ) + ) + ); + } + } + + public void addTableReferenceJoin(TableReferenceJoin join) { + if ( tableJoins == null ) { + tableJoins = new ArrayList<>(); + } + tableJoins.add( join ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupProducer.java index 446135532f..818a6f2fbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupProducer.java @@ -15,7 +15,7 @@ import org.hibernate.query.sqm.sql.SqlAliasBaseManager; * @author Steve Ebersole * @author Andrea Boriero */ -public interface TableGroupProducer extends ModelPartContainer { +public interface TableGroupProducer extends ModelPartContainer, TableReferenceContributor { /** * Get the "stem" used as the base for generating SQL table aliases for table * references that are part of the TableGroup being generated diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceCollector.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceCollector.java new file mode 100644 index 0000000000..f9b5ed6eeb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceCollector.java @@ -0,0 +1,38 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import org.hibernate.sql.ast.JoinType; + +/** + * Used in constructing {@link TableGroup} references to collect the individual table + * references + * + * @author Steve Ebersole + */ +public interface TableReferenceCollector { + void applyPrimaryReference(TableReference tableReference); + + /** + * Collect a table reference as part of the TableGroup. + * + * @param tableReference The TableReference. + * @param joinType The type of join indicated by the mapping of the table, if it is to be joined + * @param predicateProducer Function for creating the join predicate, if it is to be joined. The first + * argument passed to the function is the LHS reference. The second is the same as `tableReference`. + * The result is a SQL AST Predicate to use as the join-predicate + */ + void applySecondaryTableReferences( + TableReference tableReference, + JoinType joinType, + TableReferenceJoinPredicateProducer predicateProducer); + + /** + * Directly add a TableReferenceJoin + */ + void addTableReferenceJoin(TableReferenceJoin join); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceContributor.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceContributor.java index 1a3c63bf57..a192882ac8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceContributor.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceContributor.java @@ -6,19 +6,22 @@ */ package org.hibernate.sql.ast.tree.from; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; /** * @author Steve Ebersole */ public interface TableReferenceContributor { -// /** -// * Apply the Tables mapped by this producer to the collector as TableReferences -// */ -// void applyTableReferenceJoins( -// ColumnReferenceQualifier lhs, -// JoinType joinType, -// SqlAliasBase sqlAliasBase, -// TableReferenceJoinCollector joinCollector); + /** + * Apply the Tables mapped by this producer to the collector as TableReferences + */ + void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoinPredicateProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoinPredicateProducer.java new file mode 100644 index 0000000000..549f01a7d7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoinPredicateProducer.java @@ -0,0 +1,24 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.tree.predicate.Predicate; + +/** + * Functional contract for producing the join-predicate related to a {@link TableReferenceJoin}. + * + * @see TableReferenceJoin#getJoinPredicate + * @see TableReferenceCollector#applySecondaryTableReferences + * + * @author Steve Ebersole + */ +@FunctionalInterface +public +interface TableReferenceJoinPredicateProducer { + Predicate producePredicate(TableReference lhs, TableReference rhs, JoinType joinType); +} 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 new file mode 100644 index 0000000000..39a922d566 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/LoadingCollectionEntryImpl.java @@ -0,0 +1,97 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal; + +import java.io.Serializable; + +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionEntry; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.results.spi.CollectionInitializer; +import org.hibernate.sql.results.spi.LoadingCollectionEntry; + +/** + * Represents a collection currently being loaded. + * + * @author Steve Ebersole + */ +public class LoadingCollectionEntryImpl implements LoadingCollectionEntry { + private final CollectionPersister collectionDescriptor; + private final CollectionInitializer initializer; + private final Object key; + private final PersistentCollection collectionInstance; + + public LoadingCollectionEntryImpl( + CollectionPersister collectionDescriptor, + CollectionInitializer initializer, + Object key, + PersistentCollection collectionInstance) { + this.collectionDescriptor = collectionDescriptor; + this.initializer = initializer; + this.key = key; + this.collectionInstance = collectionInstance; + + collectionInstance.beforeInitialize( getCollectionDescriptor(), -1 ); + collectionInstance.beginRead(); + } + + @Override public CollectionPersister getCollectionDescriptor() { + return collectionDescriptor; + } + + /** + * Access to the initializer that is responsible for initializing this collection + */ + @Override public CollectionInitializer getInitializer() { + return initializer; + } + + @Override public Serializable getKey() { + // todo (6.0) : change from Serializable to Object + return (Serializable) key; + } + + @Override public PersistentCollection getCollectionInstance() { + return collectionInstance; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + getCollectionDescriptor().getNavigableRole().getFullPath() + "#" + getKey() + ")"; + } + + @Override public void finishLoading(ExecutionContext executionContext) { + collectionInstance.endRead(); + + final SharedSessionContractImplementor session = executionContext.getSession(); + 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 ); + } + + + // 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/StandardRowReader.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java index 4d0fafa4de..938f3a617a 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 @@ -11,7 +11,9 @@ import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.named.RowReaderMemento; import org.hibernate.sql.exec.spi.Callback; +import org.hibernate.sql.results.spi.CollectionInitializer; import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.EntityInitializer; import org.hibernate.sql.results.spi.Initializer; import org.hibernate.sql.results.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.spi.JdbcValuesSourceProcessingState; @@ -125,7 +127,17 @@ public class StandardRowReader implements RowReader { // old for ( int i = 0; i < initializers.size(); i++ ) { - initializers.get( i ).resolveKey( rowProcessingState ); + final Initializer initializer = initializers.get( i ); + if ( ! ( initializer instanceof CollectionInitializer ) ) { + initializer.resolveKey( rowProcessingState ); + } + } + + for ( int i = 0; i < initializers.size(); i++ ) { + final Initializer initializer = initializers.get( i ); + if ( initializer instanceof CollectionInitializer ) { + initializer.resolveKey( rowProcessingState ); + } } for ( int i = 0; i < initializers.size(); i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/CollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/CollectionFetch.java new file mode 100644 index 0000000000..6db5da4572 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/CollectionFetch.java @@ -0,0 +1,56 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain.collection; + +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; + +/** + * @author Steve Ebersole + */ +public abstract class CollectionFetch implements Fetch { + private final NavigablePath fetchedPath; + private final PluralAttributeMapping fetchedAttribute; + + private final boolean nullable; + + private final FetchParent fetchParent; + + public CollectionFetch( + NavigablePath fetchedPath, + PluralAttributeMapping fetchedAttribute, + boolean nullable, + FetchParent fetchParent) { + this.fetchedPath = fetchedPath; + this.fetchedAttribute = fetchedAttribute; + this.fetchParent = fetchParent; + this.nullable = nullable; + } + + @Override + public FetchParent getFetchParent() { + return fetchParent; + } + + @Override + public PluralAttributeMapping getFetchedMapping() { + return fetchedAttribute; + } + + @Override + public NavigablePath getNavigablePath() { + return fetchedPath; + } + + @Override + public boolean isNullable() { + return nullable; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/DelayedCollectionAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/DelayedCollectionAssembler.java new file mode 100644 index 0000000000..8cc6b47fcd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/DelayedCollectionAssembler.java @@ -0,0 +1,155 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain.collection; + +import java.io.Serializable; +import java.util.function.Consumer; + +import org.hibernate.collection.spi.CollectionSemantics; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionKey; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.CollectionInitializer; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.Initializer; +import org.hibernate.sql.results.spi.JdbcValuesSourceProcessingOptions; +import org.hibernate.sql.results.spi.LoadingCollectionEntry; +import org.hibernate.sql.results.spi.RowProcessingState; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Steve Ebersole + */ +public class DelayedCollectionAssembler implements DomainResultAssembler { + private final PluralAttributeMapping fetchedMapping; + private final FetchParentAccess parentAccess; + + private final CollectionInitializer initializer; + + public DelayedCollectionAssembler( + NavigablePath fetchPath, + PluralAttributeMapping fetchedMapping, + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + this.fetchedMapping = fetchedMapping; + this.parentAccess = parentAccess; + this.initializer = new InitializerImpl( fetchPath, fetchedMapping, parentAccess, creationState ); + collector.accept( initializer ); + } + + @Override + public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { + return initializer.getCollectionInstance(); + } + + @Override + public JavaTypeDescriptor getAssembledJavaTypeDescriptor() { + return fetchedMapping.getJavaTypeDescriptor(); + } + + private static class InitializerImpl implements CollectionInitializer { + private final NavigablePath fetchedPath; + private final PluralAttributeMapping fetchedMapping; + private final FetchParentAccess parentAccess; + + private CollectionKey collectionKey; + private PersistentCollection instance; + + public InitializerImpl( + NavigablePath fetchedPath, + PluralAttributeMapping fetchedMapping, + FetchParentAccess parentAccess, + AssemblerCreationState creationState) { + this.fetchedPath = fetchedPath; + this.fetchedMapping = fetchedMapping; + this.parentAccess = parentAccess; + } + + @Override + public NavigablePath getNavigablePath() { + return fetchedPath; + } + + @Override + public void resolveKey(RowProcessingState rowProcessingState) { + if ( collectionKey != null ) { + // already resolved + return; + } + + collectionKey = new CollectionKey( + fetchedMapping.getCollectionDescriptor(), + parentAccess.getParentKey() + ); + } + + @Override + public void resolveInstance(RowProcessingState rowProcessingState) { + final SharedSessionContractImplementor session = rowProcessingState.getSession(); + final PersistenceContext persistenceContext = session.getPersistenceContext(); + final LoadingCollectionEntry loadingEntry = persistenceContext.getLoadContexts().findLoadingCollectionEntry( collectionKey ); + final PersistentCollection registeredInstance = persistenceContext.getCollection( collectionKey ); + + if ( loadingEntry != null ) { + instance = loadingEntry.getCollectionInstance(); + return; + } + + if ( registeredInstance != null ) { + this.instance = registeredInstance; + return; + } + + this.instance = makePersistentCollection( fetchedMapping, collectionKey, rowProcessingState ); + persistenceContext.addUninitializedCollection( + getInitializingCollectionDescriptor(), + instance, + (Serializable) collectionKey.getKey() + ); + } + + private static PersistentCollection makePersistentCollection( + PluralAttributeMapping fetchedMapping, + CollectionKey collectionKey, + RowProcessingState rowProcessingState) { + final CollectionPersister collectionDescriptor = fetchedMapping.getCollectionDescriptor(); + final CollectionSemantics collectionSemantics = collectionDescriptor.getCollectionSemantics(); + return collectionSemantics.instantiateWrapper( + collectionKey.getKey(), + collectionDescriptor, + rowProcessingState.getSession() + ); + } + + @Override + public void initializeInstance(RowProcessingState rowProcessingState) { + } + + @Override + public void finishUpRow(RowProcessingState rowProcessingState) { + collectionKey = null; + instance = null; + } + + @Override + public CollectionPersister getInitializingCollectionDescriptor() { + return fetchedMapping.getCollectionDescriptor(); + } + + @Override + public PersistentCollection getCollectionInstance() { + return instance; + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/DelayedCollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/DelayedCollectionFetch.java new file mode 100644 index 0000000000..e5ebc14832 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/DelayedCollectionFetch.java @@ -0,0 +1,44 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain.collection; + +import java.util.function.Consumer; + +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.Initializer; + +/** + * @author Steve Ebersole + */ +public class DelayedCollectionFetch extends CollectionFetch { + public DelayedCollectionFetch( + NavigablePath fetchedPath, + PluralAttributeMapping fetchedAttribute, + boolean nullable, + FetchParent fetchParent) { + super( fetchedPath, fetchedAttribute, nullable, fetchParent ); + } + + @Override + public DomainResultAssembler createAssembler( + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + return new DelayedCollectionAssembler( + getNavigablePath(), + getFetchedMapping(), + parentAccess, + collector, + creationState + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/EagerCollectionAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/EagerCollectionAssembler.java new file mode 100644 index 0000000000..16d9a65d47 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/EagerCollectionAssembler.java @@ -0,0 +1,258 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain.collection; + +import java.util.function.Consumer; + +import org.hibernate.collection.spi.CollectionSemantics; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.spi.CollectionKey; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.internal.LoadingCollectionEntryImpl; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.CollectionInitializer; +import org.hibernate.sql.results.spi.DomainResult; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.Initializer; +import org.hibernate.sql.results.spi.JdbcValuesSourceProcessingOptions; +import org.hibernate.sql.results.spi.LoadContexts; +import org.hibernate.sql.results.spi.LoadingCollectionEntry; +import org.hibernate.sql.results.spi.RowProcessingState; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Steve Ebersole + */ +public class EagerCollectionAssembler implements DomainResultAssembler { + private final PluralAttributeMapping fetchedMapping; + private final FetchParentAccess parentAccess; + + private final CollectionInitializer initializer; + + public EagerCollectionAssembler( + NavigablePath fetchPath, + PluralAttributeMapping fetchedMapping, + DomainResult fkResult, + Fetch elementFetch, + Fetch indexFetch, + DomainResult identifierResult, + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + this.fetchedMapping = fetchedMapping; + this.parentAccess = parentAccess; + + this.initializer = new InitializerImpl( + fetchPath, + fetchedMapping, + parentAccess, + fkResult, + elementFetch, + indexFetch, + identifierResult, + collector, + creationState + ); + + collector.accept( initializer ); + } + + @Override + public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { + return initializer.getCollectionInstance(); + } + + @Override + public JavaTypeDescriptor getAssembledJavaTypeDescriptor() { + return fetchedMapping.getJavaTypeDescriptor(); + } + + private static class InitializerImpl implements CollectionInitializer { + private final NavigablePath fetchedPath; + private final PluralAttributeMapping fetchedMapping; + private final FetchParentAccess parentAccess; + + private final DomainResultAssembler fkAssembler; + private final DomainResultAssembler elementAssembler; + private final DomainResultAssembler indexAssembler; + private final DomainResultAssembler identifierAssembler; + + private CollectionKey collectionKey; + + private boolean managing; + private Object fkValue; + + // todo (6.0) : consider using the initializer itself as the holder of the various "temp" collections + // used while reading a collection. that would mean collection-type specific initializers (List, versus Set) + private PersistentCollection instance; + + public InitializerImpl( + NavigablePath fetchedPath, + PluralAttributeMapping fetchedMapping, + FetchParentAccess parentAccess, + DomainResult fkResult, + Fetch elementFetch, + Fetch indexFetch, + DomainResult identifierResult, + Consumer collector, + AssemblerCreationState creationState) { + this.fetchedPath = fetchedPath; + this.fetchedMapping = fetchedMapping; + this.parentAccess = parentAccess; + + this.fkAssembler = fkResult.createResultAssembler( collector, creationState ); + + // questionable what should be the parent access here + this.elementAssembler = elementFetch.createAssembler( parentAccess, collector, creationState ); + this.indexAssembler = indexFetch == null + ? null + : indexFetch.createAssembler( parentAccess, collector, creationState ); + this.identifierAssembler = identifierResult == null + ? null + : identifierResult.createResultAssembler( collector, creationState ); + } + + @Override + public CollectionPersister getInitializingCollectionDescriptor() { + return fetchedMapping.getCollectionDescriptor(); + } + + @Override + public PersistentCollection getCollectionInstance() { + return instance; + } + + @Override + public NavigablePath getNavigablePath() { + return fetchedPath; + } + + @Override + public void finishUpRow(RowProcessingState rowProcessingState) { + collectionKey = null; + managing = false; + instance = null; + } + + @Override + public void resolveKey(RowProcessingState rowProcessingState) { + if ( collectionKey != null ) { + // already resolved + return; + } + + collectionKey = new CollectionKey( + fetchedMapping.getCollectionDescriptor(), + parentAccess.getParentKey() + ); + + final Object fkValue = fkAssembler.assemble( rowProcessingState ); + if ( fkValue == null ) { + // this row has no collection element + return; + } + } + + @Override + public void resolveInstance(RowProcessingState rowProcessingState) { + if ( instance != null ) { + // already resolved + return; + } + + final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContext(); + + // see if the collection is already being initialized + + final LoadContexts loadContexts = persistenceContext.getLoadContexts(); + final LoadingCollectionEntry existingEntry = loadContexts.findLoadingCollectionEntry( collectionKey ); + + if ( existingEntry != null ) { + this.instance = existingEntry.getCollectionInstance(); + if ( existingEntry.getInitializer() == this ) { + this.managing = true; + } + return; + } + + // see if it has been already registered with the Session + + final PersistentCollection registeredInstance = persistenceContext.getCollection( collectionKey ); + if ( registeredInstance != null ) { + this.instance = registeredInstance; + // it was already registered, so use that wrapper. + if ( ! registeredInstance.wasInitialized() ) { + // if the existing wrapper is not initialized, we will take responsibility for initializing it + managing = true; + rowProcessingState.getJdbcValuesSourceProcessingState().registerLoadingCollection( + collectionKey, + new LoadingCollectionEntryImpl( + getInitializingCollectionDescriptor(), + this, + collectionKey, + registeredInstance + ) + ); + return; + } + } + + this.instance = makePersistentCollection( fetchedMapping, collectionKey, rowProcessingState ); + this.managing = true; + + rowProcessingState.getJdbcValuesSourceProcessingState().registerLoadingCollection( + collectionKey, + new LoadingCollectionEntryImpl( + fetchedMapping.getCollectionDescriptor(), + this, + collectionKey.getKey(), + instance + ) + ); + + } + + private static PersistentCollection makePersistentCollection( + PluralAttributeMapping fetchedMapping, + CollectionKey collectionKey, + RowProcessingState rowProcessingState) { + final CollectionPersister collectionDescriptor = fetchedMapping.getCollectionDescriptor(); + final CollectionSemantics collectionSemantics = collectionDescriptor.getCollectionSemantics(); + return collectionSemantics.instantiateWrapper( + collectionKey.getKey(), + collectionDescriptor, + rowProcessingState.getSession() + ); + } + + @Override + public void initializeInstance(RowProcessingState rowProcessingState) { + if ( ! managing ) { + return; + } + + final Object fkValue = fkAssembler.assemble( rowProcessingState ); + if ( fkValue == null ) { + // this row contains no collection element + return; + } + + getCollectionInstance().readFrom( + rowProcessingState, + elementAssembler, + indexAssembler, + identifierAssembler, + parentAccess.getFetchParentInstance() + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/EagerCollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/EagerCollectionFetch.java new file mode 100644 index 0000000000..76abda93f2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/collection/EagerCollectionFetch.java @@ -0,0 +1,125 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.results.internal.domain.collection; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; +import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.results.spi.AssemblerCreationState; +import org.hibernate.sql.results.spi.DomainResult; +import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.sql.results.spi.Fetch; +import org.hibernate.sql.results.spi.FetchParent; +import org.hibernate.sql.results.spi.FetchParentAccess; +import org.hibernate.sql.results.spi.FetchableContainer; +import org.hibernate.sql.results.spi.Initializer; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; + +/** + * @author Steve Ebersole + */ +public class EagerCollectionFetch extends CollectionFetch implements FetchParent { + private final DomainResult fkResult; + + private final Fetch elementFetch; + private final Fetch indexFetch; + private final DomainResult identifierResult; + + private final List fetches; + + public EagerCollectionFetch( + NavigablePath fetchedPath, + PluralAttributeMapping fetchedAttribute, + DomainResult fkResult, + boolean nullable, + FetchParent fetchParent, + DomainResultCreationState creationState) { + super( fetchedPath, fetchedAttribute, nullable, fetchParent ); + this.fkResult = fkResult; + + final CollectionIdentifierDescriptor identifierDescriptor = fetchedAttribute.getIdentifierDescriptor(); + if ( identifierDescriptor == null ) { + this.identifierResult = null; + } + else { + final TableGroup collectionTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().getTableGroup( fetchedPath ); + this.identifierResult = identifierDescriptor.createDomainResult( fetchedPath, collectionTableGroup, creationState ); + } + + fetches = creationState.visitFetches( this ); + if ( fetchedAttribute.getIndexDescriptor() != null ) { + assert fetches.size() == 2; + indexFetch = fetches.get( 0 ); + elementFetch = fetches.get( 1 ); + } + else { + assert fetches.size() == 1; + indexFetch = null; + elementFetch = fetches.get( 0 ); + } + } + + @Override + public DomainResultAssembler createAssembler( + FetchParentAccess parentAccess, + Consumer collector, + AssemblerCreationState creationState) { + return new EagerCollectionAssembler( + getNavigablePath(), + getFetchedMapping(), + fkResult, + elementFetch, + indexFetch, + identifierResult, + parentAccess, + collector, + creationState + ); + } + + @Override + public FetchableContainer getReferencedMappingContainer() { + return getFetchedMapping(); + } + + @Override + public PluralAttributeMapping getReferencedMappingType() { + return getFetchedMapping(); + } + + @Override + public List getFetches() { + return fetches; + } + + @Override + public Fetch findFetch(String fetchableName) { + if ( CollectionPart.Nature.ELEMENT.getName().equals( fetchableName ) ) { + return elementFetch; + } + else if ( CollectionPart.Nature.INDEX.getName().equals( fetchableName ) ) { + return indexFetch; + } + else { + throw new IllegalArgumentException( + "Unknown fetchable [" + getFetchedMapping().getCollectionDescriptor().getRole() + + " -> " + fetchableName + "]" + ); + } + } + + @Override + public JavaTypeDescriptor getResultJavaTypeDescriptor() { + return getFetchedMapping().getJavaTypeDescriptor(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java index f15d5e54ba..93e9a1649f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java @@ -10,7 +10,7 @@ import java.util.function.Consumer; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EmbeddableMappingType; -import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.internal.domain.AbstractFetchParent; import org.hibernate.sql.results.spi.AssemblerCreationState; @@ -29,17 +29,20 @@ import org.hibernate.sql.results.spi.Initializer; public class CompositeFetch extends AbstractFetchParent implements CompositeResultMappingNode, Fetch { private final FetchParent fetchParent; private final FetchTiming fetchTiming; + private final boolean nullable; public CompositeFetch( NavigablePath navigablePath, - EmbeddedAttributeMapping embeddedAttribute, + EmbeddableValuedModelPart embeddedPartDescriptor, FetchParent fetchParent, FetchTiming fetchTiming, + boolean nullable, DomainResultCreationState creationState) { - super( embeddedAttribute, navigablePath ); + super( embeddedPartDescriptor, navigablePath ); this.fetchParent = fetchParent; this.fetchTiming = fetchTiming; + this.nullable = nullable; creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup( getNavigablePath(), @@ -49,38 +52,34 @@ public class CompositeFetch extends AbstractFetchParent implements CompositeResu afterInitialize( creationState ); } - public EmbeddedAttributeMapping getEmbeddedAttributeMapping() { - return (EmbeddedAttributeMapping) super.getFetchContainer(); - } - @Override public FetchParent getFetchParent() { return fetchParent; } @Override - public EmbeddedAttributeMapping getFetchContainer() { - return getEmbeddedAttributeMapping(); + public EmbeddableValuedModelPart getFetchContainer() { + return (EmbeddableValuedModelPart) super.getFetchContainer(); } @Override - public EmbeddedAttributeMapping getReferencedMappingContainer() { - return getEmbeddedAttributeMapping(); + public EmbeddableValuedModelPart getReferencedMappingContainer() { + return getFetchContainer(); } @Override public Fetchable getFetchedMapping() { - return getEmbeddedAttributeMapping(); + return getFetchContainer(); } @Override public EmbeddableMappingType getReferencedMappingType() { - return getEmbeddedAttributeMapping().getEmbeddableTypeDescriptor(); + return getFetchContainer().getEmbeddableTypeDescriptor(); } @Override public boolean isNullable() { - return getEmbeddedAttributeMapping().getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(); + return nullable; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetchInitializer.java index 4733cff5cf..90cf75b29e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetchInitializer.java @@ -27,4 +27,9 @@ public class CompositeFetchInitializer AssemblerCreationState creationState) { super( resultDescriptor, fetchParentAccess, initializerConsumer, creationState ); } + + @Override + public Object getParentKey() { + return getFetchParentAccess().getParentKey(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeRootInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeRootInitializer.java index 7098a9e7e4..8d61eb731b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeRootInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeRootInitializer.java @@ -22,4 +22,9 @@ public class CompositeRootInitializer extends AbstractCompositeInitializer { AssemblerCreationState creationState) { super( resultDescriptor, null, initializerConsumer, creationState ); } + + @Override + public Object getParentKey() { + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityInitializer.java index 5f17987f65..43160a489b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/AbstractEntityInitializer.java @@ -34,7 +34,6 @@ import org.hibernate.event.spi.PreLoadEvent; import org.hibernate.event.spi.PreLoadEventListener; import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.mapping.AttributeMapping; -import org.hibernate.metamodel.mapping.StateArrayContributorMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Loadable; import org.hibernate.proxy.HibernateProxy; @@ -197,6 +196,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces return entityKey.getIdentifier(); } + @Override + public Object getParentKey() { + return getKeyValue(); + } + @Override public Object getFetchParentInstance() { if ( entityInstance == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/EntityResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/EntityResultImpl.java index 3e69ecc100..4adad9b390 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/EntityResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/entity/EntityResultImpl.java @@ -25,8 +25,6 @@ import org.hibernate.sql.results.spi.EntityResult; public class EntityResultImpl extends AbstractEntityResultNode implements EntityResult { private final String resultVariable; - - public EntityResultImpl( NavigablePath navigablePath, EntityValuedModelPart entityValuedModelPart, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java index 2aaf008025..2a9464c4a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParent.java @@ -8,7 +8,7 @@ package org.hibernate.sql.results.spi; import java.util.List; -import org.hibernate.metamodel.mapping.ManagedMappingType; +import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.query.NavigablePath; /** @@ -22,7 +22,7 @@ public interface FetchParent extends ResultSetMappingNode { /** * This parent's type */ - ManagedMappingType getReferencedMappingType(); + FetchableContainer getReferencedMappingType(); /** * Get the property path to this parent diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParentAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParentAccess.java index e9ddf16c04..893a58e1d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParentAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchParentAccess.java @@ -19,6 +19,8 @@ import org.hibernate.query.NavigablePath; public interface FetchParentAccess { FetchParentAccess findFirstEntityDescriptorAccess(); + Object getParentKey(); + /** * Access to the fetch's parent instance. */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchableContainer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchableContainer.java index 026f814f39..4edcaf3912 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchableContainer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/FetchableContainer.java @@ -15,6 +15,8 @@ import org.hibernate.metamodel.mapping.ModelPartContainer; * @author Steve Ebersole */ public interface FetchableContainer extends ModelPartContainer { + int getNumberOfFetchables(); + default void visitKeyFetchables( Consumer fetchableConsumer, EntityMappingType treatTargetType) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java index 2eb5caa02b..529d519ec3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadContexts.java @@ -6,8 +6,6 @@ */ package org.hibernate.sql.results.spi; -import java.sql.ResultSet; - import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; @@ -18,7 +16,7 @@ import org.hibernate.internal.util.collections.StandardStack; /** * Maintains a Stack of processing state related to performing load operations. - * The sCollectionLoadContexttate is defined by {@link JdbcValuesSourceProcessingState} which + * The state is defined by {@link JdbcValuesSourceProcessingState} which * encapsulates the data to be processed by the load whether the data comes from * a ResultSet or second-level cache hit. * diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java index 392eb24b90..2a94047781 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/LoadingCollectionEntry.java @@ -9,9 +9,6 @@ package org.hibernate.sql.results.spi; import java.io.Serializable; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.CollectionEntry; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.sql.exec.spi.ExecutionContext; @@ -20,76 +17,14 @@ import org.hibernate.sql.exec.spi.ExecutionContext; * * @author Steve Ebersole */ -public class LoadingCollectionEntry { - private final CollectionPersister collectionDescriptor; - private final CollectionInitializer initializer; - // todo (6.0) : change from Serializable to Object - private final Serializable key; - private final PersistentCollection collectionInstance; +public interface LoadingCollectionEntry { + CollectionPersister getCollectionDescriptor(); - public LoadingCollectionEntry( - CollectionPersister collectionDescriptor, - CollectionInitializer initializer, - Serializable key, - PersistentCollection collectionInstance) { - this.collectionDescriptor = collectionDescriptor; - this.initializer = initializer; - this.key = key; - this.collectionInstance = collectionInstance; + CollectionInitializer getInitializer(); - collectionInstance.beforeInitialize( getCollectionDescriptor(), -1 ); - collectionInstance.beginRead(); - } + Serializable getKey(); - public CollectionPersister getCollectionDescriptor() { - return collectionDescriptor; - } + PersistentCollection getCollectionInstance(); - /** - * Access to the initializer that is responsible for initializing this collection - */ - public CollectionInitializer getInitializer() { - return initializer; - } - - public Serializable getKey() { - return key; - } - - public PersistentCollection getCollectionInstance() { - return collectionInstance; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + getCollectionDescriptor().getNavigableRole().getFullPath() + "#" + getKey() + ")"; - } - - public void finishLoading(ExecutionContext executionContext) { - collectionInstance.endRead(); - - final SharedSessionContractImplementor session = executionContext.getSession(); - 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 ); - } - - - // 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 - } + void finishLoading(ExecutionContext executionContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanTypeDescriptor.java index 991d4efe4c..44ff22fed4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanTypeDescriptor.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.java; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; @@ -16,7 +17,7 @@ import static java.lang.Boolean.TRUE; * * @author Steve Ebersole */ -public class BooleanTypeDescriptor extends AbstractTypeDescriptor { +public class BooleanTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final BooleanTypeDescriptor INSTANCE = new BooleanTypeDescriptor(); private final char characterValueTrue; @@ -130,4 +131,14 @@ public class BooleanTypeDescriptor extends AbstractTypeDescriptor { public Long toLong(Boolean value) { return (long) toInt( value ); } + + @Override + public Class getPrimitiveClass() { + return boolean.class; + } + + @Override + public Boolean getDefaultValue() { + return FALSE; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteTypeDescriptor.java index 84a32f2fa5..e9cfcf0fe6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteTypeDescriptor.java @@ -6,6 +6,7 @@ */ package org.hibernate.type.descriptor.java; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Byte} handling. @@ -13,7 +14,7 @@ import org.hibernate.type.descriptor.WrapperOptions; * @author Steve Ebersole * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ -public class ByteTypeDescriptor extends AbstractTypeDescriptor { +public class ByteTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final ByteTypeDescriptor INSTANCE = new ByteTypeDescriptor(); public ByteTypeDescriptor() { @@ -58,6 +59,7 @@ public class ByteTypeDescriptor extends AbstractTypeDescriptor { } throw unknownUnwrap( type ); } + @Override public Byte wrap(X value, WrapperOptions options) { if ( value == null ) { @@ -74,4 +76,14 @@ public class ByteTypeDescriptor extends AbstractTypeDescriptor { } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return byte.class; + } + + @Override + public Byte getDefaultValue() { + return 0; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterTypeDescriptor.java index ff125d8d19..97ff35ee18 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterTypeDescriptor.java @@ -7,13 +7,14 @@ package org.hibernate.type.descriptor.java; import org.hibernate.HibernateException; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Character} handling. * * @author Steve Ebersole */ -public class CharacterTypeDescriptor extends AbstractTypeDescriptor { +public class CharacterTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final CharacterTypeDescriptor INSTANCE = new CharacterTypeDescriptor(); public CharacterTypeDescriptor() { @@ -66,4 +67,14 @@ public class CharacterTypeDescriptor extends AbstractTypeDescriptor { } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return char.class; + } + + @Override + public Character getDefaultValue() { + return 0; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleTypeDescriptor.java index f376ce8a05..7d94a78a7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleTypeDescriptor.java @@ -10,13 +10,14 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Double} handling. * * @author Steve Ebersole */ -public class DoubleTypeDescriptor extends AbstractTypeDescriptor { +public class DoubleTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final DoubleTypeDescriptor INSTANCE = new DoubleTypeDescriptor(); public DoubleTypeDescriptor() { @@ -71,15 +72,25 @@ public class DoubleTypeDescriptor extends AbstractTypeDescriptor { if ( value == null ) { return null; } - if ( Double.class.isInstance( value ) ) { + if ( value instanceof Double ) { return (Double) value; } - if ( Number.class.isInstance( value ) ) { + if ( value instanceof Number ) { return ( (Number) value ).doubleValue(); } - else if ( String.class.isInstance( value ) ) { + else if ( value instanceof String ) { return Double.valueOf( ( (String) value ) ); } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return double.class; + } + + @Override + public Double getDefaultValue() { + return 0.0; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatTypeDescriptor.java index b06c10ae84..49160da063 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatTypeDescriptor.java @@ -10,13 +10,14 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Float} handling. * * @author Steve Ebersole */ -public class FloatTypeDescriptor extends AbstractTypeDescriptor { +public class FloatTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final FloatTypeDescriptor INSTANCE = new FloatTypeDescriptor(); public FloatTypeDescriptor() { @@ -82,4 +83,14 @@ public class FloatTypeDescriptor extends AbstractTypeDescriptor { } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return float.class; + } + + @Override + public Float getDefaultValue() { + return 0F; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerTypeDescriptor.java index f853f22fa6..fb9d56c290 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerTypeDescriptor.java @@ -10,13 +10,14 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Integer} handling. * * @author Steve Ebersole */ -public class IntegerTypeDescriptor extends AbstractTypeDescriptor { +public class IntegerTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final IntegerTypeDescriptor INSTANCE = new IntegerTypeDescriptor(); public IntegerTypeDescriptor() { @@ -82,4 +83,14 @@ public class IntegerTypeDescriptor extends AbstractTypeDescriptor { } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return int.class; + } + + @Override + public Integer getDefaultValue() { + return 0; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongTypeDescriptor.java index 7fb6086b9d..79d6ccbec6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongTypeDescriptor.java @@ -10,13 +10,14 @@ import java.math.BigDecimal; import java.math.BigInteger; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Long} handling. * * @author Steve Ebersole */ -public class LongTypeDescriptor extends AbstractTypeDescriptor { +public class LongTypeDescriptor extends AbstractTypeDescriptorimplements Primitive { public static final LongTypeDescriptor INSTANCE = new LongTypeDescriptor(); public LongTypeDescriptor() { @@ -82,4 +83,14 @@ public class LongTypeDescriptor extends AbstractTypeDescriptor { } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return long.class; + } + + @Override + public Long getDefaultValue() { + return 0L; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortTypeDescriptor.java index afeca1b98a..12c5ff0904 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortTypeDescriptor.java @@ -6,13 +6,14 @@ */ package org.hibernate.type.descriptor.java; import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.spi.Primitive; /** * Descriptor for {@link Short} handling. * * @author Steve Ebersole */ -public class ShortTypeDescriptor extends AbstractTypeDescriptor { +public class ShortTypeDescriptor extends AbstractTypeDescriptor implements Primitive { public static final ShortTypeDescriptor INSTANCE = new ShortTypeDescriptor(); public ShortTypeDescriptor() { @@ -72,4 +73,14 @@ public class ShortTypeDescriptor extends AbstractTypeDescriptor { } throw unknownWrap( value.getClass() ); } + + @Override + public Class getPrimitiveClass() { + return short.class; + } + + @Override + public Short getDefaultValue() { + return 0; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeDescriptorRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeDescriptorRegistry.java index d7278279d9..3188bf59e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeDescriptorRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeDescriptorRegistry.java @@ -39,6 +39,7 @@ public class JavaTypeDescriptorRegistry implements JavaTypeDescriptorBaseline.Ba @SuppressWarnings("unused") public JavaTypeDescriptorRegistry(TypeConfiguration typeConfiguration) { this.typeConfiguration = typeConfiguration; + JavaTypeDescriptorBaseline.prime( this ); } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java index 32687e8fe9..15efed5b94 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java @@ -21,6 +21,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; @@ -58,6 +59,11 @@ import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.tuple.entity.BytecodeEnhancementMetadataNonPojoImpl; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; @@ -717,6 +723,16 @@ public class PersisterClassProviderTest { public JavaTypeDescriptor getMappedJavaTypeDescriptor() { return null; } + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + throw new NotYetImplementedFor6Exception( getClass() ); + } } public static class GoofyException extends RuntimeException { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/PluralAttributeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/PluralAttributeTests.java index 85bb0dc869..aab9d5bf1a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/PluralAttributeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/PluralAttributeTests.java @@ -6,13 +6,16 @@ */ package org.hibernate.orm.test.metamodel.mapping; -import java.sql.Statement; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.persistence.AttributeConverter; import javax.persistence.CascadeType; +import javax.persistence.Convert; import javax.persistence.ElementCollection; import javax.persistence.Embeddable; +import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -21,14 +24,20 @@ import javax.persistence.OneToMany; import javax.persistence.OrderColumn; import javax.persistence.Table; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.DomainMetamodel; + import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.ServiceRegistry; 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.BeforeAll; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + /** * @author Steve Ebersole */ @@ -36,6 +45,7 @@ import org.junit.jupiter.api.Test; annotatedClasses = { PluralAttributeTests.SimpleEntity.class, PluralAttributeTests.EntityContainingLists.class, + PluralAttributeTests.EntityContainingSets.class, PluralAttributeTests.Component.class } ) @@ -46,51 +56,50 @@ public class PluralAttributeTests { @Test public void testLists(SessionFactoryScope scope) { - System.out.println( "test" ); + final DomainMetamodel domainModel = scope.getSessionFactory().getDomainModel(); + final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityContainingLists.class ); + + assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 6 ) ); + + final AttributeMapping listOfBasics = containerEntityDescriptor.findAttributeMapping( "listOfBasics" ); + assertThat( listOfBasics, notNullValue() ); + + final AttributeMapping listOfConvertedBasics = containerEntityDescriptor.findAttributeMapping( "listOfConvertedBasics" ); + assertThat( listOfConvertedBasics, notNullValue() ); + + + final AttributeMapping listOfEnums = containerEntityDescriptor.findAttributeMapping( "listOfEnums" ); + assertThat( listOfEnums, notNullValue() ); + + final AttributeMapping listOfComponents = containerEntityDescriptor.findAttributeMapping( "listOfComponents" ); + assertThat( listOfComponents, notNullValue() ); + + final AttributeMapping listOfEntities = containerEntityDescriptor.findAttributeMapping( "listOfEntities" ); + assertThat( listOfEntities, notNullValue() ); } - @BeforeAll - public void createTestData(SessionFactoryScope scope) { - scope.inTransaction( - session -> { - final EntityContainingLists entityContainingLists = new EntityContainingLists( 1, "first" ); + @Test + public void testSets(SessionFactoryScope scope) { + final DomainMetamodel domainModel = scope.getSessionFactory().getDomainModel(); + final EntityMappingType containerEntityDescriptor = domainModel.getEntityDescriptor( EntityContainingSets.class ); - entityContainingLists.addBasic( "abc" ); - entityContainingLists.addBasic( "def" ); - entityContainingLists.addBasic( "ghi" ); + assertThat( containerEntityDescriptor.getNumberOfAttributeMappings(), is( 6 ) ); - entityContainingLists.addConvertedBasic( EnumValue.TWO ); + final AttributeMapping setOfBasics = containerEntityDescriptor.findAttributeMapping( "setOfBasics" ); + assertThat( setOfBasics, notNullValue() ); - entityContainingLists.addEnum( EnumValue.ONE ); - entityContainingLists.addEnum( EnumValue.THREE ); + final AttributeMapping setOfConvertedBasics = containerEntityDescriptor.findAttributeMapping( "setOfConvertedBasics" ); + assertThat( setOfConvertedBasics, notNullValue() ); - entityContainingLists.addComponent( new Component( "first-a1", "first-another-a1" ) ); - entityContainingLists.addComponent( new Component( "first-a2", "first-another-a2" ) ); - entityContainingLists.addSimpleEntity( new SimpleEntity( 1, "simple-1" ) ); - entityContainingLists.addSimpleEntity( new SimpleEntity( 2, "simple-2" ) ); + final AttributeMapping setOfEnums = containerEntityDescriptor.findAttributeMapping( "setOfEnums" ); + assertThat( setOfEnums, notNullValue() ); - session.save( entityContainingLists ); - } - ); - } + final AttributeMapping setOfComponents = containerEntityDescriptor.findAttributeMapping( "setOfComponents" ); + assertThat( setOfComponents, notNullValue() ); - @AfterAll - public void deleteTestData(SessionFactoryScope scope) { - scope.inTransaction( - session -> session.doWork( - conn -> { - try ( Statement stmnt = conn.createStatement() ) { - stmnt.execute( "delete from EntityContainingLists_listOfEnums" ); - stmnt.execute( "delete from EntityContainingLists_listOfConvertedBasics" ); - stmnt.execute( "delete from EntityContainingLists_listOfComponents" ); - stmnt.execute( "delete from EntityContainingLists_listOfBasics" ); - stmnt.execute( "delete from entity_containing_lists_simple_entity" ); - stmnt.execute( "delete from entity_containing_lists" ); - } - } - ) - ); + final AttributeMapping setOfEntities = containerEntityDescriptor.findAttributeMapping( "setOfEntities" ); + assertThat( setOfEntities, notNullValue() ); } public enum EnumValue { @@ -198,6 +207,7 @@ public class PluralAttributeTests { @ElementCollection @OrderColumn + @Convert( converter = Converter.class ) public List getListOfConvertedBasics() { return listOfConvertedBasics; } @@ -266,6 +276,127 @@ public class PluralAttributeTests { } } + @Entity( name = "EntityContainingSets" ) + @Table( name = "entity_containing_sets" ) + public static class EntityContainingSets { + private Integer id; + private String name; + + private Set setOfBasics; + private Set setOfConvertedBasics; + private Set setOfEnums; + private Set setOfComponents; + private Set setOfEntities; + + public EntityContainingSets() { + } + + public EntityContainingSets(Integer id, String name) { + this.id = id; + this.name = name; + } + + @Id + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ElementCollection + public Set getSetOfBasics() { + return setOfBasics; + } + + public void setSetOfBasics(Set setOfBasics) { + this.setOfBasics = setOfBasics; + } + + public void addBasic(String value) { + if ( setOfBasics == null ) { + setOfBasics = new HashSet<>(); + } + setOfBasics.add( value ); + } + + @ElementCollection + @Convert( converter = Converter.class ) + public Set getSetOfConvertedBasics() { + return setOfConvertedBasics; + } + + public void setSetOfConvertedBasics(Set setOfConvertedBasics) { + this.setOfConvertedBasics = setOfConvertedBasics; + } + + public void addConvertedBasic(EnumValue value) { + if ( setOfConvertedBasics == null ) { + setOfConvertedBasics = new HashSet<>(); + } + setOfConvertedBasics.add( value ); + } + + @ElementCollection + @Enumerated( EnumType.STRING ) + public Set getSetOfEnums() { + return setOfEnums; + } + + public void setSetOfEnums(Set setOfEnums) { + this.setOfEnums = setOfEnums; + } + + public void addEnum(EnumValue value) { + if ( setOfEnums == null ) { + setOfEnums = new HashSet<>(); + } + setOfEnums.add( value ); + } + + @ElementCollection + @Embedded + public Set getSetOfComponents() { + return setOfComponents; + } + + public void setSetOfComponents(Set setOfComponents) { + this.setOfComponents = setOfComponents; + } + + public void addComponent(Component value) { + if ( setOfComponents == null ) { + setOfComponents = new HashSet<>(); + } + setOfComponents.add( value ); + } + + @OneToMany( cascade = CascadeType.ALL ) + public Set getSetOfEntities() { + return setOfEntities; + } + + public void setSetOfEntities(Set setOfEntities) { + this.setOfEntities = setOfEntities; + } + + public void addSimpleEntity(SimpleEntity value) { + if ( setOfEntities == null ) { + setOfEntities = new HashSet<>(); + } + setOfEntities.add( value ); + } + } + @Entity( name = "SimpleEntity" ) @Table( name = "simple_entity" ) public static class SimpleEntity { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SmokeTests.java index d334939b4b..3e84731d56 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SmokeTests.java @@ -24,7 +24,10 @@ import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping; import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter; import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter; +import org.hibernate.metamodel.model.convert.spi.EnumValueConverter; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.CustomType; +import org.hibernate.usertype.UserType; import org.hibernate.testing.hamcrest.CollectionMatchers; import org.hibernate.testing.orm.junit.DomainModel; @@ -72,8 +75,12 @@ public class SmokeTests { final BasicValuedSingularAttributeMapping genderAttrMapping = (BasicValuedSingularAttributeMapping) genderPart; assert "mapping_simple_entity".equals( genderAttrMapping.getContainingTableExpression() ); assert "gender".equals( genderAttrMapping.getMappedColumnExpression() ); - assert genderAttrMapping.getConverter() != null; - assert genderAttrMapping.getConverter() instanceof OrdinalEnumValueConverter; + assert genderAttrMapping.getMappedTypeDescriptor() instanceof CustomType; + final UserType userType = ( (CustomType) genderAttrMapping.getMappedTypeDescriptor() ).getUserType(); + assert userType instanceof org.hibernate.type.EnumType; + final EnumValueConverter converter = ( (org.hibernate.type.EnumType) userType ).getEnumValueConverter(); + assert converter != null; + assert converter instanceof OrdinalEnumValueConverter; } { @@ -82,8 +89,13 @@ public class SmokeTests { final BasicValuedSingularAttributeMapping attrMapping = (BasicValuedSingularAttributeMapping) part; assert "mapping_simple_entity".equals( attrMapping.getContainingTableExpression() ); assert "gender2".equals( attrMapping.getMappedColumnExpression() ); - assert attrMapping.getConverter() != null; - assert attrMapping.getConverter() instanceof NamedEnumValueConverter; + + assert attrMapping.getMappedTypeDescriptor() instanceof CustomType; + final UserType userType = ( (CustomType) attrMapping.getMappedTypeDescriptor() ).getUserType(); + assert userType instanceof org.hibernate.type.EnumType; + final EnumValueConverter converter = ( (org.hibernate.type.EnumType) userType ).getEnumValueConverter(); + assert converter != null; + assert converter instanceof NamedEnumValueConverter; } { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java index d34438bc4b..0cbd27db4f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java @@ -35,7 +35,10 @@ import org.hibernate.sql.results.internal.domain.basic.BasicResult; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultAssembler; +import org.hibernate.type.CustomType; +import org.hibernate.type.EnumType; import org.hibernate.type.internal.StandardBasicTypeImpl; +import org.hibernate.usertype.UserType; import org.hibernate.testing.hamcrest.AssignableMatcher; import org.hibernate.testing.orm.junit.DomainModel; @@ -49,6 +52,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; /** @@ -174,10 +178,15 @@ public class SmokeTests { assertThat( columnReference.renderSqlFragment( scope.getSessionFactory() ), is( "s1_0.gender" ) ); final MappingModelExpressable selectedExpressable = selectedExpression.getExpressionType(); - assertThat( selectedExpressable, instanceOf( StandardBasicTypeImpl.class ) ); - final StandardBasicTypeImpl basicType = (StandardBasicTypeImpl) selectedExpressable; - assertThat( basicType.getJavaTypeDescriptor().getJavaType(), AssignableMatcher.assignableTo( Integer.class ) ); - assertThat( basicType.getSqlTypeDescriptor().getSqlType(), is( Types.INTEGER ) ); + //assertThat( selectedExpressable, instanceOf( StandardBasicTypeImpl.class ) ); +// assertThat( basicType.getJavaTypeDescriptor().getJavaType(), AssignableMatcher.assignableTo( Integer.class ) ); +// assertThat( basicType.getSqlTypeDescriptor().getSqlType(), is( Types.INTEGER ) ); + assertThat( selectedExpressable, instanceOf( CustomType.class ) ); + final CustomType basicType = (CustomType) selectedExpressable; + final EnumType enumType = (EnumType) basicType.getUserType(); + assertThat( enumType.getEnumValueConverter().getRelationalJavaDescriptor().getJavaType(), AssignableMatcher.assignableTo( Integer.class ) ); + assertThat( enumType.sqlTypes()[0], is( Types.INTEGER ) ); + assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) ); final DomainResult domainResult = sqlAst.getDomainResultDescriptors().get( 0 ); @@ -185,8 +194,10 @@ public class SmokeTests { final BasicResult scalarDomainResult = (BasicResult) domainResult; assertThat( scalarDomainResult.getAssembler(), instanceOf( BasicResultAssembler.class ) ); final BasicResultAssembler assembler = (BasicResultAssembler) scalarDomainResult.getAssembler(); - assertThat( assembler.getValueConverter(), notNullValue() ); - assertThat( assembler.getValueConverter(), instanceOf( OrdinalEnumValueConverter.class ) ); +// assertThat( assembler.getValueConverter(), notNullValue() ); +// assertThat( assembler.getValueConverter(), instanceOf( OrdinalEnumValueConverter.class ) ); + assertThat( assembler.getValueConverter(), nullValue() ); + final NavigablePath expectedSelectedPath = new NavigablePath( org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity.class.getName(), "e" @@ -202,9 +213,10 @@ public class SmokeTests { ); assertThat( resultAssembler, instanceOf( BasicResultAssembler.class ) ); - final BasicValueConverter valueConverter = ( (BasicResultAssembler) resultAssembler ).getValueConverter(); - assertThat( valueConverter, notNullValue() ); - assertThat( valueConverter, instanceOf( OrdinalEnumValueConverter.class ) ); +// final BasicValueConverter valueConverter = ( (BasicResultAssembler) resultAssembler ).getValueConverter(); +// assertThat( valueConverter, notNullValue() ); +// assertThat( valueConverter, instanceOf( OrdinalEnumValueConverter.class ) ); + assertThat( ( (BasicResultAssembler) resultAssembler ).getValueConverter(), nullValue() ); final JdbcSelect jdbcSelectOperation = new StandardSqlAstSelectTranslator( session.getSessionFactory() ) .interpret( sqlAst ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/PluralAttributeSmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/PluralAttributeSmokeTests.java new file mode 100644 index 0000000000..9833b17996 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/PluralAttributeSmokeTests.java @@ -0,0 +1,196 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.sql.exec; + +import java.sql.Statement; + +import org.hibernate.Hibernate; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.orm.test.metamodel.mapping.PluralAttributeTests.Component; +import org.hibernate.orm.test.metamodel.mapping.PluralAttributeTests.EntityContainingLists; +import org.hibernate.orm.test.metamodel.mapping.PluralAttributeTests.EntityContainingSets; +import org.hibernate.orm.test.metamodel.mapping.PluralAttributeTests.EnumValue; +import org.hibernate.orm.test.metamodel.mapping.PluralAttributeTests.SimpleEntity; +import org.hibernate.query.spi.QueryImplementor; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +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.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.hamcrest.CoreMatchers; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Steve Ebersole + */ +@DomainModel( + annotatedClasses = { + SimpleEntity.class, + EntityContainingLists.class, + EntityContainingSets.class, + Component.class + } +) +@ServiceRegistry +@SessionFactory +@SuppressWarnings("WeakerAccess") +public class PluralAttributeSmokeTests { + + @Test + public void listBaselineTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final QueryImplementor query = session.createQuery( + "select e from EntityContainingLists e", + EntityContainingLists.class + ); + final EntityContainingLists result = query.uniqueResult(); + + assertThat( result, notNullValue() ); + assertThat( result.getListOfBasics(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getListOfBasics() ), is( false ) ); + } + ); + } + + @Test + public void listEagerBasicTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final QueryImplementor query = session.createQuery( + "select e from EntityContainingLists e join fetch e.listOfBasics", + EntityContainingLists.class + ); + final EntityContainingLists result = query.uniqueResult(); + + assertThat( result, notNullValue() ); + + assertThat( result.getListOfBasics(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getListOfBasics() ), is( true ) ); + assertTrue( session.getPersistenceContext().containsCollection( (PersistentCollection) result.getListOfBasics() ) ); + assertThat( result.getListOfBasics().size(), is( 3 ) ); + + assertThat( result.getListOfConvertedBasics(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getListOfConvertedBasics() ), is( false ) ); + assertTrue( session.getPersistenceContext().containsCollection( (PersistentCollection) result.getListOfConvertedBasics() ) ); + + assertThat( result.getListOfEnums(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getListOfEnums() ), is( false ) ); + assertTrue( session.getPersistenceContext().containsCollection( (PersistentCollection) result.getListOfEnums() ) ); + + assertThat( result.getListOfComponents(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getListOfComponents() ), is( false ) ); + assertTrue( session.getPersistenceContext().containsCollection( (PersistentCollection) result.getListOfComponents() ) ); + + assertThat( result.getListOfEntities(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getListOfEntities() ), is( false ) ); + assertTrue( session.getPersistenceContext().containsCollection( (PersistentCollection) result.getListOfEntities() ) ); + + } + ); + } + + @Test + public void setBaselineTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final QueryImplementor query = session.createQuery( + "select e from EntityContainingSets e", + EntityContainingSets.class + ); + + final EntityContainingSets result = query.uniqueResult(); + + assertThat( result, notNullValue() ); + assertThat( result.getSetOfBasics(), notNullValue() ); + assertThat( Hibernate.isInitialized( result.getSetOfBasics() ), is( false ) ); + } + ); + } + + + @BeforeAll + public void createTestData(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + final SimpleEntity simpleEntity1 = new SimpleEntity( 1, "simple-1" ); + final SimpleEntity simpleEntity2 = new SimpleEntity( 2, "simple-2" ); + + session.save( simpleEntity1 ); + session.save( simpleEntity2 ); + + { + final EntityContainingLists entityContainingLists = new EntityContainingLists( 1, "first" ); + + entityContainingLists.addBasic( "abc" ); + entityContainingLists.addBasic( "def" ); + entityContainingLists.addBasic( "ghi" ); + + entityContainingLists.addConvertedBasic( EnumValue.TWO ); + + entityContainingLists.addEnum( EnumValue.ONE ); + entityContainingLists.addEnum( EnumValue.THREE ); + + entityContainingLists.addComponent( new Component( "first-a1", "first-another-a1" ) ); + entityContainingLists.addComponent( new Component( "first-a2", "first-another-a2" ) ); + + entityContainingLists.addSimpleEntity( simpleEntity1 ); + entityContainingLists.addSimpleEntity( simpleEntity2 ); + + session.save( entityContainingLists ); + } + + { + final EntityContainingSets entity = new EntityContainingSets( 1, "first" ); + + entity.addBasic( "abc" ); + entity.addBasic( "def" ); + entity.addBasic( "ghi" ); + + entity.addConvertedBasic( EnumValue.TWO ); + + entity.addEnum( EnumValue.ONE ); + entity.addEnum( EnumValue.THREE ); + + entity.addComponent( new Component( "first-a1", "first-another-a1" ) ); + entity.addComponent( new Component( "first-a2", "first-another-a2" ) ); + + entity.addSimpleEntity( simpleEntity1 ); + entity.addSimpleEntity( simpleEntity2 ); + + session.save( entity ); + } + } + ); + } + + @AfterAll + public void deleteTestData(SessionFactoryScope scope) { + scope.inTransaction( + session -> session.doWork( + conn -> { + try ( Statement stmnt = conn.createStatement() ) { + stmnt.execute( "delete from EntityContainingLists_listOfEnums" ); + stmnt.execute( "delete from EntityContainingLists_listOfConvertedBasics" ); + stmnt.execute( "delete from EntityContainingLists_listOfComponents" ); + stmnt.execute( "delete from EntityContainingLists_listOfBasics" ); + stmnt.execute( "delete from entity_containing_lists_simple_entity" ); + stmnt.execute( "delete from entity_containing_lists" ); + } + } + ) + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java index 165ff987e1..ac538d3e64 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/SmokeTests.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.hibernate.cfg.AvailableSettings; diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index c81e1b684b..0fd589e95a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -20,6 +20,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.CollectionDataAccess; import org.hibernate.cache.spi.access.EntityDataAccess; @@ -58,6 +59,11 @@ import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.CollectionElementDefinition; import org.hibernate.persister.walking.spi.CollectionIndexDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.tuple.entity.BytecodeEnhancementMetadataNonPojoImpl; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; @@ -703,6 +709,16 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { public JavaTypeDescriptor getMappedJavaTypeDescriptor() { return null; } + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + throw new NotYetImplementedFor6Exception( getClass() ); + } } public static class NoopCollectionPersister implements CollectionPersister { diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 5f0840d50a..f7c23bb04f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -21,6 +21,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.NaturalIdDataAccess; @@ -59,6 +60,11 @@ import org.hibernate.persister.entity.MultiLoadOptions; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; +import org.hibernate.query.sqm.sql.SqlExpressionResolver; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAliasBase; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.from.TableReferenceCollector; import org.hibernate.tuple.entity.BytecodeEnhancementMetadataNonPojoImpl; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; @@ -815,4 +821,14 @@ public class CustomPersister implements EntityPersister { public JavaTypeDescriptor getMappedJavaTypeDescriptor() { return null; } + + @Override + public void applyTableReferences( + SqlAliasBase sqlAliasBase, + JoinType baseJoinType, + TableReferenceCollector collector, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + throw new NotYetImplementedFor6Exception( getClass() ); + } }