HHH-14191 - ANY mapping support

- `@ManyToAny`
- embeddable sub-attribute
This commit is contained in:
Steve Ebersole 2020-08-27 17:37:28 -05:00
parent 97a88ebbcf
commit 3d46fabeb5
39 changed files with 1024 additions and 545 deletions

View File

@ -6,12 +6,14 @@
*/ */
package org.hibernate; package org.hibernate;
import org.hibernate.metamodel.mapping.NonTransientException;
/** /**
* Thrown from methods added for 6.0 that are not yet implemented. * Thrown from methods added for 6.0 that are not yet implemented.
* *
* todo (6.0) : prior going final, we need to find all usages of this and implement all methods (or throw a different exception) * todo (6.0) : prior going final, we need to find all usages of this and implement all methods (or throw a different exception)
*/ */
public class NotYetImplementedFor6Exception extends RuntimeException { public class NotYetImplementedFor6Exception extends RuntimeException implements NonTransientException {
public NotYetImplementedFor6Exception(String message) { public NotYetImplementedFor6Exception(String message) {
super( message ); super( message );
} }

View File

@ -61,6 +61,7 @@ import org.hibernate.mapping.SyntheticProperty;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne; import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.type.DiscriminatorType;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -953,46 +954,53 @@ public class BinderHelper {
EntityBinder entityBinder, EntityBinder entityBinder,
boolean optional, boolean optional,
MetadataBuildingContext context) { MetadataBuildingContext context) {
final XProperty xProperty = inferredData.getProperty();
//All FK columns should be in the same table //All FK columns should be in the same table
Any value = new Any( context, columns[0].getTable() ); final Any value = new Any( context, columns[0].getTable() );
AnyMetaDef metaAnnDef = inferredData.getProperty().getAnnotation( AnyMetaDef.class );
value.setLazy( lazy ); value.setLazy( lazy );
if ( metaAnnDef != null ) {
//local has precedence over general and can be mapped for future reference if named
bindAnyMetaDefs( inferredData.getProperty(), context );
}
else {
metaAnnDef = context.getMetadataCollector().getAnyMetaDef( anyMetaDefName );
}
if ( metaAnnDef != null ) {
value.setIdentifierType( metaAnnDef.idType() );
value.setMetaType( metaAnnDef.metaType() );
HashMap values = new HashMap(); final AnyMetaDef metaDefToUse;
org.hibernate.type.Type metaType = context.getMetadataCollector().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( value.getMetaType() ); final AnyMetaDef localMetaDefAnn = xProperty.getAnnotation( AnyMetaDef.class );
for (MetaValue metaValue : metaAnnDef.metaValues()) { if ( localMetaDefAnn != null ) {
try { //local has precedence over general and can be mapped for future reference if named
Object discrim = ( (org.hibernate.type.DiscriminatorType) metaType ).stringToObject( metaValue bindAnyMetaDefs(xProperty, context );
.value() ); metaDefToUse = localMetaDefAnn;
String entityName = metaValue.targetEntity().getName();
values.put( discrim, entityName );
}
catch (ClassCastException cce) {
throw new MappingException( "metaType was not a DiscriminatorType: "
+ metaType.getName() );
}
catch (Exception e) {
throw new MappingException( "could not interpret metaValue", e );
}
}
if ( !values.isEmpty() ) {
value.setMetaValues( values );
}
} }
else { else {
throw new AnnotationException( "Unable to find @AnyMetaDef for an @(ManyTo)Any mapping: " metaDefToUse = context.getMetadataCollector().getAnyMetaDef( anyMetaDefName );
+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() ) ); if ( metaDefToUse == null ) {
throw new AnnotationException(
"Unable to find @AnyMetaDef for an @(ManyTo)Any mapping: "
+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() )
);
}
}
value.setIdentifierType( metaDefToUse.idType() );
value.setMetaType( metaDefToUse.metaType() );
final HashMap<Object,String> values = new HashMap<>();
final DiscriminatorType<?> metaType = (DiscriminatorType<?>) context.getMetadataCollector()
.getTypeConfiguration()
.getBasicTypeRegistry()
.getRegisteredType( value.getMetaType() );
for (int i = 0; i < metaDefToUse.metaValues().length; i++) {
final MetaValue metaValue = metaDefToUse.metaValues()[ i ];
try {
final Object discriminator = metaType.stringToObject( metaValue.value() );
final String entityName = metaValue.targetEntity().getName();
values.put( discriminator, entityName );
}
catch (Exception e) {
throw new MappingException( "Could not interpret @MetaValue", e );
}
}
if ( !values.isEmpty() ) {
value.setMetaValues( values );
} }
value.setCascadeDeleteEnabled( cascadeOnDelete ); value.setCascadeDeleteEnabled( cascadeOnDelete );

View File

@ -0,0 +1,27 @@
/*
* 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.internal.util;
/**
* @author Steve Ebersole
*/
public class JavaHelper {
public static Package getPackageFor(String name) {
return getPackageFor( name, JavaHelper.class.getClassLoader() );
}
public static Package getPackageFor(String name, ClassLoader classLoader) {
// after Java 9 we can do -
//return classLoader.getDefinedPackage( name );
return Package.getPackage( name );
}
private JavaHelper() {
// disallow direct instantiation
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.metamodel.mapping; package org.hibernate.metamodel.mapping;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer;
/** /**
* A discriminated association. This is similar to an association to * A discriminated association. This is similar to an association to
@ -20,9 +21,9 @@ import org.hibernate.sql.results.graph.Fetchable;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface DiscriminatedAssociationModelPart extends Fetchable { public interface DiscriminatedAssociationModelPart extends Fetchable, FetchableContainer {
BasicValuedModelPart getDiscriminatorPart(); BasicValuedModelPart getDiscriminatorPart();
ModelPart getKeyPart(); BasicValuedModelPart getKeyPart();
EntityMappingType resolveDiscriminatorValue(Object discriminatorValue); EntityMappingType resolveDiscriminatorValue(Object discriminatorValue);
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.metamodel.mapping; package org.hibernate.metamodel.mapping;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -18,15 +19,19 @@ import java.util.function.Function;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
@ -34,6 +39,7 @@ import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
@ -41,12 +47,15 @@ import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
/** /**
@ -154,8 +163,10 @@ public class EmbeddableMappingType implements ManagedMappingType {
Component bootDescriptor, Component bootDescriptor,
CompositeType compositeType, CompositeType compositeType,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
final String containingTableExpression = valueMapping.getContainingTableExpression(); final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final String containingTableExpression = valueMapping.getContainingTableExpression();
final List<String> mappedColumnExpressions = valueMapping.getMappedColumnExpressions(); final List<String> mappedColumnExpressions = valueMapping.getMappedColumnExpressions();
final Type[] subtypes = compositeType.getSubtypes(); final Type[] subtypes = compositeType.getSubtypes();
@ -174,7 +185,7 @@ public class EmbeddableMappingType implements ManagedMappingType {
final Selectable selectable = basicValue.getColumn(); final Selectable selectable = basicValue.getColumn();
final String mappedColumnExpression = mappedColumnExpressions.get( columnPosition++ ); final String mappedColumnExpression = mappedColumnExpressions.get( columnPosition++ );
assert mappedColumnExpression.equals( selectable.getText( creationProcess.getCreationContext().getSessionFactory().getDialect() ) ); assert mappedColumnExpression.equals( selectable.getText( sessionFactory.getDialect() ) );
attributeMappings.put( attributeMappings.put(
bootPropertyDescriptor.getName(), bootPropertyDescriptor.getName(),
@ -196,9 +207,112 @@ public class EmbeddableMappingType implements ManagedMappingType {
) )
); );
} }
else if ( subtype instanceof AnyType ) {
final Any bootValueMapping = (Any) bootPropertyDescriptor.getValue();
final AnyType anyType = (AnyType) subtype;
final PropertyAccess propertyAccess = representationStrategy.resolvePropertyAccess( bootPropertyDescriptor );
final boolean nullable = bootValueMapping.isNullable();
final boolean insertable = bootPropertyDescriptor.isInsertable();
final boolean updateable = bootPropertyDescriptor.isUpdateable();
final boolean includeInOptimisticLocking = bootPropertyDescriptor.isOptimisticLocked();
final CascadeStyle cascadeStyle = compositeType.getCascadeStyle( attributeIndex );
final MutabilityPlan mutabilityPlan;
if ( updateable ) {
mutabilityPlan = new MutabilityPlan() {
@Override
public boolean isMutable() {
return true;
}
@Override
public Object deepCopy(Object value) {
if ( value == null ) {
return null;
}
return anyType.deepCopy( value, creationProcess.getCreationContext().getSessionFactory() );
}
@Override
public Serializable disassemble(Object value) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public Object assemble(Serializable cached) {
throw new NotYetImplementedFor6Exception( getClass() );
}
};
}
else {
mutabilityPlan = ImmutableMutabilityPlan.INSTANCE;
}
final StateArrayContributorMetadataAccess attributeMetadataAccess = entityMappingType -> new StateArrayContributorMetadata() {
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public MutabilityPlan getMutabilityPlan() {
return mutabilityPlan;
}
@Override
public boolean isNullable() {
return nullable;
}
@Override
public boolean isInsertable() {
return insertable;
}
@Override
public boolean isUpdatable() {
return updateable;
}
@Override
public boolean isIncludedInDirtyChecking() {
// todo (6.0) : do not believe this is correct
return updateable;
}
@Override
public boolean isIncludedInOptimisticLocking() {
return includeInOptimisticLocking;
}
@Override
public CascadeStyle getCascadeStyle() {
return cascadeStyle;
}
};
attributeMappings.put(
bootPropertyDescriptor.getName(),
new DiscriminatedAssociationAttributeMapping(
valueMapping.getNavigableRole().append( bootPropertyDescriptor.getName() ),
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Object.class ),
this,
attributeIndex,
attributeMetadataAccess,
bootPropertyDescriptor.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE,
propertyAccess,
bootPropertyDescriptor,
anyType,
bootValueMapping,
creationProcess
)
);
}
else if ( subtype instanceof CompositeType ) { else if ( subtype instanceof CompositeType ) {
final CompositeType subCompositeType = (CompositeType) subtype; final CompositeType subCompositeType = (CompositeType) subtype;
final int columnSpan = subCompositeType.getColumnSpan( creationProcess.getCreationContext().getSessionFactory() ); final int columnSpan = subCompositeType.getColumnSpan( sessionFactory );
final List<String> customReadExpressions = new ArrayList<>( columnSpan ); final List<String> customReadExpressions = new ArrayList<>( columnSpan );
final List<String> customWriteExpressions = new ArrayList<>( columnSpan ); final List<String> customWriteExpressions = new ArrayList<>( columnSpan );

View File

@ -11,6 +11,7 @@ import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.sql.results.graph.FetchOptions;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -25,7 +26,7 @@ public abstract class AbstractSingularAttributeMapping
String name, String name,
int stateArrayPosition, int stateArrayPosition,
StateArrayContributorMetadataAccess attributeMetadataAccess, StateArrayContributorMetadataAccess attributeMetadataAccess,
FetchStrategy mappedFetchStrategy, FetchOptions mappedFetchStrategy,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess) { PropertyAccess propertyAccess) {
super( name, attributeMetadataAccess, mappedFetchStrategy, stateArrayPosition, declaringType ); super( name, attributeMetadataAccess, mappedFetchStrategy, stateArrayPosition, declaringType );

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
@ -44,7 +43,7 @@ public abstract class AbstractStateArrayContributorMapping
public AbstractStateArrayContributorMapping( public AbstractStateArrayContributorMapping(
String name, String name,
StateArrayContributorMetadataAccess attributeMetadataAccess, StateArrayContributorMetadataAccess attributeMetadataAccess,
FetchStrategy mappedFetchStrategy, FetchOptions mappedFetchStrategy,
int stateArrayPosition, int stateArrayPosition,
ManagedMappingType declaringType) { ManagedMappingType declaringType) {
this( this(

View File

@ -42,25 +42,29 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions { public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions {
public static final String PART_NAME = EntityDiscriminatorMapping.ROLE_NAME; public static final String ROLE_NAME = EntityDiscriminatorMapping.ROLE_NAME;
private final NavigableRole navigableRole; private final NavigableRole navigableRole;
private final DiscriminatedAssociationModelPart declaringType; private final DiscriminatedAssociationModelPart declaringType;
private final String table; private final String table;
private final String column; private final String column;
private final boolean nullable;
private final MetaType metaType; private final MetaType metaType;
public AnyDiscriminatorPart( public AnyDiscriminatorPart(
NavigableRole attributeRole, NavigableRole partRole,
DiscriminatedAssociationModelPart declaringType, DiscriminatedAssociationModelPart declaringType,
String table, String table,
String column, String column,
boolean nullable,
MetaType metaType) { MetaType metaType) {
this.navigableRole = attributeRole.append( PART_NAME ); this.navigableRole = partRole;
this.declaringType = declaringType; this.declaringType = declaringType;
this.table = table; this.table = table;
this.column = column; this.column = column;
this.nullable = nullable;
this.metaType = metaType; this.metaType = metaType;
} }
@ -104,7 +108,7 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions
@Override @Override
public String getPartName() { public String getPartName() {
return PART_NAME; return ROLE_NAME;
} }
@Override @Override
@ -171,7 +175,7 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
false, nullable,
null, null,
fetchTiming, fetchTiming,
creationState creationState

View File

@ -39,14 +39,14 @@ import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping.KEY_ROLE_NAME;
/** /**
* Acts as a ModelPart for the key portion of an any-valued mapping * Acts as a ModelPart for the key portion of an any-valued mapping
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class AnyKeyPart implements BasicValuedModelPart, FetchOptions { public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
public static final String ROLE_NAME = "{key}";
private final NavigableRole navigableRole; private final NavigableRole navigableRole;
private final String table; private final String table;
private final String column; private final String column;
@ -56,9 +56,8 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
public AnyKeyPart( public AnyKeyPart(
NavigableRole navigableRole, NavigableRole navigableRole,
String table, DiscriminatedAssociationModelPart anyPart, String table,
String column, String column,
DiscriminatedAssociationModelPart anyPart,
boolean nullable, boolean nullable,
JdbcMapping jdbcMapping) { JdbcMapping jdbcMapping) {
this.navigableRole = navigableRole; this.navigableRole = navigableRole;
@ -101,7 +100,7 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
@Override @Override
public String getPartName() { public String getPartName() {
return KEY_ROLE_NAME; return ROLE_NAME;
} }
@Override @Override
@ -126,7 +125,7 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
@Override @Override
public FetchOptions getMappedFetchOptions() { public FetchOptions getMappedFetchOptions() {
return null; return this;
} }
@Override @Override
@ -149,7 +148,7 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
.getCreationContext() .getCreationContext()
.getSessionFactory(); .getSessionFactory();
final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() ); final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath().getParent() );
final TableReference tableReference = tableGroup.getTableReference( table ); final TableReference tableReference = tableGroup.getTableReference( table );
final Expression columnReference = sqlExpressionResolver.resolveSqlExpression( final Expression columnReference = sqlExpressionResolver.resolveSqlExpression(

View File

@ -6,17 +6,14 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.Iterator; import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.Any; import org.hibernate.mapping.Any;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart; import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
@ -32,14 +29,8 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.type.AnyType; import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.MetaType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import static org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationMapping.KEY_ROLE_NAME;
/** /**
* Singular, any-valued attribute * Singular, any-valued attribute
* *
@ -55,71 +46,35 @@ public class DiscriminatedAssociationAttributeMapping
public DiscriminatedAssociationAttributeMapping( public DiscriminatedAssociationAttributeMapping(
NavigableRole attributeRole, NavigableRole attributeRole,
JavaTypeDescriptor<Object> baseAssociationJtd, JavaTypeDescriptor<?> baseAssociationJtd,
ManagedMappingType declaringType, ManagedMappingType declaringType,
int stateArrayPosition, int stateArrayPosition,
StateArrayContributorMetadataAccess attributeMetadataAccess, StateArrayContributorMetadataAccess attributeMetadataAccess,
FetchStrategy mappedFetchStrategy, FetchTiming fetchTiming,
PropertyAccess propertyAccess, PropertyAccess propertyAccess,
Property bootProperty, Property bootProperty,
AnyType anyType, AnyType anyType,
Any bootValueMapping, Any bootValueMapping,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
super( bootProperty.getName(), stateArrayPosition, attributeMetadataAccess, mappedFetchStrategy, declaringType, propertyAccess ); super(
bootProperty.getName(),
stateArrayPosition,
attributeMetadataAccess,
fetchTiming == FetchTiming.IMMEDIATE
? new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT )
: new FetchStrategy( FetchTiming.DELAYED, FetchStyle.SELECT ),
declaringType,
propertyAccess
);
this.navigableRole = attributeRole; this.navigableRole = attributeRole;
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory(); this.discriminatorMapping = DiscriminatedAssociationMapping.from(
final JdbcEnvironment jdbcEnvironment = sessionFactory.getJdbcServices().getJdbcEnvironment();
final String tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
bootValueMapping.getTable().getQualifiedTableName(),
jdbcEnvironment.getDialect()
);
assert bootValueMapping.getColumnSpan() > 1;
final Iterator<Selectable> columnIterator = bootValueMapping.getColumnIterator();
assert columnIterator.hasNext();
final AnyDiscriminatorPart discriminatorPart = new AnyDiscriminatorPart(
attributeRole, attributeRole,
this,
tableName,
columnIterator.next().getText( jdbcEnvironment.getDialect() ),
(MetaType) anyType.getDiscriminatorType()
);
final Fetchable keyPart;
final Type keyType = anyType.getIdentifierType();
if ( keyType instanceof BasicType ) {
assert columnIterator.hasNext();
keyPart = new AnyKeyPart(
attributeRole.append( KEY_ROLE_NAME ),
tableName,
columnIterator.next().getText( jdbcEnvironment.getDialect() ),
this,
attributeMetadataAccess.resolveAttributeMetadata( null ).isNullable(),
(BasicType<?>) keyType
);
assert ! columnIterator.hasNext();
}
else {
assert keyType instanceof CompositeType;
throw new NotYetImplementedFor6Exception( getClass() );
}
this.discriminatorMapping = new DiscriminatedAssociationMapping(
this,
discriminatorPart,
keyPart,
baseAssociationJtd, baseAssociationJtd,
bootProperty.isLazy() this,
? FetchTiming.DELAYED anyType,
: FetchTiming.IMMEDIATE, bootValueMapping,
sessionFactory creationProcess
); );
} }
@ -129,7 +84,7 @@ public class DiscriminatedAssociationAttributeMapping
} }
@Override @Override
public ModelPart getKeyPart() { public BasicValuedModelPart getKeyPart() {
return discriminatorMapping.getKeyPart(); return discriminatorMapping.getKeyPart();
} }
@ -167,4 +122,34 @@ public class DiscriminatedAssociationAttributeMapping
public MappingType getMappedType() { public MappingType getMappedType() {
return discriminatorMapping; return discriminatorMapping;
} }
@Override
public int getNumberOfFetchables() {
return 2;
}
@Override
public void visitFetchables(Consumer<Fetchable> fetchableConsumer, EntityMappingType treatTargetType) {
fetchableConsumer.accept( getDiscriminatorPart() );
fetchableConsumer.accept( getKeyPart() );
}
@Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
if ( AnyDiscriminatorPart.ROLE_NAME.equals( name ) ) {
return getDiscriminatorPart();
}
if ( AnyKeyPart.ROLE_NAME.equals( name ) ) {
return getKeyPart();
}
return null;
}
@Override
public void visitSubParts(Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
consumer.accept( getDiscriminatorPart() );
consumer.accept( getKeyPart() );
}
} }

View File

@ -6,24 +6,33 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Selectable;
import org.hibernate.metamodel.RuntimeMetamodels; import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart; import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.DomainResultGraphNode;
import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
@ -31,6 +40,9 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.MetaType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/** /**
@ -39,11 +51,67 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class DiscriminatedAssociationMapping implements MappingType, FetchOptions { public class DiscriminatedAssociationMapping implements MappingType, FetchOptions {
public static final String KEY_ROLE_NAME = "{key}";
public static DiscriminatedAssociationMapping from(
NavigableRole containerRole,
JavaTypeDescriptor<?> baseAssociationJtd,
DiscriminatedAssociationModelPart declaringModelPart,
AnyType anyType,
Any bootValueMapping,
MappingModelCreationProcess creationProcess) {
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final JdbcEnvironment jdbcEnvironment = sessionFactory.getJdbcServices().getJdbcEnvironment();
final String tableName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
bootValueMapping.getTable().getQualifiedTableName(),
jdbcEnvironment.getDialect()
);
assert bootValueMapping.getColumnSpan() == 2;
final Iterator<Selectable> columnIterator = bootValueMapping.getColumnIterator();
assert columnIterator.hasNext();
final Selectable metaColumn = columnIterator.next();
assert columnIterator.hasNext();
final Selectable keyColumn = columnIterator.next();
assert ! columnIterator.hasNext();
final AnyDiscriminatorPart discriminatorPart = new AnyDiscriminatorPart(
containerRole.append( AnyDiscriminatorPart.ROLE_NAME),
declaringModelPart,
tableName,
metaColumn.getText( jdbcEnvironment.getDialect() ),
bootValueMapping.isNullable(),
(MetaType) anyType.getDiscriminatorType()
);
final BasicType<?> keyType = (BasicType<?>) anyType.getIdentifierType();
final BasicValuedModelPart keyPart = new AnyKeyPart(
containerRole.append( AnyKeyPart.ROLE_NAME),
declaringModelPart,
tableName,
keyColumn.getText( jdbcEnvironment.getDialect() ),
bootValueMapping.isNullable(),
keyType
);
return new DiscriminatedAssociationMapping(
declaringModelPart,
discriminatorPart,
keyPart,
baseAssociationJtd,
bootValueMapping.isLazy()
? FetchTiming.DELAYED
: FetchTiming.IMMEDIATE,
bootValueMapping.getMetaValues(),
sessionFactory
);
}
private final DiscriminatedAssociationModelPart modelPart; private final DiscriminatedAssociationModelPart modelPart;
private final AnyDiscriminatorPart discriminatorPart; private final AnyDiscriminatorPart discriminatorPart;
private final Fetchable keyPart; private final BasicValuedModelPart keyPart;
private final JavaTypeDescriptor<?> baseAssociationJtd; private final JavaTypeDescriptor<?> baseAssociationJtd;
@ -64,9 +132,10 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
public DiscriminatedAssociationMapping( public DiscriminatedAssociationMapping(
DiscriminatedAssociationModelPart modelPart, DiscriminatedAssociationModelPart modelPart,
AnyDiscriminatorPart discriminatorPart, AnyDiscriminatorPart discriminatorPart,
Fetchable keyPart, BasicValuedModelPart keyPart,
JavaTypeDescriptor<?> baseAssociationJtd, JavaTypeDescriptor<?> baseAssociationJtd,
FetchTiming fetchTiming, FetchTiming fetchTiming,
Map<Object,String> discriminatorValueMappings,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
this.modelPart = modelPart; this.modelPart = modelPart;
this.discriminatorPart = discriminatorPart; this.discriminatorPart = discriminatorPart;
@ -75,8 +144,8 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
this.fetchTiming = fetchTiming; this.fetchTiming = fetchTiming;
final RuntimeMetamodels runtimeMetamodels = sessionFactory.getRuntimeMetamodels(); final RuntimeMetamodels runtimeMetamodels = sessionFactory.getRuntimeMetamodels();
discriminatorPart.getMetaType().getDiscriminatorValuesToEntityNameMap().forEach( discriminatorValueMappings.forEach(
(value, entityName) -> discriminatorValueMappings.add( (value, entityName) -> this.discriminatorValueMappings.add(
new ValueMapping( value, runtimeMetamodels.getEntityMappingType( entityName ) ) new ValueMapping( value, runtimeMetamodels.getEntityMappingType( entityName ) )
) )
); );
@ -90,7 +159,7 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
return discriminatorPart; return discriminatorPart;
} }
public Fetchable getKeyPart() { public BasicValuedModelPart getKeyPart() {
return keyPart; return keyPart;
} }
@ -137,64 +206,65 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
LockMode lockMode, LockMode lockMode,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final Fetch discriminatorValueFetch = getDiscriminatorPart().generateFetch(
fetchParent,
fetchablePath.append( AnyDiscriminatorPart.PART_NAME ),
FetchTiming.IMMEDIATE,
selected,
lockMode,
resultVariable,
creationState
);
final Fetch keyValueFetch = getKeyPart().generateFetch(
fetchParent,
fetchablePath.append( ForeignKeyDescriptor.PART_NAME ),
FetchTiming.IMMEDIATE,
selected,
lockMode,
resultVariable,
creationState
);
return new AnyValuedFetch( return new AnyValuedFetch(
fetchablePath, fetchablePath,
baseAssociationJtd,
modelPart, modelPart,
discriminatorValueFetch,
keyValueFetch,
fetchTiming, fetchTiming,
fetchParent fetchParent,
creationState
); );
} }
private static class AnyValuedFetch implements Fetch { public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
String resultVariable,
DomainResultCreationState creationState) {
return new AnyValuedResult<>(
navigablePath,
baseAssociationJtd,
modelPart,
resultVariable
);
}
private static abstract class AnyValuedResultGraphNode implements DomainResultGraphNode, FetchParent {
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final DiscriminatedAssociationModelPart fetchedPart; private final DiscriminatedAssociationModelPart graphedPart;
private final JavaTypeDescriptor<?> baseAssociationJtd;
private final Fetch discriminatorValueFetch; private Fetch discriminatorValueFetch;
private final Fetch keyValueFetch; private Fetch keyValueFetch;
private final FetchTiming fetchTiming;
private final FetchParent fetchParent;
public AnyValuedFetch( public AnyValuedResultGraphNode(
NavigablePath navigablePath, NavigablePath navigablePath,
DiscriminatedAssociationModelPart fetchedPart, DiscriminatedAssociationModelPart graphedPart,
Fetch discriminatorValueFetch, JavaTypeDescriptor<?> baseAssociationJtd) {
Fetch keyValueFetch,
FetchTiming fetchTiming,
FetchParent fetchParent) {
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
this.fetchedPart = fetchedPart; this.graphedPart = graphedPart;
this.discriminatorValueFetch = discriminatorValueFetch; this.baseAssociationJtd = baseAssociationJtd;
this.keyValueFetch = keyValueFetch;
this.fetchTiming = fetchTiming;
this.fetchParent = fetchParent;
} }
@Override protected void afterInitialize(DomainResultCreationState creationState) {
public JavaTypeDescriptor<?> getResultJavaTypeDescriptor() { final List<Fetch> fetches = creationState.visitFetches( this );
return fetchedPart.getJavaTypeDescriptor(); assert fetches.size() == 2;
discriminatorValueFetch = fetches.get( 0 );
keyValueFetch = fetches.get( 1 );
}
public Fetch getDiscriminatorValueFetch() {
return discriminatorValueFetch;
}
public Fetch getKeyValueFetch() {
return keyValueFetch;
}
public JavaTypeDescriptor<?> getBaseAssociationJtd() {
return baseAssociationJtd;
} }
@Override @Override
@ -202,6 +272,95 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
return navigablePath; return navigablePath;
} }
@Override
public JavaTypeDescriptor<?> getResultJavaTypeDescriptor() {
return baseAssociationJtd;
}
@Override
public boolean containsAnyNonScalarResults() {
return true;
}
@Override
public DiscriminatedAssociationModelPart getReferencedMappingContainer() {
return graphedPart;
}
@Override
public DiscriminatedAssociationModelPart getReferencedMappingType() {
return graphedPart;
}
@Override
public List<Fetch> getFetches() {
return Arrays.asList( discriminatorValueFetch, keyValueFetch );
}
@Override
public Fetch findFetch(Fetchable fetchable) {
assert graphedPart.getDiscriminatorPart() == fetchable
|| graphedPart.getKeyPart() == fetchable;
if ( graphedPart.getDiscriminatorPart() == fetchable ) {
return discriminatorValueFetch;
}
if ( graphedPart.getKeyPart() == fetchable ) {
return keyValueFetch;
}
throw new IllegalArgumentException( "Given Fetchable [" + fetchable + "] did not match either discriminator nor key mapping" );
}
}
private static class AnyValuedResult<T> extends AnyValuedResultGraphNode implements DomainResult<T> {
private final String resultVariable;
public AnyValuedResult(
NavigablePath navigablePath,
JavaTypeDescriptor<?> baseAssociationJtd,
DiscriminatedAssociationModelPart fetchedPart,
String resultVariable) {
super( navigablePath, fetchedPart, baseAssociationJtd );
this.resultVariable = resultVariable;
}
@Override
public String getResultVariable() {
return resultVariable;
}
@Override
public DomainResultAssembler<T> createResultAssembler(AssemblerCreationState creationState) {
return new AnyResultAssembler<>(
getNavigablePath(),
getReferencedMappingContainer(),
true,
getDiscriminatorValueFetch().createAssembler( null, creationState ),
getKeyValueFetch().createAssembler( null, creationState )
);
}
}
private static class AnyValuedFetch extends AnyValuedResultGraphNode implements Fetch {
private final FetchTiming fetchTiming;
private final FetchParent fetchParent;
public AnyValuedFetch(
NavigablePath navigablePath,
JavaTypeDescriptor<?> baseAssociationJtd,
DiscriminatedAssociationModelPart fetchedPart,
FetchTiming fetchTiming,
FetchParent fetchParent,
DomainResultCreationState creationState) {
super( navigablePath, fetchedPart, baseAssociationJtd );
this.fetchTiming = fetchTiming;
this.fetchParent = fetchParent;
afterInitialize( creationState );
}
@Override @Override
public FetchParent getFetchParent() { public FetchParent getFetchParent() {
return fetchParent; return fetchParent;
@ -209,7 +368,7 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
@Override @Override
public DiscriminatedAssociationModelPart getFetchedMapping() { public DiscriminatedAssociationModelPart getFetchedMapping() {
return fetchedPart; return getReferencedMappingContainer();
} }
@Override @Override
@ -231,21 +390,22 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
public DomainResultAssembler<?> createAssembler( public DomainResultAssembler<?> createAssembler(
FetchParentAccess parentAccess, FetchParentAccess parentAccess,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
return new AnyResultAssembler( return new AnyResultAssembler<>(
navigablePath, getNavigablePath(),
fetchedPart, getFetchedMapping(),
fetchTiming, fetchTiming == FetchTiming.IMMEDIATE,
discriminatorValueFetch.createAssembler( parentAccess, creationState ), getDiscriminatorValueFetch().createAssembler( parentAccess, creationState ),
keyValueFetch.createAssembler( parentAccess, creationState ) getKeyValueFetch().createAssembler( parentAccess, creationState )
); );
} }
} }
private static class AnyResultAssembler implements DomainResultAssembler<Object> { private static class AnyResultAssembler<T> implements DomainResultAssembler<T> {
private final NavigablePath fetchedPath; private final NavigablePath fetchedPath;
private final DiscriminatedAssociationModelPart fetchedPart; private final DiscriminatedAssociationModelPart fetchedPart;
private final FetchTiming fetchTiming;
private final boolean eager;
private final DomainResultAssembler<?> discriminatorValueAssembler; private final DomainResultAssembler<?> discriminatorValueAssembler;
private final DomainResultAssembler<?> keyValueAssembler; private final DomainResultAssembler<?> keyValueAssembler;
@ -253,18 +413,18 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
public AnyResultAssembler( public AnyResultAssembler(
NavigablePath fetchedPath, NavigablePath fetchedPath,
DiscriminatedAssociationModelPart fetchedPart, DiscriminatedAssociationModelPart fetchedPart,
FetchTiming fetchTiming, boolean eager,
DomainResultAssembler<?> discriminatorValueAssembler, DomainResultAssembler<?> discriminatorValueAssembler,
DomainResultAssembler<?> keyValueAssembler) { DomainResultAssembler<?> keyValueAssembler) {
this.fetchedPath = fetchedPath; this.fetchedPath = fetchedPath;
this.fetchedPart = fetchedPart; this.fetchedPart = fetchedPart;
this.fetchTiming = fetchTiming; this.eager = eager;
this.discriminatorValueAssembler = discriminatorValueAssembler; this.discriminatorValueAssembler = discriminatorValueAssembler;
this.keyValueAssembler = keyValueAssembler; this.keyValueAssembler = keyValueAssembler;
} }
@Override @Override
public Object assemble( public T assemble(
RowProcessingState rowProcessingState, RowProcessingState rowProcessingState,
JdbcValuesSourceProcessingOptions options) { JdbcValuesSourceProcessingOptions options) {
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState, options ); final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState, options );
@ -283,17 +443,18 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
.getJdbcValuesSourceProcessingState() .getJdbcValuesSourceProcessingState()
.getSession(); .getSession();
return session.internalLoad( //noinspection unchecked
return (T) session.internalLoad(
entityMapping.getEntityName(), entityMapping.getEntityName(),
keyValue, keyValue,
fetchTiming != FetchTiming.DELAYED, eager,
// should not be null since we checked already. null would indicate bad data (ala, not-found handling) // should not be null since we checked already. null would indicate bad data (ala, not-found handling)
false false
); );
} }
@Override @Override
public JavaTypeDescriptor<Object> getAssembledJavaTypeDescriptor() { public JavaTypeDescriptor<T> getAssembledJavaTypeDescriptor() {
//noinspection unchecked //noinspection unchecked
return fetchedPart.getJavaTypeDescriptor(); return fetchedPart.getJavaTypeDescriptor();
} }

View File

@ -0,0 +1,167 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.function.Consumer;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.mapping.Any;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.type.AnyType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class DiscriminatedCollectionPart implements DiscriminatedAssociationModelPart, CollectionPart {
private final Nature nature;
private final NavigableRole partRole;
private final DiscriminatedAssociationMapping discriminatorMapping;
public DiscriminatedCollectionPart(
Nature nature,
NavigableRole collectionRole,
JavaTypeDescriptor<Object> baseAssociationJtd,
Any bootValueMapping,
AnyType anyType,
MappingModelCreationProcess creationProcess) {
this.nature = nature;
this.partRole = collectionRole.append( nature.getName() );
this.discriminatorMapping = DiscriminatedAssociationMapping.from(
partRole,
baseAssociationJtd,
this,
anyType,
bootValueMapping,
creationProcess
);
}
@Override
public Nature getNature() {
return nature;
}
@Override
public BasicValuedModelPart getDiscriminatorPart() {
return discriminatorMapping.getDiscriminatorPart();
}
@Override
public BasicValuedModelPart getKeyPart() {
return discriminatorMapping.getKeyPart();
}
@Override
public EntityMappingType resolveDiscriminatorValue(Object discriminatorValue) {
return discriminatorMapping.resolveDiscriminatorValueToEntityName( discriminatorValue );
}
@Override
public String getFetchableName() {
return nature.getName();
}
@Override
public FetchOptions getMappedFetchOptions() {
return discriminatorMapping;
}
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
return discriminatorMapping.generateFetch(
fetchParent,
fetchablePath,
fetchTiming,
selected,
lockMode,
resultVariable,
creationState
);
}
@Override
public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
String resultVariable,
DomainResultCreationState creationState) {
return discriminatorMapping.createDomainResult(
navigablePath,
tableGroup,
resultVariable,
creationState
);
}
@Override
public MappingType getPartMappingType() {
return discriminatorMapping;
}
@Override
public JavaTypeDescriptor<?> getJavaTypeDescriptor() {
return discriminatorMapping.getJavaTypeDescriptor();
}
@Override
public NavigableRole getNavigableRole() {
return partRole;
}
@Override
public EntityMappingType findContainingEntityMapping() {
return discriminatorMapping.getModelPart().findContainingEntityMapping();
}
@Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
if ( AnyDiscriminatorPart.ROLE_NAME.equals( name ) ) {
return getDiscriminatorPart();
}
if ( AnyKeyPart.ROLE_NAME.equals( name ) ) {
return getKeyPart();
}
return null;
}
@Override
public void visitSubParts(Consumer<ModelPart> consumer, EntityMappingType treatTargetType) {
consumer.accept( getDiscriminatorPart() );
consumer.accept( getKeyPart() );
}
@Override
public int getNumberOfFetchables() {
return 2;
}
}

View File

@ -33,6 +33,7 @@ import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
@ -90,6 +91,7 @@ import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry; import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -1261,6 +1263,24 @@ public class MappingModelCreationHelper {
return (CollectionPart) mappingType.getEmbeddedValueMapping(); return (CollectionPart) mappingType.getEmbeddedValueMapping();
} }
if ( element instanceof Any ) {
final Any anyBootMapping = (Any) element;
final SessionFactoryImplementor sessionFactory = creationProcess.getCreationContext().getSessionFactory();
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JavaTypeDescriptorRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final JavaTypeDescriptor<Object> baseJtd = jtdRegistry.getDescriptor(Object.class);
return new DiscriminatedCollectionPart(
CollectionPart.Nature.ELEMENT,
collectionDescriptor.getNavigableRole(),
baseJtd,
anyBootMapping,
anyBootMapping.getType(),
creationProcess
);
}
if ( element instanceof OneToMany || element instanceof ToOne ) { if ( element instanceof OneToMany || element instanceof ToOne ) {
final EntityType elementEntityType = (EntityType) collectionDescriptor.getElementType(); final EntityType elementEntityType = (EntityType) collectionDescriptor.getElementType();
final EntityPersister associatedEntity = creationProcess.getEntityPersister( elementEntityType.getAssociatedEntityName() ); final EntityPersister associatedEntity = creationProcess.getEntityPersister( elementEntityType.getAssociatedEntityName() );

View File

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.MappingModelCreationLogger; import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonTransientException; import org.hibernate.metamodel.mapping.NonTransientException;

View File

@ -6290,9 +6290,7 @@ public abstract class AbstractEntityPersister
return optimisticallyLocked; return optimisticallyLocked;
} }
}, },
bootProperty.isLazy() bootProperty.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE,
? new FetchStrategy( FetchTiming.DELAYED, FetchStyle.SELECT )
: new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT ),
propertyAccess, propertyAccess,
bootProperty, bootProperty,
(AnyType) attrType, (AnyType) attrType,
@ -6348,7 +6346,7 @@ public abstract class AbstractEntityPersister
} }
@Override @Override
public JavaTypeDescriptor getMappedJavaTypeDescriptor() { public JavaTypeDescriptor<?> getMappedJavaTypeDescriptor() {
return javaTypeDescriptor; return javaTypeDescriptor;
} }

View File

@ -0,0 +1,234 @@
/*
* 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.any.annotations;
import org.hibernate.LazyInitializationException;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@DomainModel(
annotatedPackageNames = "org.hibernate.orm.test.any.annotations",
annotatedClasses = {
StringProperty.class,
IntegerProperty.class,
LongProperty.class,
PropertySet.class,
LazyPropertySet.class,
PropertyMap.class,
PropertyList.class,
CharProperty.class
}
)
@SessionFactory
public class AnyTest {
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
{
final PropertySet set1 = new PropertySet("string");
final Property property = new StringProperty("name", "Alex");
set1.setSomeProperty(property);
set1.addGeneralProperty(property);
session.save(set1);
final PropertySet set2 = new PropertySet("integer");
final Property property2 = new IntegerProperty("age", 33);
set2.setSomeProperty(property2);
set2.addGeneralProperty(property2);
session.save(set2);
}
{
final PropertyMap map = new PropertyMap("sample");
map.getProperties().put("name", new StringProperty("name", "Alex"));
map.getProperties().put("age", new IntegerProperty("age", 33));
session.save(map);
}
{
final PropertyList list = new PropertyList("sample");
final StringProperty stringProperty = new StringProperty("name", "Alex");
final IntegerProperty integerProperty = new IntegerProperty("age", 33);
final LongProperty longProperty = new LongProperty("distance", 121L);
final CharProperty charProp = new CharProperty("Est", 'E');
list.setSomeProperty(longProperty);
list.addGeneralProperty(stringProperty);
list.addGeneralProperty(integerProperty);
list.addGeneralProperty(longProperty);
list.addGeneralProperty(charProp);
session.save(list);
}
{
final LazyPropertySet set = new LazyPropertySet( "string" );
final Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
session.save( set );
}
}
);
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete StringProperty" ).executeUpdate();
session.createQuery( "delete IntegerProperty" ).executeUpdate();
session.createQuery( "delete LongProperty" ).executeUpdate();
session.createQuery( "delete CharProperty" ).executeUpdate();
session.createQuery( "delete PropertyList" ).executeUpdate();
session.createQuery( "delete PropertyMap" ).executeUpdate();
session.createQuery( "delete PropertySet" ).executeUpdate();
}
);
}
@Test
public void testDefaultAnyAssociation(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final QueryImplementor<PropertySet> query = session.createQuery("select s from PropertySet s where name = :name", PropertySet.class);
{
final PropertySet result = query.setParameter( "name", "string" ).uniqueResult();
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof StringProperty );
assertEquals( "Alex", result.getSomeProperty().asString() );
assertNotNull( result.getGeneralProperties() );
assertEquals( 1, result.getGeneralProperties().size() );
assertEquals( "Alex", result.getGeneralProperties().get( 0 ).asString() );
}
{
final PropertySet result = query.setParameter( "name", "integer" ).uniqueResult();
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof IntegerProperty );
assertEquals( "33", result.getSomeProperty().asString() );
assertNotNull( result.getGeneralProperties() );
assertEquals( 1, result.getGeneralProperties().size() );
assertEquals( "33", result.getGeneralProperties().get( 0 ).asString() );
}
}
);
}
@Test
public void testManyToAnyWithMap(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final PropertyMap actualMap = session
.createQuery( "SELECT m FROM PropertyMap m WHERE m.name = :name", PropertyMap.class )
.setParameter( "name", "sample" )
.uniqueResult();
assertNotNull( actualMap );
assertNotNull( actualMap.getProperties() );
Property property = actualMap.getProperties().get( "name" );
assertNotNull( property );
assertTrue( property instanceof StringProperty );
assertEquals( "Alex", property.asString() );
property = actualMap.getProperties().get( "age" );
assertNotNull( property );
assertTrue( property instanceof IntegerProperty );
assertEquals( "33", property.asString() );
}
);
}
@Test
public void testMetaDataUseWithManyToAny(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
//noinspection unchecked
final PropertyList<Property> actualList = session
.createQuery( "SELECT l FROM PropertyList l WHERE l.name = :name", PropertyList.class )
.setParameter( "name", "sample" )
.uniqueResult();
assertNotNull( actualList );
assertNotNull( actualList.getGeneralProperties() );
assertEquals( 4, actualList.getGeneralProperties().size() );
Property property = actualList.getSomeProperty();
assertNotNull( property );
assertTrue( property instanceof LongProperty );
assertEquals( "121", property.asString() );
assertEquals( "Alex", actualList.getGeneralProperties().get( 0 )
.asString() );
assertEquals( "33", actualList.getGeneralProperties().get( 1 ).asString() );
assertEquals( "121", actualList.getGeneralProperties().get( 2 ).asString() );
assertEquals( "E", actualList.getGeneralProperties().get( 3 ).asString() );
}
);
}
@Test
public void testFetchEager(SessionFactoryScope scope) {
final PropertySet result = scope.fromTransaction(
session -> {
final PropertySet localResult = session.createQuery("select s from PropertySet s where name = :name", PropertySet.class)
.setParameter("name", "string")
.getSingleResult();
assertNotNull( localResult );
assertNotNull( localResult.getSomeProperty() );
return localResult;
}
);
assertTrue( result.getSomeProperty() instanceof StringProperty );
assertEquals( "Alex", result.getSomeProperty().asString() );
}
@Test
public void testFetchLazy(SessionFactoryScope scope) {
final LazyPropertySet result = scope.fromTransaction(
session -> {
final LazyPropertySet localResult = session.createQuery("select s from LazyPropertySet s where name = :name", LazyPropertySet.class)
.setParameter("name", "string")
.getSingleResult();
assertNotNull( localResult );
assertNotNull( localResult.getSomeProperty() );
return localResult;
}
);
try {
result.getSomeProperty().asString();
fail( "should not get the property string after session closed." );
}
catch (LazyInitializationException e) {
// expected
}
catch (Exception e) {
fail( "should not throw exception other than LazyInitializationException." );
}
}
}

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;

View File

@ -1,14 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
@ -21,67 +17,63 @@ import javax.persistence.Table;
import org.hibernate.annotations.Any; import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef; import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.MetaValue; import org.hibernate.annotations.MetaValue;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
public class EmbeddedAnyTest extends BaseEntityManagerFunctionalTestCase { import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@Override import static org.junit.Assert.assertEquals;
protected Class<?>[] getAnnotatedClasses() { import static org.junit.Assert.assertTrue;
return new Class<?>[]{ Foo.class, Bar1.class, Bar2.class };
@DomainModel( annotatedClasses = { EmbeddedAnyTest.Foo.class, EmbeddedAnyTest.Bar1.class, EmbeddedAnyTest.Bar2.class } )
@SessionFactory
public class EmbeddedAnyTest {
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final Foo foo1 = new Foo();
foo1.setId( 1 );
final Bar1 bar1 = new Bar1();
bar1.setId( 1 );
bar1.setBar1( "bar 1" );
bar1.setBarType( "1" );
final FooEmbeddable foo1Embedded = new FooEmbeddable();
foo1Embedded.setBar( bar1 );
foo1.setFooEmbedded( foo1Embedded );
session.persist( bar1 );
session.persist( foo1 );
}
);
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete Bar2" ).executeUpdate();
session.createQuery( "delete Bar1" ).executeUpdate();
session.createQuery( "delete Foo" ).executeUpdate();
}
);
} }
@Test @Test
public void testEmbeddedAny() { public void testEmbeddedAny(SessionFactoryScope scope) {
doInJPA( this::entityManagerFactory, em -> { scope.inTransaction(
Foo foo1 = new Foo(); session -> {
foo1.setId( 1 ); final Foo foo = session.find( Foo.class, 1 );
assertTrue( foo.getFooEmbedded().getBar() instanceof Bar1 );
Bar1 bar1 = new Bar1(); assertEquals( "bar 1", ( (Bar1) foo.getFooEmbedded().getBar() ).getBar1() );
bar1.setId( 1 ); }
bar1.setBar1( "bar 1" ); );
bar1.setBarType( "1" );
FooEmbeddable foo1Embedded = new FooEmbeddable();
foo1Embedded.setBar( bar1 );
foo1.setFooEmbedded( foo1Embedded );
em.persist( bar1 );
em.persist( foo1 );
} );
doInJPA( this::entityManagerFactory, em -> {
Foo foo2 = new Foo();
foo2.setId( 2 );
Bar2 bar2 = new Bar2();
bar2.setId( 2 );
bar2.setBar2( "bar 2" );
bar2.setBarType( "2" );
FooEmbeddable foo2Embedded = new FooEmbeddable();
foo2Embedded.setBar( bar2 );
foo2.setFooEmbedded( foo2Embedded );
em.persist( bar2 );
em.persist( foo2 );
} );
doInJPA( this::entityManagerFactory, em -> {
Foo foo1 = em.find( Foo.class, 1 );
assertTrue( foo1.getFooEmbedded().getBar() instanceof Bar1 );
assertEquals( "bar 1", ( (Bar1) foo1.getFooEmbedded().getBar() ).getBar1() );
} );
doInJPA( this::entityManagerFactory, em -> {
Foo foo2 = em.find( Foo.class, 2 );
assertTrue( foo2.getFooEmbedded().getBar() instanceof Bar2 );
assertEquals( "bar 2", ( (Bar2) foo2.getFooEmbedded().getBar() ).getBar2() );
} );
} }
@Entity(name = "Foo") @Entity(name = "Foo")

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
public interface Property { public interface Property {

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.Column; import javax.persistence.Column;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.persistence.Column; import javax.persistence.Column;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.persistence.Column; import javax.persistence.Column;

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;

View File

@ -1,8 +1,8 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
// $Id$ // $Id$
@ -14,7 +14,7 @@
@MetaValue(value = "L", targetEntity = LongProperty.class) @MetaValue(value = "L", targetEntity = LongProperty.class)
}) })
package org.hibernate.test.annotations.any; package org.hibernate.orm.test.any.annotations;
import org.hibernate.annotations.AnyMetaDef; import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.MetaValue; import org.hibernate.annotations.MetaValue;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.orm.test.any; package org.hibernate.orm.test.any.hbm;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.orm.test.any; package org.hibernate.orm.test.any.hbm;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.stat.spi.StatisticsImplementor;
@ -12,10 +12,10 @@ import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.test.annotations.any.IntegerProperty; import org.hibernate.orm.test.any.annotations.IntegerProperty;
import org.hibernate.test.annotations.any.Property; import org.hibernate.orm.test.any.annotations.Property;
import org.hibernate.test.annotations.any.PropertySet; import org.hibernate.orm.test.any.annotations.PropertySet;
import org.hibernate.test.annotations.any.StringProperty; import org.hibernate.orm.test.any.annotations.StringProperty;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -28,7 +28,7 @@ import static org.junit.Assert.assertTrue;
@DomainModel( @DomainModel(
annotatedClasses = { StringProperty.class, IntegerProperty.class }, annotatedClasses = { StringProperty.class, IntegerProperty.class },
xmlMappings = "org/hibernate/orm/test/any/AnyTestEagerPropertySet.hbm.xml" xmlMappings = "org/hibernate/orm/test/any/hbm/AnyTestEagerPropertySet.hbm.xml"
) )
@SessionFactory( generateStatistics = true ) @SessionFactory( generateStatistics = true )
public class AnyEagerHbmTest { public class AnyEagerHbmTest {

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.orm.test.any; package org.hibernate.orm.test.any.hbm;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.stat.spi.StatisticsImplementor;
@ -12,10 +12,10 @@ import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.test.annotations.any.IntegerProperty; import org.hibernate.orm.test.any.annotations.IntegerProperty;
import org.hibernate.test.annotations.any.LazyPropertySet; import org.hibernate.orm.test.any.annotations.LazyPropertySet;
import org.hibernate.test.annotations.any.Property; import org.hibernate.orm.test.any.annotations.Property;
import org.hibernate.test.annotations.any.StringProperty; import org.hibernate.orm.test.any.annotations.StringProperty;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -28,7 +28,7 @@ import static org.junit.Assert.assertTrue;
@DomainModel( @DomainModel(
annotatedClasses = { StringProperty.class, IntegerProperty.class }, annotatedClasses = { StringProperty.class, IntegerProperty.class },
xmlMappings = "org/hibernate/orm/test/any/AnyTestLazyPropertySet.hbm.xml" xmlMappings = "org/hibernate/orm/test/any/hbm/AnyTestLazyPropertySet.hbm.xml"
) )
@SessionFactory( generateStatistics = true ) @SessionFactory( generateStatistics = true )
public class AnyLazyHbmTest { public class AnyLazyHbmTest {

View File

@ -9,7 +9,7 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.annotations.any"> <hibernate-mapping package="org.hibernate.orm.test.any.annotations">
<class name="PropertySet" table="eager_property_set"> <class name="PropertySet" table="eager_property_set">
<id name="id" type="java.lang.Integer"> <id name="id" type="java.lang.Integer">
<generator class="increment"/> <generator class="increment"/>

View File

@ -2,14 +2,14 @@
<!-- <!--
~ Hibernate, Relational Persistence for Idiomatic Java ~ Hibernate, Relational Persistence for Idiomatic Java
~ ~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later. ~ 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>. ~ See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
--> -->
<!DOCTYPE hibernate-mapping PUBLIC <!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.annotations.any"> <hibernate-mapping package="org.hibernate.orm.test.any.annotations">
<class name="LazyPropertySet" table="lazy_property_set"> <class name="LazyPropertySet" table="lazy_property_set">
<id name="id" type="java.lang.Integer"> <id name="id" type="java.lang.Integer">
<generator class="increment"/> <generator class="increment"/>

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.orm.test.any; package org.hibernate.orm.test.any.hbm;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
@ -27,7 +27,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
*/ */
@TestForIssue(jiraKey = "HHH-1663") @TestForIssue(jiraKey = "HHH-1663")
@ServiceRegistry( settings = @ServiceRegistry.Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" ) ) @ServiceRegistry( settings = @ServiceRegistry.Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" ) )
@DomainModel( xmlMappings = "org/hibernate/orm/test/any/Person.hbm.xml" ) @DomainModel( xmlMappings = "org/hibernate/orm/test/any/hbm/Person.hbm.xml" )
@SessionFactory @SessionFactory
public class AnyTypeTest { public class AnyTypeTest {

View File

@ -9,7 +9,7 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.orm.test.any"> <hibernate-mapping package="org.hibernate.orm.test.any.hbm">
<class name="Person" table="T_ANY_PERSON"> <class name="Person" table="T_ANY_PERSON">
<id name="id" column="ID_"> <id name="id" column="ID_">

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.orm.test.any; package org.hibernate.orm.test.any.hbm;
/** /**

View File

@ -1,240 +0,0 @@
/*
* 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.test.annotations.any;
import org.hibernate.LazyInitializationException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
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;
import static org.junit.Assert.fail;
public class AnyTest extends BaseCoreFunctionalTestCase {
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Test
public void testDefaultAnyAssociation() {
Session s = openSession();
Transaction t = s.beginTransaction();
PropertySet set1 = new PropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set1.setSomeProperty( property );
set1.addGeneralProperty( property );
s.save( set1 );
PropertySet set2 = new PropertySet( "integer" );
property = new IntegerProperty( "age", 33 );
set2.setSomeProperty( property );
set2.addGeneralProperty( property );
s.save( set2 );
s.flush();
s.clear();
Query q = s
.createQuery( "select s from PropertySet s where name = :name" );
q.setParameter( "name", "string" );
PropertySet result = (PropertySet) q.uniqueResult();
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof StringProperty );
assertEquals( "Alex", result.getSomeProperty().asString() );
assertNotNull( result.getGeneralProperties() );
assertEquals( 1, result.getGeneralProperties().size() );
assertEquals( "Alex", result.getGeneralProperties().get( 0 ).asString() );
q.setParameter( "name", "integer" );
result = (PropertySet) q.uniqueResult();
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof IntegerProperty );
assertEquals( "33", result.getSomeProperty().asString() );
assertNotNull( result.getGeneralProperties() );
assertEquals( 1, result.getGeneralProperties().size() );
assertEquals( "33", result.getGeneralProperties().get( 0 ).asString() );
t.rollback();
s.close();
}
@Test
public void testManyToAnyWithMap() throws Exception {
Session s = openSession();
Transaction t = s.beginTransaction();
PropertyMap map = new PropertyMap( "sample" );
map.getProperties().put( "name", new StringProperty( "name", "Alex" ) );
map.getProperties().put( "age", new IntegerProperty( "age", 33 ) );
s.save( map );
s.flush();
s.clear();
Query q = s
.createQuery( "SELECT map FROM PropertyMap map WHERE map.name = :name" );
q.setParameter( "name", "sample" );
PropertyMap actualMap = (PropertyMap) q.uniqueResult();
assertNotNull( actualMap );
assertNotNull( actualMap.getProperties() );
Property property = actualMap.getProperties().get( "name" );
assertNotNull( property );
assertTrue( property instanceof StringProperty );
assertEquals( "Alex", property.asString() );
property = actualMap.getProperties().get( "age" );
assertNotNull( property );
assertTrue( property instanceof IntegerProperty );
assertEquals( "33", property.asString() );
t.rollback();
s.close();
}
@Test
public void testMetaDataUseWithManyToAny() {
Session s = openSession();
Transaction t = s.beginTransaction();
PropertyList list = new PropertyList( "sample" );
StringProperty stringProperty = new StringProperty( "name", "Alex" );
IntegerProperty integerProperty = new IntegerProperty( "age", 33 );
LongProperty longProperty = new LongProperty( "distance", 121L );
CharProperty charProp = new CharProperty( "Est", 'E' );
list.setSomeProperty( longProperty );
list.addGeneralProperty( stringProperty );
list.addGeneralProperty( integerProperty );
list.addGeneralProperty( longProperty );
list.addGeneralProperty( charProp );
s.save( list );
s.flush();
s.clear();
Query q = s
.createQuery( "SELECT list FROM PropertyList list WHERE list.name = :name" );
q.setParameter( "name", "sample" );
PropertyList<Property> actualList = (PropertyList<Property>) q
.uniqueResult();
assertNotNull( actualList );
assertNotNull( actualList.getGeneralProperties() );
assertEquals( 4, actualList.getGeneralProperties().size() );
Property property = actualList.getSomeProperty();
assertNotNull( property );
assertTrue( property instanceof LongProperty );
assertEquals( "121", property.asString() );
assertEquals( "Alex", actualList.getGeneralProperties().get( 0 )
.asString() );
assertEquals( "33", actualList.getGeneralProperties().get( 1 ).asString() );
assertEquals( "121", actualList.getGeneralProperties().get( 2 ).asString() );
assertEquals( "E", actualList.getGeneralProperties().get( 3 ).asString() );
t.rollback();
s.close();
}
@Test
public void testFetchEager() {
doInHibernate( this::sessionFactory, s -> {
PropertySet set = new PropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
s.save( set );
} );
PropertySet result = doInHibernate( this::sessionFactory, s -> {
return s.createQuery( "select s from PropertySet s where name = :name", PropertySet.class )
.setParameter( "name", "string" )
.getSingleResult();
} );
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof StringProperty );
assertEquals( "Alex", result.getSomeProperty().asString() );
}
@Test
public void testFetchLazy() {
doInHibernate( this::sessionFactory, s -> {
LazyPropertySet set = new LazyPropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
s.save( set );
} );
LazyPropertySet result = doInHibernate( this::sessionFactory, s -> {
return s.createQuery( "select s from LazyPropertySet s where name = :name", LazyPropertySet.class )
.setParameter( "name", "string" )
.getSingleResult();
} );
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
try {
result.getSomeProperty().asString();
fail( "should not get the property string after session closed." );
}
catch (LazyInitializationException e) {
// expected
}
catch (Exception e) {
fail( "should not throw exception other than LazyInitializationException." );
}
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
StringProperty.class,
IntegerProperty.class,
LongProperty.class,
PropertySet.class,
LazyPropertySet.class,
PropertyMap.class,
PropertyList.class,
CharProperty.class
};
}
@Override
protected String[] getAnnotatedPackages() {
return new String[] {
"org.hibernate.test.annotations.any"
};
}
// Simply having this orm.xml file in the classpath reproduces HHH-4261.
@Override
protected String[] getOrmXmlFiles() {
return new String[] { "org/hibernate/test/annotations/any/orm.xml" };
}
}

View File

@ -2,8 +2,8 @@
<!-- <!--
~ Hibernate, Relational Persistence for Idiomatic Java ~ Hibernate, Relational Persistence for Idiomatic Java
~ ~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later. ~ 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>. ~ See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
--> -->
<!DOCTYPE hibernate-mapping PUBLIC <!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

View File

@ -96,6 +96,7 @@ public @interface DomainModel {
Class<? extends DomainModelDescriptor>[] modelDescriptorClasses() default {}; Class<? extends DomainModelDescriptor>[] modelDescriptorClasses() default {};
Class[] annotatedClasses() default {}; Class[] annotatedClasses() default {};
String[] annotatedClassNames() default {}; String[] annotatedClassNames() default {};
String[] annotatedPackageNames() default {};
String[] xmlMappings() default {}; String[] xmlMappings() default {};
ExtraQueryImport[] extraQueryImports() default {}; ExtraQueryImport[] extraQueryImports() default {};
Class<?>[] extraQueryImportClasses() default {}; Class<?>[] extraQueryImportClasses() default {};

View File

@ -16,6 +16,7 @@ import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources; import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.internal.util.JavaHelper;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
@ -88,6 +89,10 @@ public class DomainModelExtension
final MetadataSources metadataSources = new MetadataSources( serviceRegistry ); final MetadataSources metadataSources = new MetadataSources( serviceRegistry );
for ( String annotatedPackageName : domainModelAnnotation.annotatedPackageNames() ) {
metadataSources.addPackage( JavaHelper.getPackageFor( annotatedPackageName ) );
}
for ( StandardDomainModel standardDomainModel : domainModelAnnotation.standardModels() ) { for ( StandardDomainModel standardDomainModel : domainModelAnnotation.standardModels() ) {
standardDomainModel.getDescriptor().applyDomainModel( metadataSources ); standardDomainModel.getDescriptor().applyDomainModel( metadataSources );
} }