diff --git a/hibernate-core/src/main/java/org/hibernate/boot/package-info.java b/hibernate-core/src/main/java/org/hibernate/boot/package-info.java index e69de29bb2..6a27710d96 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/package-info.java @@ -0,0 +1,2 @@ +package org.hibernate.boot; + diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java index 33634d9db1..98b670fa3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java @@ -23,6 +23,8 @@ */ package org.hibernate.metamodel.internal; +import static org.hibernate.engine.spi.SyntheticAttributeHelper.SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME; + import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -32,8 +34,6 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.jboss.logging.Logger; - import org.hibernate.AssertionFailure; import org.hibernate.EntityMode; import org.hibernate.MultiTenancyStrategy; @@ -42,7 +42,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.cfg.ObjectNameNormalizer; import org.hibernate.engine.FetchTiming; -import org.hibernate.engine.spi.SyntheticAttributeHelper; import org.hibernate.id.EntityIdentifierNature; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentityGenerator; @@ -57,7 +56,6 @@ import org.hibernate.metamodel.internal.source.hbm.MapAttributeSource; import org.hibernate.metamodel.spi.binding.AbstractPluralAttributeBinding; import org.hibernate.metamodel.spi.binding.AttributeBinding; import org.hibernate.metamodel.spi.binding.AttributeBindingContainer; -import org.hibernate.metamodel.spi.binding.BackRefAttributeBinding; import org.hibernate.metamodel.spi.binding.BagBinding; import org.hibernate.metamodel.spi.binding.BasicAttributeBinding; import org.hibernate.metamodel.spi.binding.BasicPluralAttributeElementBinding; @@ -155,8 +153,7 @@ import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.type.ComponentType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; - -import static org.hibernate.engine.spi.SyntheticAttributeHelper.SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME; +import org.jboss.logging.Logger; /** * The common binder shared between annotations and {@code hbm.xml} processing. @@ -170,6 +167,78 @@ import static org.hibernate.engine.spi.SyntheticAttributeHelper.SYNTHETIC_COMPOS public class Binder { private static final Logger log = Logger.getLogger( Binder.class ); + private static org.hibernate.internal.util.ValueHolder< Class< ? >> createSingularAttributeJavaType( + final Class< ? > attributeContainerClassReference, + final String attributeName ) { + org.hibernate.internal.util.ValueHolder.DeferredInitializer< Class< ? >> deferredInitializer = + new org.hibernate.internal.util.ValueHolder.DeferredInitializer< Class< ? >>() { + public Class< ? > initialize() { + return ReflectHelper.reflectedPropertyClass( + attributeContainerClassReference, + attributeName ); + } + }; + return new org.hibernate.internal.util.ValueHolder< Class< ? >>( deferredInitializer ); + } + + private static org.hibernate.internal.util.ValueHolder< Class< ? >> createSingularAttributeJavaType( + final SingularAttribute attribute ) { + return createSingularAttributeJavaType( + attribute.getAttributeContainer().getClassReference(), + attribute.getName() + ); + } + + private static String interpretIdentifierUnsavedValue( IdentifierSource identifierSource, IdGenerator generator ) { + if ( identifierSource == null ) { + throw new IllegalArgumentException( "identifierSource must be non-null." ); + } + if ( generator == null || StringHelper.isEmpty( generator.getStrategy() ) ) { + throw new IllegalArgumentException( "generator must be non-null and its strategy must be non-empty." ); + } + String unsavedValue = null; + if ( identifierSource.getUnsavedValue() != null ) { + unsavedValue = identifierSource.getUnsavedValue(); + } else if ( "assigned".equals( generator.getStrategy() ) ) { + unsavedValue = "undefined"; + } else { + switch ( identifierSource.getNature() ) { + case SIMPLE: { + // unsavedValue = null; + break; + } + case COMPOSITE: { + // The generator strategy should be "assigned" and processed above. + throw new IllegalStateException( String.format( + "Expected generator strategy for composite ID: 'assigned'; instead it is: %s", + generator.getStrategy() ) ); + } + case AGGREGATED_COMPOSITE: { + // TODO: if the component only contains 1 attribute (when flattened) + // and it is not an association then null should be returned; + // otherwise "undefined" should be returned. + throw new NotYetImplementedException( String.format( + "Unsaved value for (%s) identifier not implemented yet.", + identifierSource.getNature() ) ); + } + default: { + throw new AssertionFailure( String.format( "Unexpected identifier nature: %s", identifierSource.getNature() ) ); + } + } + } + return unsavedValue; + } + + private static boolean toBoolean( final TruthValue truthValue, final boolean truthValueDefault ) { + if ( truthValue == TruthValue.TRUE ) { + return true; + } + if ( truthValue == TruthValue.FALSE ) { + return false; + } + return truthValueDefault; + } + private final MetadataImplementor metadata; private final IdentifierGeneratorFactory identifierGeneratorFactory; private final ObjectNameNormalizer nameNormalizer; @@ -177,15 +246,12 @@ public class Binder { private final HashMap entityHierarchiesByRootEntitySource = new HashMap(); private final HashMap attributeSourcesByName = new HashMap(); + // todo : apply org.hibernate.metamodel.MetadataSources.getExternalCacheRegionDefinitions() private final LinkedList bindingContexts = new LinkedList(); private final LinkedList inheritanceTypes = new LinkedList(); - private final LinkedList entityModes = new LinkedList(); - private final HibernateTypeHelper typeHelper; // todo: refactor helper and remove redundant methods in this class - // todo : apply org.hibernate.metamodel.MetadataSources.getExternalCacheRegionDefinitions() - public Binder( final MetadataImplementor metadata, final IdentifierGeneratorFactory identifierGeneratorFactory ) { this.metadata = metadata; this.identifierGeneratorFactory = identifierGeneratorFactory; @@ -193,6 +259,11 @@ public class Binder { this.nameNormalizer = metadata.getObjectNameNormalizer(); } + private void addUniqueConstraintForNaturalIdColumn(final TableSpecification table, final Column column) { + final UniqueKey uniqueKey = table.getOrCreateUniqueKey( "natural_id_unique_key_" ); + uniqueKey.addColumn( column ); + } + private AttributeBinding attributeBinding( final String entityName, final String attributeName ) { // Check if binding has already been created final EntityBinding entityBinding = entityBinding( entityName ); @@ -205,20 +276,29 @@ public class Binder { return entityName + "." + attributeName; } - // Top-level attributes handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + private void bindAggregatedCompositeIdentifier( + EntityBinding rootEntityBinding, + AggregatedCompositeIdentifierSource identifierSource ) { + // locate the attribute binding + final CompositeAttributeBinding idAttributeBinding = ( CompositeAttributeBinding ) bindIdentifierAttribute( + rootEntityBinding, identifierSource.getIdentifierAttributeSource() + ); - private void bindAttributes( - final AttributeBindingContainer attributeBindingContainer, - final AttributeSourceContainer attributeSourceContainer ) { - for ( final AttributeSource attributeSource : attributeSourceContainer.attributeSources() ) { - bindAttribute( attributeBindingContainer, attributeSource ); + // Configure ID generator + IdGenerator generator = identifierSource.getIdentifierGeneratorDescriptor(); + if ( generator == null ) { + final Map< String, String > params = new HashMap< String, String >(); + params.put( IdentifierGenerator.ENTITY_NAME, rootEntityBinding.getEntity().getName() ); + generator = new IdGenerator( "default_assign_identity_generator", "assigned", params ); } - } - private SingularAttributeBinding bindIdentifierAttribute( - final AttributeBindingContainer attributeBindingContainer, - final SingularAttributeSource attributeSource) { - return bindSingularAttribute( attributeBindingContainer, attributeSource, true ); + // determine the unsaved value mapping + final String unsavedValue = interpretIdentifierUnsavedValue( identifierSource, generator ); + + rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsAggregatedCompositeIdentifier( + idAttributeBinding, + generator, + unsavedValue ); } private AttributeBinding bindAttribute( @@ -239,36 +319,31 @@ public class Binder { bindPluralAttribute( attributeBindingContainer,PluralAttributeSource.class.cast( attributeSource ) ); } - // Singular attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - private SingularAttributeBinding bindSingularAttribute( + private void bindAttributes( final AttributeBindingContainer attributeBindingContainer, - final SingularAttributeSource attributeSource, - boolean isIdentifierAttribute) { - final SingularAttributeNature nature = attributeSource.getNature(); - final SingularAttribute attribute = - attributeBindingContainer.getAttributeContainer().locateSingularAttribute( attributeSource.getName() ); - switch ( nature ) { - case BASIC: - return bindBasicAttribute( attributeBindingContainer, attributeSource, attribute ); - case MANY_TO_ONE: - return bindManyToOneAttribute( - attributeBindingContainer, - ToOneAttributeSource.class.cast( attributeSource ), - attribute - ); - case COMPONENT: - return bindComponentAttribute( - attributeBindingContainer, - ComponentAttributeSource.class.cast( attributeSource ), - attribute, - isIdentifierAttribute - ); - default: - throw new NotYetImplementedException( nature.toString() ); + final AttributeSourceContainer attributeSourceContainer ) { + for ( final AttributeSource attributeSource : attributeSourceContainer.attributeSources() ) { + bindAttribute( attributeBindingContainer, attributeSource ); } } + private AbstractPluralAttributeBinding bindBagAttribute( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource, + PluralAttribute attribute ) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createBag( attributeSource.getName() ); + } + return attributeBindingContainer.makeBagAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ) + ); + } + private BasicAttributeBinding bindBasicAttribute( final AttributeBindingContainer attributeBindingContainer, final SingularAttributeSource attributeSource, @@ -304,6 +379,186 @@ public class Binder { return attributeBinding; } + private void bindBasicCollectionElement( + final BasicPluralAttributeElementBinding elementBinding, + final BasicPluralAttributeElementSource elementSource, + final String defaultElementJavaTypeName ) { + bindBasicPluralElementRelationalValues( elementSource, elementBinding ); + bindHibernateTypeDescriptor( + elementBinding.getHibernateTypeDescriptor(), + elementSource.getExplicitHibernateTypeSource(), + defaultElementJavaTypeName + ); + Type resolvedElementType = heuristicType( elementBinding.getHibernateTypeDescriptor() ); + bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); + bindJdbcDataType( + resolvedElementType, + (AbstractValue) elementBinding.getRelationalValueBindings().get( 0 ).getValue() + ); + } + + private void bindBasicCollectionKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeSource attributeSource) { + if ( attributeSource.getElementSource().getNature() != org.hibernate.metamodel.spi.source.PluralAttributeElementNature.BASIC ) { + throw new AssertionFailure( + String.format( + "Expected basic attribute binding; instead got {%s}", + attributeSource.getElementSource().getNature() + ) + ); + } + attributeBinding.getPluralAttributeKeyBinding().setInverse( false ); + TableSpecification collectionTable = createCollectionTable( attributeBinding, attributeSource ); + if ( StringHelper.isNotEmpty( attributeSource.getCollectionTableComment() ) ) { + collectionTable.addComment( attributeSource.getCollectionTableComment() ); + } + if ( StringHelper.isNotEmpty( attributeSource.getCollectionTableCheck() ) ) { + collectionTable.addCheckConstraint( attributeSource.getCollectionTableCheck() ); + } + bindCollectionTableForeignKey( + attributeBinding, + attributeSource.getKeySource(), + collectionTable + ); + } + + private void bindBasicPluralElementRelationalValues( + final RelationalValueSourceContainer relationalValueSourceContainer, + final BasicPluralAttributeElementBinding elementBinding ) { + elementBinding.setRelationalValueBindings( bindValues( + elementBinding.getPluralAttributeBinding().getContainer(), + relationalValueSourceContainer, + elementBinding.getPluralAttributeBinding().getAttribute(), + elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().getCollectionTable() ) ); + } + + private void bindBasicSetElementTablePrimaryKey(final PluralAttributeBinding attributeBinding) { + final BasicPluralAttributeElementBinding elementBinding = + ( BasicPluralAttributeElementBinding ) attributeBinding.getPluralAttributeElementBinding(); + if ( elementBinding.getPluralAttributeElementNature() != PluralAttributeElementNature.BASIC ) { + throw new MappingException( String.format( + "Expected a SetBinding with an element of nature PluralAttributeElementNature.BASIC; instead was %s", + elementBinding.getPluralAttributeElementNature() ), bindingContexts.peek().getOrigin() ); + } + if ( hasAnyNonNullableColumns( elementBinding.getRelationalValueBindings() ) ) { + final PrimaryKey primaryKey = attributeBinding.getPluralAttributeKeyBinding().getCollectionTable().getPrimaryKey(); + final ForeignKey foreignKey = attributeBinding.getPluralAttributeKeyBinding().getForeignKey(); + for ( final Column foreignKeyColumn : foreignKey.getSourceColumns() ) { + primaryKey.addColumn( foreignKeyColumn ); + } + for ( final RelationalValueBinding elementValueBinding : elementBinding.getRelationalValueBindings() ) { + if ( elementValueBinding.getValue() instanceof Column && !elementValueBinding.isNullable() ) { + primaryKey.addColumn( ( Column ) elementValueBinding.getValue() ); + } + } + } + else { + // for backward compatibility, allow a set with no not-null + // element columns, using all columns in the row locater SQL + // todo: create an implicit not null constraint on all cols? + } + } + + private void bindCollectionIndex( + final IndexedPluralAttributeBinding attributeBinding, + final PluralAttributeIndexSource attributeSource, + final String defaultIndexJavaTypeName ) { + IndexedPluralAttributeBinding indexedAttributeBinding = attributeBinding; + final BasicPluralAttributeIndexBinding indexBinding = + ( BasicPluralAttributeIndexBinding ) indexedAttributeBinding.getPluralAttributeIndexBinding(); + indexBinding.setIndexRelationalValue( bindValues( + indexedAttributeBinding.getContainer(), + attributeSource, + indexedAttributeBinding.getAttribute(), + indexedAttributeBinding.getPluralAttributeKeyBinding().getCollectionTable() ).get( 0 ).getValue() ); + if ( attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() == PluralAttributeElementNature.ONE_TO_MANY ) { + if ( !Column.class.isInstance( indexBinding.getIndexRelationalValue() ) ) { + throw new NotYetImplementedException( "derived value as collection index is not supported yet." ); + } + // TODO: fix this when column nullability is refactored + ( (Column) indexBinding.getIndexRelationalValue() ).setNullable( true ); + } + + bindHibernateTypeDescriptor( + indexBinding.getHibernateTypeDescriptor(), + attributeSource.explicitHibernateTypeSource(), + defaultIndexJavaTypeName ); + Type resolvedElementType = heuristicType( indexBinding.getHibernateTypeDescriptor() ); + bindHibernateResolvedType( indexBinding.getHibernateTypeDescriptor(), resolvedElementType ); + bindJdbcDataType( resolvedElementType, (AbstractValue) indexBinding.getIndexRelationalValue() ); + } + + void bindCollectionTableForeignKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeKeySource keySource, + TableSpecification collectionTable) { + + final AttributeBindingContainer attributeBindingContainer = attributeBinding.getContainer(); + final PluralAttributeKeyBinding keyBinding = attributeBinding.getPluralAttributeKeyBinding(); + + List sourceColumnBindings = + bindValues( attributeBindingContainer, keySource, attributeBinding.getAttribute(), collectionTable ); + List targetColumns = + determineForeignKeyTargetColumns( + attributeBindingContainer.seekEntityBinding(), + keySource + ); + + final String foreignKeyName = + StringHelper.isEmpty( keySource.getExplicitForeignKeyName() ) + ? null + : quotedIdentifier( keySource.getExplicitForeignKeyName() ); + ForeignKey foreignKey = bindForeignKey( + foreignKeyName, + extractColumnsFromRelationalValueBindings( sourceColumnBindings ), + targetColumns + ); + foreignKey.setDeleteRule( keySource.getOnDeleteAction() ); + keyBinding.setForeignKey( foreignKey ); + + final HibernateTypeDescriptor pluralAttributeKeyTypeDescriptor = keyBinding.getHibernateTypeDescriptor(); + final HibernateTypeDescriptor referencedTypeDescriptor = + keyBinding.getReferencedAttributeBinding().getHibernateTypeDescriptor(); + pluralAttributeKeyTypeDescriptor.setExplicitTypeName( referencedTypeDescriptor.getExplicitTypeName() ); + pluralAttributeKeyTypeDescriptor.setJavaTypeName( referencedTypeDescriptor.getJavaTypeName() ); + // TODO: not sure about the following... + pluralAttributeKeyTypeDescriptor.setToOne( referencedTypeDescriptor.isToOne() ); + pluralAttributeKeyTypeDescriptor.getTypeParameters().putAll( referencedTypeDescriptor.getTypeParameters() ); + final Type resolvedKeyType = referencedTypeDescriptor.getResolvedTypeMapping(); + pluralAttributeKeyTypeDescriptor.setResolvedTypeMapping( resolvedKeyType ); + + Iterator< Column > fkColumnIterator = keyBinding.getForeignKey().getSourceColumns().iterator(); + if ( resolvedKeyType.isComponentType() ) { + ComponentType componentType = ( ComponentType ) resolvedKeyType; + for ( Type subType : componentType.getSubtypes() ) { + bindJdbcDataType( subType, fkColumnIterator.next() ); + } + } else { + bindJdbcDataType( resolvedKeyType, fkColumnIterator.next() ); + } + } + + private void bindCollectionTablePrimaryKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeSource attributeSource, + final HibernateTypeHelper.ReflectedCollectionJavaTypes reflectedCollectionJavaTypes ) { + PluralAttributeNature pluralAttributeNature = attributeSource.getPluralAttributeNature(); + if ( attributeSource.getElementSource().getNature() == org.hibernate.metamodel.spi.source.PluralAttributeElementNature.ONE_TO_MANY + || pluralAttributeNature == PluralAttributeNature.BAG ) { + return; + } + if ( attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() == PluralAttributeElementNature.BASIC ) { + if ( pluralAttributeNature == PluralAttributeNature.SET ) { + bindBasicSetElementTablePrimaryKey( attributeBinding ); + } else if ( pluralAttributeNature == PluralAttributeNature.LIST || pluralAttributeNature == PluralAttributeNature.MAP ) { + bindIndexedTablePrimaryKey( ( IndexedPluralAttributeBinding ) attributeBinding ); + } else { + throw new NotYetImplementedException( "Only Sets with basic elements are supported so far." ); + } + } + } + private CompositeAttributeBinding bindComponentAttribute( final AttributeBindingContainer attributeBindingContainer, final ComponentAttributeSource attributeSource, @@ -382,6 +637,363 @@ public class Binder { return attributeBinding; } + private void bindDiscriminator( final EntityBinding rootEntityBinding, final RootEntitySource rootEntitySource ) { + final DiscriminatorSource discriminatorSource = rootEntitySource.getDiscriminatorSource(); + if ( discriminatorSource == null ) { + return; + } + final RelationalValueSource valueSource = discriminatorSource.getDiscriminatorRelationalValueSource(); + final TableSpecification table = rootEntityBinding.locateTable( valueSource.getContainingTableName() ); + AbstractValue value; + if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { + value = + createColumn( + table, + ( ColumnSource ) valueSource, + bindingContexts.peek().getMappingDefaults().getDiscriminatorColumnName(), + false, + false, + false ); + } else { + value = table.locateOrCreateDerivedValue( ( ( DerivedValueSource ) valueSource ).getExpression() ); + } + final EntityDiscriminator discriminator = + new EntityDiscriminator( value, discriminatorSource.isInserted(), discriminatorSource.isForced() ); + rootEntityBinding.getHierarchyDetails().setEntityDiscriminator( discriminator ); + rootEntityBinding.setDiscriminatorMatchValue( rootEntitySource.getDiscriminatorMatchValue() ); + // Configure discriminator hibernate type + final String typeName = + discriminatorSource.getExplicitHibernateTypeName() != null + ? discriminatorSource.getExplicitHibernateTypeName() + : "string"; + final HibernateTypeDescriptor hibernateTypeDescriptor = discriminator.getExplicitHibernateTypeDescriptor(); + hibernateTypeDescriptor.setExplicitTypeName( typeName ); + Type resolvedType = heuristicType( hibernateTypeDescriptor ); + bindHibernateResolvedType( hibernateTypeDescriptor, resolvedType ); + bindJdbcDataType( resolvedType, value ); + } + + private EntityBinding bindEntities( final EntityHierarchy entityHierarchy ) { + final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource(); + // Return existing binding if available + EntityBinding rootEntityBinding = metadata.getEntityBinding( rootEntitySource.getEntityName() ); + if ( rootEntityBinding != null ) { + return rootEntityBinding; + } + // Save inheritance type and entity mode that will apply to entire hierarchy + inheritanceTypes.push( entityHierarchy.getHierarchyInheritanceType() ); + entityModes.push( rootEntitySource.getEntityMode() ); + final LocalBindingContext bindingContext = rootEntitySource.getLocalBindingContext(); + bindingContexts.push( bindingContext ); + try { + // Create root entity binding + rootEntityBinding = createEntityBinding( rootEntitySource, null ); + // Create/Bind root-specific information + bindIdentifier( rootEntityBinding, rootEntitySource.getIdentifierSource() ); + bindSecondaryTables( rootEntityBinding, rootEntitySource ); + bindUniqueConstraints( rootEntityBinding, rootEntitySource ); + bindVersion( rootEntityBinding, rootEntitySource.getVersioningAttributeSource() ); + bindDiscriminator( rootEntityBinding, rootEntitySource ); + bindIdentifierGenerator( rootEntityBinding ); + bindMultiTenancy( rootEntityBinding, rootEntitySource ); + rootEntityBinding.getHierarchyDetails().setCaching( rootEntitySource.getCaching() ); + rootEntityBinding.getHierarchyDetails().setNaturalIdCaching( rootEntitySource.getNaturalIdCaching() ); + rootEntityBinding.getHierarchyDetails().setExplicitPolymorphism( rootEntitySource.isExplicitPolymorphism() ); + rootEntityBinding.getHierarchyDetails().setOptimisticLockStyle( rootEntitySource.getOptimisticLockStyle() ); + rootEntityBinding.setMutable( rootEntitySource.isMutable() ); + rootEntityBinding.setWhereFilter( rootEntitySource.getWhere() ); + rootEntityBinding.setRowId( rootEntitySource.getRowId() ); + // Bind attributes and sub-entities to root entity + bindAttributes( rootEntityBinding, rootEntitySource ); + if ( inheritanceTypes.peek() != InheritanceType.NO_INHERITANCE ) { + bindSubEntities( rootEntityBinding, rootEntitySource ); + } + } finally { + bindingContexts.pop(); + inheritanceTypes.pop(); + entityModes.pop(); + } + return rootEntityBinding; + } + + public void bindEntities( final Iterable< EntityHierarchy > entityHierarchies ) { + entitySourcesByName.clear(); + attributeSourcesByName.clear(); + inheritanceTypes.clear(); + entityModes.clear(); + bindingContexts.clear(); + // Index sources by name so we can find and resolve entities on the fly as references to them + // are encountered (e.g., within associations) + for ( final EntityHierarchy entityHierarchy : entityHierarchies ) { + entityHierarchiesByRootEntitySource.put( entityHierarchy.getRootEntitySource(), entityHierarchy ); + mapSourcesByName( entityHierarchy.getRootEntitySource() ); + } + // Bind each entity hierarchy + for ( final EntityHierarchy entityHierarchy : entityHierarchies ) { + bindEntities( entityHierarchy ); + } + } + + private EntityBinding bindEntity( final EntitySource entitySource, final EntityBinding superEntityBinding ) { + // Return existing binding if available + EntityBinding entityBinding = metadata.getEntityBinding( entitySource.getEntityName() ); + if ( entityBinding != null ) { + return entityBinding; + } + final LocalBindingContext bindingContext = entitySource.getLocalBindingContext(); + bindingContexts.push( bindingContext ); + try { + // Create new entity binding + entityBinding = createEntityBinding( entitySource, superEntityBinding ); + bindSecondaryTables( entityBinding, entitySource ); + bindUniqueConstraints( entityBinding, entitySource ); + bindAttributes( entityBinding, entitySource ); + bindSubEntities( entityBinding, entitySource ); + return entityBinding; + } finally { + bindingContexts.pop(); + } + } + + private ForeignKey bindForeignKey( + String foreignKeyName, + List sourceColumns, + List targetColumns) { + ForeignKey foreignKey = null; + if ( foreignKeyName != null ) { + foreignKey = locateAndBindForeignKeyByName( foreignKeyName, sourceColumns, targetColumns ); + } + if ( foreignKey == null ) { + foreignKey = locateForeignKeyByColumnMapping( sourceColumns, targetColumns ); + if ( foreignKey != null && foreignKeyName != null ) { + if ( foreignKey.getName() == null ) { + // the foreign key name has not be initialized; set it to foreignKeyName + foreignKey.setName( foreignKeyName ); + } + else { + // the foreign key name has already been initialized so cannot rename it + // TODO: should this just be INFO? + log.warn( + String.format( + "A foreign key mapped as %s will not be created because foreign key %s already exists with the same column mapping.", + foreignKeyName, + foreignKey.getName()) + ); + } + } + } + if ( foreignKey == null ) { + // no foreign key found; create one + final TableSpecification sourceTable = sourceColumns.get( 0 ).getTable(); + final TableSpecification targetTable = targetColumns.get( 0 ).getTable(); + foreignKey = sourceTable.createForeignKey( targetTable, foreignKeyName ); + bindForeignKeyColumns( foreignKey, sourceColumns, targetColumns ); + } + return foreignKey; + } + + private void bindForeignKeyColumns( + ForeignKey foreignKey, + List sourceColumns, + List targetColumns) { + if ( sourceColumns.size() != targetColumns.size() ) { + throw bindingContext().makeMappingException( + String.format( + "Non-matching number columns in foreign key source columns [%s : %s] and target columns [%s : %s]", + sourceColumns.get( 0 ).getTable().getLogicalName().getName(), + sourceColumns.size(), + targetColumns.get( 0 ).getTable().getLogicalName().getName(), + targetColumns.size() + ) + ); + } + for ( int i = 0; i < sourceColumns.size(); i++ ) { + foreignKey.addColumnMapping( sourceColumns.get( i ), targetColumns.get( i ) ); + } + } + + private void bindHibernateResolvedType( final HibernateTypeDescriptor hibernateTypeDescriptor, final Type resolvedType ) { + // Configure relational value JDBC type from Hibernate type descriptor now that its configured + if ( resolvedType != null ) { + hibernateTypeDescriptor.setResolvedTypeMapping( resolvedType ); + if ( hibernateTypeDescriptor.getJavaTypeName() == null ) { + hibernateTypeDescriptor.setJavaTypeName( resolvedType.getReturnedClass().getName() ); + } + hibernateTypeDescriptor.setToOne( resolvedType.isEntityType() ); + } + } + + private void bindHibernateTypeDescriptor( + final HibernateTypeDescriptor hibernateTypeDescriptor, + final ExplicitHibernateTypeSource explicitTypeSource, + final org.hibernate.internal.util.ValueHolder< Class< ? >> defaultJavaType ) { + // if there is an explicit type name specified, then there's no reason to + // initialize the default Java type name; simply pass a null default instead. + bindHibernateTypeDescriptor( + hibernateTypeDescriptor, + explicitTypeSource, + explicitTypeSource == null || explicitTypeSource.getName() == null + ? defaultJavaType.getValue().getName() + : null + ); + } + + private void bindHibernateTypeDescriptor( + final HibernateTypeDescriptor hibernateTypeDescriptor, + final ExplicitHibernateTypeSource explicitTypeSource, + final String defaultJavaTypeName ) { + if ( explicitTypeSource == null ) { + bindHibernateTypeDescriptor( + hibernateTypeDescriptor, null, null, defaultJavaTypeName + ); + } + else { + bindHibernateTypeDescriptor( + hibernateTypeDescriptor, + explicitTypeSource.getName(), + explicitTypeSource.getParameters(), + defaultJavaTypeName + ); + } + } + + private void bindHibernateTypeDescriptor( + final HibernateTypeDescriptor hibernateTypeDescriptor, + final String explicitTypeName, + final Map explictTypeParameters, + final String defaultJavaTypeName ) { + if ( explicitTypeName == null ) { + if ( hibernateTypeDescriptor.getJavaTypeName() != null ) { + throw new MappingException( String.format( + "Attempt to re-initialize (non-explicit) Java type name; current=%s new=%s", + hibernateTypeDescriptor.getJavaTypeName(), + defaultJavaTypeName ), bindingContexts.peek().getOrigin() ); + } + hibernateTypeDescriptor.setJavaTypeName( defaultJavaTypeName ); + } else { + // Check if user-specified name is of a User-Defined Type (UDT) + final TypeDefinition typeDef = metadata.getTypeDefinition( explicitTypeName ); + if ( hibernateTypeDescriptor.getExplicitTypeName() != null ) { + throw new MappingException( String.format( + "Attempt to re-initialize explicity-mapped Java type name; current=%s new=%s", + hibernateTypeDescriptor.getExplicitTypeName(), + explicitTypeName ), bindingContexts.peek().getOrigin() ); + } + if ( typeDef == null ) { + hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName ); + } else { + hibernateTypeDescriptor.setExplicitTypeName( typeDef.getTypeImplementorClass().getName() ); + hibernateTypeDescriptor.setTypeParameters( typeDef.getParameters() ); + } + if ( explictTypeParameters != null ) { + hibernateTypeDescriptor.getTypeParameters().putAll( explictTypeParameters ); + } + } + } + + private void bindIdentifier( final EntityBinding rootEntityBinding, final IdentifierSource identifierSource ) { + final EntityIdentifierNature nature = identifierSource.getNature(); + switch ( nature ) { + case SIMPLE: { + bindSimpleIdentifier( rootEntityBinding, ( SimpleIdentifierSource ) identifierSource ); + break; + } + case AGGREGATED_COMPOSITE: { + bindAggregatedCompositeIdentifier( rootEntityBinding, ( AggregatedCompositeIdentifierSource ) identifierSource ); + break; + } + case COMPOSITE: { + bindNonAggregatedCompositeIdentifier( + rootEntityBinding, + ( NonAggregatedCompositeIdentifierSource ) identifierSource ); + break; + } + default: { + throw bindingContext().makeMappingException( "Unknown identifier nature : " + nature.name() ); + } + } + } + + private SingularAttributeBinding bindIdentifierAttribute( + final AttributeBindingContainer attributeBindingContainer, + final SingularAttributeSource attributeSource) { + return bindSingularAttribute( attributeBindingContainer, attributeSource, true ); + } + + private void bindIdentifierGenerator(final EntityBinding rootEntityBinding) { + final Properties properties = new Properties(); + properties.putAll( metadata.getServiceRegistry().getService( ConfigurationService.class ).getSettings() ); + if ( !properties.contains( AvailableSettings.PREFER_POOLED_VALUES_LO ) ) { + properties.put( AvailableSettings.PREFER_POOLED_VALUES_LO, "false" ); + } + if ( !properties.contains( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER ) ) { + properties.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, nameNormalizer ); + } + final EntityIdentifier entityIdentifier = rootEntityBinding.getHierarchyDetails().getEntityIdentifier(); + entityIdentifier.createIdentifierGenerator( identifierGeneratorFactory, properties ); + if ( IdentityGenerator.class.isInstance( entityIdentifier.getIdentifierGenerator() ) ) { + if ( rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumnSpan() != 1 ) { + throw new MappingException( + String.format( + "ID for %s is mapped as an identity with %d columns. IDs mapped as an identity can only have 1 column.", + rootEntityBinding.getEntity().getName(), + rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumnSpan() + ), + bindingContexts.peek().getOrigin() + ); + } + rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumns().get( 0 ).setIdentity( true ); + } + } + + private void bindIndexedTablePrimaryKey( IndexedPluralAttributeBinding attributeBinding ) { + final PrimaryKey primaryKey = attributeBinding.getPluralAttributeKeyBinding().getCollectionTable().getPrimaryKey(); + final ForeignKey foreignKey = attributeBinding.getPluralAttributeKeyBinding().getForeignKey(); + final PluralAttributeIndexBinding indexBinding = attributeBinding.getPluralAttributeIndexBinding(); + for ( final Column foreignKeyColumn : foreignKey.getSourceColumns() ) { + primaryKey.addColumn( foreignKeyColumn ); + } + final Value value = indexBinding.getIndexRelationalValue(); + if ( value instanceof Column ) { + primaryKey.addColumn( ( Column ) value ); + } + } + + LocalBindingContext bindingContext() { + return bindingContexts.peek(); + } + + private void bindJdbcDataType( final Type resolvedType, final AbstractValue value ) { + if ( resolvedType != null && value != null ) { + final Type resolvedRelationalType = + resolvedType.isEntityType() + ? EntityType.class.cast( resolvedType ).getIdentifierOrUniqueKeyType( metadata ) + : resolvedType; + value.setJdbcDataType( new JdbcDataType( + resolvedRelationalType.sqlTypes( metadata )[ 0 ], + resolvedRelationalType.getName(), + resolvedRelationalType.getReturnedClass() ) ); + } + } + + private AbstractPluralAttributeBinding bindListAttribute( + final AttributeBindingContainer attributeBindingContainer, + final ListAttributeSource attributeSource, + PluralAttribute attribute ) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createList( attributeSource.getName() ); + } + return attributeBindingContainer.makeListAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ), + attributeSource.getIndexSource().base() + ); + } + private ManyToOneAttributeBinding bindManyToOneAttribute( final AttributeBindingContainer attributeBindingContainer, final ToOneAttributeSource attributeSource, @@ -476,7 +1088,136 @@ public class Binder { return attributeBinding; } - // Plural attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + private AbstractPluralAttributeBinding bindMapAttribute( + final AttributeBindingContainer attributeBindingContainer, + final MapAttributeSource attributeSource, + PluralAttribute attribute ) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createMap( attributeSource.getName() ); + } + return attributeBindingContainer.makeMapAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + pluralAttributeIndexNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + + createMetaAttributeContext( attributeBindingContainer, attributeSource ) + ); + } + + private void bindMultiTenancy(EntityBinding rootEntityBinding, RootEntitySource rootEntitySource) { + final MultiTenancySource multiTenancySource = rootEntitySource.getMultiTenancySource(); + if ( multiTenancySource == null ) { + return; + } + + // if (1) the strategy is discriminator based and (2) the entity is not shared, we need to either (a) extract + // the user supplied tenant discriminator value mapping or (b) generate an implicit one + final boolean needsTenantIdentifierValueMapping = + MultiTenancyStrategy.DISCRIMINATOR == metadata.getOptions().getMultiTenancyStrategy() + && ! multiTenancySource.isShared(); + + if ( needsTenantIdentifierValueMapping ) { + // NOTE : the table for tenant identifier/discriminator is always the primary table + final Value tenantDiscriminatorValue; + final RelationalValueSource valueSource = multiTenancySource.getRelationalValueSource(); + if ( valueSource == null ) { + // user supplied no explicit information, so use implicit mapping with default name + tenantDiscriminatorValue = rootEntityBinding.getPrimaryTable().locateOrCreateColumn( "tenant_id" ); + } + else { + tenantDiscriminatorValue = buildRelationValue( valueSource, rootEntityBinding.getPrimaryTable() ); + } + rootEntityBinding.getHierarchyDetails().getTenantDiscrimination().setDiscriminatorValue( tenantDiscriminatorValue ); + } + + rootEntityBinding.getHierarchyDetails().getTenantDiscrimination().setShared( multiTenancySource.isShared() ); + rootEntityBinding.getHierarchyDetails().getTenantDiscrimination().setUseParameterBinding( multiTenancySource.bindAsParameter() ); + } + + private void bindNonAggregatedCompositeIdentifier( + EntityBinding rootEntityBinding, + NonAggregatedCompositeIdentifierSource identifierSource ) { + // locate the attribute bindings for the real attributes + List< SingularAttributeBinding > idAttributeBindings = new ArrayList< SingularAttributeBinding >(); + for ( SingularAttributeSource attributeSource : identifierSource.getAttributeSourcesMakingUpIdentifier() ) { + idAttributeBindings.add( bindIdentifierAttribute( rootEntityBinding, attributeSource ) ); + } + + // Create the synthetic attribute + SingularAttribute syntheticAttribute = + rootEntityBinding.getEntity().createSyntheticCompositeAttribute( + SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME, + rootEntityBinding.getEntity() ); + + // Create the synthetic attribute binding. + final CompositeAttributeBinding syntheticAttributeBinding = + rootEntityBinding.makeVirtualComponentAttributeBinding( + syntheticAttribute, + idAttributeBindings, + createMetaAttributeContext( rootEntityBinding, identifierSource.getMetaAttributeSources() ) ); + + rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsNonAggregatedCompositeIdentifier( + syntheticAttributeBinding, + identifierSource.getLookupIdClass() ); + } + + private void bindOneToManyCollectionElement( + final OneToManyPluralAttributeElementBinding elementBinding, + final OneToManyPluralAttributeElementSource elementSource, + final EntityBinding referencedEntityBinding, + final String defaultElementJavaTypeName) { + + //referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding().addEntityReferencingAttributeBinding( ); + + elementBinding.setRelationalValueBindings( + referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding().getRelationalValueBindings() + ); + final HibernateTypeDescriptor hibernateTypeDescriptor = elementBinding.getHibernateTypeDescriptor(); + bindHibernateTypeDescriptor( + hibernateTypeDescriptor, + referencedEntityBinding.getEntity().getName(), + null, + defaultElementJavaTypeName + ); + + Type resolvedElementType = metadata.getTypeResolver().getTypeFactory().manyToOne( + referencedEntityBinding.getEntity().getName(), + null, + false, + false, + true, + false, //TODO: should be attributeBinding.isIgnoreNotFound(), + false + ); + bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); + // no need to bind JDBC data types because element is referenced EntityBinding's ID + elementBinding.setCascadeStyles( elementSource.getCascadeStyles() ); + } + + private void bindOneToManyCollectionKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeSource attributeSource, + final EntityBinding referencedEntityBinding) { + if ( attributeSource.getElementSource().getNature() != org.hibernate.metamodel.spi.source.PluralAttributeElementNature.ONE_TO_MANY ) { + throw new AssertionFailure( + String.format( + "Expected one-to-many attribute binding; instead got {%s}", + attributeSource.getElementSource().getNature() + ) + ); + } + if ( attributeSource.getCollectionTableSpecificationSource() != null ) { + // TODO: Need to look up the table to be able to create the foreign key + throw new NotYetImplementedException( "one-to-many using a join table is not supported yet." ); + } + TableSpecification collectionTable = referencedEntityBinding.getPrimaryTable(); + bindCollectionTableForeignKey( attributeBinding, attributeSource.getKeySource(), collectionTable ); + + attributeBinding.getPluralAttributeKeyBinding().setInverse( attributeSource.isInverse() ); + } private AbstractPluralAttributeBinding bindPluralAttribute( final AttributeBindingContainer attributeBindingContainer, @@ -589,862 +1330,6 @@ public class Binder { return attributeBinding; } - private AbstractPluralAttributeBinding bindBagAttribute( - final AttributeBindingContainer attributeBindingContainer, - final PluralAttributeSource attributeSource, - PluralAttribute attribute ) { - if ( attribute == null ) { - attribute = attributeBindingContainer.getAttributeContainer().createBag( attributeSource.getName() ); - } - return attributeBindingContainer.makeBagAttributeBinding( - attribute, - pluralAttributeElementNature( attributeSource ), - determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - createMetaAttributeContext( attributeBindingContainer, attributeSource ) - ); - } - - private AbstractPluralAttributeBinding bindSetAttribute( - final AttributeBindingContainer attributeBindingContainer, - final PluralAttributeSource attributeSource, - PluralAttribute attribute ) { - if ( attribute == null ) { - attribute = attributeBindingContainer.getAttributeContainer().createSet( attributeSource.getName() ); - } - return attributeBindingContainer.makeSetAttributeBinding( - attribute, - pluralAttributeElementNature( attributeSource ), - determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - createMetaAttributeContext( attributeBindingContainer, attributeSource ) - ); - } - - private AbstractPluralAttributeBinding bindListAttribute( - final AttributeBindingContainer attributeBindingContainer, - final ListAttributeSource attributeSource, - PluralAttribute attribute ) { - if ( attribute == null ) { - attribute = attributeBindingContainer.getAttributeContainer().createList( attributeSource.getName() ); - } - return attributeBindingContainer.makeListAttributeBinding( - attribute, - pluralAttributeElementNature( attributeSource ), - determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - createMetaAttributeContext( attributeBindingContainer, attributeSource ), - attributeSource.getIndexSource().base() - ); - } - - private AbstractPluralAttributeBinding bindMapAttribute( - final AttributeBindingContainer attributeBindingContainer, - final MapAttributeSource attributeSource, - PluralAttribute attribute ) { - if ( attribute == null ) { - attribute = attributeBindingContainer.getAttributeContainer().createMap( attributeSource.getName() ); - } - return attributeBindingContainer.makeMapAttributeBinding( - attribute, - pluralAttributeElementNature( attributeSource ), - pluralAttributeIndexNature( attributeSource ), - determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - - createMetaAttributeContext( attributeBindingContainer, attributeSource ) - ); - } - - private void bindBasicCollectionElement( - final BasicPluralAttributeElementBinding elementBinding, - final BasicPluralAttributeElementSource elementSource, - final String defaultElementJavaTypeName ) { - bindBasicPluralElementRelationalValues( elementSource, elementBinding ); - bindHibernateTypeDescriptor( - elementBinding.getHibernateTypeDescriptor(), - elementSource.getExplicitHibernateTypeSource(), - defaultElementJavaTypeName - ); - Type resolvedElementType = heuristicType( elementBinding.getHibernateTypeDescriptor() ); - bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); - bindJdbcDataType( - resolvedElementType, - (AbstractValue) elementBinding.getRelationalValueBindings().get( 0 ).getValue() - ); - } - - private void bindOneToManyCollectionElement( - final OneToManyPluralAttributeElementBinding elementBinding, - final OneToManyPluralAttributeElementSource elementSource, - final EntityBinding referencedEntityBinding, - final String defaultElementJavaTypeName) { - - //referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding().addEntityReferencingAttributeBinding( ); - - elementBinding.setRelationalValueBindings( - referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding().getRelationalValueBindings() - ); - final HibernateTypeDescriptor hibernateTypeDescriptor = elementBinding.getHibernateTypeDescriptor(); - bindHibernateTypeDescriptor( - hibernateTypeDescriptor, - referencedEntityBinding.getEntity().getName(), - null, - defaultElementJavaTypeName - ); - - Type resolvedElementType = metadata.getTypeResolver().getTypeFactory().manyToOne( - referencedEntityBinding.getEntity().getName(), - null, - false, - false, - true, - false, //TODO: should be attributeBinding.isIgnoreNotFound(), - false - ); - bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); - // no need to bind JDBC data types because element is referenced EntityBinding's ID - elementBinding.setCascadeStyles( elementSource.getCascadeStyles() ); - } - - private void bindBasicSetElementTablePrimaryKey(final PluralAttributeBinding attributeBinding) { - final BasicPluralAttributeElementBinding elementBinding = - ( BasicPluralAttributeElementBinding ) attributeBinding.getPluralAttributeElementBinding(); - if ( elementBinding.getPluralAttributeElementNature() != PluralAttributeElementNature.BASIC ) { - throw new MappingException( String.format( - "Expected a SetBinding with an element of nature PluralAttributeElementNature.BASIC; instead was %s", - elementBinding.getPluralAttributeElementNature() ), bindingContexts.peek().getOrigin() ); - } - if ( hasAnyNonNullableColumns( elementBinding.getRelationalValueBindings() ) ) { - final PrimaryKey primaryKey = attributeBinding.getPluralAttributeKeyBinding().getCollectionTable().getPrimaryKey(); - final ForeignKey foreignKey = attributeBinding.getPluralAttributeKeyBinding().getForeignKey(); - for ( final Column foreignKeyColumn : foreignKey.getSourceColumns() ) { - primaryKey.addColumn( foreignKeyColumn ); - } - for ( final RelationalValueBinding elementValueBinding : elementBinding.getRelationalValueBindings() ) { - if ( elementValueBinding.getValue() instanceof Column && !elementValueBinding.isNullable() ) { - primaryKey.addColumn( ( Column ) elementValueBinding.getValue() ); - } - } - } - else { - // for backward compatibility, allow a set with no not-null - // element columns, using all columns in the row locater SQL - // todo: create an implicit not null constraint on all cols? - } - } - - private boolean hasAnyNonNullableColumns(List relationalValueBindings) { - for ( RelationalValueBinding relationalValueBinding : relationalValueBindings ) { - if ( Column.class.isInstance( relationalValueBinding.getValue() ) && !relationalValueBinding.isNullable() ) { - return true; - } - } - return false; - } - - private void bindBasicPluralElementRelationalValues( - final RelationalValueSourceContainer relationalValueSourceContainer, - final BasicPluralAttributeElementBinding elementBinding ) { - elementBinding.setRelationalValueBindings( bindValues( - elementBinding.getPluralAttributeBinding().getContainer(), - relationalValueSourceContainer, - elementBinding.getPluralAttributeBinding().getAttribute(), - elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().getCollectionTable() ) ); - } - - private void bindCollectionIndex( - final IndexedPluralAttributeBinding attributeBinding, - final PluralAttributeIndexSource attributeSource, - final String defaultIndexJavaTypeName ) { - IndexedPluralAttributeBinding indexedAttributeBinding = attributeBinding; - final BasicPluralAttributeIndexBinding indexBinding = - ( BasicPluralAttributeIndexBinding ) indexedAttributeBinding.getPluralAttributeIndexBinding(); - indexBinding.setIndexRelationalValue( bindValues( - indexedAttributeBinding.getContainer(), - attributeSource, - indexedAttributeBinding.getAttribute(), - indexedAttributeBinding.getPluralAttributeKeyBinding().getCollectionTable() ).get( 0 ).getValue() ); - if ( attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() == PluralAttributeElementNature.ONE_TO_MANY ) { - if ( !Column.class.isInstance( indexBinding.getIndexRelationalValue() ) ) { - throw new NotYetImplementedException( "derived value as collection index is not supported yet." ); - } - // TODO: fix this when column nullability is refactored - ( (Column) indexBinding.getIndexRelationalValue() ).setNullable( true ); - } - - bindHibernateTypeDescriptor( - indexBinding.getHibernateTypeDescriptor(), - attributeSource.explicitHibernateTypeSource(), - defaultIndexJavaTypeName ); - Type resolvedElementType = heuristicType( indexBinding.getHibernateTypeDescriptor() ); - bindHibernateResolvedType( indexBinding.getHibernateTypeDescriptor(), resolvedElementType ); - bindJdbcDataType( resolvedElementType, (AbstractValue) indexBinding.getIndexRelationalValue() ); - } - - private void bindBasicCollectionKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeSource attributeSource) { - if ( attributeSource.getElementSource().getNature() != org.hibernate.metamodel.spi.source.PluralAttributeElementNature.BASIC ) { - throw new AssertionFailure( - String.format( - "Expected basic attribute binding; instead got {%s}", - attributeSource.getElementSource().getNature() - ) - ); - } - attributeBinding.getPluralAttributeKeyBinding().setInverse( false ); - TableSpecification collectionTable = createCollectionTable( attributeBinding, attributeSource ); - if ( StringHelper.isNotEmpty( attributeSource.getCollectionTableComment() ) ) { - collectionTable.addComment( attributeSource.getCollectionTableComment() ); - } - if ( StringHelper.isNotEmpty( attributeSource.getCollectionTableCheck() ) ) { - collectionTable.addCheckConstraint( attributeSource.getCollectionTableCheck() ); - } - bindCollectionTableForeignKey( - attributeBinding, - attributeSource.getKeySource(), - collectionTable - ); - } - - private void bindOneToManyCollectionKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeSource attributeSource, - final EntityBinding referencedEntityBinding) { - if ( attributeSource.getElementSource().getNature() != org.hibernate.metamodel.spi.source.PluralAttributeElementNature.ONE_TO_MANY ) { - throw new AssertionFailure( - String.format( - "Expected one-to-many attribute binding; instead got {%s}", - attributeSource.getElementSource().getNature() - ) - ); - } - if ( attributeSource.getCollectionTableSpecificationSource() != null ) { - // TODO: Need to look up the table to be able to create the foreign key - throw new NotYetImplementedException( "one-to-many using a join table is not supported yet." ); - } - TableSpecification collectionTable = referencedEntityBinding.getPrimaryTable(); - bindCollectionTableForeignKey( attributeBinding, attributeSource.getKeySource(), collectionTable ); - - attributeBinding.getPluralAttributeKeyBinding().setInverse( attributeSource.isInverse() ); - if ( !attributeSource.isInverse() && !attributeBinding.getPluralAttributeKeyBinding().isNullable() ) { - // Create the synthetic attribute - SingularAttribute syntheticAttribute = referencedEntityBinding.getEntity().createSyntheticSingularAttribute( - SyntheticAttributeHelper.createBackRefAttributeName( attributeBinding.getAttribute().getRole() ) - ); - // Create the backref attribute binding. - BackRefAttributeBinding backRefAttributeBinding = - referencedEntityBinding.makeBackRefAttributeBinding( syntheticAttribute, attributeBinding ); - final HibernateTypeDescriptor keyTypeDescriptor = attributeBinding.getPluralAttributeKeyBinding().getHibernateTypeDescriptor(); - final HibernateTypeDescriptor hibernateTypeDescriptor = backRefAttributeBinding.getHibernateTypeDescriptor(); - hibernateTypeDescriptor.setJavaTypeName( keyTypeDescriptor.getJavaTypeName() ); - hibernateTypeDescriptor.setExplicitTypeName( keyTypeDescriptor.getExplicitTypeName() ); - hibernateTypeDescriptor.setToOne( keyTypeDescriptor.isToOne() ); - hibernateTypeDescriptor.getTypeParameters().putAll( keyTypeDescriptor.getTypeParameters() ); - hibernateTypeDescriptor.setResolvedTypeMapping( keyTypeDescriptor.getResolvedTypeMapping() ); - backRefAttributeBinding.getAttribute().resolveType( - attributeBinding.getPluralAttributeKeyBinding().getReferencedAttributeBinding().getAttribute().getSingularAttributeType() - ); - } - } - - void bindCollectionTableForeignKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeKeySource keySource, - TableSpecification collectionTable) { - - final AttributeBindingContainer attributeBindingContainer = attributeBinding.getContainer(); - final PluralAttributeKeyBinding keyBinding = attributeBinding.getPluralAttributeKeyBinding(); - - List sourceColumnBindings = - bindValues( attributeBindingContainer, keySource, attributeBinding.getAttribute(), collectionTable ); - List targetColumns = - determineForeignKeyTargetColumns( - attributeBindingContainer.seekEntityBinding(), - keySource - ); - - final String foreignKeyName = - StringHelper.isEmpty( keySource.getExplicitForeignKeyName() ) - ? null - : quotedIdentifier( keySource.getExplicitForeignKeyName() ); - ForeignKey foreignKey = bindForeignKey( - foreignKeyName, - extractColumnsFromRelationalValueBindings( sourceColumnBindings ), - targetColumns - ); - foreignKey.setDeleteRule( keySource.getOnDeleteAction() ); - keyBinding.setForeignKey( foreignKey ); - - final HibernateTypeDescriptor pluralAttributeKeyTypeDescriptor = keyBinding.getHibernateTypeDescriptor(); - final HibernateTypeDescriptor referencedTypeDescriptor = - keyBinding.getReferencedAttributeBinding().getHibernateTypeDescriptor(); - pluralAttributeKeyTypeDescriptor.setExplicitTypeName( referencedTypeDescriptor.getExplicitTypeName() ); - pluralAttributeKeyTypeDescriptor.setJavaTypeName( referencedTypeDescriptor.getJavaTypeName() ); - // TODO: not sure about the following... - pluralAttributeKeyTypeDescriptor.setToOne( referencedTypeDescriptor.isToOne() ); - pluralAttributeKeyTypeDescriptor.getTypeParameters().putAll( referencedTypeDescriptor.getTypeParameters() ); - final Type resolvedKeyType = referencedTypeDescriptor.getResolvedTypeMapping(); - pluralAttributeKeyTypeDescriptor.setResolvedTypeMapping( resolvedKeyType ); - - Iterator< Column > fkColumnIterator = keyBinding.getForeignKey().getSourceColumns().iterator(); - if ( resolvedKeyType.isComponentType() ) { - ComponentType componentType = ( ComponentType ) resolvedKeyType; - for ( Type subType : componentType.getSubtypes() ) { - bindJdbcDataType( subType, fkColumnIterator.next() ); - } - } else { - bindJdbcDataType( resolvedKeyType, fkColumnIterator.next() ); - } - } - - private void bindCollectionTablePrimaryKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeSource attributeSource, - final HibernateTypeHelper.ReflectedCollectionJavaTypes reflectedCollectionJavaTypes ) { - PluralAttributeNature pluralAttributeNature = attributeSource.getPluralAttributeNature(); - if ( attributeSource.getElementSource().getNature() == org.hibernate.metamodel.spi.source.PluralAttributeElementNature.ONE_TO_MANY - || pluralAttributeNature == PluralAttributeNature.BAG ) { - return; - } - if ( attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() == PluralAttributeElementNature.BASIC ) { - if ( pluralAttributeNature == PluralAttributeNature.SET ) { - bindBasicSetElementTablePrimaryKey( attributeBinding ); - } else if ( pluralAttributeNature == PluralAttributeNature.LIST || pluralAttributeNature == PluralAttributeNature.MAP ) { - bindIndexedTablePrimaryKey( ( IndexedPluralAttributeBinding ) attributeBinding ); - } else { - throw new NotYetImplementedException( "Only Sets with basic elements are supported so far." ); - } - } - } - - private void bindDiscriminator( final EntityBinding rootEntityBinding, final RootEntitySource rootEntitySource ) { - final DiscriminatorSource discriminatorSource = rootEntitySource.getDiscriminatorSource(); - if ( discriminatorSource == null ) { - return; - } - final RelationalValueSource valueSource = discriminatorSource.getDiscriminatorRelationalValueSource(); - final TableSpecification table = rootEntityBinding.locateTable( valueSource.getContainingTableName() ); - AbstractValue value; - if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { - value = - createColumn( - table, - ( ColumnSource ) valueSource, - bindingContexts.peek().getMappingDefaults().getDiscriminatorColumnName(), - false, - false, - false ); - } else { - value = table.locateOrCreateDerivedValue( ( ( DerivedValueSource ) valueSource ).getExpression() ); - } - final EntityDiscriminator discriminator = - new EntityDiscriminator( value, discriminatorSource.isInserted(), discriminatorSource.isForced() ); - rootEntityBinding.getHierarchyDetails().setEntityDiscriminator( discriminator ); - rootEntityBinding.setDiscriminatorMatchValue( rootEntitySource.getDiscriminatorMatchValue() ); - // Configure discriminator hibernate type - final String typeName = - discriminatorSource.getExplicitHibernateTypeName() != null - ? discriminatorSource.getExplicitHibernateTypeName() - : "string"; - final HibernateTypeDescriptor hibernateTypeDescriptor = discriminator.getExplicitHibernateTypeDescriptor(); - hibernateTypeDescriptor.setExplicitTypeName( typeName ); - Type resolvedType = heuristicType( hibernateTypeDescriptor ); - bindHibernateResolvedType( hibernateTypeDescriptor, resolvedType ); - bindJdbcDataType( resolvedType, value ); - } - - private void bindMultiTenancy(EntityBinding rootEntityBinding, RootEntitySource rootEntitySource) { - final MultiTenancySource multiTenancySource = rootEntitySource.getMultiTenancySource(); - if ( multiTenancySource == null ) { - return; - } - - // if (1) the strategy is discriminator based and (2) the entity is not shared, we need to either (a) extract - // the user supplied tenant discriminator value mapping or (b) generate an implicit one - final boolean needsTenantIdentifierValueMapping = - MultiTenancyStrategy.DISCRIMINATOR == metadata.getOptions().getMultiTenancyStrategy() - && ! multiTenancySource.isShared(); - - if ( needsTenantIdentifierValueMapping ) { - // NOTE : the table for tenant identifier/discriminator is always the primary table - final Value tenantDiscriminatorValue; - final RelationalValueSource valueSource = multiTenancySource.getRelationalValueSource(); - if ( valueSource == null ) { - // user supplied no explicit information, so use implicit mapping with default name - tenantDiscriminatorValue = rootEntityBinding.getPrimaryTable().locateOrCreateColumn( "tenant_id" ); - } - else { - tenantDiscriminatorValue = buildRelationValue( valueSource, rootEntityBinding.getPrimaryTable() ); - } - rootEntityBinding.getHierarchyDetails().getTenantDiscrimination().setDiscriminatorValue( tenantDiscriminatorValue ); - } - - rootEntityBinding.getHierarchyDetails().getTenantDiscrimination().setShared( multiTenancySource.isShared() ); - rootEntityBinding.getHierarchyDetails().getTenantDiscrimination().setUseParameterBinding( multiTenancySource.bindAsParameter() ); - } - - private Value buildRelationValue(RelationalValueSource valueSource, TableSpecification table) { - if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { - return createColumn( - table, - ( ColumnSource ) valueSource, - bindingContexts.peek().getMappingDefaults().getDiscriminatorColumnName(), - false, - false, - false - ); - } - else { - return table.locateOrCreateDerivedValue( ( ( DerivedValueSource ) valueSource ).getExpression() ); - } - } - - private EntityBinding bindEntities( final EntityHierarchy entityHierarchy ) { - final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource(); - // Return existing binding if available - EntityBinding rootEntityBinding = metadata.getEntityBinding( rootEntitySource.getEntityName() ); - if ( rootEntityBinding != null ) { - return rootEntityBinding; - } - // Save inheritance type and entity mode that will apply to entire hierarchy - inheritanceTypes.push( entityHierarchy.getHierarchyInheritanceType() ); - entityModes.push( rootEntitySource.getEntityMode() ); - final LocalBindingContext bindingContext = rootEntitySource.getLocalBindingContext(); - bindingContexts.push( bindingContext ); - try { - // Create root entity binding - rootEntityBinding = createEntityBinding( rootEntitySource, null ); - // Create/Bind root-specific information - bindIdentifier( rootEntityBinding, rootEntitySource.getIdentifierSource() ); - bindSecondaryTables( rootEntityBinding, rootEntitySource ); - bindUniqueConstraints( rootEntityBinding, rootEntitySource ); - bindVersion( rootEntityBinding, rootEntitySource.getVersioningAttributeSource() ); - bindDiscriminator( rootEntityBinding, rootEntitySource ); - bindIdentifierGenerator( rootEntityBinding ); - bindMultiTenancy( rootEntityBinding, rootEntitySource ); - rootEntityBinding.getHierarchyDetails().setCaching( rootEntitySource.getCaching() ); - rootEntityBinding.getHierarchyDetails().setNaturalIdCaching( rootEntitySource.getNaturalIdCaching() ); - rootEntityBinding.getHierarchyDetails().setExplicitPolymorphism( rootEntitySource.isExplicitPolymorphism() ); - rootEntityBinding.getHierarchyDetails().setOptimisticLockStyle( rootEntitySource.getOptimisticLockStyle() ); - rootEntityBinding.setMutable( rootEntitySource.isMutable() ); - rootEntityBinding.setWhereFilter( rootEntitySource.getWhere() ); - rootEntityBinding.setRowId( rootEntitySource.getRowId() ); - // Bind attributes and sub-entities to root entity - bindAttributes( rootEntityBinding, rootEntitySource ); - if ( inheritanceTypes.peek() != InheritanceType.NO_INHERITANCE ) { - bindSubEntities( rootEntityBinding, rootEntitySource ); - } - } finally { - bindingContexts.pop(); - inheritanceTypes.pop(); - entityModes.pop(); - } - return rootEntityBinding; - } - - public void bindEntities( final Iterable< EntityHierarchy > entityHierarchies ) { - entitySourcesByName.clear(); - attributeSourcesByName.clear(); - inheritanceTypes.clear(); - entityModes.clear(); - bindingContexts.clear(); - // Index sources by name so we can find and resolve entities on the fly as references to them - // are encountered (e.g., within associations) - for ( final EntityHierarchy entityHierarchy : entityHierarchies ) { - entityHierarchiesByRootEntitySource.put( entityHierarchy.getRootEntitySource(), entityHierarchy ); - mapSourcesByName( entityHierarchy.getRootEntitySource() ); - } - // Bind each entity hierarchy - for ( final EntityHierarchy entityHierarchy : entityHierarchies ) { - bindEntities( entityHierarchy ); - } - } - - private EntityBinding bindEntity( final EntitySource entitySource, final EntityBinding superEntityBinding ) { - // Return existing binding if available - EntityBinding entityBinding = metadata.getEntityBinding( entitySource.getEntityName() ); - if ( entityBinding != null ) { - return entityBinding; - } - final LocalBindingContext bindingContext = entitySource.getLocalBindingContext(); - bindingContexts.push( bindingContext ); - try { - // Create new entity binding - entityBinding = createEntityBinding( entitySource, superEntityBinding ); - bindSecondaryTables( entityBinding, entitySource ); - bindUniqueConstraints( entityBinding, entitySource ); - bindAttributes( entityBinding, entitySource ); - bindSubEntities( entityBinding, entitySource ); - return entityBinding; - } finally { - bindingContexts.pop(); - } - } - - private void bindHibernateResolvedType( final HibernateTypeDescriptor hibernateTypeDescriptor, final Type resolvedType ) { - // Configure relational value JDBC type from Hibernate type descriptor now that its configured - if ( resolvedType != null ) { - hibernateTypeDescriptor.setResolvedTypeMapping( resolvedType ); - if ( hibernateTypeDescriptor.getJavaTypeName() == null ) { - hibernateTypeDescriptor.setJavaTypeName( resolvedType.getReturnedClass().getName() ); - } - hibernateTypeDescriptor.setToOne( resolvedType.isEntityType() ); - } - } - - // TODO: simplify how HibernateTypeDescriptor is bound - - private void bindHibernateTypeDescriptor( - final HibernateTypeDescriptor hibernateTypeDescriptor, - final ExplicitHibernateTypeSource explicitTypeSource, - final org.hibernate.internal.util.ValueHolder< Class< ? >> defaultJavaType ) { - // if there is an explicit type name specified, then there's no reason to - // initialize the default Java type name; simply pass a null default instead. - bindHibernateTypeDescriptor( - hibernateTypeDescriptor, - explicitTypeSource, - explicitTypeSource == null || explicitTypeSource.getName() == null - ? defaultJavaType.getValue().getName() - : null - ); - } - - private void bindHibernateTypeDescriptor( - final HibernateTypeDescriptor hibernateTypeDescriptor, - final ExplicitHibernateTypeSource explicitTypeSource, - final String defaultJavaTypeName ) { - if ( explicitTypeSource == null ) { - bindHibernateTypeDescriptor( - hibernateTypeDescriptor, null, null, defaultJavaTypeName - ); - } - else { - bindHibernateTypeDescriptor( - hibernateTypeDescriptor, - explicitTypeSource.getName(), - explicitTypeSource.getParameters(), - defaultJavaTypeName - ); - } - } - - private void bindHibernateTypeDescriptor( - final HibernateTypeDescriptor hibernateTypeDescriptor, - final String explicitTypeName, - final Map explictTypeParameters, - final String defaultJavaTypeName ) { - if ( explicitTypeName == null ) { - if ( hibernateTypeDescriptor.getJavaTypeName() != null ) { - throw new MappingException( String.format( - "Attempt to re-initialize (non-explicit) Java type name; current=%s new=%s", - hibernateTypeDescriptor.getJavaTypeName(), - defaultJavaTypeName ), bindingContexts.peek().getOrigin() ); - } - hibernateTypeDescriptor.setJavaTypeName( defaultJavaTypeName ); - } else { - // Check if user-specified name is of a User-Defined Type (UDT) - final TypeDefinition typeDef = metadata.getTypeDefinition( explicitTypeName ); - if ( hibernateTypeDescriptor.getExplicitTypeName() != null ) { - throw new MappingException( String.format( - "Attempt to re-initialize explicity-mapped Java type name; current=%s new=%s", - hibernateTypeDescriptor.getExplicitTypeName(), - explicitTypeName ), bindingContexts.peek().getOrigin() ); - } - if ( typeDef == null ) { - hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName ); - } else { - hibernateTypeDescriptor.setExplicitTypeName( typeDef.getTypeImplementorClass().getName() ); - hibernateTypeDescriptor.setTypeParameters( typeDef.getParameters() ); - } - if ( explictTypeParameters != null ) { - hibernateTypeDescriptor.getTypeParameters().putAll( explictTypeParameters ); - } - } - } - - private void bindIdentifier( final EntityBinding rootEntityBinding, final IdentifierSource identifierSource ) { - final EntityIdentifierNature nature = identifierSource.getNature(); - switch ( nature ) { - case SIMPLE: { - bindSimpleIdentifier( rootEntityBinding, ( SimpleIdentifierSource ) identifierSource ); - break; - } - case AGGREGATED_COMPOSITE: { - bindAggregatedCompositeIdentifier( rootEntityBinding, ( AggregatedCompositeIdentifierSource ) identifierSource ); - break; - } - case COMPOSITE: { - bindNonAggregatedCompositeIdentifier( - rootEntityBinding, - ( NonAggregatedCompositeIdentifierSource ) identifierSource ); - break; - } - default: { - throw bindingContext().makeMappingException( "Unknown identifier nature : " + nature.name() ); - } - } - } - - private void bindSimpleIdentifier( final EntityBinding rootEntityBinding, final SimpleIdentifierSource identifierSource ) { - // locate the attribute binding - final BasicAttributeBinding idAttributeBinding = ( BasicAttributeBinding ) bindIdentifierAttribute( - rootEntityBinding, identifierSource.getIdentifierAttributeSource() - ); - - // Configure ID generator - IdGenerator generator = identifierSource.getIdentifierGeneratorDescriptor(); - if ( generator == null ) { - final Map< String, String > params = new HashMap< String, String >(); - params.put( IdentifierGenerator.ENTITY_NAME, rootEntityBinding.getEntity().getName() ); - generator = new IdGenerator( "default_assign_identity_generator", "assigned", params ); - } - - // determine the unsaved value mapping - final String unsavedValue = interpretIdentifierUnsavedValue( identifierSource, generator ); - - rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsSimpleIdentifier( - idAttributeBinding, - generator, - unsavedValue ); - } - - private void bindAggregatedCompositeIdentifier( - EntityBinding rootEntityBinding, - AggregatedCompositeIdentifierSource identifierSource ) { - // locate the attribute binding - final CompositeAttributeBinding idAttributeBinding = ( CompositeAttributeBinding ) bindIdentifierAttribute( - rootEntityBinding, identifierSource.getIdentifierAttributeSource() - ); - - // Configure ID generator - IdGenerator generator = identifierSource.getIdentifierGeneratorDescriptor(); - if ( generator == null ) { - final Map< String, String > params = new HashMap< String, String >(); - params.put( IdentifierGenerator.ENTITY_NAME, rootEntityBinding.getEntity().getName() ); - generator = new IdGenerator( "default_assign_identity_generator", "assigned", params ); - } - - // determine the unsaved value mapping - final String unsavedValue = interpretIdentifierUnsavedValue( identifierSource, generator ); - - rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsAggregatedCompositeIdentifier( - idAttributeBinding, - generator, - unsavedValue ); - } - - private void bindNonAggregatedCompositeIdentifier( - EntityBinding rootEntityBinding, - NonAggregatedCompositeIdentifierSource identifierSource ) { - // locate the attribute bindings for the real attributes - List< SingularAttributeBinding > idAttributeBindings = new ArrayList< SingularAttributeBinding >(); - for ( SingularAttributeSource attributeSource : identifierSource.getAttributeSourcesMakingUpIdentifier() ) { - idAttributeBindings.add( bindIdentifierAttribute( rootEntityBinding, attributeSource ) ); - } - - // Create the synthetic attribute - SingularAttribute syntheticAttribute = - rootEntityBinding.getEntity().createSyntheticCompositeAttribute( - SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME, - rootEntityBinding.getEntity() ); - - // Create the synthetic attribute binding. - final CompositeAttributeBinding syntheticAttributeBinding = - rootEntityBinding.makeVirtualComponentAttributeBinding( - syntheticAttribute, - idAttributeBindings, - createMetaAttributeContext( rootEntityBinding, identifierSource.getMetaAttributeSources() ) ); - - rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsNonAggregatedCompositeIdentifier( - syntheticAttributeBinding, - identifierSource.getLookupIdClass() ); - } - - private void bindIndexedTablePrimaryKey( IndexedPluralAttributeBinding attributeBinding ) { - final PrimaryKey primaryKey = attributeBinding.getPluralAttributeKeyBinding().getCollectionTable().getPrimaryKey(); - final ForeignKey foreignKey = attributeBinding.getPluralAttributeKeyBinding().getForeignKey(); - final PluralAttributeIndexBinding indexBinding = attributeBinding.getPluralAttributeIndexBinding(); - for ( final Column foreignKeyColumn : foreignKey.getSourceColumns() ) { - primaryKey.addColumn( foreignKeyColumn ); - } - final Value value = indexBinding.getIndexRelationalValue(); - if ( value instanceof Column ) { - primaryKey.addColumn( ( Column ) value ); - } - } - - LocalBindingContext bindingContext() { - return bindingContexts.peek(); - } - - private void bindJdbcDataType( final Type resolvedType, final AbstractValue value ) { - if ( resolvedType != null && value != null ) { - final Type resolvedRelationalType = - resolvedType.isEntityType() - ? EntityType.class.cast( resolvedType ).getIdentifierOrUniqueKeyType( metadata ) - : resolvedType; - value.setJdbcDataType( new JdbcDataType( - resolvedRelationalType.sqlTypes( metadata )[ 0 ], - resolvedRelationalType.getName(), - resolvedRelationalType.getReturnedClass() ) ); - } - } - - // Foreign Keys ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // TODO: try to get rid of this... - private List extractColumnsFromRelationalValueBindings( - List valueBindings) { - List columns = new ArrayList( valueBindings.size() ); - for ( RelationalValueBinding relationalValueBinding : valueBindings ) { - final Value value = relationalValueBinding.getValue(); - // todo : currently formulas are not supported here... :( - if ( !Column.class.isInstance( value ) ) { - throw new NotYetImplementedException( - "Derived values are not supported when creating a foreign key that targets columns." ); - } - columns.add( (Column) value ); - } - return columns; - } - - private ForeignKey bindForeignKey( - String foreignKeyName, - List sourceColumns, - List targetColumns) { - ForeignKey foreignKey = null; - if ( foreignKeyName != null ) { - foreignKey = locateAndBindForeignKeyByName( foreignKeyName, sourceColumns, targetColumns ); - } - if ( foreignKey == null ) { - foreignKey = locateForeignKeyByColumnMapping( sourceColumns, targetColumns ); - if ( foreignKey != null && foreignKeyName != null ) { - if ( foreignKey.getName() == null ) { - // the foreign key name has not be initialized; set it to foreignKeyName - foreignKey.setName( foreignKeyName ); - } - else { - // the foreign key name has already been initialized so cannot rename it - // TODO: should this just be INFO? - log.warn( - String.format( - "A foreign key mapped as %s will not be created because foreign key %s already exists with the same column mapping.", - foreignKeyName, - foreignKey.getName()) - ); - } - } - } - if ( foreignKey == null ) { - // no foreign key found; create one - final TableSpecification sourceTable = sourceColumns.get( 0 ).getTable(); - final TableSpecification targetTable = targetColumns.get( 0 ).getTable(); - foreignKey = sourceTable.createForeignKey( targetTable, foreignKeyName ); - bindForeignKeyColumns( foreignKey, sourceColumns, targetColumns ); - } - return foreignKey; - } - - private ForeignKey locateAndBindForeignKeyByName ( - String foreignKeyName, - List sourceColumns, - List targetColumns) { - if ( foreignKeyName == null ) { - throw new AssertionFailure( "foreignKeyName must be non-null." ); - } - final TableSpecification sourceTable = sourceColumns.get( 0 ).getTable(); - final TableSpecification targetTable = targetColumns.get( 0 ).getTable(); - ForeignKey foreignKey = sourceTable.locateForeignKey( foreignKeyName ); - if ( foreignKey != null ) { - if ( ! targetTable.equals( foreignKey.getTargetTable() ) ) { - throw bindingContext().makeMappingException( - String.format( - "Unexpected target table defined for foreign key \"%s\"; expected \"%s\"; found \"%s\"", - foreignKeyName, targetTable.getLogicalName(), foreignKey.getTargetTable().getLogicalName() - ) - ); - } - // check if source and target columns have been bound already - if ( foreignKey.getColumnSpan() == 0 ) { - // foreign key was found, but no columns bound to it yet - bindForeignKeyColumns( foreignKey, sourceColumns, targetColumns ); - } - else { - // The located foreign key already has columns bound; - // Make sure they are the same columns. - if ( ! foreignKey.getSourceColumns().equals( sourceColumns ) || - foreignKey.getTargetColumns().equals( targetColumns ) ) { - throw bindingContext().makeMappingException( - String.format( - "Attempt to bind exisitng foreign key \"%s\" with different columns.", - foreignKeyName - ) - ); - } - } - } - return foreignKey; - } - - private ForeignKey locateForeignKeyByColumnMapping( - List sourceColumns, - List targetColumns) { - final TableSpecification sourceTable = sourceColumns.get( 0 ).getTable(); - final TableSpecification targetTable = targetColumns.get( 0 ).getTable(); - // check for an existing foreign key with the same source/target columns - ForeignKey foreignKey = null; - Iterable possibleForeignKeys = sourceTable.locateForeignKey( targetTable ); - if ( possibleForeignKeys != null ) { - for ( ForeignKey possibleFK : possibleForeignKeys ) { - if ( possibleFK.getSourceColumns().equals( sourceColumns ) && - possibleFK.getTargetColumns().equals( targetColumns ) ) { - // this is the foreign key - foreignKey = possibleFK; - break; - } - } - } - return foreignKey; - } - - private void bindForeignKeyColumns( - ForeignKey foreignKey, - List sourceColumns, - List targetColumns) { - if ( sourceColumns.size() != targetColumns.size() ) { - throw bindingContext().makeMappingException( - String.format( - "Non-matching number columns in foreign key source columns [%s : %s] and target columns [%s : %s]", - sourceColumns.get( 0 ).getTable().getLogicalName().getName(), - sourceColumns.size(), - targetColumns.get( 0 ).getTable().getLogicalName().getName(), - targetColumns.size() - ) - ); - } - for ( int i = 0; i < sourceColumns.size(); i++ ) { - foreignKey.addColumnMapping( sourceColumns.get( i ), targetColumns.get( i ) ); - } - } - - private AttributeBinding determineReferencedAttributeBinding( - ForeignKeyContributingSource.JoinColumnResolutionDelegate resolutionDelegate, - ForeignKeyContributingSource.JoinColumnResolutionContext resolutionContext, - EntityBinding referencedEntityBinding) { - if ( resolutionDelegate == null ) { - return referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); - } - - final String explicitName = resolutionDelegate.getReferencedAttributeName(); - return explicitName != null ? referencedEntityBinding.locateAttributeBinding( explicitName ) :referencedEntityBinding.locateAttributeBinding(resolutionDelegate.getJoinColumns( resolutionContext ) ); - } - private void bindPrimaryTable( final EntityBinding entityBinding, final EntitySource entitySource ) { entityBinding.setPrimaryTable( createTable( entitySource.getPrimaryTable(), new DefaultNamingStrategy() { @@ -1500,66 +1385,71 @@ public class Binder { } } - private List< Column > determineForeignKeyTargetColumns( - final EntityBinding entityBinding, - ForeignKeyContributingSource foreignKeyContributingSource ) { - final ForeignKeyContributingSource.JoinColumnResolutionDelegate fkColumnResolutionDelegate = - foreignKeyContributingSource.getForeignKeyTargetColumnResolutionDelegate(); + private AbstractPluralAttributeBinding bindSetAttribute( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource, + PluralAttribute attribute ) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createSet( attributeSource.getName() ); + } + return attributeBindingContainer.makeSetAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ) + ); + } - if ( fkColumnResolutionDelegate == null ) { - return entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); - } else { - final List< Column > columns = new ArrayList< Column >(); - final ForeignKeyContributingSource.JoinColumnResolutionContext resolutionContext = - new ForeignKeyContributingSource.JoinColumnResolutionContext() { - @Override - public List< Value > resolveRelationalValuesForAttribute( String attributeName ) { - final AttributeBinding referencedAttributeBinding = - entityBinding.locateAttributeBinding( attributeName ); - if ( referencedAttributeBinding == null ) { - throw bindingContext().makeMappingException( - String.format( - "Could not resolve named property-ref [%s] against entity [%s]", - attributeName, - entityBinding.getEntity().getName() ) ); - } - if ( ! referencedAttributeBinding.getAttribute().isSingular() ) { - throw bindingContext().makeMappingException( - String.format( - "Property-ref [%s] against entity [%s] is a plural attribute; it must be a singular attribute.", - attributeName, - entityBinding.getEntity().getName() ) ); - } - List valueBindings = - ( (SingularAttributeBinding) referencedAttributeBinding ).getRelationalValueBindings(); - List values = new ArrayList( valueBindings.size()); - for ( RelationalValueBinding valueBinding : valueBindings ) { - values.add( valueBinding.getValue() ); - } - return values; - } + private void bindSimpleIdentifier( final EntityBinding rootEntityBinding, final SimpleIdentifierSource identifierSource ) { + // locate the attribute binding + final BasicAttributeBinding idAttributeBinding = ( BasicAttributeBinding ) bindIdentifierAttribute( + rootEntityBinding, identifierSource.getIdentifierAttributeSource() + ); - @Override - public Column resolveColumn( - String logicalColumnName, - String logicalTableName, - String logicalSchemaName, - String logicalCatalogName ) { - // ignore table, schema, catalog name - Column column = entityBinding.getPrimaryTable().locateColumn( logicalColumnName ); - if ( column == null ) { - entityBinding.getPrimaryTable().createColumn( logicalColumnName ); - } - return column; - } - }; - for ( Value relationalValue : fkColumnResolutionDelegate.getJoinColumns( resolutionContext ) ) { - if ( !Column.class.isInstance( relationalValue ) ) { - throw bindingContext().makeMappingException( "Foreign keys can currently only name columns, not formulas" ); - } - columns.add( ( Column ) relationalValue ); - } - return columns; + // Configure ID generator + IdGenerator generator = identifierSource.getIdentifierGeneratorDescriptor(); + if ( generator == null ) { + final Map< String, String > params = new HashMap< String, String >(); + params.put( IdentifierGenerator.ENTITY_NAME, rootEntityBinding.getEntity().getName() ); + generator = new IdGenerator( "default_assign_identity_generator", "assigned", params ); + } + + // determine the unsaved value mapping + final String unsavedValue = interpretIdentifierUnsavedValue( identifierSource, generator ); + + rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsSimpleIdentifier( + idAttributeBinding, + generator, + unsavedValue ); + } + + private SingularAttributeBinding bindSingularAttribute( + final AttributeBindingContainer attributeBindingContainer, + final SingularAttributeSource attributeSource, + boolean isIdentifierAttribute) { + final SingularAttributeNature nature = attributeSource.getNature(); + final SingularAttribute attribute = + attributeBindingContainer.getAttributeContainer().locateSingularAttribute( attributeSource.getName() ); + switch ( nature ) { + case BASIC: + return bindBasicAttribute( attributeBindingContainer, attributeSource, attribute ); + case MANY_TO_ONE: + return bindManyToOneAttribute( + attributeBindingContainer, + ToOneAttributeSource.class.cast( attributeSource ), + attribute + ); + case COMPONENT: + return bindComponentAttribute( + attributeBindingContainer, + ComponentAttributeSource.class.cast( attributeSource ), + attribute, + isIdentifierAttribute + ); + default: + throw new NotYetImplementedException( nature.toString() ); } } @@ -1671,11 +1561,6 @@ public class Binder { return valueBindings; } - private void addUniqueConstraintForNaturalIdColumn(final TableSpecification table, final Column column) { - final UniqueKey uniqueKey = table.getOrCreateUniqueKey( "natural_id_unique_key_" ); - uniqueKey.addColumn( column ); - } - private void bindVersion( final EntityBinding rootEntityBinding, final VersionAttributeSource versionAttributeSource ) { if ( versionAttributeSource == null ) { return; @@ -1695,6 +1580,29 @@ public class Binder { ); } + private Value buildRelationValue(RelationalValueSource valueSource, TableSpecification table) { + if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { + return createColumn( + table, + ( ColumnSource ) valueSource, + bindingContexts.peek().getMappingDefaults().getDiscriminatorColumnName(), + false, + false, + false + ); + } + else { + return table.locateOrCreateDerivedValue( ( ( DerivedValueSource ) valueSource ).getExpression() ); + } + } + + private String createAttributePath(final AttributeBinding attributeBinding) { + return new StringBuffer( attributeBinding.getContainer().getPathBase() ) + .append( '.' ) + .append( attributeBinding.getAttribute().getName() ) + .toString(); + } + private TableSpecification createCollectionTable( final AbstractPluralAttributeBinding pluralAttributeBinding, final PluralAttributeSource attributeSource ) { @@ -1719,13 +1627,6 @@ public class Binder { return createTable( attributeSource.getCollectionTableSpecificationSource(), defaultNamingStategy ); } - private String createAttributePath(final AttributeBinding attributeBinding) { - return new StringBuffer( attributeBinding.getContainer().getPathBase() ) - .append( '.' ) - .append( attributeBinding.getAttribute().getName() ) - .toString(); - } - private Column createColumn( final TableSpecification table, final ColumnSource columnSource, @@ -1857,32 +1758,6 @@ public class Binder { return Identifier.toIdentifier( name ); } - private void bindIdentifierGenerator(final EntityBinding rootEntityBinding) { - final Properties properties = new Properties(); - properties.putAll( metadata.getServiceRegistry().getService( ConfigurationService.class ).getSettings() ); - if ( !properties.contains( AvailableSettings.PREFER_POOLED_VALUES_LO ) ) { - properties.put( AvailableSettings.PREFER_POOLED_VALUES_LO, "false" ); - } - if ( !properties.contains( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER ) ) { - properties.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, nameNormalizer ); - } - final EntityIdentifier entityIdentifier = rootEntityBinding.getHierarchyDetails().getEntityIdentifier(); - entityIdentifier.createIdentifierGenerator( identifierGeneratorFactory, properties ); - if ( IdentityGenerator.class.isInstance( entityIdentifier.getIdentifierGenerator() ) ) { - if ( rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumnSpan() != 1 ) { - throw new MappingException( - String.format( - "ID for %s is mapped as an identity with %d columns. IDs mapped as an identity can only have 1 column.", - rootEntityBinding.getEntity().getName(), - rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumnSpan() - ), - bindingContexts.peek().getOrigin() - ); - } - rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumns().get( 0 ).setIdentity( true ); - } - } - private MetaAttributeContext createMetaAttributeContext( final AttributeBindingContainer attributeBindingContainer, final AttributeSource attributeSource ) { @@ -1982,61 +1857,67 @@ public class Binder { : attributeSource.getPluralAttributeNature().reportedJavaType().getName(); } - private EntityBinding entityBinding( final String entityName ) { - // Check if binding has already been created - EntityBinding entityBinding = metadata.getEntityBinding( entityName ); - if ( entityBinding == null ) { - // Find appropriate source to create binding - final EntitySource entitySource = entitySourcesByName.get( entityName ); - // Get super entity binding (creating it if necessary using recursive call to this method) - final EntityBinding superEntityBinding = - SubclassEntitySource.class.isInstance( entitySource ) - ? entityBinding( ( ( SubclassEntitySource ) entitySource ).superclassEntitySource().getEntityName() ) - : null; - // Create entity binding - entityBinding = - superEntityBinding == null - ? bindEntities( entityHierarchiesByRootEntitySource.get( entitySource ) ) - : bindEntity( entitySource, superEntityBinding ); - } - return entityBinding; - } + private List< Column > determineForeignKeyTargetColumns( + final EntityBinding entityBinding, + ForeignKeyContributingSource foreignKeyContributingSource ) { + final ForeignKeyContributingSource.JoinColumnResolutionDelegate fkColumnResolutionDelegate = + foreignKeyContributingSource.getForeignKeyTargetColumnResolutionDelegate(); - private Type heuristicType( HibernateTypeDescriptor hibernateTypeDescriptor ) { - final String typeName = - hibernateTypeDescriptor.getExplicitTypeName() != null - ? hibernateTypeDescriptor.getExplicitTypeName() - : hibernateTypeDescriptor.getJavaTypeName(); - final Properties properties = new Properties(); - properties.putAll( hibernateTypeDescriptor.getTypeParameters() ); - return metadata.getTypeResolver().heuristicType( typeName, properties ); - } + if ( fkColumnResolutionDelegate == null ) { + return entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); + } else { + final List< Column > columns = new ArrayList< Column >(); + final ForeignKeyContributingSource.JoinColumnResolutionContext resolutionContext = + new ForeignKeyContributingSource.JoinColumnResolutionContext() { + @Override + public Column resolveColumn( + String logicalColumnName, + String logicalTableName, + String logicalSchemaName, + String logicalCatalogName ) { + // ignore table, schema, catalog name + Column column = entityBinding.getPrimaryTable().locateColumn( logicalColumnName ); + if ( column == null ) { + entityBinding.getPrimaryTable().createColumn( logicalColumnName ); + } + return column; + } - private void mapSourcesByName( final EntitySource entitySource ) { - String entityName = entitySource.getEntityName(); - entitySourcesByName.put( entityName, entitySource ); - log.debugf( "Mapped entity source \"%s\"", entityName ); - for ( final AttributeSource attributeSource : entitySource.attributeSources() ) { - String key = attributeSourcesByNameKey( entityName, attributeSource.getName() ); - attributeSourcesByName.put( key, attributeSource ); - log.debugf( "Mapped attribute source \"%s\" for entity source \"%s\"", key, entitySource.getEntityName() ); + @Override + public List< Value > resolveRelationalValuesForAttribute( String attributeName ) { + final AttributeBinding referencedAttributeBinding = + entityBinding.locateAttributeBinding( attributeName ); + if ( referencedAttributeBinding == null ) { + throw bindingContext().makeMappingException( + String.format( + "Could not resolve named property-ref [%s] against entity [%s]", + attributeName, + entityBinding.getEntity().getName() ) ); + } + if ( ! referencedAttributeBinding.getAttribute().isSingular() ) { + throw bindingContext().makeMappingException( + String.format( + "Property-ref [%s] against entity [%s] is a plural attribute; it must be a singular attribute.", + attributeName, + entityBinding.getEntity().getName() ) ); + } + List valueBindings = + ( (SingularAttributeBinding) referencedAttributeBinding ).getRelationalValueBindings(); + List values = new ArrayList( valueBindings.size()); + for ( RelationalValueBinding valueBinding : valueBindings ) { + values.add( valueBinding.getValue() ); + } + return values; + } + }; + for ( Value relationalValue : fkColumnResolutionDelegate.getJoinColumns( resolutionContext ) ) { + if ( !Column.class.isInstance( relationalValue ) ) { + throw bindingContext().makeMappingException( "Foreign keys can currently only name columns, not formulas" ); + } + columns.add( ( Column ) relationalValue ); + } + return columns; } - for ( final SubclassEntitySource subclassEntitySource : entitySource.subclassEntitySources() ) { - mapSourcesByName( subclassEntitySource ); - } - } - - private PluralAttributeElementNature pluralAttributeElementNature(PluralAttributeSource attributeSource) { - return PluralAttributeElementNature.valueOf( attributeSource.getElementSource().getNature().name() ); - } - - private PluralAttributeIndexNature pluralAttributeIndexNature(PluralAttributeSource attributeSource) { - if ( ! IndexedPluralAttributeSource.class.isInstance( attributeSource ) ) { - return null; - } - return PluralAttributeIndexNature.valueOf( - ( (IndexedPluralAttributeSource) attributeSource ).getIndexSource().getNature().name() - ); } private SingularAttributeBinding determinePluralAttributeKeyReferencedBinding( @@ -2074,6 +1955,162 @@ public class Binder { return ( SingularAttributeBinding ) referencedAttributeBinding; } + private AttributeBinding determineReferencedAttributeBinding( + ForeignKeyContributingSource.JoinColumnResolutionDelegate resolutionDelegate, + ForeignKeyContributingSource.JoinColumnResolutionContext resolutionContext, + EntityBinding referencedEntityBinding) { + if ( resolutionDelegate == null ) { + return referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); + } + + final String explicitName = resolutionDelegate.getReferencedAttributeName(); + return explicitName != null ? referencedEntityBinding.locateAttributeBinding( explicitName ) :referencedEntityBinding.locateAttributeBinding(resolutionDelegate.getJoinColumns( resolutionContext ) ); + } + + private EntityBinding entityBinding( final String entityName ) { + // Check if binding has already been created + EntityBinding entityBinding = metadata.getEntityBinding( entityName ); + if ( entityBinding == null ) { + // Find appropriate source to create binding + final EntitySource entitySource = entitySourcesByName.get( entityName ); + // Get super entity binding (creating it if necessary using recursive call to this method) + final EntityBinding superEntityBinding = + SubclassEntitySource.class.isInstance( entitySource ) + ? entityBinding( ( ( SubclassEntitySource ) entitySource ).superclassEntitySource().getEntityName() ) + : null; + // Create entity binding + entityBinding = + superEntityBinding == null + ? bindEntities( entityHierarchiesByRootEntitySource.get( entitySource ) ) + : bindEntity( entitySource, superEntityBinding ); + } + return entityBinding; + } + + // TODO: try to get rid of this... + private List extractColumnsFromRelationalValueBindings( + List valueBindings) { + List columns = new ArrayList( valueBindings.size() ); + for ( RelationalValueBinding relationalValueBinding : valueBindings ) { + final Value value = relationalValueBinding.getValue(); + // todo : currently formulas are not supported here... :( + if ( !Column.class.isInstance( value ) ) { + throw new NotYetImplementedException( + "Derived values are not supported when creating a foreign key that targets columns." ); + } + columns.add( (Column) value ); + } + return columns; + } + + private boolean hasAnyNonNullableColumns(List relationalValueBindings) { + for ( RelationalValueBinding relationalValueBinding : relationalValueBindings ) { + if ( Column.class.isInstance( relationalValueBinding.getValue() ) && !relationalValueBinding.isNullable() ) { + return true; + } + } + return false; + } + + private Type heuristicType( HibernateTypeDescriptor hibernateTypeDescriptor ) { + final String typeName = + hibernateTypeDescriptor.getExplicitTypeName() != null + ? hibernateTypeDescriptor.getExplicitTypeName() + : hibernateTypeDescriptor.getJavaTypeName(); + final Properties properties = new Properties(); + properties.putAll( hibernateTypeDescriptor.getTypeParameters() ); + return metadata.getTypeResolver().heuristicType( typeName, properties ); + } + + private ForeignKey locateAndBindForeignKeyByName ( + String foreignKeyName, + List sourceColumns, + List targetColumns) { + if ( foreignKeyName == null ) { + throw new AssertionFailure( "foreignKeyName must be non-null." ); + } + final TableSpecification sourceTable = sourceColumns.get( 0 ).getTable(); + final TableSpecification targetTable = targetColumns.get( 0 ).getTable(); + ForeignKey foreignKey = sourceTable.locateForeignKey( foreignKeyName ); + if ( foreignKey != null ) { + if ( ! targetTable.equals( foreignKey.getTargetTable() ) ) { + throw bindingContext().makeMappingException( + String.format( + "Unexpected target table defined for foreign key \"%s\"; expected \"%s\"; found \"%s\"", + foreignKeyName, targetTable.getLogicalName(), foreignKey.getTargetTable().getLogicalName() + ) + ); + } + // check if source and target columns have been bound already + if ( foreignKey.getColumnSpan() == 0 ) { + // foreign key was found, but no columns bound to it yet + bindForeignKeyColumns( foreignKey, sourceColumns, targetColumns ); + } + else { + // The located foreign key already has columns bound; + // Make sure they are the same columns. + if ( ! foreignKey.getSourceColumns().equals( sourceColumns ) || + foreignKey.getTargetColumns().equals( targetColumns ) ) { + throw bindingContext().makeMappingException( + String.format( + "Attempt to bind exisitng foreign key \"%s\" with different columns.", + foreignKeyName + ) + ); + } + } + } + return foreignKey; + } + + private ForeignKey locateForeignKeyByColumnMapping( + List sourceColumns, + List targetColumns) { + final TableSpecification sourceTable = sourceColumns.get( 0 ).getTable(); + final TableSpecification targetTable = targetColumns.get( 0 ).getTable(); + // check for an existing foreign key with the same source/target columns + ForeignKey foreignKey = null; + Iterable possibleForeignKeys = sourceTable.locateForeignKey( targetTable ); + if ( possibleForeignKeys != null ) { + for ( ForeignKey possibleFK : possibleForeignKeys ) { + if ( possibleFK.getSourceColumns().equals( sourceColumns ) && + possibleFK.getTargetColumns().equals( targetColumns ) ) { + // this is the foreign key + foreignKey = possibleFK; + break; + } + } + } + return foreignKey; + } + + private void mapSourcesByName( final EntitySource entitySource ) { + String entityName = entitySource.getEntityName(); + entitySourcesByName.put( entityName, entitySource ); + log.debugf( "Mapped entity source \"%s\"", entityName ); + for ( final AttributeSource attributeSource : entitySource.attributeSources() ) { + String key = attributeSourcesByNameKey( entityName, attributeSource.getName() ); + attributeSourcesByName.put( key, attributeSource ); + log.debugf( "Mapped attribute source \"%s\" for entity source \"%s\"", key, entitySource.getEntityName() ); + } + for ( final SubclassEntitySource subclassEntitySource : entitySource.subclassEntitySources() ) { + mapSourcesByName( subclassEntitySource ); + } + } + + private PluralAttributeElementNature pluralAttributeElementNature(PluralAttributeSource attributeSource) { + return PluralAttributeElementNature.valueOf( attributeSource.getElementSource().getNature().name() ); + } + + private PluralAttributeIndexNature pluralAttributeIndexNature(PluralAttributeSource attributeSource) { + if ( ! IndexedPluralAttributeSource.class.isInstance( attributeSource ) ) { + return null; + } + return PluralAttributeIndexNature.valueOf( + ( (IndexedPluralAttributeSource) attributeSource ).getIndexSource().getNature().name() + ); + } + private String propertyAccessorName( final AttributeSource attributeSource ) { return attributeSource.getPropertyAccessorName() == null ? bindingContexts.peek().getMappingDefaults().getPropertyAccessorName() @@ -2122,6 +2159,7 @@ public class Binder { } } + private Type resolveMapType( MapBinding mapBinding ) { if ( mapBinding.getHibernateTypeDescriptor().getExplicitTypeName() != null ) { return resolveCustomCollectionType( mapBinding ); @@ -2148,79 +2186,6 @@ public class Binder { } } - private static org.hibernate.internal.util.ValueHolder< Class< ? >> createSingularAttributeJavaType( - final SingularAttribute attribute ) { - return createSingularAttributeJavaType( - attribute.getAttributeContainer().getClassReference(), - attribute.getName() - ); - } - - private static org.hibernate.internal.util.ValueHolder< Class< ? >> createSingularAttributeJavaType( - final Class< ? > attributeContainerClassReference, - final String attributeName ) { - org.hibernate.internal.util.ValueHolder.DeferredInitializer< Class< ? >> deferredInitializer = - new org.hibernate.internal.util.ValueHolder.DeferredInitializer< Class< ? >>() { - public Class< ? > initialize() { - return ReflectHelper.reflectedPropertyClass( - attributeContainerClassReference, - attributeName ); - } - }; - return new org.hibernate.internal.util.ValueHolder< Class< ? >>( deferredInitializer ); - } - - - private static String interpretIdentifierUnsavedValue( IdentifierSource identifierSource, IdGenerator generator ) { - if ( identifierSource == null ) { - throw new IllegalArgumentException( "identifierSource must be non-null." ); - } - if ( generator == null || StringHelper.isEmpty( generator.getStrategy() ) ) { - throw new IllegalArgumentException( "generator must be non-null and its strategy must be non-empty." ); - } - String unsavedValue = null; - if ( identifierSource.getUnsavedValue() != null ) { - unsavedValue = identifierSource.getUnsavedValue(); - } else if ( "assigned".equals( generator.getStrategy() ) ) { - unsavedValue = "undefined"; - } else { - switch ( identifierSource.getNature() ) { - case SIMPLE: { - // unsavedValue = null; - break; - } - case COMPOSITE: { - // The generator strategy should be "assigned" and processed above. - throw new IllegalStateException( String.format( - "Expected generator strategy for composite ID: 'assigned'; instead it is: %s", - generator.getStrategy() ) ); - } - case AGGREGATED_COMPOSITE: { - // TODO: if the component only contains 1 attribute (when flattened) - // and it is not an association then null should be returned; - // otherwise "undefined" should be returned. - throw new NotYetImplementedException( String.format( - "Unsaved value for (%s) identifier not implemented yet.", - identifierSource.getNature() ) ); - } - default: { - throw new AssertionFailure( String.format( "Unexpected identifier nature: %s", identifierSource.getNature() ) ); - } - } - } - return unsavedValue; - } - - private static boolean toBoolean( final TruthValue truthValue, final boolean truthValueDefault ) { - if ( truthValue == TruthValue.TRUE ) { - return true; - } - if ( truthValue == TruthValue.FALSE ) { - return false; - } - return truthValueDefault; - } - private interface DefaultNamingStrategy { String defaultName(); @@ -2237,26 +2202,6 @@ public class Binder { this.attributeSource = attributeSource; } - @Override - public List< Value > resolveRelationalValuesForAttribute( String attributeName ) { - final AttributeBinding referencedAttributeBinding = - referencedEntityBinding.locateAttributeBinding( attributeName ); - if ( !referencedAttributeBinding.getAttribute().isSingular() ) { - throw bindingContext().makeMappingException( - String.format( - "Many-to-one attribute [%s] named plural attribute as property-ref [%s]", - attributeSource.getName(), - attributeName ) ); - } - List< Value > values = new ArrayList< Value >(); - SingularAttributeBinding referencedAttributeBindingAsSingular = - ( SingularAttributeBinding ) referencedAttributeBinding; - for ( RelationalValueBinding valueBinding : referencedAttributeBindingAsSingular.getRelationalValueBindings() ) { - values.add( valueBinding.getValue() ); - } - return values; - } - @Override public Column resolveColumn( String logicalColumnName, @@ -2277,5 +2222,25 @@ public class Binder { return table.locateColumn( logicalColumnName ); } + + @Override + public List< Value > resolveRelationalValuesForAttribute( String attributeName ) { + final AttributeBinding referencedAttributeBinding = + referencedEntityBinding.locateAttributeBinding( attributeName ); + if ( !referencedAttributeBinding.getAttribute().isSingular() ) { + throw bindingContext().makeMappingException( + String.format( + "Many-to-one attribute [%s] named plural attribute as property-ref [%s]", + attributeSource.getName(), + attributeName ) ); + } + List< Value > values = new ArrayList< Value >(); + SingularAttributeBinding referencedAttributeBindingAsSingular = + ( SingularAttributeBinding ) referencedAttributeBinding; + for ( RelationalValueBinding valueBinding : referencedAttributeBindingAsSingular.getRelationalValueBindings() ) { + values.add( valueBinding.getValue() ); + } + return values; + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java index 81e2a59748..9952701e38 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java @@ -25,10 +25,9 @@ package org.hibernate.metamodel.internal; import java.io.Serializable; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.jboss.logging.Logger; - import org.hibernate.AssertionFailure; import org.hibernate.DuplicateMappingException; import org.hibernate.MappingException; @@ -41,6 +40,7 @@ import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.NamedQueryDefinition; import org.hibernate.engine.spi.NamedSQLQueryDefinition; +import org.hibernate.engine.spi.SyntheticAttributeHelper; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory; import org.hibernate.integrator.spi.Integrator; @@ -55,13 +55,21 @@ import org.hibernate.metamodel.internal.source.annotations.AnnotationMetadataSou import org.hibernate.metamodel.internal.source.hbm.HbmMetadataSourceProcessorImpl; import org.hibernate.metamodel.spi.MetadataSourceProcessor; import org.hibernate.metamodel.spi.binding.AttributeBinding; +import org.hibernate.metamodel.spi.binding.BackRefAttributeBinding; import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.metamodel.spi.binding.FetchProfile; +import org.hibernate.metamodel.spi.binding.HibernateTypeDescriptor; import org.hibernate.metamodel.spi.binding.IdGenerator; +import org.hibernate.metamodel.spi.binding.ManyToOneAttributeBinding; import org.hibernate.metamodel.spi.binding.PluralAttributeBinding; +import org.hibernate.metamodel.spi.binding.PluralAttributeElementNature; +import org.hibernate.metamodel.spi.binding.PluralAttributeKeyBinding; +import org.hibernate.metamodel.spi.binding.RelationalValueBinding; import org.hibernate.metamodel.spi.binding.TypeDefinition; import org.hibernate.metamodel.spi.domain.BasicType; +import org.hibernate.metamodel.spi.domain.SingularAttribute; import org.hibernate.metamodel.spi.domain.Type; +import org.hibernate.metamodel.spi.relational.Column; import org.hibernate.metamodel.spi.relational.Database; import org.hibernate.metamodel.spi.source.FilterDefinitionSource; import org.hibernate.metamodel.spi.source.IdentifierGeneratorSource; @@ -69,10 +77,10 @@ import org.hibernate.metamodel.spi.source.MappingDefaults; import org.hibernate.metamodel.spi.source.MetaAttributeContext; import org.hibernate.metamodel.spi.source.MetadataImplementor; import org.hibernate.metamodel.spi.source.TypeDescriptorSource; -import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.classloading.spi.ClassLoaderService; import org.hibernate.type.TypeResolver; +import org.jboss.logging.Logger; /** * Container for configuration data collected during binding the metamodel. @@ -92,7 +100,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable { private final Options options; private final ValueHolder classLoaderService; - private final ValueHolder persisterClassResolverService; +// private final ValueHolder persisterClassResolverService; private TypeResolver typeResolver = new TypeResolver(); @@ -159,14 +167,14 @@ public class MetadataImpl implements MetadataImplementor, Serializable { } } ); - this.persisterClassResolverService = new ValueHolder( - new ValueHolder.DeferredInitializer() { - @Override - public PersisterClassResolver initialize() { - return serviceRegistry.getService( PersisterClassResolver.class ); - } - } - ); +// this.persisterClassResolverService = new ValueHolder( +// new ValueHolder.DeferredInitializer() { +// @Override +// public PersisterClassResolver initialize() { +// return serviceRegistry.getService( PersisterClassResolver.class ); +// } +// } +// ); //check for typeContributingIntegrators integrators for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) { @@ -303,6 +311,71 @@ public class MetadataImpl implements MetadataImplementor, Serializable { } private void bindMappingDependentMetadata(MetadataSourceProcessor[] metadataSourceProcessors) { + // Create required back references, which are required for one-to-many associations with key bindings that are non-inverse, + // non-nullable, and unidirectional + for ( PluralAttributeBinding pluralAttributeBinding : collectionBindingMap.values() ) { + // Find one-to-many associations with key bindings that are non-inverse and non-nullable + PluralAttributeKeyBinding keyBinding = pluralAttributeBinding.getPluralAttributeKeyBinding(); + if ( keyBinding.isInverse() || keyBinding.isNullable() || + pluralAttributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() != + PluralAttributeElementNature.ONE_TO_MANY ) { + continue; + } + // Ensure this isn't a bidirectional association by ensuring FK columns don't match relational columns of any + // many-to-one on opposite side + EntityBinding referencedEntityBinding = + entityBindingMap.get( + pluralAttributeBinding.getPluralAttributeElementBinding().getHibernateTypeDescriptor(). + getResolvedTypeMapping().getName() ); + List columns = keyBinding.getForeignKey().getColumns(); + boolean bidirectional = false; + for ( AttributeBinding attributeBinding : referencedEntityBinding.attributeBindings() ) { + if ( !(attributeBinding instanceof ManyToOneAttributeBinding) ) { + continue; + } + // Check if the opposite many-to-one attribute binding references the one-to-many attribute binding being processed + ManyToOneAttributeBinding manyToOneAttributeBinding = ( ManyToOneAttributeBinding ) attributeBinding; + if ( !manyToOneAttributeBinding.getReferencedEntityBinding().equals( + pluralAttributeBinding.getContainer().seekEntityBinding() ) ) { + continue; + } + // Check if the many-to-one attribute binding's columns match the one-to-many attribute binding's FK columns + // (meaning this is a bidirectional association, and no back reference should be created) + List valueBindings = manyToOneAttributeBinding.getRelationalValueBindings(); + if ( columns.size() != valueBindings.size() ) { + continue; + } + bidirectional = true; + for ( int ndx = valueBindings.size(); --ndx >= 0; ) { + if ( columns.get(ndx) != valueBindings.get( ndx ).getValue() ) { + bidirectional = false; + break; + } + } + if ( bidirectional ) { + break; + } + } + if ( bidirectional ) continue; + + // Create the synthetic back reference attribute + SingularAttribute syntheticAttribute = + referencedEntityBinding.getEntity().createSyntheticSingularAttribute( + SyntheticAttributeHelper.createBackRefAttributeName( pluralAttributeBinding.getAttribute().getRole() ) ); + // Create the back reference attribute binding. + BackRefAttributeBinding backRefAttributeBinding = + referencedEntityBinding.makeBackRefAttributeBinding( syntheticAttribute, pluralAttributeBinding ); + final HibernateTypeDescriptor keyTypeDescriptor = keyBinding.getHibernateTypeDescriptor(); + final HibernateTypeDescriptor hibernateTypeDescriptor = backRefAttributeBinding.getHibernateTypeDescriptor(); + hibernateTypeDescriptor.setJavaTypeName( keyTypeDescriptor.getJavaTypeName() ); + hibernateTypeDescriptor.setExplicitTypeName( keyTypeDescriptor.getExplicitTypeName() ); + hibernateTypeDescriptor.setToOne( keyTypeDescriptor.isToOne() ); + hibernateTypeDescriptor.getTypeParameters().putAll( keyTypeDescriptor.getTypeParameters() ); + hibernateTypeDescriptor.setResolvedTypeMapping( keyTypeDescriptor.getResolvedTypeMapping() ); + backRefAttributeBinding.getAttribute().resolveType( + keyBinding.getReferencedAttributeBinding().getAttribute().getSingularAttributeType() ); + } + for ( MetadataSourceProcessor metadataSourceProcessor : metadataSourceProcessors ) { metadataSourceProcessor.processMappingDependentMetadata(); } @@ -383,9 +456,9 @@ public class MetadataImpl implements MetadataImplementor, Serializable { return classLoaderService.getValue(); } - private PersisterClassResolver persisterClassResolverService() { - return persisterClassResolverService.getValue(); - } +// private PersisterClassResolver persisterClassResolverService() { +// return persisterClassResolverService.getValue(); +// } @Override public Options getOptions() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml index 43cce15b42..923747beea 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml @@ -57,8 +57,10 @@ diff --git a/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParentTest.java b/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParentTest.java index 9d91b65074..d91d9bf955 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParentTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cascade/circle/CascadeMergeToChildBeforeParentTest.java @@ -23,6 +23,10 @@ */ package org.hibernate.test.cascade.circle; +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +import static org.junit.Assert.assertThat; + import org.junit.Test; import org.hibernate.Session; @@ -119,7 +123,7 @@ public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestC route.getNodes().add( pickupNode ); route.getNodes().add( deliveryNode ); - Route mergedRoute = (Route) s.merge( route ); + assertThat( s.merge( route ), instanceOf( Route.class ) ); s.getTransaction().commit(); s.close(); @@ -189,7 +193,7 @@ public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestC vehicle.setTransientField( "anewvalue" ); vehicle.setRoute( route ); - Route mergedRoute = (Route) s.merge( route ); + assertThat( s.merge( route ), instanceOf( Route.class ) ); s.getTransaction().commit(); s.close(); @@ -276,7 +280,7 @@ public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestC vehicle.setTransientField( "anewvalue" ); vehicle.setRoute( route ); - Route mergedRoute = (Route) s.merge( route ); + assertThat( s.merge( route ), instanceOf( Route.class ) ); s.getTransaction().commit(); s.close();