`@CollectionType` + better CollectionSemantics resolution handling from annotations;

HHH-9688 - Combination of @OrderBy and @SortComparator

HHH-9688 was simple change in the code I was working on already, so went ahead and just did it
This commit is contained in:
Steve Ebersole 2021-12-30 09:00:03 -06:00
parent e16c78f7ea
commit 452f114957
72 changed files with 1739 additions and 1228 deletions

View File

@ -31,8 +31,7 @@ public @interface CollectionType {
* Names the type.
*
* Could name the implementation class (an implementation of {@link org.hibernate.type.CollectionType} or
* {@link org.hibernate.usertype.UserCollectionType}). Could also name a custom type defined via a
* {@link TypeDef @TypeDef}
* {@link org.hibernate.usertype.UserCollectionType}).
*/
String type();

View File

@ -2028,7 +2028,6 @@ public final class AnnotationBinder {
);
}
CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
propertyHolder.getEntityName(),
property,
!indexColumn.isImplicit(),
// ugh
@ -2060,7 +2059,6 @@ public final class AnnotationBinder {
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
collectionBinder.setIgnoreNotFound( ignoreNotFound );
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
collectionBinder.setBuildingContext( context );
collectionBinder.setAccessType( inferredData.getDefaultAccess() );
Ejb3Column[] elementColumns;

View File

@ -6,9 +6,11 @@
*/
package org.hibernate.cfg.annotations;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Array;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SemanticsResolver;
/**
* Bind an Array
@ -16,11 +18,11 @@ import org.hibernate.mapping.PersistentClass;
* @author Anthony Patricio
*/
public class ArrayBinder extends ListBinder {
public ArrayBinder() {
public ArrayBinder(SemanticsResolver semanticsResolver, MetadataBuildingContext buildingContext) {
super( semanticsResolver, buildingContext );
}
protected Collection createCollection(PersistentClass persistentClass) {
return new Array( getBuildingContext(), persistentClass );
protected Collection createCollection(PersistentClass owner) {
return new Array( getSemanticsResolver(), owner, getBuildingContext() );
}
}

View File

@ -5,8 +5,11 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cfg.annotations;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SemanticsResolver;
/**
* Bind a bag.
@ -14,11 +17,11 @@ import org.hibernate.mapping.PersistentClass;
* @author Matthew Inger
*/
public class BagBinder extends CollectionBinder {
public BagBinder() {
super( false );
public BagBinder(SemanticsResolver semanticsResolver, MetadataBuildingContext context) {
super( semanticsResolver, false, context );
}
protected Collection createCollection(PersistentClass persistentClass) {
return new org.hibernate.mapping.Bag( getBuildingContext(), persistentClass );
protected Collection createCollection(PersistentClass owner) {
return new org.hibernate.mapping.Bag( getSemanticsResolver(), owner, getBuildingContext() );
}
}

View File

@ -72,11 +72,13 @@ import org.hibernate.cfg.PropertyHolderBuilder;
import org.hibernate.cfg.PropertyInferredData;
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.cfg.SecondPass;
import org.hibernate.collection.internal.CustomCollectionTypeSemantics;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.spi.MutableJpaCompliance;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Backref;
import org.hibernate.mapping.Collection;
@ -89,8 +91,10 @@ import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SemanticsResolver;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.EmbeddableInstantiator;
import org.jboss.logging.Logger;
@ -134,7 +138,9 @@ public abstract class CollectionBinder {
java.util.Collection.class
);
private MetadataBuildingContext buildingContext;
private final MetadataBuildingContext buildingContext;
private final SemanticsResolver semanticsResolver;
private final boolean isSortedCollection;
protected Collection collection;
protected String propertyName;
@ -169,7 +175,6 @@ public abstract class CollectionBinder {
private AccessType accessType;
private boolean hibernateExtensionMapping;
private boolean isSortedCollection;
private jakarta.persistence.OrderBy jpaOrderBy;
private OrderBy sqlOrderBy;
private SortNatural naturalSort;
@ -178,16 +183,18 @@ public abstract class CollectionBinder {
private String explicitType;
private final Properties explicitTypeParameters = new Properties();
protected CollectionBinder(boolean isSortedCollection) {
protected CollectionBinder(SemanticsResolver semanticsResolver, boolean isSortedCollection, MetadataBuildingContext buildingContext) {
this.semanticsResolver = semanticsResolver;
this.isSortedCollection = isSortedCollection;
this.buildingContext = buildingContext;
}
protected MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
public void setBuildingContext(MetadataBuildingContext buildingContext) {
this.buildingContext = buildingContext;
protected SemanticsResolver getSemanticsResolver() {
return semanticsResolver;
}
public boolean isMap() {
@ -260,151 +267,185 @@ public abstract class CollectionBinder {
* collection binder factory
*/
public static CollectionBinder getCollectionBinder(
String entityName,
XProperty property,
boolean isIndexed,
boolean isHibernateExtensionMapping,
MetadataBuildingContext buildingContext) {
final CollectionBinder result;
final CollectionType typeAnnotation = property.getAnnotation( CollectionType.class );
if ( property.isArray() ) {
if ( property.getElementClass().isPrimitive() ) {
result = new PrimitiveArrayBinder();
}
else {
result = new ArrayBinder();
final CollectionBinder binder;
if ( typeAnnotation != null ) {
binder = createBinderFromCustomTypeAnnotation( property, isIndexed, typeAnnotation, buildingContext );
}
else {
binder = createBinderFromProperty( property, isIndexed, buildingContext );
}
binder.setIsHibernateExtensionMapping( isHibernateExtensionMapping );
if ( typeAnnotation != null ) {
binder.explicitType = typeAnnotation.type();
for ( Parameter param : typeAnnotation.parameters() ) {
binder.explicitTypeParameters.setProperty( param.name(), param.value() );
}
}
else if ( property.isCollection() ) {
//TODO consider using an XClass
final Class<?> returnedClass = property.getCollectionClass();
final CollectionBinder basicBinder = getBinderFromBasicCollectionType(
returnedClass,
property,
entityName,
isIndexed
);
if ( basicBinder != null ) {
result = basicBinder;
return binder;
}
private static CollectionBinder createBinderFromProperty(
XProperty property,
boolean isIndexed,
MetadataBuildingContext buildingContext) {
final CollectionClassification classification = determineCollectionClassification( property, null, isIndexed, buildingContext );
return createBinder( property, classification, null, buildingContext );
}
private static CollectionBinder createBinderFromCustomTypeAnnotation(
XProperty property,
boolean isIndexed,
CollectionType typeAnnotation,
MetadataBuildingContext buildingContext) {
final CollectionClassification classification = determineCollectionClassification( property, typeAnnotation, isIndexed, buildingContext );
final SemanticsResolver semanticsResolver = (collectionType) -> new CustomCollectionTypeSemantics<>( collectionType, classification );
return createBinder( property, classification, semanticsResolver, buildingContext );
}
private static CollectionBinder createBinder(
XProperty property,
CollectionClassification classification,
SemanticsResolver semanticsResolver,
MetadataBuildingContext buildingContext) {
switch ( classification ) {
case ARRAY: {
if ( property.getElementClass().isPrimitive() ) {
return new PrimitiveArrayBinder( semanticsResolver, buildingContext );
}
return new ArrayBinder( semanticsResolver, buildingContext );
}
else if ( property.isAnnotationPresent( CollectionType.class ) ) {
Class<?> semanticsClass = property.getAnnotation( CollectionType.class ).semantics();
case BAG: {
return new BagBinder( semanticsResolver, buildingContext );
}
case IDBAG: {
return new IdBagBinder( semanticsResolver, buildingContext );
}
case LIST: {
return new ListBinder( semanticsResolver, buildingContext );
}
case MAP:
case ORDERED_MAP: {
return new MapBinder( semanticsResolver, false, buildingContext );
}
case SORTED_MAP: {
return new MapBinder( semanticsResolver, true, buildingContext );
}
case SET:
case ORDERED_SET: {
return new SetBinder( semanticsResolver, false, buildingContext );
}
case SORTED_SET: {
return new SetBinder( semanticsResolver, true, buildingContext );
}
}
if ( semanticsClass != void.class ) {
result = getBinderFromBasicCollectionType( semanticsClass, property, entityName, isIndexed );
}
else {
final Class<?> inferredClass = inferCollectionClassFromSubclass( returnedClass );
result = inferredClass != null ? getBinderFromBasicCollectionType(
inferredClass,
property,
entityName,
isIndexed
) : null;
}
final XClass declaringClass = property.getDeclaringClass();
throw new AnnotationException(
String.format(
Locale.ROOT,
"Unable to determine proper CollectionBinder (`%s) : %s.%s",
classification,
declaringClass.getName(),
property.getName()
)
);
}
private static CollectionClassification determineCollectionClassification(
XProperty property,
CollectionType typeAnnotation,
boolean isIndexed,
MetadataBuildingContext buildingContext) {
if ( property.isArray() ) {
return CollectionClassification.ARRAY;
}
return determineCollectionClassification(
determineSemanticJavaType( property, typeAnnotation, isIndexed, buildingContext ),
property
);
}
private static CollectionClassification determineCollectionClassification(Class<?> semanticJavaType, XProperty property) {
if ( semanticJavaType.isArray() ) {
return CollectionClassification.ARRAY;
}
else if ( java.util.List.class.isAssignableFrom( semanticJavaType ) ) {
return CollectionClassification.LIST;
}
else if ( java.util.SortedSet.class.isAssignableFrom( semanticJavaType ) ) {
return CollectionClassification.SORTED_SET;
}
else if ( java.util.Set.class.isAssignableFrom( semanticJavaType ) ) {
return CollectionClassification.SET;
}
else if ( java.util.SortedMap.class.isAssignableFrom( semanticJavaType ) ) {
return CollectionClassification.SORTED_MAP;
}
else if ( java.util.Map.class.isAssignableFrom( semanticJavaType ) ) {
return CollectionClassification.MAP;
}
else if ( java.util.Collection.class.isAssignableFrom( semanticJavaType ) ) {
if ( property.isAnnotationPresent( CollectionId.class ) ) {
return CollectionClassification.IDBAG;
}
else {
result = null;
}
if ( result == null ) {
throw new AnnotationException(
returnedClass.getName() + " collection type not supported for property: "
+ StringHelper.qualify( entityName, property.getName() )
);
return CollectionClassification.BAG;
}
}
else {
return null;
}
}
private static Class<?> determineSemanticJavaType(XProperty property, CollectionType typeAnnotation, boolean isIndexed, MetadataBuildingContext buildingContext) {
final Class<?> returnedJavaType = property.getCollectionClass();
if ( typeAnnotation != null ) {
final Class<?> requestedSemanticsJavaType = typeAnnotation.semantics();
if ( requestedSemanticsJavaType != null && requestedSemanticsJavaType != void.class ) {
return inferCollectionClassFromSubclass( requestedSemanticsJavaType, isIndexed, buildingContext );
}
}
if ( returnedJavaType == null ) {
throw new AnnotationException(
"Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @ElementCollection: "
+ StringHelper.qualify( entityName, property.getName() )
String.format(
Locale.ROOT,
"Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: %s.%s",
property.getDeclaringClass().getName(),
property.getName()
)
);
}
result.setIsHibernateExtensionMapping( isHibernateExtensionMapping );
final CollectionType typeAnnotation = property.getAnnotation( CollectionType.class );
if ( typeAnnotation != null ) {
final String typeName = typeAnnotation.type();
// see if it names a type-def
final TypeDefinition typeDef = buildingContext.getMetadataCollector().getTypeDefinition( typeName );
if ( typeDef != null ) {
result.explicitType = typeDef.getTypeImplementorClass().getName();
result.explicitTypeParameters.putAll( typeDef.getParameters() );
}
else {
result.explicitType = typeName;
for ( Parameter param : typeAnnotation.parameters() ) {
result.explicitTypeParameters.setProperty( param.name(), param.value() );
}
}
}
return result;
return inferCollectionClassFromSubclass( returnedJavaType, isIndexed, buildingContext );
}
private static CollectionBinder getBinderFromBasicCollectionType(Class<?> clazz, XProperty property,
String entityName, boolean isIndexed) {
if ( java.util.Set.class.equals( clazz) ) {
if ( property.isAnnotationPresent( CollectionId.class) ) {
throw new AnnotationException("Set does not support @CollectionId: "
+ StringHelper.qualify( entityName, property.getName() ) );
}
return new SetBinder( false );
private static Class<?> inferCollectionClassFromSubclass(Class<?> clazz, boolean isIndexed, MetadataBuildingContext buildingContext) {
if ( java.util.List.class.isAssignableFrom( clazz ) && !isIndexed ) {
final MutableJpaCompliance jpaCompliance = buildingContext.getBootstrapContext().getJpaCompliance();
return jpaCompliance.isJpaListComplianceEnabled()
? java.util.List.class
: java.util.Collection.class;
}
else if ( java.util.SortedSet.class.equals( clazz ) ) {
if ( property.isAnnotationPresent( CollectionId.class ) ) {
throw new AnnotationException( "SortedSet does not support @CollectionId: "
+ StringHelper.qualify( entityName, property.getName() ) );
}
return new SetBinder( true );
}
else if ( Map.class.equals( clazz ) ) {
if ( property.isAnnotationPresent( CollectionId.class ) ) {
throw new AnnotationException( "Map does not support @CollectionId: "
+ StringHelper.qualify( entityName, property.getName() ) );
}
return new MapBinder( false );
}
else if ( java.util.SortedMap.class.equals( clazz ) ) {
if ( property.isAnnotationPresent( CollectionId.class ) ) {
throw new AnnotationException( "SortedMap does not support @CollectionId: "
+ StringHelper.qualify( entityName, property.getName() ) );
}
return new MapBinder( true );
}
else if ( java.util.Collection.class.equals( clazz ) ) {
if ( property.isAnnotationPresent( CollectionId.class ) ) {
return new IdBagBinder();
}
else {
return new BagBinder();
}
}
else if ( List.class.equals( clazz ) ) {
if ( isIndexed ) {
if ( property.isAnnotationPresent( CollectionId.class ) ) {
throw new AnnotationException(
"List does not support @CollectionId and @OrderColumn (or @IndexColumn) at the same time: "
+ StringHelper.qualify( entityName, property.getName() ) );
}
return new ListBinder();
}
else if ( property.isAnnotationPresent( CollectionId.class ) ) {
return new IdBagBinder();
}
else {
return new BagBinder();
}
}
return null;
}
private static Class<?> inferCollectionClassFromSubclass(Class<?> clazz) {
for ( Class<?> priorityClass : INFERRED_CLASS_PRIORITY ) {
if ( priorityClass.isAssignableFrom( clazz ) ) {
return priorityClass;
}
}
return null;
}
@ -488,7 +529,8 @@ public abstract class CollectionBinder {
Persister persisterAnn = property.getAnnotation( Persister.class );
if ( persisterAnn != null ) {
collection.setCollectionPersisterClass( persisterAnn.impl() );
//noinspection rawtypes
collection.setCollectionPersisterClass( (Class) persisterAnn.impl() );
}
applySortingAndOrdering( collection );
@ -614,37 +656,32 @@ public abstract class CollectionBinder {
}
private void applySortingAndOrdering(Collection collection) {
boolean hadOrderBy = false;
boolean hadExplicitSort = false;
final boolean hadExplicitSort;
final Class<? extends Comparator<?>> comparatorClass;
Class<? extends Comparator<?>> comparatorClass = null;
if ( jpaOrderBy == null && sqlOrderBy == null ) {
if ( naturalSort != null ) {
if ( comparatorSort != null ) {
throw buildIllegalSortCombination();
}
hadExplicitSort = true;
}
else if ( comparatorSort != null ) {
hadExplicitSort = true;
comparatorClass = comparatorSort.value();
if ( naturalSort != null ) {
if ( comparatorSort != null ) {
throw buildIllegalSortCombination();
}
hadExplicitSort = true;
comparatorClass = null;
}
else if ( comparatorSort != null ) {
hadExplicitSort = true;
comparatorClass = comparatorSort.value();
}
else {
hadExplicitSort = false;
comparatorClass = null;
}
boolean hadOrderBy = false;
if ( jpaOrderBy != null || sqlOrderBy != null ) {
if ( jpaOrderBy != null && sqlOrderBy != null ) {
throw new AnnotationException(
String.format(
"Illegal combination of @%s and @%s on %s",
jakarta.persistence.OrderBy.class.getName(),
OrderBy.class.getName(),
safeCollectionRole()
)
);
throw buildIllegalOrderCombination();
}
hadOrderBy = true;
hadExplicitSort = false;
// we can only apply the sql-based order by up front. The jpa order by has to wait for second pass
if ( sqlOrderBy != null ) {
@ -652,7 +689,13 @@ public abstract class CollectionBinder {
}
}
collection.setSorted( isSortedCollection || hadExplicitSort );
final boolean isSorted = isSortedCollection || hadExplicitSort;
if ( isSorted && hadOrderBy ) {
throw buildIllegalOrderAndSortCombination();
}
collection.setSorted( isSorted );
if ( comparatorClass != null ) {
try {
@ -670,10 +713,36 @@ public abstract class CollectionBinder {
}
}
private AnnotationException buildIllegalOrderCombination() {
return new AnnotationException(
String.format(
Locale.ROOT,
"Illegal combination of ordering and sorting annotations (`%s`) - only one of `@%s` and `@%s` may be used",
jakarta.persistence.OrderBy.class.getName(),
OrderBy.class.getName(),
safeCollectionRole()
)
);
}
private AnnotationException buildIllegalOrderAndSortCombination() {
throw new AnnotationException(
String.format(
Locale.ROOT,
"Illegal combination of ordering and sorting annotations (`%s`) - only one of `@%s`, `@%s`, `@%s` and `@%s` can be used",
safeCollectionRole(),
jakarta.persistence.OrderBy.class.getName(),
OrderBy.class.getName(),
SortComparator.class.getName(),
SortNatural.class.getName()
)
);
}
private AnnotationException buildIllegalSortCombination() {
return new AnnotationException(
String.format(
"Illegal combination of annotations on %s. Only one of @%s and @%s can be used",
"Illegal combination of sorting annotations (`%s`) - only one of `@%s` and `@%s` can be used",
safeCollectionRole(),
SortNatural.class.getName(),
SortComparator.class.getName()

View File

@ -26,6 +26,7 @@ import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SemanticsResolver;
import org.hibernate.mapping.Table;
import jakarta.persistence.Column;
@ -34,11 +35,12 @@ import jakarta.persistence.Column;
* @author Emmanuel Bernard
*/
public class IdBagBinder extends BagBinder {
public IdBagBinder() {
public IdBagBinder(SemanticsResolver semanticsResolver, MetadataBuildingContext buildingContext) {
super( semanticsResolver, buildingContext );
}
protected Collection createCollection(PersistentClass persistentClass) {
return new org.hibernate.mapping.IdentifierBag( getBuildingContext(), persistentClass );
protected Collection createCollection(PersistentClass owner) {
return new org.hibernate.mapping.IdentifierBag( getSemanticsResolver(), owner, getBuildingContext() );
}
@Override

View File

@ -27,6 +27,7 @@ import org.hibernate.mapping.IndexBackref;
import org.hibernate.mapping.List;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SemanticsResolver;
import org.hibernate.mapping.SimpleValue;
import org.jboss.logging.Logger;
@ -41,13 +42,13 @@ import org.jboss.logging.Logger;
public class ListBinder extends CollectionBinder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, ListBinder.class.getName() );
public ListBinder() {
super( false );
public ListBinder(SemanticsResolver semanticsResolver, MetadataBuildingContext buildingContext) {
super( semanticsResolver, false, buildingContext );
}
@Override
protected Collection createCollection(PersistentClass persistentClass) {
return new List( getBuildingContext(), persistentClass );
protected Collection createCollection(PersistentClass owner) {
return new List( getSemanticsResolver(), owner, getBuildingContext() );
}
@Override

View File

@ -9,15 +9,6 @@ package org.hibernate.cfg.annotations;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.MapKeyClass;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.MapKeyJoinColumn;
import jakarta.persistence.MapKeyJoinColumns;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
@ -40,27 +31,32 @@ import org.hibernate.cfg.PropertyData;
import org.hibernate.cfg.PropertyHolderBuilder;
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.cfg.SecondPass;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantBasicValue;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SemanticsResolver;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.hibernate.sql.Template;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.MapKeyClass;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.MapKeyJoinColumn;
import jakarta.persistence.MapKeyJoinColumns;
/**
* Implementation to bind a Map
@ -68,16 +64,16 @@ import org.hibernate.sql.Template;
* @author Emmanuel Bernard
*/
public class MapBinder extends CollectionBinder {
public MapBinder(boolean sorted) {
super( sorted );
public MapBinder(SemanticsResolver semanticsResolver, boolean sorted, MetadataBuildingContext buildingContext) {
super( semanticsResolver, sorted, buildingContext );
}
public boolean isMap() {
return true;
}
protected Collection createCollection(PersistentClass persistentClass) {
return new org.hibernate.mapping.Map( getBuildingContext(), persistentClass );
protected Collection createCollection(PersistentClass owner) {
return new org.hibernate.mapping.Map( getSemanticsResolver(), owner, getBuildingContext() );
}
@Override

View File

@ -6,16 +6,22 @@
*/
package org.hibernate.cfg.annotations;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.PrimitiveArray;
import org.hibernate.mapping.SemanticsResolver;
/**
* @author Emmanuel Bernard
*/
public class PrimitiveArrayBinder extends ArrayBinder {
public PrimitiveArrayBinder(SemanticsResolver semanticsResolver, MetadataBuildingContext buildingContext) {
super( semanticsResolver, buildingContext );
}
@Override
protected Collection createCollection(PersistentClass persistentClass) {
return new PrimitiveArray( getBuildingContext(), persistentClass );
protected Collection createCollection(PersistentClass owner) {
return new PrimitiveArray( getSemanticsResolver(), owner, getBuildingContext() );
}
}

View File

@ -7,8 +7,10 @@
package org.hibernate.cfg.annotations;
import org.hibernate.annotations.OrderBy;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SemanticsResolver;
/**
* Bind a set.
@ -16,13 +18,13 @@ import org.hibernate.mapping.PersistentClass;
* @author Matthew Inger
*/
public class SetBinder extends CollectionBinder {
public SetBinder(boolean sorted) {
super( sorted );
public SetBinder(SemanticsResolver semanticsResolver, boolean sorted, MetadataBuildingContext buildingContext) {
super( semanticsResolver, sorted, buildingContext );
}
@Override
protected Collection createCollection(PersistentClass persistentClass) {
return new org.hibernate.mapping.Set( getBuildingContext(), persistentClass );
return new org.hibernate.mapping.Set( getSemanticsResolver(), persistentClass, getBuildingContext() );
}
@Override

View File

@ -13,14 +13,12 @@ import java.util.function.Consumer;
import org.hibernate.collection.spi.BagSemantics;
import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.engine.FetchTiming;
import org.hibernate.collection.spi.InitializerProducerBuilder;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.collection.internal.BagInitializerProducer;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
@ -60,35 +58,6 @@ public abstract class AbstractBagSemantics<E> implements BagSemantics<Collection
}
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
return new BagInitializerProducer(
attributeMapping,
attributeMapping.getIdentifierDescriptor() == null ? null : fetchParent.generateFetchableFetch(
attributeMapping.getIdentifierDescriptor(),
navigablePath.append( CollectionPart.Nature.ID.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
),
fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
)
);
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
@ -98,31 +67,14 @@ public abstract class AbstractBagSemantics<E> implements BagSemantics<Collection
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState){
if ( indexFetch == null ) {
indexFetch = attributeMapping.getIdentifierDescriptor() == null ? null : fetchParent.generateFetchableFetch(
attributeMapping.getIdentifierDescriptor(),
navigablePath.append( CollectionPart.Nature.ID.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new BagInitializerProducer(
DomainResultCreationState creationState) {
return InitializerProducerBuilder.createBagInitializerProducer(
navigablePath,
attributeMapping,
indexFetch,
elementFetch
fetchParent,
selected,
elementFetch,
creationState
);
}

View File

@ -13,13 +13,11 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.collection.spi.InitializerProducerBuilder;
import org.hibernate.collection.spi.MapSemantics;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.collection.internal.MapInitializerProducer;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
@ -72,35 +70,6 @@ public abstract class AbstractMapSemantics<MKV extends Map<K,V>, K, V> implement
}
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
return new MapInitializerProducer(
attributeMapping,
fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
),
fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
)
);
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
@ -110,31 +79,15 @@ public abstract class AbstractMapSemantics<MKV extends Map<K,V>, K, V> implement
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState){
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new MapInitializerProducer(
DomainResultCreationState creationState) {
return InitializerProducerBuilder.createMapInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
indexFetch,
elementFetch
elementFetch,
creationState
);
}

View File

@ -12,13 +12,11 @@ import java.util.function.Consumer;
import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.collection.spi.InitializerProducerBuilder;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.collection.internal.SetInitializerProducer;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
/**
@ -45,27 +43,6 @@ public abstract class AbstractSetSemantics<SE extends Set<E>,E> implements Colle
}
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
return new SetInitializerProducer(
attributeMapping,
fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
)
);
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
@ -75,17 +52,15 @@ public abstract class AbstractSetSemantics<SE extends Set<E>,E> implements Colle
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState){
if ( elementFetch == null ) {
return createInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
resultVariable,
creationState
);
}
return new SetInitializerProducer( attributeMapping, elementFetch );
DomainResultCreationState creationState) {
assert indexFetch == null;
return InitializerProducerBuilder.createSetInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
elementFetch,
creationState
);
}
}

View File

@ -11,20 +11,16 @@ import java.util.function.Consumer;
import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.collection.spi.InitializerProducerBuilder;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.collection.internal.BagInitializerProducer;
import org.hibernate.sql.results.graph.collection.internal.ListInitializerProducer;
import org.hibernate.sql.results.graph.collection.internal.MapInitializerProducer;
import org.hibernate.type.CollectionType;
/**
@ -33,16 +29,17 @@ import org.hibernate.type.CollectionType;
* @author Christian Beikov
*/
public class CustomCollectionTypeSemantics<CE, E> implements CollectionSemantics<CE, E> {
private final CollectionType collectionType;
private final CollectionClassification classification;
public CustomCollectionTypeSemantics(CollectionType collectionType) {
public CustomCollectionTypeSemantics(CollectionType collectionType, CollectionClassification classification) {
this.collectionType = collectionType;
this.classification = classification;
}
@Override
public CollectionClassification getCollectionClassification() {
return CollectionClassification.BAG;
return classification;
}
@Override
@ -52,11 +49,13 @@ public class CustomCollectionTypeSemantics<CE, E> implements CollectionSemantics
@Override
public CE instantiateRaw(int anticipatedSize, CollectionPersister collectionDescriptor) {
//noinspection unchecked
return (CE) collectionType.instantiate( anticipatedSize );
}
@Override
public Iterator<E> getElementIterator(CE rawCollection) {
//noinspection unchecked
return collectionType.getElementsIterator( rawCollection, null );
}
@ -65,47 +64,6 @@ public class CustomCollectionTypeSemantics<CE, E> implements CollectionSemantics
getElementIterator( rawCollection ).forEachRemaining( action );
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
final Fetch indexFetch;
if ( attributeMapping.getIndexDescriptor() == null ) {
indexFetch = null;
}
else {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
final Fetch elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
if ( indexFetch == null ) {
return new BagInitializerProducer( attributeMapping, null, elementFetch );
}
else if ( indexFetch.getResultJavaTypeDescriptor().getJavaTypeClass() == Integer.class ) {
return new ListInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
else {
return new MapInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
@ -116,35 +74,16 @@ public class CustomCollectionTypeSemantics<CE, E> implements CollectionSemantics
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( indexFetch == null ) {
return new BagInitializerProducer( attributeMapping, null, elementFetch );
}
else if ( indexFetch.getResultJavaTypeDescriptor().getJavaTypeClass() == Integer.class ) {
return new ListInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
else {
return new MapInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
return InitializerProducerBuilder.createCollectionTypeWrapperInitializerProducer(
navigablePath,
attributeMapping,
classification,
fetchParent,
selected,
indexFetch,
elementFetch,
creationState
);
}
@Override
@ -152,6 +91,7 @@ public class CustomCollectionTypeSemantics<CE, E> implements CollectionSemantics
Object key,
CollectionPersister collectionDescriptor,
SharedSessionContractImplementor session) {
//noinspection unchecked
return collectionType.instantiate( session, collectionDescriptor, key );
}
@ -160,6 +100,7 @@ public class CustomCollectionTypeSemantics<CE, E> implements CollectionSemantics
CE rawCollection,
CollectionPersister collectionDescriptor,
SharedSessionContractImplementor session) {
//noinspection unchecked
return collectionType.wrap( session, rawCollection );
}
}

View File

@ -12,16 +12,14 @@ import java.util.function.Consumer;
import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.collection.spi.InitializerProducerBuilder;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.collection.internal.ArrayInitializerProducer;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
@ -93,35 +91,6 @@ public class StandardArraySemantics<E> implements CollectionSemantics<E[], E> {
}
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
return new ArrayInitializerProducer(
attributeMapping,
fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
),
fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
)
);
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
@ -131,31 +100,15 @@ public class StandardArraySemantics<E> implements CollectionSemantics<E[], E> {
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState){
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new ArrayInitializerProducer(
DomainResultCreationState creationState) {
return InitializerProducerBuilder.createArrayInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
indexFetch,
elementFetch
elementFetch,
creationState
);
}

View File

@ -12,18 +12,16 @@ import java.util.function.Consumer;
import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.collection.spi.InitializerProducerBuilder;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.collection.internal.ListInitializerProducer;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
/**
@ -45,6 +43,7 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
return CollectionClassification.LIST;
}
@SuppressWarnings("rawtypes")
@Override
public Class<List> getCollectionJavaType() {
return List.class;
@ -67,72 +66,6 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
rawCollection.forEach( action );
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
return new ListInitializerProducer(
attributeMapping,
fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
),
fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
)
);
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new ListInitializerProducer(
attributeMapping,
indexFetch,
elementFetch
);
}
@Override
public PersistentCollection<E> instantiateWrapper(
Object key,
@ -148,4 +81,17 @@ public class StandardListSemantics<E> implements CollectionSemantics<List<E>, E>
SharedSessionContractImplementor session) {
return new PersistentList<>( session, rawCollection );
}
@Override
public CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
return InitializerProducerBuilder.createListInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState );
}
}

View File

@ -10,7 +10,6 @@ import java.util.Iterator;
import java.util.function.Consumer;
import org.hibernate.Incubating;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
@ -36,38 +35,69 @@ public interface CollectionSemantics<CE, E> {
*/
CollectionClassification getCollectionClassification();
/**
* The collection's Java type
*/
Class<?> getCollectionJavaType();
/**
* Create a raw (unwrapped) version of the collection
*/
CE instantiateRaw(
int anticipatedSize,
CollectionPersister collectionDescriptor);
/**
* Create a wrapper for the collection
*/
PersistentCollection<E> instantiateWrapper(
Object key,
CollectionPersister collectionDescriptor,
SharedSessionContractImplementor session);
/**
* Wrap a raw collection in wrapper
*/
PersistentCollection<E> wrap(
CE rawCollection,
CollectionPersister collectionDescriptor,
SharedSessionContractImplementor session);
/**
* Obtain an iterator over the collection elements
*/
Iterator<E> getElementIterator(CE rawCollection);
/**
* Visit the elements of the collection
*/
void visitElements(CE rawCollection, Consumer<? super E> action);
/**
* todo (6.0) : clean this contract up!
* Create a producer for {@link org.hibernate.sql.results.graph.collection.CollectionInitializer}
* instances for the given collection semantics
*
* @see InitializerProducerBuilder
*/
CollectionInitializerProducer createInitializerProducer(
default CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
String resultVariable,
DomainResultCreationState creationState);
DomainResultCreationState creationState) {
return createInitializerProducer(
navigablePath, attributeMapping, fetchParent, selected, resultVariable, null, null, creationState
);
}
CollectionInitializerProducer createInitializerProducer(
/**
* Create a producer for {@link org.hibernate.sql.results.graph.collection.CollectionInitializer}
* instances for the given collection semantics
*
* @see InitializerProducerBuilder
*/
default CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
@ -75,5 +105,16 @@ public interface CollectionSemantics<CE, E> {
String resultVariable,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState);
DomainResultCreationState creationState) {
return InitializerProducerBuilder.createInitializerProducer(
navigablePath,
attributeMapping,
getCollectionClassification(),
fetchParent,
selected,
indexFetch,
elementFetch,
creationState
);
}
}

View File

@ -0,0 +1,296 @@
/*
* 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.spi;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.collection.internal.ArrayInitializerProducer;
import org.hibernate.sql.results.graph.collection.internal.BagInitializerProducer;
import org.hibernate.sql.results.graph.collection.internal.ListInitializerProducer;
import org.hibernate.sql.results.graph.collection.internal.MapInitializerProducer;
import org.hibernate.sql.results.graph.collection.internal.SetInitializerProducer;
/**
* @author Steve Ebersole
*/
public class InitializerProducerBuilder {
public static CollectionInitializerProducer createInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
CollectionClassification classification,
FetchParent fetchParent,
boolean selected,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
switch ( classification ) {
case ARRAY: {
return createArrayInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState );
}
case BAG:
case IDBAG: {
return createBagInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, elementFetch, creationState );
}
case LIST: {
return createListInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState );
}
case MAP:
case ORDERED_MAP:
case SORTED_MAP: {
return createMapInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState );
}
case SET:
case ORDERED_SET:
case SORTED_SET: {
return createSetInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, elementFetch, creationState );
}
default: {
throw new IllegalArgumentException( "Unknown CollectionClassification : " + classification );
}
}
}
public static CollectionInitializerProducer createArrayInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new ArrayInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
public static CollectionInitializerProducer createBagInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
Fetch elementFetch,
DomainResultCreationState creationState) {
final Fetch idBagIdFetch;
if ( attributeMapping.getIdentifierDescriptor() != null ) {
idBagIdFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIdentifierDescriptor(),
navigablePath.append( CollectionPart.Nature.ID.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
else {
idBagIdFetch = null;
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new BagInitializerProducer( attributeMapping, idBagIdFetch, elementFetch );
}
public static CollectionInitializerProducer createListInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new ListInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
public static CollectionInitializerProducer createMapInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
assert attributeMapping.getIndexDescriptor() != null;
if ( indexFetch == null ) {
indexFetch = fetchParent.generateFetchableFetch(
attributeMapping.getIndexDescriptor(),
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new MapInitializerProducer( attributeMapping, indexFetch, elementFetch );
}
public static CollectionInitializerProducer createSetInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
FetchParent fetchParent,
boolean selected,
Fetch elementFetch,
DomainResultCreationState creationState) {
if ( elementFetch == null ) {
elementFetch = fetchParent.generateFetchableFetch(
attributeMapping.getElementDescriptor(),
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
FetchTiming.IMMEDIATE,
selected,
null,
creationState
);
}
return new SetInitializerProducer( attributeMapping, elementFetch );
}
public static CollectionInitializerProducer createCollectionTypeWrapperInitializerProducer(
NavigablePath navigablePath,
PluralAttributeMapping attributeMapping,
CollectionClassification classification,
FetchParent fetchParent,
boolean selected,
Fetch indexFetch,
Fetch elementFetch,
DomainResultCreationState creationState) {
switch ( classification ) {
case ARRAY: {
return createArrayInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
indexFetch,
elementFetch,
creationState
);
}
case BAG:
case IDBAG: {
assert indexFetch == null;
return createBagInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
elementFetch,
creationState
);
}
case LIST: {
return createListInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
indexFetch,
elementFetch,
creationState
);
}
case MAP:
case ORDERED_MAP:
case SORTED_MAP: {
return createMapInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
indexFetch,
elementFetch,
creationState
);
}
case SET:
case ORDERED_SET:
case SORTED_SET: {
return createSetInitializerProducer(
navigablePath,
attributeMapping,
fetchParent,
selected,
elementFetch,
creationState
);
}
default: {
throw new IllegalArgumentException( "Unknown CollectionClassification : " + classification );
}
}
}
private InitializerProducerBuilder() {
}
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.mapping;
import java.util.function.Function;
import org.hibernate.MappingException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
@ -29,7 +31,11 @@ public class Array extends List {
super( buildingContext, owner );
}
public Class getElementClass() throws MappingException {
public Array(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public Class<?> getElementClass() throws MappingException {
if ( elementClassName == null ) {
final org.hibernate.type.Type elementType = getElement().getType();
if ( isPrimitiveArray() ) {
@ -51,7 +57,7 @@ public class Array extends List {
}
@Override
public CollectionSemantics getDefaultCollectionSemantics() {
public CollectionSemantics<?,?> getDefaultCollectionSemantics() {
return StandardArraySemantics.INSTANCE;
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.mapping;
import java.util.function.Function;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.collection.internal.StandardBagSemantics;
import org.hibernate.collection.spi.CollectionSemantics;
@ -18,10 +20,20 @@ import org.hibernate.type.CollectionType;
* @author Gavin King
*/
public class Bag extends Collection {
/**
* hbm.xml binding
*/
public Bag(MetadataBuildingContext buildingContext, PersistentClass owner) {
super( buildingContext, owner );
}
/**
* Annotation binding
*/
public Bag(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public CollectionType getDefaultCollectionType() {
return new BagType( getMetadata().getTypeConfiguration(), getRole(), getReferencedPropertyName() );
}

View File

@ -14,6 +14,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.SortedMap;
import java.util.SortedSet;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
@ -27,6 +29,8 @@ import org.hibernate.engine.spi.Mapping;
import org.hibernate.internal.FilterConfiguration;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.CollectionType;
import org.hibernate.type.Type;
@ -63,18 +67,21 @@ public abstract class Collection implements Fetchable, Value, Filterable {
private String referencedPropertyName;
private String mappedByProperty;
private boolean sorted;
private Comparator comparator;
private Comparator<?> comparator;
private String comparatorClassName;
private boolean orphanDelete;
private int batchSize = -1;
private FetchMode fetchMode;
private boolean embedded = true;
private boolean optimisticLocked = true;
private Class collectionPersisterClass;
private String typeName;
private Properties typeParameters;
private final List filters = new ArrayList();
private final List manyToManyFilters = new ArrayList();
private SemanticsResolver customSemanticsResolver;
private Class<? extends CollectionPersister> collectionPersisterClass;
private final List<FilterConfiguration> filters = new ArrayList<>();
private final List<FilterConfiguration> manyToManyFilters = new ArrayList<>();
private final java.util.Set<String> synchronizedTables = new HashSet<>();
private String customSQLInsert;
@ -92,11 +99,23 @@ public abstract class Collection implements Fetchable, Value, Filterable {
private String loaderName;
/**
* hbm.xml binding
*/
protected Collection(MetadataBuildingContext buildingContext, PersistentClass owner) {
this.buildingContext = buildingContext;
this.owner = owner;
}
/**
* Annotation binding
*/
protected Collection(SemanticsResolver customSemanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
this.customSemanticsResolver = customSemanticsResolver;
this.owner = owner;
this.buildingContext = buildingContext;
}
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
@ -142,13 +161,13 @@ public abstract class Collection implements Fetchable, Value, Filterable {
return sorted;
}
public Comparator getComparator() {
public Comparator<?> getComparator() {
if ( comparator == null && comparatorClassName != null ) {
try {
final ClassLoaderService classLoaderService = getMetadata().getMetadataBuildingOptions()
.getServiceRegistry()
.getService( ClassLoaderService.class );
setComparator( (Comparator) classLoaderService.classForName( comparatorClassName ).newInstance() );
setComparator( (Comparator<?>) classLoaderService.classForName( comparatorClassName ).getConstructor().newInstance() );
}
catch (Exception e) {
throw new MappingException(
@ -202,7 +221,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
return orderBy;
}
public void setComparator(Comparator comparator) {
public void setComparator(@SuppressWarnings("rawtypes") Comparator comparator) {
this.comparator = comparator;
}
@ -296,11 +315,11 @@ public abstract class Collection implements Fetchable, Value, Filterable {
this.fetchMode = fetchMode;
}
public void setCollectionPersisterClass(Class persister) {
public void setCollectionPersisterClass(Class<? extends CollectionPersister> persister) {
this.collectionPersisterClass = persister;
}
public Class getCollectionPersisterClass() {
public Class<? extends CollectionPersister> getCollectionPersisterClass() {
return collectionPersisterClass;
}
@ -328,7 +347,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
checkColumnDuplication();
}
private void checkColumnDuplication(java.util.Set distinctColumns, Value value)
private void checkColumnDuplication(java.util.Set<String> distinctColumns, Value value)
throws MappingException {
final boolean[] insertability = value.getColumnInsertability();
final boolean[] updatability = value.getColumnUpdateability();
@ -354,7 +373,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
}
private void checkColumnDuplication() throws MappingException {
HashSet cols = new HashSet();
HashSet<String> cols = new HashSet<>();
checkColumnDuplication( cols, getKey() );
if ( isIndexed() ) {
checkColumnDuplication(
@ -374,7 +393,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
}
public Iterator<Selectable> getColumnIterator() {
return Collections.<Selectable>emptyList().iterator();
return Collections.emptyIterator();
}
@Override
@ -390,23 +409,78 @@ public abstract class Collection implements Fetchable, Value, Filterable {
return getCollectionType();
}
private CollectionSemantics<?,?> cachedCollectionSemantics;
@SuppressWarnings("rawtypes")
public CollectionSemantics getCollectionSemantics() {
if ( cachedCollectionSemantics == null ) {
cachedCollectionSemantics = resolveCollectionSemantics();
}
return cachedCollectionSemantics;
}
private CollectionSemantics<?, ?> resolveCollectionSemantics() {
final CollectionType collectionType;
if ( typeName == null ) {
return getDefaultCollectionSemantics();
collectionType = null;
}
else {
final CollectionType collectionType = MappingHelper.customCollection(
collectionType = MappingHelper.customCollection(
typeName,
typeParameters,
role,
referencedPropertyName,
getMetadata()
);
return new CustomCollectionTypeSemantics( collectionType );
}
if ( customSemanticsResolver != null ) {
return customSemanticsResolver.resolve( collectionType );
}
if ( collectionType == null ) {
return getDefaultCollectionSemantics();
}
final Class<?> semanticJavaType = collectionType.getReturnedClass();
final CollectionClassification classification;
if ( semanticJavaType.isArray() ) {
classification = CollectionClassification.ARRAY;
}
else if ( List.class.isAssignableFrom( semanticJavaType ) ) {
classification = CollectionClassification.LIST;
}
else if ( SortedSet.class.isAssignableFrom( semanticJavaType ) ) {
classification = CollectionClassification.SORTED_SET;
}
else if ( Set.class.isAssignableFrom( semanticJavaType ) ) {
classification = CollectionClassification.SET;
}
else if ( SortedMap.class.isAssignableFrom( semanticJavaType ) ) {
classification = CollectionClassification.SORTED_MAP;
}
else if ( Map.class.isAssignableFrom( semanticJavaType ) ) {
classification = CollectionClassification.MAP;
}
else if ( Collection.class.isAssignableFrom( semanticJavaType ) ) {
if ( isIdentified() ) {
classification = CollectionClassification.IDBAG;
}
else {
classification = CollectionClassification.BAG;
}
}
else {
throw new IllegalArgumentException( "Unexpected collection-semantics Java type : " + semanticJavaType );
}
return new CustomCollectionTypeSemantics<>( collectionType, classification );
}
public CollectionSemantics getDefaultCollectionSemantics() {
public CollectionSemantics<?,?> getDefaultCollectionSemantics() {
throw new MappingException(
"Unexpected org.hibernate.mapping.Collection impl ["
+ this + "]; unknown CollectionSemantics"
@ -598,7 +672,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
);
}
public List getFilters() {
public List<FilterConfiguration> getFilters() {
return filters;
}
@ -620,7 +694,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
);
}
public List getManyToManyFilters() {
public List<FilterConfiguration> getManyToManyFilters() {
return manyToManyFilters;
}
@ -677,6 +751,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
this.typeParameters = parameterMap;
}
@SuppressWarnings("rawtypes")
public void setTypeParameters(java.util.Map parameterMap) {
if ( parameterMap instanceof Properties ) {
this.typeParameters = (Properties) parameterMap;

View File

@ -15,7 +15,7 @@ import org.hibernate.internal.FilterConfiguration;
* @author Steve Ebersole
*/
public interface Filterable {
public void addFilter(String name, String condition, boolean autoAliasInjection, java.util.Map<String,String> aliasTableMap, java.util.Map<String,String> aliasEntityMap);
void addFilter(String name, String condition, boolean autoAliasInjection, java.util.Map<String,String> aliasTableMap, java.util.Map<String,String> aliasEntityMap);
public java.util.List<FilterConfiguration> getFilters();
java.util.List<FilterConfiguration> getFilters();
}

View File

@ -17,10 +17,21 @@ import org.hibernate.type.IdentifierBagType;
* just the identifier column
*/
public class IdentifierBag extends IdentifierCollection {
/**
* hbm.xml binding
*/
public IdentifierBag(MetadataBuildingContext buildingContext, PersistentClass owner) {
super( buildingContext, owner );
}
/**
* annotation binding
*/
public IdentifierBag(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public CollectionType getDefaultCollectionType() {
return new IdentifierBagType( getMetadata().getTypeConfiguration(), getRole(), getReferencedPropertyName() );
}

View File

@ -8,6 +8,7 @@ package org.hibernate.mapping;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.engine.spi.Mapping;
/**
@ -23,6 +24,10 @@ public abstract class IdentifierCollection extends Collection {
super( buildingContext, owner );
}
public IdentifierCollection(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public KeyValue getIdentifier() {
return identifier;
}

View File

@ -7,10 +7,13 @@
package org.hibernate.mapping;
import java.util.Iterator;
import java.util.function.Function;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.type.CollectionType;
/**
* Indexed collections include Lists, Maps, arrays and
@ -27,6 +30,10 @@ public abstract class IndexedCollection extends Collection {
super( buildingContext, owner );
}
public IndexedCollection(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public Value getIndex() {
return index;
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.mapping;
import java.util.function.Function;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.collection.internal.StandardListSemantics;
@ -22,14 +24,24 @@ public class List extends IndexedCollection {
private int baseIndex;
public boolean isList() {
return true;
}
/**
* hbm.xml binding
*/
public List(MetadataBuildingContext buildingContext, PersistentClass owner) {
super( buildingContext, owner );
}
/**
* annotation binding
*/
public List(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public boolean isList() {
return true;
}
@Override
public CollectionSemantics getDefaultCollectionSemantics() {
return StandardListSemantics.INSTANCE;

View File

@ -29,6 +29,10 @@ public class Map extends IndexedCollection {
super( buildingContext, owner );
}
public Map(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public boolean isMap() {
return true;
}

View File

@ -18,6 +18,10 @@ public class PrimitiveArray extends Array {
super( buildingContext, owner );
}
public PrimitiveArray(SemanticsResolver semanticsResolver, PersistentClass owner, MetadataBuildingContext buildingContext) {
super( semanticsResolver, owner, buildingContext );
}
public boolean isPrimitiveArray() {
return true;
}

View File

@ -0,0 +1,17 @@
/*
* 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.mapping;
import org.hibernate.collection.spi.CollectionSemantics;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface SemanticsResolver {
CollectionSemantics<?, ?> resolve(org.hibernate.type.CollectionType explicitType);
}

View File

@ -26,10 +26,20 @@ import org.hibernate.type.SortedSetType;
* @author Gavin King
*/
public class Set extends Collection {
/**
* Used by hbm.xml binding
*/
public Set(MetadataBuildingContext buildingContext, PersistentClass owner) {
super( buildingContext, owner );
}
/**
* Used by annotation binding
*/
public Set(SemanticsResolver semanticsResolver, PersistentClass persistentClass, MetadataBuildingContext buildingContext) {
super( semanticsResolver, persistentClass, buildingContext );
}
public void validate(Mapping mapping) throws MappingException {
super.validate( mapping );
//for backward compatibility, disable this:

View File

@ -22,6 +22,7 @@ import org.hibernate.cfg.Ejb3JoinColumn;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.collection.internal.StandardBagSemantics;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
@ -55,7 +56,7 @@ public class CollectionBinderTest extends BaseUnitTestCase {
String expectMessage = "Association [abc] for entity [CollectionBinderTest] references unmapped class [List]";
try {
new CollectionBinder( false) {
new CollectionBinder( (t) -> StandardBagSemantics.INSTANCE, false, buildingContext ) {
{
final PropertyHolder propertyHolder = Mockito.mock(PropertyHolder.class);

View File

@ -0,0 +1,88 @@
/*
* 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.mapping.collections;
import java.util.Set;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.SortNatural;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Basic;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author Steve Ebersole
*/
@JiraKey( "https://hibernate.atlassian.net/browse/HHH-9688" )
@ServiceRegistry
public class SortAndOrderTests {
@Test
void test(ServiceRegistryScope scope) {
final StandardServiceRegistry registry = scope.getRegistry();
final MetadataSources sources = new MetadataSources( registry ).addAnnotatedClass( AnEntity.class );
try {
sources.buildMetadata();
fail( "Expecting to fail" );
}
catch (AnnotationException expected) {
assertThat( expected ).hasMessageStartingWith( "Illegal combination of ordering and sorting annotations" );
}
}
@Entity( name = "AnEntity" )
@Table( name = "t_entity" )
public static class AnEntity {
@Id
private Integer id;
@Basic
private String name;
@ElementCollection
@SortNatural
@OrderBy
private Set<String> aliases;
private AnEntity() {
// for use by Hibernate
}
public AnEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import java.util.List;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import java.util.ArrayList;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import java.util.Iterator;
import java.util.Map;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import org.hibernate.collection.internal.PersistentList;
import org.hibernate.engine.spi.SharedSessionContractImplementor;

View File

@ -4,11 +4,10 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@ -17,9 +16,9 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import org.hibernate.annotations.CollectionType;
import org.hibernate.annotations.NaturalId;
/**
* @author Gavin King
@ -28,43 +27,41 @@ import org.hibernate.annotations.CollectionType;
@Entity
@Table(name = "UC_BSC_USER")
public class User {
@Id
private Integer id;
@NaturalId
private String userName;
private IMyList<Email> emailAddresses = new MyList<Email>();
private Map sessionData = new HashMap();
User() {
@OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true )
@CollectionType( type = "org.hibernate.orm.test.mapping.type.collection.custom.basic.MyListType" )
@JoinColumn( name = "userName" )
@OrderColumn( name = "displayOrder" )
private IMyList<Email> emailAddresses = new MyList<>();
private User() {
// for use by Hibernate
}
public User(String name) {
public User(Integer id, String name) {
this.id = id;
userName = name;
}
@Id
public Integer getId() {
return id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true )
@CollectionType( type = "org.hibernate.test.collection.custom.basic.MyListType" )
@JoinColumn( name = "userName" )
@OrderColumn( name = "displayOrder" )
public List<Email> getEmailAddresses() {
// does not work :(
// public IMyList<Email> getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(IMyList<Email> emailAddresses) {
this.emailAddresses = emailAddresses;
}
@Transient
public Map getSessionData() {
return sessionData;
}
public void setSessionData(Map sessionData) {
this.sessionData = sessionData;
}
}

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
/**
* @author Steve Ebersole

View File

@ -4,14 +4,19 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
/**
* @author Steve Ebersole
*/
public class UserCollectionTypeHbmVariantTest extends UserCollectionTypeTest {
@Override
protected String getBaseForMappings() {
return "";
}
@Override
public String[] getMappings() {
return new String[] { "collection/custom/basic/UserPermissions.hbm.xml" };
return new String[] { "/org/hibernate/orm/test/mapping/type/collection/custom/basic/UserPermissions.hbm.xml" };
}
}

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.basic;
package org.hibernate.orm.test.mapping.type.collection.custom.basic;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
@ -29,7 +29,7 @@ public abstract class UserCollectionTypeTest extends BaseNonConfigCoreFunctional
@Test
public void testBasicOperation() {
User u = new User("max");
User u = new User( 1, "max" );
inTransaction(
s -> {
u.getEmailAddresses().add( new Email("max@hibernate.org") );
@ -53,7 +53,7 @@ public abstract class UserCollectionTypeTest extends BaseNonConfigCoreFunctional
inTransaction(
s -> {
User u2 = s.get( User.class, u.getUserName() );
User u2 = s.get( User.class, u.getId() );
u2.getEmailAddresses().size();
assertEquals( 2, MyListType.lastInstantiationRequest );

View File

@ -13,13 +13,14 @@
This mapping is a basic example of how to write a UserCollectionType.
-->
<hibernate-mapping package="org.hibernate.test.collection.custom.basic">
<hibernate-mapping package="org.hibernate.orm.test.mapping.type.collection.custom.basic" default-access="field">
<import class="Permission"/>
<class name="User" table="UC_BSC_USER">
<id name="userName"/>
<list name="emailAddresses" fetch="join" cascade="all, delete-orphan" collection-type="org.hibernate.test.collection.custom.basic.MyListType">
<id name="id"/>
<property name="userName"/>
<list name="emailAddresses" fetch="join" cascade="all, delete-orphan" collection-type="org.hibernate.orm.test.mapping.type.collection.custom.basic.MyListType">
<key column="userName"/>
<list-index column="displayOrder" base="1"/>
<one-to-many class="Email"/>

View File

@ -1,4 +1,4 @@
package org.hibernate.test.collection.custom.declaredtype;
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;

View File

@ -0,0 +1,23 @@
/*
* 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.mapping.type.collection.custom.declaredtype;
import java.util.ArrayList;
/**
* A custom collection class that implements a simple method just for illustration.
* We extend a java.util.Collection class which is required for annotations-based entities, but not xml-based.
*
* @author David Weinberg
*/
public class HeadList<X> extends ArrayList<X> implements IHeadList<X> {
@Override
public X head() {
return isEmpty() ? null : get( 0 );
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.mapping.type.collection.custom.declaredtype;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;
/**
* @author Steve Ebersole
*/
public class HeadListType implements UserCollectionType {
@Override
public PersistentCollection instantiate(SharedSessionContractImplementor session, CollectionPersister persister) throws HibernateException {
return new PersistentHeadList( session );
}
@Override
public PersistentCollection wrap(SharedSessionContractImplementor session, Object collection) {
return new PersistentHeadList( session, (IHeadList) collection );
}
public Iterator getElementsIterator(Object collection) {
return ( (IHeadList) collection ).iterator();
}
public boolean contains(Object collection, Object entity) {
return ( (IHeadList) collection ).contains( entity );
}
public Object indexOf(Object collection, Object entity) {
int l = ( (IHeadList) collection ).indexOf( entity );
if ( l < 0 ) {
return null;
}
else {
return l;
}
}
@Override
public Object replaceElements(
Object original,
Object target,
CollectionPersister persister,
Object owner,
Map copyCache,
SharedSessionContractImplementor session) throws HibernateException {
IHeadList result = (IHeadList) target;
result.clear();
result.addAll( (HeadList) original );
return result;
}
public Object instantiate(int anticipatedSize) {
return new HeadList();
}
}

View File

@ -0,0 +1,16 @@
/*
* 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.mapping.type.collection.custom.declaredtype;
import java.util.List;
/**
* @author Steve Ebersole
*/
public interface IHeadList<X> extends List<X> {
X head();
}

View File

@ -0,0 +1,30 @@
/*
* 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.mapping.type.collection.custom.declaredtype;
import org.hibernate.collection.internal.PersistentList;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
public class PersistentHeadList extends PersistentList implements IHeadList {
public PersistentHeadList(SharedSessionContractImplementor session) {
super( session );
}
public PersistentHeadList(SharedSessionContractImplementor session, IHeadList list) {
super( session, list );
}
@Override
public Object head() {
return ( (IHeadList) list ).head();
}
}

View File

@ -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.orm.test.mapping.type.collection.custom.declaredtype;
import org.hibernate.annotations.CollectionType;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.Table;
/**
* @author Gavin King
* @author Steve Ebersole
*/
@Entity
@Table(name = "UC_BSC_USER")
public class User {
@Id
private Integer id;
private String userName;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@CollectionType(type = "org.hibernate.orm.test.mapping.type.collection.custom.declaredtype.HeadListType")
@JoinColumn(name = "userName")
@OrderColumn(name = "displayOrder")
private IHeadList<Email> emailAddresses = new HeadList<Email>();
private User() {
}
public User(Integer id, String name) {
this.id = id;
this.userName = name;
}
public Integer getId() {
return id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public IHeadList<Email> getEmailAddresses() { //can declare a custom interface type
return emailAddresses;
}
public void setEmailAddresses(IHeadList<Email> emailAddresses) {
this.emailAddresses = emailAddresses;
}
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.custom.declaredtype;
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype;
/**
* @author Steve Ebersole

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.custom.declaredtype;
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype;
/**
* @author Steve Ebersole
@ -29,6 +29,6 @@ package org.hibernate.test.collection.custom.declaredtype;
public class UserCollectionTypeHbmVariantTest extends UserCollectionTypeTest {
@Override
public String[] getMappings() {
return new String[] { "collection/custom/declaredtype/UserPermissions.hbm.xml" };
return new String[] { "mapping/type/collection/custom/declaredtype/UserPermissions.hbm.xml" };
}
}

View File

@ -0,0 +1,75 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import org.hibernate.Hibernate;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Max Rydahl Andersen
* @author David Weinberg
*/
public abstract class UserCollectionTypeTest extends BaseCoreFunctionalTestCase {
@Override
protected String getCacheConcurrencyStrategy() {
return "nonstrict-read-write";
}
@Test
public void testBasicOperation() {
inTransaction(
s -> {
User u = new User( 1, "max" );
u.getEmailAddresses().add( new Email("max@hibernate.org") );
u.getEmailAddresses().add( new Email("max.andersen@jboss.com") );
s.persist(u);
}
);
inTransaction(
s -> {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<User> criteria = criteriaBuilder.createQuery( User.class );
criteria.from( User.class );
User u2 = s.createQuery( criteria ).uniqueResult();
// User u2 = (User) s.createCriteria(User.class).uniqueResult();
assertTrue( Hibernate.isInitialized( u2.getEmailAddresses() ) );
assertEquals( u2.getEmailAddresses().size(), 2 );
assertNotNull( u2.getEmailAddresses().head());
}
);
}
}

View File

@ -7,13 +7,14 @@
This mapping is a basic example of how to write a UserCollectionType.
-->
<hibernate-mapping package="org.hibernate.test.collection.custom.declaredtype">
<hibernate-mapping package="org.hibernate.orm.test.mapping.type.collection.custom.declaredtype" default-access="field">
<import class="Permission"/>
<class name="org.hibernate.test.collection.custom.declaredtype.UserCollectionTypeTest$User" table="UC_BSC_USER">
<id name="userName"/>
<list name="emailAddresses" fetch="join" cascade="all, delete-orphan" collection-type="org.hibernate.test.collection.custom.declaredtype.UserCollectionTypeTest$HeadListType">
<class name="org.hibernate.orm.test.mapping.type.collection.custom.declaredtype.User" table="UC_BSC_USER">
<id name="id"/>
<property name="userName"/>
<list name="emailAddresses" fetch="join" cascade="all, delete-orphan" collection-type="org.hibernate.orm.test.mapping.type.collection.custom.declaredtype.HeadListType">
<key column="userName"/>
<list-index column="displayOrder" base="1"/>
<one-to-many class="Email"/>

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.custom.declaredtype;
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype;
import org.hibernate.annotations.CollectionType;

View File

@ -21,12 +21,13 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.custom.declaredtype;
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype;
import org.hibernate.AnnotationException;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@ -54,10 +55,9 @@ public class UserWithUnimplementedCollectionTest extends BaseCoreFunctionalTestC
fail( "Expected exception" );
}
catch (Exception e) {
assertTrue( e instanceof AnnotationException );
assertEquals(
"Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: org.hibernate.test.collection.custom.declaredtype.UserWithUnimplementedCollection.emailAddresses",
e.getMessage() );
assertThat( e ).isInstanceOf( AnnotationException.class );
assertThat( e ).hasMessageStartingWith( "Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements:" );
assertThat( e ).hasMessageEndingWith( ".emailAddresses" );
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.mapping.type.collection.custom.declaredtype.explicitsemantics;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
/**
* @author Gavin King
* @author Steve Ebersole
*/
@Entity(name = "Email")
public class Email {
private Long id;
private String address;
Email() {
}
public Email(String address) {
this.address = address;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String type) {
this.address = type;
}
@Override
public boolean equals(Object that) {
if ( !( that instanceof Email ) ) {
return false;
}
Email p = (Email) that;
return this.address.equals( p.address );
}
@Override
public int hashCode() {
return address.hashCode();
}
}

View File

@ -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.orm.test.mapping.type.collection.custom.declaredtype.explicitsemantics;
import java.util.ArrayList;
/**
* A custom collection class that has both List and Set interfaces, but only really implements set for persistence
* (e.g. extends PersistentSet). Without setting the semantics on the CollectionType annotation, List semantics
* would be inferred, and that would not match the implemented methods in PersistentSet and would fail. HeadSetList
* is very much a toy collection type.
*
* @author David Weinberg
*/
public class HeadSetList<X> extends ArrayList<X> implements IHeadSetList<X> {
@Override
public X head() {
return isEmpty() ? null : get( 0 );
}
}

View File

@ -0,0 +1,73 @@
/*
* 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.mapping.type.collection.custom.declaredtype.explicitsemantics;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;
/**
* @author Steve Ebersole
*/
public class HeadSetListType implements UserCollectionType {
@Override
public PersistentCollection instantiate(SharedSessionContractImplementor session, CollectionPersister persister)
throws HibernateException {
return new PersistentHeadList( session );
}
@Override
public PersistentCollection wrap(SharedSessionContractImplementor session, Object collection) {
return new PersistentHeadList( session, (IHeadSetList) collection );
}
@Override
public Iterator getElementsIterator(Object collection) {
return ( (IHeadSetList) collection ).iterator();
}
@Override
public boolean contains(Object collection, Object entity) {
return ( (IHeadSetList) collection ).contains( entity );
}
@Override
public Object indexOf(Object collection, Object entity) {
int l = ( (IHeadSetList) collection ).indexOf( entity );
if ( l < 0 ) {
return null;
}
else {
return l;
}
}
@Override
public Object replaceElements(
Object original,
Object target,
CollectionPersister persister,
Object owner,
Map copyCache,
SharedSessionContractImplementor session) throws HibernateException {
IHeadSetList result = (IHeadSetList) target;
result.clear();
result.addAll( (HeadSetList) original );
return result;
}
@Override
public Object instantiate(int anticipatedSize) {
return new HeadSetList();
}
}

View File

@ -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.orm.test.mapping.type.collection.custom.declaredtype.explicitsemantics;
import java.util.List;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
/**
* @author Steve Ebersole
*/
public interface IHeadSetList<X> extends Set<X>, List<X> {
X head();
@Override
default Spliterator<X> spliterator() {
return Spliterators.spliterator( this, Spliterator.DISTINCT );
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.mapping.type.collection.custom.declaredtype.explicitsemantics;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.hibernate.collection.internal.PersistentSet;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
public class PersistentHeadList extends PersistentSet implements IHeadSetList {
public PersistentHeadList(SharedSessionContractImplementor session) {
super( session );
}
public PersistentHeadList(SharedSessionContractImplementor session, IHeadSetList list) {
super( session, list );
}
@Override
public Object head() {
return ( (IHeadSetList) set ).head();
}
@Override
public boolean addAll(int index, Collection c) {
return set.addAll( c );
}
@Override
public Object get(int index) {
Iterator iterator = iterator();
Object next = null;
for ( int i = 0; i <= index; i++ ) {
next = iterator.next();
}
return next;
}
@Override
public Object set(int index, Object element) {
remove( index );
return add( element );
}
@Override
public void add(int index, Object element) {
add( element );
}
@Override
public Object remove(int index) {
return remove( get( index ) );
}
@Override
public int indexOf(Object o) {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public int lastIndexOf(Object o) {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public ListIterator listIterator() {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public ListIterator listIterator(int index) {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public List subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException( "Toy class" );
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.mapping.type.collection.custom.declaredtype.explicitsemantics;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.hibernate.annotations.CollectionType;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
/**
* @author Steve Ebersole
*/
@Entity(name = "User")
@Table(name = "UC_BSC_USER")
public class User {
private String userName;
private IHeadSetList<Email> emailAddresses = new HeadSetList<Email>();
private Map sessionData = new HashMap();
User() {
}
public User(String name) {
userName = name;
}
@Id
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@CollectionType(type = "org.hibernate.orm.test.mapping.type.collection.custom.declaredtype.explicitsemantics.HeadSetListType", semantics = Set.class)
@JoinColumn(name = "userName")
@OrderColumn(name = "displayOrder")
public IHeadSetList<Email> getEmailAddresses() { // can declare a custom interface type
return emailAddresses;
}
public void setEmailAddresses(IHeadSetList<Email> emailAddresses) {
this.emailAddresses = emailAddresses;
}
@Transient
public Map getSessionData() {
return sessionData;
}
public void setSessionData(Map sessionData) {
this.sessionData = sessionData;
}
}

View File

@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.orm.test.mapping.type.collection.custom.declaredtype.explicitsemantics;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import org.hibernate.Hibernate;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Max Rydahl Andersen
* @author David Weinberg
*/
public class UserCollectionTypeTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[]{ User.class, Email.class };
}
@Override
protected String getCacheConcurrencyStrategy() {
return "nonstrict-read-write";
}
@Test
public void testBasicOperation() {
doInHibernate( this::sessionFactory, session -> {
User u = new User( "max" );
u.getEmailAddresses().add( new Email( "max@hibernate.org" ) );
u.getEmailAddresses().add( new Email( "max.andersen@jboss.com" ) );
session.persist( u );
} );
doInHibernate( this::sessionFactory, session -> {
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<User> criteria = criteriaBuilder.createQuery( User.class );
criteria.from( User.class );
User u2 = session.createQuery( criteria ).getSingleResult();
// User u2 = (User) session.createCriteria( User.class ).uniqueResult();
assertTrue( Hibernate.isInitialized( u2.getEmailAddresses() ) );
assertEquals( u2.getEmailAddresses().size(), 2 );
assertNotNull( u2.getEmailAddresses().head() );
} );
}
}

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
import java.util.List;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
import java.util.ArrayList;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
import java.util.Iterator;
import java.util.List;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
import java.util.ArrayList;
import java.util.List;
@ -16,6 +16,7 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.OrderColumn;
import org.hibernate.annotations.CollectionType;
import org.hibernate.annotations.Parameter;
/**
@ -23,11 +24,6 @@ import org.hibernate.annotations.CollectionType;
*
* @author Steve Ebersole
*/
//@TypeDef(
// name = "DefaultableList",
// typeClass = DefaultableListType.class,
// parameters = @Parameter(name = "default", value = "Hello" )
//)
@jakarta.persistence.Entity
public class Entity {
private String name;
@ -50,7 +46,10 @@ public class Entity {
}
@ElementCollection( targetClass = String.class, fetch = FetchType.EAGER )
@CollectionType( type = "DefaultableList" )
@CollectionType(
type = "org.hibernate.orm.test.mapping.type.collection.custom.parameterized.DefaultableListType",
parameters = @Parameter(name = "default", value = "Hello" )
)
@JoinColumn( name = "ENT_ID" )
@OrderColumn( name = "POS" )
@Column(name = "VAL")

View File

@ -9,9 +9,9 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.collection.custom.parameterized" default-access="field">
<hibernate-mapping package="org.hibernate.orm.test.mapping.type.collection.custom.parameterized" default-access="field">
<typedef name="DefaultableList" class="org.hibernate.test.collection.custom.parameterized.DefaultableListType">
<typedef name="DefaultableList" class="org.hibernate.orm.test.mapping.type.collection.custom.parameterized.DefaultableListType">
<param name="default">Hello</param>
</typedef>

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
/**
* @author Steve Ebersole

View File

@ -4,13 +4,13 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
/**
* @author Steve Ebersole
*/
public class ParameterizedUserCollectionTypeHbmVariantTest extends ParameterizedUserCollectionTypeTest {
public String[] getMappings() {
return new String[] { "collection/custom/parameterized/Mapping.hbm.xml" };
return new String[] { "mapping/type/collection/custom/parameterized/Mapping.hbm.xml" };
}
}

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
import org.hibernate.Hibernate;
import org.hibernate.Session;

View File

@ -4,7 +4,7 @@
* 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.test.collection.custom.parameterized;
package org.hibernate.orm.test.mapping.type.collection.custom.parameterized;
import java.util.List;

View File

@ -1,229 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.custom.declaredtype;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.annotations.CollectionType;
import org.hibernate.collection.internal.PersistentList;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Max Rydahl Andersen
* @author David Weinberg
*/
public abstract class UserCollectionTypeTest extends BaseCoreFunctionalTestCase {
@Override
protected String getCacheConcurrencyStrategy() {
return "nonstrict-read-write";
}
@Test
public void testBasicOperation() {
inTransaction(
s -> {
User u = new User("max");
u.getEmailAddresses().add( new Email("max@hibernate.org") );
u.getEmailAddresses().add( new Email("max.andersen@jboss.com") );
s.persist(u);
}
);
inTransaction(
s -> {
CriteriaBuilder criteriaBuilder = s.getCriteriaBuilder();
CriteriaQuery<User> criteria = criteriaBuilder.createQuery( User.class );
criteria.from( User.class );
User u2 = s.createQuery( criteria ).uniqueResult();
// User u2 = (User) s.createCriteria(User.class).uniqueResult();
assertTrue( Hibernate.isInitialized( u2.getEmailAddresses() ) );
assertEquals( u2.getEmailAddresses().size(), 2 );
assertNotNull( u2.getEmailAddresses().head());
}
);
}
/**
* A custom collection class that implements a simple method just for illustration.
* We extend a java.util.Collection class which is required for annotations-based entities, but not xml-based.
*
* @author David Weinberg
*/
public static class HeadList<X> extends ArrayList<X> implements IHeadList<X> {
@Override
public X head() {
return isEmpty() ? null : get( 0 );
}
}
public static class HeadListType implements UserCollectionType {
@Override
public PersistentCollection instantiate(SharedSessionContractImplementor session, CollectionPersister persister) throws HibernateException {
return new PersistentHeadList(session);
}
@Override
public PersistentCollection wrap(SharedSessionContractImplementor session, Object collection) {
return new PersistentHeadList( session, (IHeadList) collection );
}
public Iterator getElementsIterator(Object collection) {
return ( (IHeadList) collection ).iterator();
}
public boolean contains(Object collection, Object entity) {
return ( (IHeadList) collection ).contains(entity);
}
public Object indexOf(Object collection, Object entity) {
int l = ( (IHeadList) collection ).indexOf(entity);
if(l<0) {
return null;
} else {
return l;
}
}
@Override
public Object replaceElements(
Object original,
Object target,
CollectionPersister persister,
Object owner,
Map copyCache,
SharedSessionContractImplementor session) throws HibernateException {
IHeadList result = (IHeadList) target;
result.clear();
result.addAll( (HeadList) original );
return result;
}
public Object instantiate(int anticipatedSize) {
return new HeadList();
}
}
public interface IHeadList<X> extends List<X> {
X head();
}
public static class PersistentHeadList extends PersistentList implements IHeadList {
public PersistentHeadList(SharedSessionContractImplementor session) {
super(session);
}
public PersistentHeadList(SharedSessionContractImplementor session, IHeadList list) {
super(session, list);
}
@Override
public Object head() {
return ((IHeadList) list).head();
}
}
/**
* @author Gavin King
* @author Steve Ebersole
*/
@Entity
@Table(name = "UC_BSC_USER")
//@TypeDef( name = "HeadListType", typeClass = HeadListType.class )
public static class User {
private String userName;
private IHeadList<Email> emailAddresses = new HeadList<Email>();
private Map sessionData = new HashMap();
User() {
}
public User(String name) {
userName = name;
}
@Id
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true )
@CollectionType( type = "HeadListType" )
@JoinColumn( name = "userName" )
@OrderColumn( name = "displayOrder" )
public IHeadList<Email> getEmailAddresses() { //can declare a custom interface type
return emailAddresses;
}
public void setEmailAddresses(IHeadList<Email> emailAddresses) {
this.emailAddresses = emailAddresses;
}
@Transient
public Map getSessionData() {
return sessionData;
}
public void setSessionData(Map sessionData) {
this.sessionData = sessionData;
}
}
}

View File

@ -1,355 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.collection.custom.declaredtype.explicitsemantics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.annotations.CollectionType;
import org.hibernate.collection.internal.PersistentSet;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.usertype.UserCollectionType;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Max Rydahl Andersen
* @author David Weinberg
*/
public class UserCollectionTypeTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[]{ User.class, Email.class };
}
@Override
protected String getCacheConcurrencyStrategy() {
return "nonstrict-read-write";
}
@Test
public void testBasicOperation() {
doInHibernate( this::sessionFactory, session -> {
User u = new User( "max" );
u.getEmailAddresses().add( new Email( "max@hibernate.org" ) );
u.getEmailAddresses().add( new Email( "max.andersen@jboss.com" ) );
session.persist( u );
} );
doInHibernate( this::sessionFactory, session -> {
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<User> criteria = criteriaBuilder.createQuery( User.class );
criteria.from( User.class );
User u2 = session.createQuery( criteria ).uniqueResult();
// User u2 = (User) session.createCriteria( User.class ).uniqueResult();
assertTrue( Hibernate.isInitialized( u2.getEmailAddresses() ) );
assertEquals( u2.getEmailAddresses().size(), 2 );
assertNotNull( u2.getEmailAddresses().head() );
} );
}
/**
* @author Gavin King
* @author Steve Ebersole
*/
@Entity(name = "Email")
public static class Email {
private Long id;
private String address;
Email() {
}
public Email(String address) {
this.address = address;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String type) {
this.address = type;
}
@Override
public boolean equals(Object that) {
if ( !( that instanceof Email ) )
return false;
Email p = (Email) that;
return this.address.equals( p.address );
}
@Override
public int hashCode() {
return address.hashCode();
}
}
@Entity(name = "User")
@Table(name = "UC_BSC_USER")
// @TypeDef(name = "HeadSetListType", typeClass = HeadSetListType.class)
public static class User {
private String userName;
private IHeadSetList<Email> emailAddresses = new HeadSetList<Email>();
private Map sessionData = new HashMap();
User() {
}
public User(String name) {
userName = name;
}
@Id
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@CollectionType(type = "HeadSetListType", semantics = Set.class)
@JoinColumn(name = "userName")
@OrderColumn(name = "displayOrder")
public IHeadSetList<Email> getEmailAddresses() { // can declare a custom interface type
return emailAddresses;
}
public void setEmailAddresses(IHeadSetList<Email> emailAddresses) {
this.emailAddresses = emailAddresses;
}
@Transient
public Map getSessionData() {
return sessionData;
}
public void setSessionData(Map sessionData) {
this.sessionData = sessionData;
}
}
public static class HeadSetListType implements UserCollectionType {
@Override
public PersistentCollection instantiate(SharedSessionContractImplementor session, CollectionPersister persister)
throws HibernateException {
return new PersistentHeadList( session );
}
@Override
public PersistentCollection wrap(SharedSessionContractImplementor session, Object collection) {
return new PersistentHeadList( session, (IHeadSetList) collection );
}
@Override
public Iterator getElementsIterator(Object collection) {
return ( (IHeadSetList) collection ).iterator();
}
@Override
public boolean contains(Object collection, Object entity) {
return ( (IHeadSetList) collection ).contains( entity );
}
@Override
public Object indexOf(Object collection, Object entity) {
int l = ( (IHeadSetList) collection ).indexOf( entity );
if ( l < 0 ) {
return null;
}
else {
return l;
}
}
@Override
public Object replaceElements(
Object original,
Object target,
CollectionPersister persister,
Object owner,
Map copyCache,
SharedSessionContractImplementor session) throws HibernateException {
IHeadSetList result = (IHeadSetList) target;
result.clear();
result.addAll( (HeadSetList) original );
return result;
}
@Override
public Object instantiate(int anticipatedSize) {
return new HeadSetList();
}
}
public interface IHeadSetList<X> extends Set<X>, List<X> {
X head();
@Override
default Spliterator<X> spliterator() {
return Spliterators.spliterator( this, Spliterator.DISTINCT );
}
}
/**
* A custom collection class that has both List and Set interfaces, but only really implements set for persistence
* (e.g. extends PersistentSet). Without setting the semantics on the CollectionType annotation, List semantics
* would be inferred, and that would not match the implemented methods in PersistentSet and would fail. HeadSetList
* is very much a toy collection type.
*
* @author David Weinberg
*/
public static class HeadSetList<X> extends ArrayList<X> implements IHeadSetList<X> {
@Override
public X head() {
return isEmpty() ? null : get( 0 );
}
}
public static class PersistentHeadList extends PersistentSet implements IHeadSetList {
public PersistentHeadList(SharedSessionContractImplementor session) {
super( session );
}
public PersistentHeadList(SharedSessionContractImplementor session, IHeadSetList list) {
super( session, list );
}
@Override
public Object head() {
return ( (IHeadSetList) set ).head();
}
@Override
public boolean addAll(int index, Collection c) {
return set.addAll( c );
}
@Override
public Object get(int index) {
Iterator iterator = iterator();
Object next = null;
for ( int i = 0; i <= index; i++ ) {
next = iterator.next();
}
return next;
}
@Override
public Object set(int index, Object element) {
remove( index );
return add( element );
}
@Override
public void add(int index, Object element) {
add( element );
}
@Override
public Object remove(int index) {
return remove( get( index ) );
}
@Override
public int indexOf(Object o) {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public int lastIndexOf(Object o) {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public ListIterator listIterator() {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public ListIterator listIterator(int index) {
throw new UnsupportedOperationException( "Toy class" );
}
@Override
public List subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException( "Toy class" );
}
}
}