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 1446e3d45b..6a368d2f7b 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 @@ -92,7 +92,6 @@ import org.hibernate.metamodel.spi.binding.SetBinding; import org.hibernate.metamodel.spi.binding.SingularAssociationAttributeBinding; import org.hibernate.metamodel.spi.binding.SingularAttributeBinding; import org.hibernate.metamodel.spi.binding.SingularAttributeBinding.NaturalIdMutability; -import org.hibernate.metamodel.spi.binding.TypeDefinition; import org.hibernate.metamodel.spi.domain.Aggregate; import org.hibernate.metamodel.spi.domain.Attribute; import org.hibernate.metamodel.spi.domain.Entity; @@ -121,7 +120,6 @@ import org.hibernate.metamodel.spi.source.DerivedValueSource; import org.hibernate.metamodel.spi.source.DiscriminatorSource; import org.hibernate.metamodel.spi.source.EntityHierarchy; import org.hibernate.metamodel.spi.source.EntitySource; -import org.hibernate.metamodel.spi.source.ExplicitHibernateTypeSource; import org.hibernate.metamodel.spi.source.ForeignKeyContributingSource; import org.hibernate.metamodel.spi.source.ForeignKeyContributingSource.JoinColumnResolutionContext; import org.hibernate.metamodel.spi.source.ForeignKeyContributingSource.JoinColumnResolutionDelegate; @@ -163,7 +161,7 @@ import org.hibernate.type.ComponentType; import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.Type; -import org.hibernate.type.TypeFactory; + import org.jboss.logging.Logger; /** @@ -182,68 +180,6 @@ public class Binder { Binder.class.getName() ); - private static ValueHolder< Class< ? >> createSingularAttributeJavaType( - final Class< ? > attributeContainerClassReference, - final String attributeName ) { - ValueHolder.DeferredInitializer< Class< ? >> deferredInitializer = - new ValueHolder.DeferredInitializer< Class< ? >>() { - public Class< ? > initialize() { - return ReflectHelper.reflectedPropertyClass( - attributeContainerClassReference, - attributeName ); - } - }; - return new ValueHolder< Class< ? >>( deferredInitializer ); - } - - private static ValueHolder< Class< ? >> createSingularAttributeJavaType( - final SingularAttribute attribute ) { - return createSingularAttributeJavaType( - attribute.getAttributeContainer().getClassReference(), - attribute.getName() - ); - } - - private static String interpretIdentifierUnsavedValue( final IdentifierSource identifierSource,final 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 NON_AGGREGATED_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 final MetadataImplementor metadata; private final IdentifierGeneratorFactory identifierGeneratorFactory; private final ObjectNameNormalizer nameNormalizer; @@ -257,33 +193,555 @@ public class Binder { private final LinkedList entityModes = new LinkedList(); private final HibernateTypeHelper typeHelper; // todo: refactor helper and remove redundant methods in this class - public Binder( final MetadataImplementor metadata, final IdentifierGeneratorFactory identifierGeneratorFactory ) { + public Binder(final MetadataImplementor metadata, + final IdentifierGeneratorFactory identifierGeneratorFactory) { this.metadata = metadata; this.identifierGeneratorFactory = identifierGeneratorFactory; this.typeHelper = new HibernateTypeHelper( this, metadata ); this.nameNormalizer = metadata.getObjectNameNormalizer(); } - private static void addUniqueConstraintForNaturalIdColumn(final TableSpecification table, final Column column) { - final UniqueKey uniqueKey = table.getOrCreateUniqueKey( "natural_id_unique_key_" ); - uniqueKey.addColumn( column ); + /** + * The entry point of {@linkplain Binder} class, binds all the entity hierarchy one by one. + * + * @param entityHierarchies The entity hierarchies resolved from mappings + */ + public void bindEntityHierarchies(final Iterable 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 ) { + bindEntityHierarchy( entityHierarchy ); + } } - private AttributeBinding attributeBinding( final String entityName, final String attributeName ) { + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Entity binding relates methods + + /** + * Binding a single entity hierarchy. + * + * @param entityHierarchy The entity hierarchy to be binded. + * + * @return The root {@link EntityBinding} of the entity hierarchy mapping. + */ + private EntityBinding bindEntityHierarchy(final EntityHierarchy entityHierarchy) { + final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource(); + // Return existing binding if available + EntityBinding rootEntityBinding = metadata.getEntityBinding( rootEntitySource.getEntityName() ); + if ( rootEntityBinding != null ) { + return rootEntityBinding; + } + setupBindingContext( entityHierarchy, rootEntitySource ); + try { + // Create root entity binding + rootEntityBinding = createEntityBinding( null, rootEntitySource ); + bindPrimaryTable( rootEntityBinding, rootEntitySource ); + // 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 { + cleanupBindingContext(); + } + return rootEntityBinding; + } + + private EntityBinding findOrBindEntityBinding( + final ValueHolder> entityJavaTypeValue, + final String explicitEntityName) { + final String referencedEntityName = + explicitEntityName != null + ? explicitEntityName + : entityJavaTypeValue.getValue().getName(); + return findOrBindEntityBinding( referencedEntityName ); + } + + private EntityBinding findOrBindEntityBinding(final String entityName) { // Check if binding has already been created - final EntityBinding entityBinding = findOrBindEntityBinding( entityName ); - final AttributeSource attributeSource = attributeSourcesByName.get( attributeSourcesByNameKey( entityName, attributeName ) ); - bindAttribute( entityBinding, attributeSource ); - return entityBinding.locateAttributeBinding( attributeName ); + EntityBinding entityBinding = metadata.getEntityBinding( entityName ); + if ( entityBinding == null ) { + // Find appropriate source to create binding + final EntitySource entitySource = entitySourcesByName.get( entityName ); + if ( entitySource == null ) { + String msg = log.missingEntitySource( entityName ); + throw bindingContext().makeMappingException( msg ); + } + + // Get super entity binding (creating it if necessary using recursive call to this method) + if ( SubclassEntitySource.class.isInstance( entitySource ) ) { + String superEntityName = ( (SubclassEntitySource) entitySource ).superclassEntitySource() + .getEntityName(); + EntityBinding superEntityBinding = findOrBindEntityBinding( superEntityName ); + entityBinding = bindSubEntity( superEntityBinding, entitySource ); + } + else { + EntityHierarchy entityHierarchy = entityHierarchiesByRootEntitySource.get( + RootEntitySource.class.cast( + entitySource + ) + ); + entityBinding = bindEntityHierarchy( entityHierarchy ); + } + } + return entityBinding; } - private static String attributeSourcesByNameKey( final String entityName, final String attributeName ) { - return entityName + "." + attributeName; + private EntityBinding createEntityBinding( + final EntityBinding superEntityBinding, + final EntitySource entitySource) { + // Create binding + final InheritanceType inheritanceType = inheritanceTypes.peek(); + final EntityMode entityMode = entityModes.peek(); + final EntityBinding entityBinding = + entitySource instanceof RootEntitySource ? new EntityBinding( + inheritanceType, + entityMode + ) : new EntityBinding( + superEntityBinding + ); + // Create domain entity + final String entityClassName = entityMode == EntityMode.POJO ? entitySource.getClassName() : null; + LocalBindingContext bindingContext = bindingContext(); + entityBinding.setEntity( + new Entity( + entitySource.getEntityName(), + entityClassName, + bindingContext.makeClassReference( entityClassName ), + superEntityBinding == null ? null : superEntityBinding.getEntity() + ) + ); + + entityBinding.setEntityName( entitySource.getEntityName() ); + entityBinding.setJpaEntityName( entitySource.getJpaEntityName() ); //must before creating primary table + entityBinding.setDynamicUpdate( entitySource.isDynamicUpdate() ); + entityBinding.setDynamicInsert( entitySource.isDynamicInsert() ); + entityBinding.setBatchSize( entitySource.getBatchSize() ); + entityBinding.setSelectBeforeUpdate( entitySource.isSelectBeforeUpdate() ); + entityBinding.setAbstract( entitySource.isAbstract() ); + entityBinding.setCustomLoaderName( entitySource.getCustomLoaderName() ); + entityBinding.setCustomInsert( entitySource.getCustomSqlInsert() ); + entityBinding.setCustomUpdate( entitySource.getCustomSqlUpdate() ); + entityBinding.setCustomDelete( entitySource.getCustomSqlDelete() ); + entityBinding.setJpaCallbackClasses( entitySource.getJpaCallbackClasses() ); + + // todo: deal with joined and unioned subclass bindings + // todo: bind fetch profiles + // Configure rest of binding + final String customTuplizerClassName = entitySource.getCustomTuplizerClassName(); + if ( customTuplizerClassName != null ) { + entityBinding.setCustomEntityTuplizerClass( + bindingContext.locateClassByName( + customTuplizerClassName + ) + ); + } + final String customPersisterClassName = entitySource.getCustomPersisterClassName(); + if ( customPersisterClassName != null ) { + entityBinding.setCustomEntityPersisterClass( + bindingContext.locateClassByName( + customPersisterClassName + ) + ); + } + entityBinding.setMetaAttributeContext( + createMetaAttributeContext( + entitySource.getMetaAttributeSources(), + true, + metadata.getGlobalMetaAttributeContext() + ) + ); + + if ( entitySource.getSynchronizedTableNames() != null ) { + entityBinding.addSynchronizedTableNames( entitySource.getSynchronizedTableNames() ); + } + resolveEntityLaziness( entityBinding, entitySource ); + // Register binding with metadata + metadata.addEntity( entityBinding ); + return entityBinding; + } + + private void resolveEntityLaziness( + final EntityBinding entityBinding, + final EntitySource entitySource) { + if ( entityModes.peek() == EntityMode.POJO ) { + final String proxy = entitySource.getProxy(); + if ( proxy == null ) { + if ( entitySource.isLazy() ) { + entityBinding.setProxyInterfaceType( entityBinding.getEntity().getClassReferenceUnresolved() ); + entityBinding.setLazy( true ); + } + } + else { + entityBinding.setProxyInterfaceType( + bindingContext().makeClassReference( + bindingContext().qualifyClassName( + proxy + ) + ) + ); + entityBinding.setLazy( true ); + } + } + else { + entityBinding.setProxyInterfaceType( null ); + entityBinding.setLazy( entitySource.isLazy() ); + } + } + + private void bindSubEntities( + final EntityBinding entityBinding, + final EntitySource entitySource) { + for ( final SubclassEntitySource subEntitySource : entitySource.subclassEntitySources() ) { + bindSubEntity( entityBinding, subEntitySource ); + } + } + + private EntityBinding bindSubEntity( + final EntityBinding superEntityBinding, + final EntitySource entitySource) { + // 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( superEntityBinding, entitySource ); + entityBinding.setMutable( entityBinding.getHierarchyDetails().getRootEntityBinding().isMutable() ); + markSuperEntityTableAbstractIfNecessary( superEntityBinding ); + bindPrimaryTable( entityBinding, entitySource ); + bindSubEntityPrimaryKey( entityBinding, entitySource ); + bindSecondaryTables( entityBinding, entitySource ); + bindUniqueConstraints( entityBinding, entitySource ); + bindAttributes( entityBinding, entitySource ); + bindSubEntities( entityBinding, entitySource ); + return entityBinding; + } + finally { + bindingContexts.pop(); + } + } + + 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, + bindingContext().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 ); + final String discriminatorValue = rootEntitySource.getDiscriminatorMatchValue(); + if ( discriminatorValue != null ) { + rootEntityBinding.setDiscriminatorMatchValue( discriminatorValue ); + } + else if ( !Modifier.isAbstract( + bindingContext().locateClassByName( rootEntitySource.getEntityName() ) + .getModifiers() + ) ) { + // Use the class name as a default if no dscriminator value. + // However, skip abstract classes -- obviously no discriminators there. + rootEntityBinding.setDiscriminatorMatchValue( rootEntitySource.getEntityName() ); + } + // Configure discriminator hibernate type + final String typeName = + discriminatorSource.getExplicitHibernateTypeName() != null + ? discriminatorSource.getExplicitHibernateTypeName() + : "string"; + final HibernateTypeDescriptor hibernateTypeDescriptor = discriminator.getExplicitHibernateTypeDescriptor(); + hibernateTypeDescriptor.setExplicitTypeName( typeName ); + Type resolvedType = typeHelper.heuristicType( hibernateTypeDescriptor ); + HibernateTypeHelper.bindHibernateResolvedType( hibernateTypeDescriptor, resolvedType ); + typeHelper.bindJdbcDataType( resolvedType, value ); + } + + private void bindVersion( + final EntityBinding rootEntityBinding, + final VersionAttributeSource versionAttributeSource) { + if ( versionAttributeSource == null ) { + return; + } + final EntityVersion version = rootEntityBinding.getHierarchyDetails().getEntityVersion(); + version.setVersioningAttributeBinding( + (BasicAttributeBinding) bindAttribute( + rootEntityBinding, + versionAttributeSource + ) + ); + // ensure version is non-nullable + for ( RelationalValueBinding valueBinding : version.getVersioningAttributeBinding() + .getRelationalValueBindings() ) { + if ( valueBinding.getValue() instanceof Column ) { + ( (Column) valueBinding.getValue() ).setNullable( false ); + } + } + version.setUnsavedValue( + versionAttributeSource.getUnsavedValue() == null + ? "undefined" + : versionAttributeSource.getUnsavedValue() + ); + } + + private void bindMultiTenancy( + final EntityBinding rootEntityBinding, + final 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 bindPrimaryTable( + final EntityBinding entityBinding, + final EntitySource entitySource) { + final EntityBinding superEntityBinding = entityBinding.getSuperEntityBinding(); + final InheritanceType inheritanceType = entityBinding.getHierarchyDetails().getInheritanceType(); + final TableSpecification table; + final String tableName; + // single table and sub entity + if ( superEntityBinding != null && inheritanceType == InheritanceType.SINGLE_TABLE ) { + table = superEntityBinding.getPrimaryTable(); + tableName = superEntityBinding.getPrimaryTableName(); + // Configure discriminator if present + final String discriminatorValue = entitySource.getDiscriminatorMatchValue() != null ? + entitySource.getDiscriminatorMatchValue() + : entitySource.getEntityName(); + entityBinding.setDiscriminatorMatchValue( discriminatorValue ); + } + + // single table and root entity + // joined + // table per class and non-abstract entity + else { + Table includedTable = null; + if ( superEntityBinding != null + && inheritanceType == InheritanceType.TABLE_PER_CLASS + && Table.class.isInstance( superEntityBinding.getPrimaryTable() ) ) { + includedTable = Table.class.cast( superEntityBinding.getPrimaryTable() ); + } + table = createTable( + entitySource.getPrimaryTable(), new DefaultNamingStrategy() { + + @Override + public String defaultName() { + String name = StringHelper.isNotEmpty( entityBinding.getJpaEntityName() ) ? entityBinding.getJpaEntityName() : entityBinding + .getEntity().getName(); + return bindingContext().getNamingStrategy().classToTableName( name ); + } + }, + includedTable + ); + tableName = table.getLogicalName().getText(); + } + entityBinding.setPrimaryTable( table ); + entityBinding.setPrimaryTableName( tableName ); + } + + private void bindSecondaryTables( + final EntityBinding entityBinding, + final EntitySource entitySource) { + for ( final SecondaryTableSource secondaryTableSource : entitySource.getSecondaryTables() ) { + final TableSpecification table = createTable( secondaryTableSource.getTableSource(), null ); + table.addComment( secondaryTableSource.getComment() ); + List joinRelationalValueBindings; + // TODO: deal with property-refs??? + if ( secondaryTableSource.getPrimaryKeyColumnSources().isEmpty() ) { + final List joinedColumns = entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); + joinRelationalValueBindings = new ArrayList( joinedColumns.size() ); + for ( Column joinedColumn : joinedColumns ) { + Column joinColumn = table.locateOrCreateColumn( + bindingContext().getNamingStrategy().joinKeyColumnName( + joinedColumn.getColumnName().getText(), + entityBinding.getPrimaryTable().getLogicalName().getText() + ) + ); + joinRelationalValueBindings.add( new RelationalValueBinding( joinColumn, true, false ) ); + } + } + else { + joinRelationalValueBindings = new ArrayList( + secondaryTableSource.getPrimaryKeyColumnSources() + .size() + ); + final List primaryKeyColumns = entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); + if ( primaryKeyColumns.size() != secondaryTableSource.getPrimaryKeyColumnSources().size() ) { + throw bindingContext().makeMappingException( + String.format( + "The number of primary key column sources provided for a secondary table is not equal to the number of columns in the primary key for [%s].", + entityBinding.getEntityName() + ) + ); + } + for ( int i = 0; i < primaryKeyColumns.size(); i++ ) { + // todo : apply naming strategy to infer missing column name + final ColumnSource joinColumnSource = secondaryTableSource.getPrimaryKeyColumnSources().get( i ); + Column column = table.locateColumn( joinColumnSource.getName() ); + if ( column == null ) { + column = table.createColumn( joinColumnSource.getName() ); + if ( joinColumnSource.getSqlType() != null ) { + column.setSqlType( joinColumnSource.getSqlType() ); + } + } + joinRelationalValueBindings.add( new RelationalValueBinding( column, true, false ) ); + } + } + typeHelper.bindJdbcDataType( + entityBinding.getHierarchyDetails() + .getEntityIdentifier() + .getAttributeBinding() + .getHibernateTypeDescriptor() + .getResolvedTypeMapping(), + joinRelationalValueBindings + ); + + // TODO: make the foreign key column the primary key??? + final ForeignKey foreignKey = bindForeignKey( + quoteIdentifierIfNonEmpty( secondaryTableSource.getExplicitForeignKeyName() ), + extractColumnsFromRelationalValueBindings( joinRelationalValueBindings ), + determineForeignKeyTargetColumns( entityBinding, secondaryTableSource ) + ); + SecondaryTable secondaryTable = new SecondaryTable( table, foreignKey ); + secondaryTable.setFetchStyle( secondaryTableSource.getFetchStyle() ); + secondaryTable.setInverse( secondaryTableSource.isInverse() ); + secondaryTable.setOptional( secondaryTableSource.isOptional() ); + secondaryTable.setCascadeDeleteEnabled( secondaryTableSource.isCascadeDeleteEnabled() ); + entityBinding.addSecondaryTable( secondaryTable ); + } + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ identifier binding relates methods + 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 NON_AGGREGATED_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 params = new HashMap(); + 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 ) { + final EntityBinding rootEntityBinding, + final AggregatedCompositeIdentifierSource identifierSource) { // locate the attribute binding final CompositeAttributeBinding idAttributeBinding = (CompositeAttributeBinding) bindIdentifierAttribute( @@ -293,7 +751,7 @@ public class Binder { // Configure ID generator IdGenerator generator = identifierSource.getIdentifierGeneratorDescriptor(); if ( generator == null ) { - final Map< String, String > params = new HashMap< String, String >(); + final Map params = new HashMap(); params.put( IdentifierGenerator.ENTITY_NAME, rootEntityBinding.getEntity().getName() ); generator = new IdGenerator( "default_assign_identity_generator", "assigned", params ); } @@ -304,7 +762,158 @@ public class Binder { rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsAggregatedCompositeIdentifier( idAttributeBinding, generator, - unsavedValue ); + unsavedValue + ); + } + + private void bindNonAggregatedCompositeIdentifier( + final EntityBinding rootEntityBinding, + final NonAggregatedCompositeIdentifierSource identifierSource) { + // locate the attribute bindings for the real attributes + List idAttributeBindings = + new ArrayList(); + for ( SingularAttributeSource attributeSource : identifierSource.getAttributeSourcesMakingUpIdentifier() ) { + SingularAttributeBinding singularAttributeBinding = + bindIdentifierAttribute( rootEntityBinding, attributeSource ); + if ( singularAttributeBinding.isAssociation() ) { + throw new NotYetImplementedException( + "composite IDs containing an association attribute is not implemented yet." + ); + } + idAttributeBindings.add( singularAttributeBinding ); + } + + final Class idClassClass = identifierSource.getLookupIdClass(); + final String idClassPropertyAccessorName = + idClassClass == null ? + null : + propertyAccessorName( identifierSource.getIdClassPropertyAccessorName() ); + + // Configure ID generator + IdGenerator generator = identifierSource.getIdentifierGeneratorDescriptor(); + if ( generator == null ) { + final Map params = new HashMap(); + params.put( IdentifierGenerator.ENTITY_NAME, rootEntityBinding.getEntity().getName() ); + generator = new IdGenerator( "default_assign_identity_generator", "assigned", params ); + } + // Create the synthetic attribute + final SingularAttribute syntheticAttribute = + rootEntityBinding.getEntity().createSyntheticCompositeAttribute( + SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME, + rootEntityBinding.getEntity() + ); + + final CompositeAttributeBinding syntheticAttributeBinding = + rootEntityBinding.makeVirtualCompositeAttributeBinding( + syntheticAttribute, + createMetaAttributeContext( rootEntityBinding, identifierSource.getMetaAttributeSources() ), + idAttributeBindings + ); + typeHelper.bindHibernateTypeDescriptor( + syntheticAttributeBinding.getHibernateTypeDescriptor(), + syntheticAttribute.getSingularAttributeType().getClassName(), + null, + null + ); + + // Create the synthetic attribute binding. + rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsNonAggregatedCompositeIdentifier( + syntheticAttributeBinding, + generator, + interpretIdentifierUnsavedValue( identifierSource, generator ), + idClassClass, + idClassPropertyAccessorName + ); + + final Type resolvedType = metadata.getTypeResolver().getTypeFactory().embeddedComponent( + new ComponentMetamodel( syntheticAttributeBinding, true, false ) + ); + HibernateTypeHelper.bindHibernateResolvedType( + syntheticAttributeBinding.getHibernateTypeDescriptor(), + resolvedType + ); + } + + 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 bindingContext().makeMappingException( + 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() + ) + ); + } + rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumns().get( 0 ).setIdentity( true ); + } + if ( PersistentIdentifierGenerator.class.isInstance( entityIdentifier.getIdentifierGenerator() ) ) { + ( (PersistentIdentifierGenerator) entityIdentifier.getIdentifierGenerator() ).registerExportables( metadata.getDatabase() ); + } + } + + private SingularAttributeBinding bindIdentifierAttribute( + final AttributeBindingContainer attributeBindingContainer, + final SingularAttributeSource attributeSource) { + return bindSingularAttribute( attributeBindingContainer, attributeSource, true ); + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Attributes binding relates methods + private void bindAttributes( + final AttributeBindingContainer attributeBindingContainer, + final AttributeSourceContainer attributeSourceContainer) { + for ( final AttributeSource attributeSource : attributeSourceContainer.attributeSources() ) { + bindAttribute( attributeBindingContainer, attributeSource ); + } + } + + private void bindAttributes( + final CompositeAttributeBindingContainer compositeAttributeBindingContainer, + final AttributeSourceContainer attributeSourceContainer) { + if ( compositeAttributeBindingContainer.getParentReference() == null ) { + bindAttributes( + (AttributeBindingContainer) compositeAttributeBindingContainer, + attributeSourceContainer + ); + } + else { + for ( final AttributeSource subAttributeSource : attributeSourceContainer.attributeSources() ) { + if ( !subAttributeSource.getName() + .equals( compositeAttributeBindingContainer.getParentReference().getName() ) ) { + bindAttribute( + compositeAttributeBindingContainer, + subAttributeSource + ); + } + } + } + + } + + + private AttributeBinding attributeBinding( + final String entityName, + final String attributeName) { + // Check if binding has already been created + final EntityBinding entityBinding = findOrBindEntityBinding( entityName ); + final AttributeSource attributeSource = attributeSourcesByName.get( + attributeSourcesByNameKey( + entityName, + attributeName + ) + ); + bindAttribute( entityBinding, attributeSource ); + return entityBinding.locateAttributeBinding( attributeName ); } private AttributeBinding bindAttribute( @@ -322,71 +931,24 @@ public class Binder { SingularAttributeSource.class.cast( attributeSource ), false ) : - bindPluralAttribute( attributeBindingContainer,PluralAttributeSource.class.cast( attributeSource ) ); - } - - private void bindAttributes( - final CompositeAttributeBindingContainer compositeAttributeBindingContainer, - final AttributeSourceContainer attributeSourceContainer - ) { - if ( compositeAttributeBindingContainer.getParentReference() == null ) { - bindAttributes( - (AttributeBindingContainer) compositeAttributeBindingContainer, - attributeSourceContainer - ); - } - else { - for ( final AttributeSource subAttributeSource : attributeSourceContainer.attributeSources() ) { - if ( ! subAttributeSource.getName().equals( compositeAttributeBindingContainer.getParentReference().getName() ) ) { - bindAttribute( - compositeAttributeBindingContainer, - subAttributeSource - ); - } - } - } - - } - - private void bindAttributes( - final AttributeBindingContainer attributeBindingContainer, - 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 ) - ); + bindPluralAttribute( attributeBindingContainer, PluralAttributeSource.class.cast( attributeSource ) ); } private BasicAttributeBinding bindBasicAttribute( final AttributeBindingContainer attributeBindingContainer, final SingularAttributeSource attributeSource, - SingularAttribute attribute ) { + SingularAttribute attribute) { if ( attribute == null ) { attribute = createSingularAttribute( attributeBindingContainer, attributeSource ); } - final List< RelationalValueBinding > relationalValueBindings = + final List relationalValueBindings = bindValues( attributeBindingContainer, attributeSource, attribute, locateDefaultTableSpecificationForAttribute( attributeBindingContainer, attributeSource ), - false); + false + ); final BasicAttributeBinding attributeBinding = attributeBindingContainer.makeBasicAttributeBinding( attribute, @@ -396,324 +958,47 @@ public class Binder { attributeSource.isLazy(), attributeSource.getNaturalIdMutability(), createMetaAttributeContext( attributeBindingContainer, attributeSource ), - attributeSource.getGeneration() ); - typeHelper.bindSingularAttributeTypeInformation( attributeSource, - attributeBinding ); + attributeSource.getGeneration() + ); + typeHelper.bindSingularAttributeTypeInformation( + attributeSource, + attributeBinding + ); return attributeBinding; } - private void bindBasicCollectionElement( - final BasicPluralAttributeElementBinding elementBinding, - final BasicPluralAttributeElementSource elementSource, - final String defaultElementJavaTypeName) { - bindBasicPluralElementRelationalValues( elementSource, elementBinding ); - bindBasicCollectionElementType( elementBinding, elementSource, defaultElementJavaTypeName ); - } - - private void bindBasicCollectionElementType( - final BasicPluralAttributeElementBinding elementBinding, - final BasicPluralAttributeElementSource elementSource, - final String defaultElementJavaTypeName) { - bindHibernateTypeDescriptor( - elementBinding.getHibernateTypeDescriptor(), - elementSource.getExplicitHibernateTypeSource(), - defaultElementJavaTypeName - ); - Type resolvedElementType = heuristicType( elementBinding.getHibernateTypeDescriptor() ); - bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); - typeHelper.bindJdbcDataType( - resolvedElementType, - elementBinding.getRelationalValueBindings() - ); - } - - private void bindNonAssociationCollectionKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeSource attributeSource) { - if ( attributeSource.getElementSource().getNature() != PluralAttributeElementSource.Nature.BASIC && - attributeSource.getElementSource().getNature() != PluralAttributeElementSource.Nature.AGGREGATE) { - throw new AssertionFailure( - String.format( - "Expected basic or aggregate attribute binding; instead got {%s}", - attributeSource.getElementSource().getNature() - ) - ); - } - attributeBinding.getPluralAttributeKeyBinding().setInverse( false ); - TableSpecification collectionTable = createBasicCollectionTable( - attributeBinding, attributeSource.getCollectionTableSpecificationSource() - ); - 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(), - false) ); - } - - private void bindBasicSetCollectionTablePrimaryKey(final SetBinding attributeBinding) { - final BasicPluralAttributeElementBinding elementBinding = - ( BasicPluralAttributeElementBinding ) attributeBinding.getPluralAttributeElementBinding(); - if ( elementBinding.getNature() != PluralAttributeElementBinding.Nature.BASIC ) { - throw bindingContext().makeMappingException( String.format( - "Expected a SetBinding with an element of nature Nature.BASIC; instead was %s", - elementBinding.getNature() ) ); - } - if ( hasAnyNonNullableColumns( elementBinding.getRelationalValueBindings() ) ) { - bindSetCollectionTablePrimaryKey( attributeBinding ); - } - 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 bindSetCollectionTablePrimaryKey(final SetBinding attributeBinding) { - final PluralAttributeElementBinding elementBinding = attributeBinding.getPluralAttributeElementBinding(); - 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() ); - } - } - } - - private void bindCompositeCollectionElement( - final CompositePluralAttributeElementBinding elementBinding, - final CompositePluralAttributeElementSource elementSource, - final String defaultElementJavaTypeName ) { - final PluralAttributeBinding pluralAttributeBinding = elementBinding.getPluralAttributeBinding(); - ValueHolder> defaultElementJavaClassReference = null; - // Create the aggregate type - // TODO: aggregateName should be set to elementSource.getPath() (which is currently not implemented) - // or Binder should define AttributeBindingContainer paths instead. - String aggregateName = pluralAttributeBinding.getAttribute().getRole() + ".element"; - final Aggregate aggregate; - if ( elementSource.getClassName() != null ) { - aggregate = new Aggregate( - aggregateName, - elementSource.getClassName(), - elementSource.getClassReference() != null ? - elementSource.getClassReference() : - bindingContext().makeClassReference( elementSource.getClassName() ), - null - ); - } - else { - defaultElementJavaClassReference = bindingContext().makeClassReference( defaultElementJavaTypeName ); - aggregate = new Aggregate( - aggregateName, - defaultElementJavaClassReference.getValue().getName(), - defaultElementJavaClassReference, - null - ); - } - final SingularAttribute parentAttribute = - StringHelper.isEmpty( elementSource.getParentReferenceAttributeName() ) ? - null : - aggregate.createSingularAttribute( elementSource.getParentReferenceAttributeName() ); - final CompositeAttributeBindingContainer compositeAttributeBindingContainer = - elementBinding.createCompositeAttributeBindingContainer( - aggregate, - createMetaAttributeContext( - pluralAttributeBinding.getContainer(), - elementSource.getMetaAttributeSources() - ), - parentAttribute - ); - - bindAttributes( compositeAttributeBindingContainer, elementSource ); - Type resolvedType = metadata.getTypeResolver().getTypeFactory().component( - new ComponentMetamodel( compositeAttributeBindingContainer, false, false ) - ); - // TODO: binding the HibernateTypeDescriptor should be simplified since we know the class name already - bindHibernateTypeDescriptor( - elementBinding.getHibernateTypeDescriptor(), - aggregate.getClassName(), - null, - defaultElementJavaClassReference == null ? null : defaultElementJavaClassReference.getValue().getName() - ); - bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedType ); - /** - * TODO - * don't know why, but see org.hibernate.mapping.Property#getCompositeCascadeStyle - * - * and not sure if this is the right place to apply this logic, apparently source level is not okay, so here it is, for now. - */ - for ( AttributeBinding ab : compositeAttributeBindingContainer.attributeBindings() ) { - if ( ab.isCascadeable() ) { - CascadeStyle cascadeStyle = Cascadeable.class.cast( ab ).getCascadeStyle(); - if ( cascadeStyle != CascadeStyles.NONE ) { - elementBinding.setCascadeStyle( CascadeStyles.ALL ); - } - } - } - if ( elementBinding.getCascadeStyle() == null || elementBinding.getCascadeStyle() == CascadeStyles.NONE ) { - elementBinding.setCascadeStyle( determineCascadeStyle( elementSource.getCascadeStyles() ) ); - } - } - - 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(), - attributeBinding.getPluralAttributeElementBinding().getNature() != PluralAttributeElementBinding.Nature.ONE_TO_MANY ) - .get( 0 ).getValue() ); - if ( attributeBinding.getPluralAttributeElementBinding().getNature() == PluralAttributeElementBinding.Nature.ONE_TO_MANY ) { - if ( Column.class.isInstance( indexBinding.getIndexRelationalValue() ) ) { - // TODO: fix this when column nullability is refactored - ( (Column) indexBinding.getIndexRelationalValue() ).setNullable( true ); - } - } - // TODO: create a foreign key if non-inverse and the index is an association - - bindHibernateTypeDescriptor( - indexBinding.getHibernateTypeDescriptor(), - attributeSource.explicitHibernateTypeSource(), - defaultIndexJavaTypeName ); - Type resolvedElementType = heuristicType( indexBinding.getHibernateTypeDescriptor() ); - bindHibernateResolvedType( indexBinding.getHibernateTypeDescriptor(), resolvedElementType ); - typeHelper.bindJdbcDataType( resolvedElementType, indexBinding.getIndexRelationalValue() ); - } - - private void bindCollectionTableForeignKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeKeySource keySource, - TableSpecification collectionTable) { - - final AttributeBindingContainer attributeBindingContainer = attributeBinding.getContainer(); - final PluralAttributeKeyBinding keyBinding = attributeBinding.getPluralAttributeKeyBinding(); - - List sourceColumnBindings = - bindValues( + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ singular attributes binding + private SingularAttributeBinding bindSingularAttribute( + final AttributeBindingContainer attributeBindingContainer, + final SingularAttributeSource attributeSource, + final boolean isIdentifierAttribute) { + final SingularAttributeSource.Nature nature = attributeSource.getNature(); + final SingularAttribute attribute = + attributeBindingContainer.getAttributeContainer().locateSingularAttribute( attributeSource.getName() ); + switch ( nature ) { + case BASIC: + return bindBasicAttribute( attributeBindingContainer, attributeSource, attribute ); + case ONE_TO_ONE: + return bindOneToOneAttribute( attributeBindingContainer, - keySource, - attributeBinding.getAttribute(), - collectionTable, - attributeBinding.getPluralAttributeElementBinding().getNature() != PluralAttributeElementBinding.Nature.ONE_TO_MANY + ToOneAttributeSource.class.cast( attributeSource ), + attribute ); - // Determine if the foreign key (source) column is updatable and also extract the columns out - // of the RelationalValueBindings. - boolean isInsertable = false; - boolean isUpdatable = false; - List sourceColumns = new ArrayList( sourceColumnBindings.size() ); - for ( RelationalValueBinding relationalValueBinding : sourceColumnBindings ) { - 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." ); - } - isInsertable = isInsertable || relationalValueBinding.isIncludeInInsert(); - isUpdatable = isUpdatable || relationalValueBinding.isIncludeInUpdate(); - sourceColumns.add( (Column) value ); - } - keyBinding.setInsertable( isInsertable ); - keyBinding.setUpdatable( isUpdatable ); - - List targetColumns = - determineForeignKeyTargetColumns( - attributeBindingContainer.seekEntityBinding(), - keySource + case MANY_TO_ONE: + return bindManyToOneAttribute( + attributeBindingContainer, + ToOneAttributeSource.class.cast( attributeSource ), + attribute ); - - ForeignKey foreignKey = bindForeignKey( - quoteIdentifierIfNonEmpty( keySource.getExplicitForeignKeyName() ), - sourceColumns, - 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() ) { - typeHelper.bindJdbcDataType( subType, fkColumnIterator.next() ); - } - } else { - typeHelper.bindJdbcDataType( resolvedKeyType, fkColumnIterator.next() ); - } - } - - private void bindCollectionTablePrimaryKey( - final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeSource attributeSource) { - PluralAttributeSource.Nature pluralAttributeNature = attributeSource.getNature(); - if ( attributeSource.getElementSource().getNature() == PluralAttributeElementSource.Nature.ONE_TO_MANY - || pluralAttributeNature == PluralAttributeSource.Nature.BAG ) { - return; - } - if ( attributeBinding.getPluralAttributeElementBinding().getNature() == PluralAttributeElementBinding.Nature.BASIC ) { - if ( pluralAttributeNature == PluralAttributeSource.Nature.SET ) { - bindBasicSetCollectionTablePrimaryKey( (SetBinding) attributeBinding ); - } else if ( - pluralAttributeNature == PluralAttributeSource.Nature.LIST - || pluralAttributeNature == PluralAttributeSource.Nature.MAP - || pluralAttributeNature == PluralAttributeSource.Nature.ARRAY ) { - bindIndexedCollectionTablePrimaryKey( (IndexedPluralAttributeBinding) attributeBinding ); - } else { - throw new NotYetImplementedException( - String.format( "%s of basic elements is not supported yet.", pluralAttributeNature ) + case COMPOSITE: + return bindAggregatedCompositeAttribute( + attributeBindingContainer, + ComponentAttributeSource.class.cast( attributeSource ), + attribute, + isIdentifierAttribute ); - } - } - else if ( attributeBinding.getPluralAttributeElementBinding().getNature() == PluralAttributeElementBinding.Nature.MANY_TO_MANY ) { - if ( ! attributeBinding.getPluralAttributeKeyBinding().isInverse() ) { - if ( pluralAttributeNature == PluralAttributeSource.Nature.SET ) { - bindSetCollectionTablePrimaryKey( (SetBinding) attributeBinding ); - } - else if ( - pluralAttributeNature == PluralAttributeSource.Nature.LIST - || pluralAttributeNature == PluralAttributeSource.Nature.MAP - || pluralAttributeNature == PluralAttributeSource.Nature.ARRAY) { - bindIndexedCollectionTablePrimaryKey( (IndexedPluralAttributeBinding) attributeBinding ); - } - else { - throw new NotYetImplementedException( - String.format( "Many-to-many %s is not supported yet.", pluralAttributeNature ) - ); - } - } + default: + throw new NotYetImplementedException( nature.toString() ); } } @@ -776,528 +1061,21 @@ public class Binder { new ComponentMetamodel( attributeBinding, isAttributeIdentifier, false ) ); // TODO: binding the HibernateTypeDescriptor should be simplified since we know the class name already - bindHibernateTypeDescriptor( + typeHelper.bindHibernateTypeDescriptor( attributeBinding.getHibernateTypeDescriptor(), composite.getClassName(), null, defaultJavaClassReference == null ? null : defaultJavaClassReference.getValue().getName() ); - bindHibernateResolvedType( attributeBinding.getHibernateTypeDescriptor(), resolvedType ); + HibernateTypeHelper.bindHibernateResolvedType( attributeBinding.getHibernateTypeDescriptor(), resolvedType ); 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, - bindingContext().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 ); - final String discriminatorValue = rootEntitySource.getDiscriminatorMatchValue(); - if ( discriminatorValue != null ) { - rootEntityBinding.setDiscriminatorMatchValue( discriminatorValue ); - } - else if ( !Modifier.isAbstract( bindingContext().locateClassByName( rootEntitySource.getEntityName() ).getModifiers() ) ) { - // Use the class name as a default if no dscriminator value. - // However, skip abstract classes -- obviously no discriminators there. - rootEntityBinding.setDiscriminatorMatchValue( rootEntitySource.getEntityName() ); - } - // 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 ); - typeHelper.bindJdbcDataType( resolvedType, value ); - } - - private EntityBinding bindSubEntity(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 ); - entityBinding.setMutable( entityBinding.getHierarchyDetails().getRootEntityBinding().isMutable() ); - markSuperEntityTableAbstractIfNecessary(superEntityBinding); - bindPrimaryTable( entityBinding, entitySource ); - bindSubEntityPrimaryKey( entityBinding, entitySource ); - bindSecondaryTables( entityBinding, entitySource ); - bindUniqueConstraints( entityBinding, entitySource ); - bindAttributes( entityBinding, entitySource ); - bindSubEntities( entityBinding, entitySource ); - return entityBinding; - } finally { - bindingContexts.pop(); - } - } - - /** - * applied on sub entity - * @param superEntityBinding - */ - private static void markSuperEntityTableAbstractIfNecessary(EntityBinding superEntityBinding) { - if ( superEntityBinding == null ) { - return; - } - if ( superEntityBinding.getHierarchyDetails().getInheritanceType() != InheritanceType.TABLE_PER_CLASS ) { - return; - } - if ( superEntityBinding.isAbstract() != Boolean.TRUE ) { - return; - } - if ( !Table.class.isInstance( superEntityBinding.getPrimaryTable() ) ) { - return; - } - Table.class.cast( superEntityBinding.getPrimaryTable() ).setPhysicalTable( false ); - } - - private void bindSubEntityPrimaryKey(final EntityBinding entityBinding, final EntitySource entitySource) { - final InheritanceType inheritanceType = entityBinding.getHierarchyDetails().getInheritanceType(); - final EntityBinding superEntityBinding = entityBinding.getSuperEntityBinding(); - if ( superEntityBinding == null ) { - throw new AssertionFailure( "super entitybinding is null " ); - } - if ( inheritanceType == InheritanceType.TABLE_PER_CLASS ) { - - } - if ( inheritanceType == InheritanceType.JOINED ) { - SubclassEntitySource subclassEntitySource = (SubclassEntitySource) entitySource; - ForeignKey fk = entityBinding.getPrimaryTable().createForeignKey( - superEntityBinding.getPrimaryTable(), - subclassEntitySource.getJoinedForeignKeyName() - ); - final List primaryKeyJoinColumnSources = subclassEntitySource.getPrimaryKeyJoinColumnSources(); - final boolean hasPrimaryKeyJoinColumns = CollectionHelper.isNotEmpty( primaryKeyJoinColumnSources ); - final List superEntityBindingPrimaryKeyColumns = superEntityBinding.getPrimaryTable().getPrimaryKey().getColumns(); - - for ( int i = 0, size = superEntityBindingPrimaryKeyColumns.size(); i < size; i++ ) { - Column superEntityBindingPrimaryKeyColumn = superEntityBindingPrimaryKeyColumns.get( i ); - PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource = hasPrimaryKeyJoinColumns && i < primaryKeyJoinColumnSources - .size() ? primaryKeyJoinColumnSources.get( i ) : null; - final String columnName; - if ( primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( primaryKeyJoinColumnSource.getColumnName() ) ) { - columnName = bindingContext().getNamingStrategy() - .columnName( primaryKeyJoinColumnSource.getColumnName() ); - } - else { - columnName = superEntityBindingPrimaryKeyColumn.getColumnName().getText(); - } - Column column = entityBinding.getPrimaryTable().locateOrCreateColumn( columnName ); - column.setCheckCondition( superEntityBindingPrimaryKeyColumn.getCheckCondition() ); - column.setComment( superEntityBindingPrimaryKeyColumn.getComment() ); - column.setDefaultValue( superEntityBindingPrimaryKeyColumn.getDefaultValue() ); - column.setIdentity( superEntityBindingPrimaryKeyColumn.isIdentity() ); - column.setNullable( superEntityBindingPrimaryKeyColumn.isNullable() ); - column.setReadFragment( superEntityBindingPrimaryKeyColumn.getReadFragment() ); - column.setWriteFragment( superEntityBindingPrimaryKeyColumn.getWriteFragment() ); - column.setUnique( superEntityBindingPrimaryKeyColumn.isUnique() ); - final String sqlType = getSqlTypeFromPrimaryKeyJoinColumnSourceIfExist( - superEntityBindingPrimaryKeyColumn, - primaryKeyJoinColumnSource - ); - column.setSqlType( sqlType ); - column.setSize( superEntityBindingPrimaryKeyColumn.getSize() ); - column.setJdbcDataType( superEntityBindingPrimaryKeyColumn.getJdbcDataType() ); - entityBinding.getPrimaryTable().getPrimaryKey().addColumn( column ); - //todo still need to figure out how to handle the referencedColumnName property - fk.addColumnMapping( column, superEntityBindingPrimaryKeyColumn ); - } - } - } - - private static String getSqlTypeFromPrimaryKeyJoinColumnSourceIfExist(Column superEntityBindingPrimaryKeyColumn, PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource) { - final boolean isColumnDefOverrided = primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( - primaryKeyJoinColumnSource.getColumnDefinition() - ); - return isColumnDefOverrided ? primaryKeyJoinColumnSource.getColumnDefinition() : superEntityBindingPrimaryKeyColumn - .getSqlType(); - } - - /** - * Binding a single entity hierarchy. - * - * @param entityHierarchy The entity hierarchy to be binded. - * - * @return The root {@link EntityBinding} of the entity hierarchy mapping. - */ - private EntityBinding bindEntityHierarchy(final EntityHierarchy entityHierarchy) { - final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource(); - // Return existing binding if available - EntityBinding rootEntityBinding = metadata.getEntityBinding( rootEntitySource.getEntityName() ); - if ( rootEntityBinding != null ) { - return rootEntityBinding; - } - setupBindingContext( entityHierarchy, rootEntitySource ); - try { - // Create root entity binding - rootEntityBinding = createEntityBinding( rootEntitySource, null ); - bindPrimaryTable( rootEntityBinding, rootEntitySource ); - // 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 { - cleanupBindingContext(); - } - return rootEntityBinding; - } - - private void cleanupBindingContext() { - bindingContexts.pop(); - inheritanceTypes.pop(); - entityModes.pop(); - } - - private void setupBindingContext(EntityHierarchy entityHierarchy, RootEntitySource rootEntitySource) { - // Save inheritance type and entity mode that will apply to entire hierarchy - inheritanceTypes.push( entityHierarchy.getHierarchyInheritanceType() ); - entityModes.push( rootEntitySource.getEntityMode() ); - bindingContexts.push( rootEntitySource.getLocalBindingContext() ); - } - - /** - * The entry point of {@linkplain Binder} class, binds all the entity hierarchy one by one. - * - * @param entityHierarchies The entity hierarchies resolved from mappings - */ - public void bindEntityHierarchies(final Iterable 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 ) { - bindEntityHierarchy( entityHierarchy ); - } - } - - private SingularAttributeBinding determineReferencedAttributeBinding( - ForeignKeyContributingSource foreignKeyContributingSource, - EntityBinding referencedEntityBinding) { - final JoinColumnResolutionDelegate resolutionDelegate = - foreignKeyContributingSource.getForeignKeyTargetColumnResolutionDelegate(); - final JoinColumnResolutionContext resolutionContext = resolutionDelegate == null ? null : new JoinColumnResolutionContextImpl( - referencedEntityBinding - ); - return determineReferencedAttributeBinding( - resolutionDelegate, - resolutionContext, - referencedEntityBinding - ); - } - - 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().getText(), - sourceColumns.size(), - targetColumns.get( 0 ).getTable().getLogicalName().getText(), - targetColumns.size() - ) - ); - } - for ( int i = 0; i < sourceColumns.size(); i++ ) { - foreignKey.addColumnMapping( sourceColumns.get( i ), targetColumns.get( i ) ); - } - } - - private static 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: The following 3 methods should eventually be replaced w/ - // typeHelper use. - - private void bindHibernateTypeDescriptor( - final HibernateTypeDescriptor hibernateTypeDescriptor, - final ExplicitHibernateTypeSource explicitTypeSource, - final 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 bindingContext().makeMappingException( String.format( - "Attempt to re-initialize (non-explicit) Java type name; current=%s new=%s", - hibernateTypeDescriptor.getJavaTypeName(), - defaultJavaTypeName ) ); - } - 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 bindingContext().makeMappingException( String.format( - "Attempt to re-initialize explicity-mapped Java type name; current=%s new=%s", - hibernateTypeDescriptor.getExplicitTypeName(), - explicitTypeName ) ); - } - if ( typeDef == null ) { - hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName ); - } else { - hibernateTypeDescriptor.setExplicitTypeName( typeDef.getTypeImplementorClass().getName() ); - // Don't use set() -- typeDef#parameters is unmodifiable - hibernateTypeDescriptor.getTypeParameters().putAll( 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 NON_AGGREGATED_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 bindingContext().makeMappingException( 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() - ) ); - } - rootEntityBinding.getPrimaryTable().getPrimaryKey().getColumns().get( 0 ).setIdentity( true ); - } - if ( PersistentIdentifierGenerator.class.isInstance( entityIdentifier.getIdentifierGenerator() ) ) { - ( (PersistentIdentifierGenerator) entityIdentifier.getIdentifierGenerator() ).registerExportables( metadata.getDatabase() ); - } - } - - private static void bindIndexedCollectionTablePrimaryKey(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 ); - } - } - - public LocalBindingContext bindingContext() { - return bindingContexts.peek(); - } - - - private AbstractPluralAttributeBinding bindListAttribute( - final AttributeBindingContainer attributeBindingContainer, - final PluralAttributeSource attributeSource, - PluralAttribute attribute ) { - if ( attribute == null ) { - attribute = attributeBindingContainer.getAttributeContainer().createList( attributeSource.getName() ); - } - final int base = IndexedPluralAttributeSource.class.isInstance( attributeSource ) ? IndexedPluralAttributeSource.class.cast( attributeSource ).getIndexSource().base() : 0; - return attributeBindingContainer.makeListAttributeBinding( - attribute, - pluralAttributeElementNature( attributeSource ), - determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - createMetaAttributeContext( attributeBindingContainer, attributeSource ), - base - ); - } - - private AbstractPluralAttributeBinding bindArrayAttribute( - final AttributeBindingContainer attributeBindingContainer, - final PluralAttributeSource attributeSource, - PluralAttribute attribute ) { - if ( attribute == null ) { - attribute = attributeBindingContainer.getAttributeContainer().createArray( attributeSource.getName() ); - } - final int base = IndexedPluralAttributeSource.class.isInstance( attributeSource ) ? IndexedPluralAttributeSource.class.cast( attributeSource ).getIndexSource().base() : 0; - return attributeBindingContainer.makeArrayAttributeBinding( - attribute, - pluralAttributeElementNature( attributeSource ), - determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - createMetaAttributeContext( attributeBindingContainer, attributeSource ), - base - ); - } - private static interface ToOneAttributeBindingContext { - SingularAssociationAttributeBinding createToOneAttributeBinding( - EntityBinding referencedEntityBinding, - SingularAttributeBinding referencedAttributeBinding - ); - EntityType resolveEntityType( - EntityBinding referencedEntityBinding, - SingularAttributeBinding referencedAttributeBinding - ); - } private ManyToOneAttributeBinding bindManyToOneAttribute( final AttributeBindingContainer attributeBindingContainer, final ToOneAttributeSource attributeSource, - SingularAttribute attribute ) { + SingularAttribute attribute) { final SingularAttribute actualAttribute = attribute != null ? attribute : @@ -1334,16 +1112,16 @@ public class Binder { // todo : currently a chicken-egg problem here between creating the attribute binding and binding its FK values... // now we have everything to create a ManyToOneAttributeBinding return attributeBindingContainer.makeManyToOneAttributeBinding( - actualAttribute, - propertyAccessorName( attributeSource ), - attributeSource.isIncludedInOptimisticLocking(), - attributeSource.isLazy(), - attributeSource.getNaturalIdMutability(), - createMetaAttributeContext( attributeBindingContainer, attributeSource ), - referencedEntityBinding, - referencedAttributeBinding, - relationalValueBindings - ); + actualAttribute, + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + attributeSource.isLazy(), + attributeSource.getNaturalIdMutability(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ), + referencedEntityBinding, + referencedAttributeBinding, + relationalValueBindings + ); } @Override @@ -1353,7 +1131,8 @@ public class Binder { final boolean isRefToPk = referencedEntityBinding .getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( - referencedAttributeBinding ); + referencedAttributeBinding + ); final String uniqueKeyAttributeName = isRefToPk ? null : getRelativePathFromEntityName( referencedAttributeBinding ); return metadata.getTypeResolver().getTypeFactory().manyToOne( @@ -1361,7 +1140,6 @@ public class Binder { uniqueKeyAttributeName, attributeSource.getFetchTiming() != FetchTiming.IMMEDIATE, attributeSource.isUnWrapProxy(), - true, //TODO: is isEmbedded() obsolete? false, //TODO: should be attributeBinding.isIgnoreNotFound(), false //TODO: determine if isLogicalOneToOne ); @@ -1393,7 +1171,7 @@ public class Binder { private OneToOneAttributeBinding bindOneToOneAttribute( final AttributeBindingContainer attributeBindingContainer, final ToOneAttributeSource attributeSource, - SingularAttribute attribute ) { + SingularAttribute attribute) { final SingularAttribute actualAttribute = attribute != null ? attribute : @@ -1426,7 +1204,8 @@ public class Binder { final boolean isRefToPk = referencedEntityBinding .getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( - referencedAttributeBinding ); + referencedAttributeBinding + ); final String uniqueKeyAttributeName = isRefToPk ? null : getRelativePathFromEntityName( referencedAttributeBinding ); return metadata.getTypeResolver().getTypeFactory().oneToOne( @@ -1435,7 +1214,6 @@ public class Binder { uniqueKeyAttributeName, attributeSource.getFetchTiming() != FetchTiming.IMMEDIATE, attributeSource.isUnWrapProxy(), - true, //TODO: is isEmbedded() obsolete? attributeBindingContainer.seekEntityBinding().getEntityName(), actualAttribute.getName() ); @@ -1472,10 +1250,10 @@ public class Binder { private SingularAssociationAttributeBinding bindToOneAttribute( SingularAttribute attribute, - ToOneAttributeSource attributeSource, + final ToOneAttributeSource attributeSource, final ToOneAttributeBindingContext attributeBindingContext) { - final ValueHolder< Class< ? >> referencedEntityJavaTypeValue = createSingularAttributeJavaType( attribute ); + final ValueHolder> referencedEntityJavaTypeValue = createSingularAttributeJavaType( attribute ); final EntityBinding referencedEntityBinding = findOrBindEntityBinding( referencedEntityJavaTypeValue, attributeSource.getReferencedEntityName() @@ -1500,7 +1278,9 @@ public class Binder { referencedAttributeBinding ); - if ( referencedAttributeBinding != referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding() ) { + if ( referencedAttributeBinding != referencedEntityBinding.getHierarchyDetails() + .getEntityIdentifier() + .getAttributeBinding() ) { referencedAttributeBinding.setAlternateUniqueKey( true ); } @@ -1510,32 +1290,185 @@ public class Binder { final Type resolvedType = attributeBindingContext.resolveEntityType( referencedEntityBinding, referencedAttributeBinding ); - bindHibernateTypeDescriptor( + typeHelper.bindHibernateTypeDescriptor( attributeBinding.getHibernateTypeDescriptor(), attributeSource.getTypeInformation(), - referencedEntityJavaTypeValue ); - bindHibernateResolvedType( attributeBinding.getHibernateTypeDescriptor(), resolvedType ); - + referencedEntityJavaTypeValue + ); + HibernateTypeHelper.bindHibernateResolvedType( attributeBinding.getHibernateTypeDescriptor(), resolvedType ); return attributeBinding; } - private static String getRelativePathFromEntityName(AttributeBinding attributeBinding) { - final String fullPath = attributeBinding.getContainer().getPathBase() + "." + attributeBinding.getAttribute().getName(); - return fullPath.substring( attributeBinding.getContainer().seekEntityBinding().getEntityName().length() + 1 ); + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ plural attributes binding + private AbstractPluralAttributeBinding bindPluralAttribute( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource) { + final PluralAttributeSource.Nature nature = attributeSource.getNature(); + final PluralAttribute attribute = + attributeBindingContainer.getAttributeContainer().locatePluralAttribute( attributeSource.getName() ); + final AbstractPluralAttributeBinding attributeBinding; + switch ( nature ) { + case BAG: + attributeBinding = bindBagAttribute( attributeBindingContainer, attributeSource, attribute ); + break; + case SET: + attributeBinding = bindSetAttribute( attributeBindingContainer, attributeSource, attribute ); + break; + case LIST: + attributeBinding = bindListAttribute( + attributeBindingContainer, + attributeSource, + attribute + ); + break; + case MAP: + attributeBinding = bindMapAttribute( + attributeBindingContainer, + attributeSource, + attribute + ); + break; + case ARRAY: + attributeBinding = bindArrayAttribute( + attributeBindingContainer, + attributeSource, + attribute + ); + break; + default: + throw new NotYetImplementedException( nature.toString() ); + } + + // Must do first -- sorting/ordering can determine the resolved type + // (ex: Set vs. SortedSet). + bindSortingAndOrdering( attributeBinding, attributeSource ); + + final Type resolvedType = typeHelper.resolvePluralType( attributeBinding, attributeSource, nature ); + final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor(); + ReflectedCollectionJavaTypes reflectedCollectionJavaTypes = typeHelper.getReflectedCollectionJavaTypes( + attributeBinding + ); + typeHelper.bindHibernateTypeDescriptor( + hibernateTypeDescriptor, + attributeSource.getTypeInformation(), + HibernateTypeHelper.defaultCollectionJavaTypeName( reflectedCollectionJavaTypes, attributeSource ) + ); + HibernateTypeHelper.bindHibernateResolvedType( hibernateTypeDescriptor, resolvedType ); + // Note: Collection types do not have a relational model + attributeBinding.setFetchTiming( attributeSource.getFetchTiming() ); + attributeBinding.setFetchStyle( attributeSource.getFetchStyle() ); + if ( attributeSource.getFetchStyle() == FetchStyle.SUBSELECT ) { + attributeBindingContainer.seekEntityBinding().setSubselectLoadableCollections( true ); + } + attributeBinding.setCaching( attributeSource.getCaching() ); + if ( StringHelper.isNotEmpty( attributeSource.getCustomPersisterClassName() ) ) { + attributeBinding.setExplicitPersisterClass( + bindingContext().locateClassByName( + attributeSource.getCustomPersisterClassName() + ) + ); + } + attributeBinding.setCustomLoaderName( attributeSource.getCustomLoaderName() ); + attributeBinding.setCustomSqlInsert( attributeSource.getCustomSqlInsert() ); + attributeBinding.setCustomSqlUpdate( attributeSource.getCustomSqlUpdate() ); + attributeBinding.setCustomSqlDelete( attributeSource.getCustomSqlDelete() ); + attributeBinding.setCustomSqlDeleteAll( attributeSource.getCustomSqlDeleteAll() ); + attributeBinding.setWhere( attributeSource.getWhere() ); + + switch ( attributeSource.getElementSource().getNature() ) { + case BASIC: + bindBasicPluralAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); + break; + case ONE_TO_MANY: + bindOneToManyAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); + break; + case MANY_TO_MANY: + bindManyToManyAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); + break; + case AGGREGATE: + bindPluralAggregateAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); + break; + case MANY_TO_ANY: + //todo?? + default: + throw bindingContext().makeMappingException( + String.format( + "Unknown type of collection element: %s", + attributeSource.getElementSource().getNature() + ) + ); + } + bindIndexedPluralAttributeIfPossible( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); + bindCollectionTablePrimaryKey( attributeBinding, attributeSource ); + metadata.addCollection( attributeBinding ); + return attributeBinding; } - private TableSpecification locateDefaultTableSpecificationForAttribute( - AttributeBindingContainer attributeBindingContainer, - SingularAttributeSource attributeSource) { - return attributeSource.getContainingTableName() == null ? - attributeBindingContainer.getPrimaryTable() : - attributeBindingContainer.seekEntityBinding().locateTable( attributeSource.getContainingTableName() ); + 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 bindListAttribute( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource, + PluralAttribute attribute) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createList( attributeSource.getName() ); + } + final int base = IndexedPluralAttributeSource.class.isInstance( attributeSource ) ? IndexedPluralAttributeSource.class + .cast( attributeSource ) + .getIndexSource() + .base() : 0; + return attributeBindingContainer.makeListAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ), + base + ); + } + + private AbstractPluralAttributeBinding bindArrayAttribute( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource, + PluralAttribute attribute) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createArray( attributeSource.getName() ); + } + final int base = IndexedPluralAttributeSource.class.isInstance( attributeSource ) ? IndexedPluralAttributeSource.class + .cast( attributeSource ) + .getIndexSource() + .base() : 0; + return attributeBindingContainer.makeArrayAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ), + base + ); } private AbstractPluralAttributeBinding bindMapAttribute( final AttributeBindingContainer attributeBindingContainer, final PluralAttributeSource attributeSource, - PluralAttribute attribute ) { + PluralAttribute attribute) { if ( attribute == null ) { attribute = attributeBindingContainer.getAttributeContainer().createMap( attributeSource.getName() ); } @@ -1551,99 +1484,193 @@ public class Binder { ); } - private void bindMultiTenancy(EntityBinding rootEntityBinding, RootEntitySource rootEntitySource) { - final MultiTenancySource multiTenancySource = rootEntitySource.getMultiTenancySource(); - if ( multiTenancySource == null ) { - return; + private AbstractPluralAttributeBinding bindSetAttribute( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource, + PluralAttribute attribute) { + if ( attribute == null ) { + attribute = attributeBindingContainer.getAttributeContainer().createSet( attributeSource.getName() ); } - - // 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() ); + return attributeBindingContainer.makeSetAttributeBinding( + attribute, + pluralAttributeElementNature( attributeSource ), + determinePluralAttributeKeyReferencedBinding( attributeBindingContainer, attributeSource ), + propertyAccessorName( attributeSource ), + attributeSource.isIncludedInOptimisticLocking(), + createMetaAttributeContext( attributeBindingContainer, attributeSource ) + ); } - private void bindNonAggregatedCompositeIdentifier( - EntityBinding rootEntityBinding, - NonAggregatedCompositeIdentifierSource identifierSource ) { - // locate the attribute bindings for the real attributes - List idAttributeBindings = - new ArrayList< SingularAttributeBinding >(); - for ( SingularAttributeSource attributeSource : identifierSource.getAttributeSourcesMakingUpIdentifier() ) { - SingularAttributeBinding singularAttributeBinding = - bindIdentifierAttribute( rootEntityBinding, attributeSource ); - if ( singularAttributeBinding.isAssociation() ) { - throw new NotYetImplementedException( "composite IDs containing an association attribute is not implemented yet." ); - } - idAttributeBindings.add( singularAttributeBinding ); - } - final Class idClassClass = identifierSource.getLookupIdClass(); - final String idClassPropertyAccessorName = - idClassClass == null ? + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ collection attributes binding + + private void bindBasicCollectionElement( + final BasicPluralAttributeElementBinding elementBinding, + final BasicPluralAttributeElementSource elementSource, + final String defaultElementJavaTypeName) { + bindBasicPluralElementRelationalValues( elementSource, elementBinding ); + typeHelper.bindBasicCollectionElementType( elementBinding, elementSource, defaultElementJavaTypeName ); + } + + + private void bindNonAssociationCollectionKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeSource attributeSource) { + if ( attributeSource.getElementSource().getNature() != PluralAttributeElementSource.Nature.BASIC && + attributeSource.getElementSource().getNature() != PluralAttributeElementSource.Nature.AGGREGATE ) { + throw new AssertionFailure( + String.format( + "Expected basic or aggregate attribute binding; instead got {%s}", + attributeSource.getElementSource().getNature() + ) + ); + } + attributeBinding.getPluralAttributeKeyBinding().setInverse( false ); + TableSpecification collectionTable = createBasicCollectionTable( + attributeBinding, attributeSource.getCollectionTableSpecificationSource() + ); + 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 bindCompositeCollectionElement( + final CompositePluralAttributeElementBinding elementBinding, + final CompositePluralAttributeElementSource elementSource, + final String defaultElementJavaTypeName) { + final PluralAttributeBinding pluralAttributeBinding = elementBinding.getPluralAttributeBinding(); + ValueHolder> defaultElementJavaClassReference = null; + // Create the aggregate type + // TODO: aggregateName should be set to elementSource.getPath() (which is currently not implemented) + // or Binder should define AttributeBindingContainer paths instead. + String aggregateName = pluralAttributeBinding.getAttribute().getRole() + ".element"; + final Aggregate aggregate; + if ( elementSource.getClassName() != null ) { + aggregate = new Aggregate( + aggregateName, + elementSource.getClassName(), + elementSource.getClassReference() != null ? + elementSource.getClassReference() : + bindingContext().makeClassReference( elementSource.getClassName() ), + null + ); + } + else { + defaultElementJavaClassReference = bindingContext().makeClassReference( defaultElementJavaTypeName ); + aggregate = new Aggregate( + aggregateName, + defaultElementJavaClassReference.getValue().getName(), + defaultElementJavaClassReference, + null + ); + } + final SingularAttribute parentAttribute = + StringHelper.isEmpty( elementSource.getParentReferenceAttributeName() ) ? null : - propertyAccessorName( identifierSource.getIdClassPropertyAccessorName() ); - - // 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 ); - } - // Create the synthetic attribute - final SingularAttribute syntheticAttribute = - rootEntityBinding.getEntity().createSyntheticCompositeAttribute( - SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME, - rootEntityBinding.getEntity() + aggregate.createSingularAttribute( elementSource.getParentReferenceAttributeName() ); + final CompositeAttributeBindingContainer compositeAttributeBindingContainer = + elementBinding.createCompositeAttributeBindingContainer( + aggregate, + createMetaAttributeContext( + pluralAttributeBinding.getContainer(), + elementSource.getMetaAttributeSources() + ), + parentAttribute ); - final CompositeAttributeBinding syntheticAttributeBinding = - rootEntityBinding.makeVirtualCompositeAttributeBinding( - syntheticAttribute, - createMetaAttributeContext( rootEntityBinding, identifierSource.getMetaAttributeSources() ), - idAttributeBindings - ); - bindHibernateTypeDescriptor( - syntheticAttributeBinding.getHibernateTypeDescriptor(), - syntheticAttribute.getSingularAttributeType().getClassName(), + bindAttributes( compositeAttributeBindingContainer, elementSource ); + Type resolvedType = metadata.getTypeResolver().getTypeFactory().component( + new ComponentMetamodel( compositeAttributeBindingContainer, false, false ) + ); + // TODO: binding the HibernateTypeDescriptor should be simplified since we know the class name already + typeHelper.bindHibernateTypeDescriptor( + elementBinding.getHibernateTypeDescriptor(), + aggregate.getClassName(), null, - null + defaultElementJavaClassReference == null ? null : defaultElementJavaClassReference.getValue().getName() ); - - // Create the synthetic attribute binding. - rootEntityBinding.getHierarchyDetails().getEntityIdentifier().prepareAsNonAggregatedCompositeIdentifier( - syntheticAttributeBinding, - generator, - interpretIdentifierUnsavedValue( identifierSource, generator ), - idClassClass, - idClassPropertyAccessorName - ); - - final Type resolvedType = metadata.getTypeResolver().getTypeFactory().embeddedComponent( - new ComponentMetamodel( syntheticAttributeBinding, true, false ) - ); - bindHibernateResolvedType( syntheticAttributeBinding.getHibernateTypeDescriptor(), resolvedType ); + HibernateTypeHelper.bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedType ); + /** + * TODO + * don't know why, but see org.hibernate.mapping.Property#getCompositeCascadeStyle + * + * and not sure if this is the right place to apply this logic, apparently source level is not okay, so here it is, for now. + */ + for ( AttributeBinding ab : compositeAttributeBindingContainer.attributeBindings() ) { + if ( ab.isCascadeable() ) { + CascadeStyle cascadeStyle = Cascadeable.class.cast( ab ).getCascadeStyle(); + if ( cascadeStyle != CascadeStyles.NONE ) { + elementBinding.setCascadeStyle( CascadeStyles.ALL ); + } + } + } + if ( elementBinding.getCascadeStyle() == null || elementBinding.getCascadeStyle() == CascadeStyles.NONE ) { + elementBinding.setCascadeStyle( determineCascadeStyle( elementSource.getCascadeStyles() ) ); + } } + 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(), + attributeBinding.getPluralAttributeElementBinding() + .getNature() != PluralAttributeElementBinding.Nature.ONE_TO_MANY + ) + .get( 0 ).getValue() + ); + if ( attributeBinding.getPluralAttributeElementBinding() + .getNature() == PluralAttributeElementBinding.Nature.ONE_TO_MANY ) { + if ( Column.class.isInstance( indexBinding.getIndexRelationalValue() ) ) { + // TODO: fix this when column nullability is refactored + ( (Column) indexBinding.getIndexRelationalValue() ).setNullable( true ); + } + } + // TODO: create a foreign key if non-inverse and the index is an association + + typeHelper.bindHibernateTypeDescriptor( + indexBinding.getHibernateTypeDescriptor(), + attributeSource.explicitHibernateTypeSource(), + defaultIndexJavaTypeName + ); + Type resolvedElementType = typeHelper.heuristicType( indexBinding.getHibernateTypeDescriptor() ); + HibernateTypeHelper.bindHibernateResolvedType( indexBinding.getHibernateTypeDescriptor(), resolvedElementType ); + typeHelper.bindJdbcDataType( resolvedElementType, indexBinding.getIndexRelationalValue() ); + } + + + private SingularAttributeBinding determineReferencedAttributeBinding( + final ForeignKeyContributingSource foreignKeyContributingSource, + final EntityBinding referencedEntityBinding) { + final JoinColumnResolutionDelegate resolutionDelegate = + foreignKeyContributingSource.getForeignKeyTargetColumnResolutionDelegate(); + final JoinColumnResolutionContext resolutionContext = resolutionDelegate == null ? null : new JoinColumnResolutionContextImpl( + referencedEntityBinding + ); + return determineReferencedAttributeBinding( + resolutionDelegate, + resolutionContext, + referencedEntityBinding + ); + } + + private void bindOneToManyCollectionElement( final OneToManyPluralAttributeElementBinding elementBinding, final OneToManyPluralAttributeElementSource elementSource, @@ -1651,10 +1678,10 @@ public class Binder { final String defaultElementJavaTypeName) { elementBinding.setElementEntityIdentifier( - referencedEntityBinding.getHierarchyDetails().getEntityIdentifier() + referencedEntityBinding.getHierarchyDetails().getEntityIdentifier() ); final HibernateTypeDescriptor hibernateTypeDescriptor = elementBinding.getHibernateTypeDescriptor(); - bindHibernateTypeDescriptor( + typeHelper.bindHibernateTypeDescriptor( hibernateTypeDescriptor, referencedEntityBinding.getEntity().getName(), null, @@ -1666,11 +1693,13 @@ public class Binder { null, false, false, - true, false, //TODO: should be attributeBinding.isIgnoreNotFound(), false ); - bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); + HibernateTypeHelper.bindHibernateResolvedType( + elementBinding.getHibernateTypeDescriptor(), + resolvedElementType + ); // no need to bind JDBC data types because element is referenced EntityBinding's ID elementBinding.setCascadeStyle( determineCascadeStyle( elementSource.getCascadeStyles() ) ); } @@ -1692,7 +1721,7 @@ public class Binder { ); if ( !elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().isInverse() && - ! hasDerivedValue( elementBinding.getRelationalValueBindings() ) ) { + !hasDerivedValue( elementBinding.getRelationalValueBindings() ) ) { bindForeignKey( quoteIdentifierIfNonEmpty( elementSource.getExplicitForeignKeyName() ), extractColumnsFromRelationalValueBindings( elementBinding.getRelationalValueBindings() ), @@ -1701,7 +1730,7 @@ public class Binder { } final HibernateTypeDescriptor hibernateTypeDescriptor = elementBinding.getHibernateTypeDescriptor(); - bindHibernateTypeDescriptor( + typeHelper.bindHibernateTypeDescriptor( hibernateTypeDescriptor, referencedEntityBinding.getEntity().getName(), null, @@ -1713,11 +1742,13 @@ public class Binder { elementSource.getReferencedEntityAttributeName(), false, false, - true, false, //TODO: should be attributeBinding.isIgnoreNotFound(), false ); - bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); + HibernateTypeHelper.bindHibernateResolvedType( + elementBinding.getHibernateTypeDescriptor(), + resolvedElementType + ); typeHelper.bindJdbcDataType( resolvedElementType, elementBinding.getRelationalValueBindings() @@ -1728,38 +1759,6 @@ public class Binder { //TODO: initialize filters from elementSource } - private static boolean hasDerivedValue(List relationalValueBindings) { - for ( RelationalValueBinding relationalValueBinding : relationalValueBindings ) { - if ( DerivedValue.class.isInstance( relationalValueBinding.getValue() ) ) { - return true; - } - } - return false; - } - - // TODO: should this be moved to CascadeStyles as a static method? - // TODO: sources already factor in default cascade; should that be done here instead? - private static CascadeStyle determineCascadeStyle(Iterable cascadeStyles) { - CascadeStyle cascadeStyleResult; - List cascadeStyleList = new ArrayList(); - for ( CascadeStyle style : cascadeStyles ) { - if ( style != CascadeStyles.NONE ) { - cascadeStyleList.add( style ); - } - } - if ( cascadeStyleList.isEmpty() ) { - cascadeStyleResult = CascadeStyles.NONE; - } - else if ( cascadeStyleList.size() == 1 ) { - cascadeStyleResult = cascadeStyleList.get( 0 ); - } - else { - cascadeStyleResult = new CascadeStyles.MultipleCascadeStyle( - cascadeStyleList.toArray( new CascadeStyle[ cascadeStyleList.size() ] ) - ); - } - return cascadeStyleResult; - } private void bindOneToManyCollectionKey( final AbstractPluralAttributeBinding attributeBinding, @@ -1776,7 +1775,7 @@ public class Binder { // TODO - verify whether table spec source should/can be null or whether there will always be one (HF) if ( attributeSource.getCollectionTableSpecificationSource() != null ) { TableSpecificationSource tableSpecificationSource = attributeSource.getCollectionTableSpecificationSource(); - if(tableSpecificationSource.getExplicitCatalogName() != null || tableSpecificationSource.getExplicitSchemaName() != null) { + if ( tableSpecificationSource.getExplicitCatalogName() != null || tableSpecificationSource.getExplicitSchemaName() != 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." ); } @@ -1846,137 +1845,58 @@ public class Binder { bindCollectionTableForeignKey( attributeBinding, attributeSource.getKeySource(), collectionTable ); } - private AbstractPluralAttributeBinding bindPluralAttribute( - final AttributeBindingContainer attributeBindingContainer, - final PluralAttributeSource attributeSource ) { - final PluralAttributeSource.Nature nature = attributeSource.getNature(); - final PluralAttribute attribute = - attributeBindingContainer.getAttributeContainer().locatePluralAttribute( attributeSource.getName() ); - final AbstractPluralAttributeBinding attributeBinding; - switch ( nature ) { - case BAG: - attributeBinding = bindBagAttribute( attributeBindingContainer, attributeSource, attribute ); - break; - case SET: - attributeBinding = bindSetAttribute( attributeBindingContainer, attributeSource, attribute ); - break; - case LIST: - attributeBinding = bindListAttribute( - attributeBindingContainer, - attributeSource, - attribute - ); - break; - case MAP: - attributeBinding = bindMapAttribute( - attributeBindingContainer, - attributeSource, - attribute - ); - break; - case ARRAY: - attributeBinding = bindArrayAttribute( - attributeBindingContainer, - attributeSource, - attribute - ); - break; - default: - throw new NotYetImplementedException( nature.toString() ); - } - // Must do first -- sorting/ordering can determine the resolved type - // (ex: Set vs. SortedSet). - bindSortingAndOrdering( attributeBinding, attributeSource ); - - final Type resolvedType = resolvePluralType( attributeBinding, attributeSource, nature ); - final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor(); - ReflectedCollectionJavaTypes reflectedCollectionJavaTypes = typeHelper.getReflectedCollectionJavaTypes( attributeBinding ); - bindHibernateTypeDescriptor( - hibernateTypeDescriptor, - attributeSource.getTypeInformation(), - defaultCollectionJavaTypeName( reflectedCollectionJavaTypes, attributeSource ) ); - bindHibernateResolvedType( hibernateTypeDescriptor, resolvedType ); - // Note: Collection types do not have a relational model - attributeBinding.setFetchTiming( attributeSource.getFetchTiming() ); - attributeBinding.setFetchStyle( attributeSource.getFetchStyle() ); - if ( attributeSource.getFetchStyle() == FetchStyle.SUBSELECT ) { - attributeBindingContainer.seekEntityBinding().setSubselectLoadableCollections( true ); - } - attributeBinding.setCaching( attributeSource.getCaching() ); - if ( StringHelper.isNotEmpty( attributeSource.getCustomPersisterClassName() ) ) { - attributeBinding.setExplicitPersisterClass( bindingContext().< CollectionPersister >locateClassByName( - attributeSource.getCustomPersisterClassName() ) ); - } - attributeBinding.setCustomLoaderName( attributeSource.getCustomLoaderName() ); - attributeBinding.setCustomSqlInsert( attributeSource.getCustomSqlInsert() ); - attributeBinding.setCustomSqlUpdate( attributeSource.getCustomSqlUpdate() ); - attributeBinding.setCustomSqlDelete( attributeSource.getCustomSqlDelete() ); - attributeBinding.setCustomSqlDeleteAll( attributeSource.getCustomSqlDeleteAll() ); - attributeBinding.setWhere( attributeSource.getWhere() ); - - switch ( attributeSource.getElementSource().getNature() ){ - case BASIC: - bindBasicPluralAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); - break; - case ONE_TO_MANY: - bindOneToManyAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); - break; - case MANY_TO_MANY: - bindManyToManyAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); - break; - case AGGREGATE: - bindPluralAggregateAttribute( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); - break; - case MANY_TO_ANY: - //todo?? - default: - throw bindingContext().makeMappingException( - String.format( - "Unknown type of collection element: %s", - attributeSource.getElementSource().getNature() - ) - ); - } - bindIndexedPluralAttributeIfPossible( attributeSource, attributeBinding, reflectedCollectionJavaTypes ); - bindCollectionTablePrimaryKey( attributeBinding, attributeSource ); - metadata.addCollection( attributeBinding ); - return attributeBinding; - } - - private void bindIndexedPluralAttributeIfPossible(PluralAttributeSource attributeSource, AbstractPluralAttributeBinding attributeBinding, ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { - if ( attributeBinding.getAttribute().getNature().isIndexable() && attributeSource instanceof IndexedPluralAttributeSource ) { + private void bindIndexedPluralAttributeIfPossible( + final PluralAttributeSource attributeSource, + final AbstractPluralAttributeBinding attributeBinding, + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + if ( attributeBinding.getAttribute() + .getNature() + .isIndexable() && attributeSource instanceof IndexedPluralAttributeSource ) { attributeBinding.setIndex( true ); bindCollectionIndex( (IndexedPluralAttributeBinding) attributeBinding, - ( ( IndexedPluralAttributeSource ) attributeSource ).getIndexSource(), - defaultCollectionIndexJavaTypeName( reflectedCollectionJavaTypes ) ); - } else { + ( (IndexedPluralAttributeSource) attributeSource ).getIndexSource(), + HibernateTypeHelper.defaultCollectionIndexJavaTypeName( reflectedCollectionJavaTypes ) + ); + } + else { attributeBinding.setIndex( false ); } } - private void bindPluralAggregateAttribute(PluralAttributeSource attributeSource, AbstractPluralAttributeBinding attributeBinding, ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + private void bindPluralAggregateAttribute( + final PluralAttributeSource attributeSource, + final AbstractPluralAttributeBinding attributeBinding, + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { bindNonAssociationCollectionKey( attributeBinding, attributeSource ); bindCompositeCollectionElement( (CompositePluralAttributeElementBinding) attributeBinding.getPluralAttributeElementBinding(), (CompositePluralAttributeElementSource) attributeSource.getElementSource(), - defaultCollectionElementJavaTypeName( reflectedCollectionJavaTypes ) + HibernateTypeHelper.defaultCollectionElementJavaTypeName( reflectedCollectionJavaTypes ) ); } - private void bindManyToManyAttribute(PluralAttributeSource attributeSource, AbstractPluralAttributeBinding attributeBinding, ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + private void bindManyToManyAttribute( + final PluralAttributeSource attributeSource, + final AbstractPluralAttributeBinding attributeBinding, + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { final ManyToManyPluralAttributeElementSource elementSource = (ManyToManyPluralAttributeElementSource) attributeSource.getElementSource(); - final String defaultElementJavaTypeName = defaultCollectionElementJavaTypeName( reflectedCollectionJavaTypes ); + final String defaultElementJavaTypeName = HibernateTypeHelper.defaultCollectionElementJavaTypeName( + reflectedCollectionJavaTypes + ); String referencedEntityName = elementSource.getReferencedEntityName() != null ? elementSource.getReferencedEntityName() : defaultElementJavaTypeName; - if ( referencedEntityName == null ) { - throw bindingContext().makeMappingException( String.format( "The mapping for the entity associated with one-to-many attribute (%s) is undefined.", - createAttributePath( attributeBinding ) - ) ); + if ( referencedEntityName == null ) { + throw bindingContext().makeMappingException( + String.format( + "The mapping for the entity associated with one-to-many attribute (%s) is undefined.", + createAttributePath( attributeBinding ) + ) + ); } EntityBinding referencedEntityBinding = findOrBindEntityBinding( referencedEntityName ); bindManyToManyCollectionKey( attributeBinding, attributeSource, referencedEntityBinding ); @@ -1988,18 +1908,26 @@ public class Binder { ); } - private void bindOneToManyAttribute(PluralAttributeSource attributeSource, AbstractPluralAttributeBinding attributeBinding, ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + private void bindOneToManyAttribute( + final PluralAttributeSource attributeSource, + final AbstractPluralAttributeBinding attributeBinding, + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { final OneToManyPluralAttributeElementSource elementSource = (OneToManyPluralAttributeElementSource) attributeSource.getElementSource(); - final String defaultElementJavaTypeName = defaultCollectionElementJavaTypeName( reflectedCollectionJavaTypes ); + final String defaultElementJavaTypeName = HibernateTypeHelper.defaultCollectionElementJavaTypeName( + reflectedCollectionJavaTypes + ); String referencedEntityName = elementSource.getReferencedEntityName() != null ? elementSource.getReferencedEntityName() : defaultElementJavaTypeName; - if ( referencedEntityName == null ) { - throw bindingContext().makeMappingException( String.format( "The mapping for the entity associated with one-to-many attribute (%s) is undefined.", - createAttributePath( attributeBinding ) - ) ); + if ( referencedEntityName == null ) { + throw bindingContext().makeMappingException( + String.format( + "The mapping for the entity associated with one-to-many attribute (%s) is undefined.", + createAttributePath( attributeBinding ) + ) + ); } EntityBinding referencedEntityBinding = findOrBindEntityBinding( referencedEntityName ); bindOneToManyCollectionKey( attributeBinding, attributeSource, referencedEntityBinding ); @@ -2011,216 +1939,41 @@ public class Binder { ); } - private void bindBasicPluralAttribute(PluralAttributeSource attributeSource, AbstractPluralAttributeBinding attributeBinding, ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + private void bindBasicPluralAttribute( + final PluralAttributeSource attributeSource, + final AbstractPluralAttributeBinding attributeBinding, + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { bindNonAssociationCollectionKey( attributeBinding, attributeSource ); bindBasicCollectionElement( (BasicPluralAttributeElementBinding) attributeBinding.getPluralAttributeElementBinding(), (BasicPluralAttributeElementSource) attributeSource.getElementSource(), - defaultCollectionElementJavaTypeName( reflectedCollectionJavaTypes ) ); - } - - private void bindPrimaryTable(final EntityBinding entityBinding, final EntitySource entitySource) { - final EntityBinding superEntityBinding = entityBinding.getSuperEntityBinding(); - final InheritanceType inheritanceType = entityBinding.getHierarchyDetails().getInheritanceType(); - final TableSpecification table; - final String tableName; - // single table and sub entity - if ( superEntityBinding != null && inheritanceType == InheritanceType.SINGLE_TABLE ) { - table = superEntityBinding.getPrimaryTable(); - tableName = superEntityBinding.getPrimaryTableName(); - // Configure discriminator if present - final String discriminatorValue = entitySource.getDiscriminatorMatchValue() != null ? - entitySource.getDiscriminatorMatchValue() - : entitySource.getEntityName(); - entityBinding.setDiscriminatorMatchValue( discriminatorValue ); - } - - // single table and root entity - // joined - // table per class and non-abstract entity - else { - Table includedTable = null; - if ( superEntityBinding != null - && inheritanceType == InheritanceType.TABLE_PER_CLASS - && Table.class.isInstance( superEntityBinding.getPrimaryTable() ) ) { - includedTable = Table.class.cast( superEntityBinding.getPrimaryTable() ); - } - table = createTable( - entitySource.getPrimaryTable(), new DefaultNamingStrategy() { - - @Override - public String defaultName() { - String name = StringHelper.isNotEmpty( entityBinding.getJpaEntityName() ) ? entityBinding.getJpaEntityName() : entityBinding - .getEntity().getName(); - return bindingContext().getNamingStrategy().classToTableName( name ); - } - }, - includedTable - ); - tableName = table.getLogicalName().getText(); - } - entityBinding.setPrimaryTable( table ); - entityBinding.setPrimaryTableName( tableName ); - } - - private void bindSecondaryTables( final EntityBinding entityBinding, final EntitySource entitySource ) { - for ( final SecondaryTableSource secondaryTableSource : entitySource.getSecondaryTables() ) { - final TableSpecification table = createTable( secondaryTableSource.getTableSource(), null ); - table.addComment( secondaryTableSource.getComment() ); - - List joinRelationalValueBindings; - // TODO: deal with property-refs??? - if ( secondaryTableSource.getPrimaryKeyColumnSources().isEmpty() ) { - final List joinedColumns = entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); - joinRelationalValueBindings = new ArrayList( joinedColumns.size() ); - for ( Column joinedColumn : joinedColumns ) { - Column joinColumn = table.locateOrCreateColumn( - bindingContext().getNamingStrategy().joinKeyColumnName( - joinedColumn.getColumnName().getText(), - entityBinding.getPrimaryTable().getLogicalName().getText() - ) - ); - joinRelationalValueBindings.add( new RelationalValueBinding( joinColumn, true, false ) ); - } - } - else { - joinRelationalValueBindings = new ArrayList( secondaryTableSource.getPrimaryKeyColumnSources().size() ); - final List primaryKeyColumns = entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); - if ( primaryKeyColumns.size() != secondaryTableSource.getPrimaryKeyColumnSources().size() ) { - throw bindingContext().makeMappingException( - String.format( - "The number of primary key column sources provided for a secondary table is not equal to the number of columns in the primary key for [%s].", - entityBinding.getEntityName() - ) - ); - } - for ( int i = 0 ; i < primaryKeyColumns.size() ; i++ ) { - // todo : apply naming strategy to infer missing column name - final ColumnSource joinColumnSource = secondaryTableSource.getPrimaryKeyColumnSources().get( i ); - Column column = table.locateColumn( joinColumnSource.getName() ); - if ( column == null ) { - column = table.createColumn( joinColumnSource.getName() ); - if ( joinColumnSource.getSqlType() != null ) { - column.setSqlType( joinColumnSource.getSqlType() ); - } - } - joinRelationalValueBindings.add( new RelationalValueBinding( column, true, false ) ); - } - } - - typeHelper.bindJdbcDataType( - entityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding().getHibernateTypeDescriptor().getResolvedTypeMapping(), - joinRelationalValueBindings - ); - // TODO: make the foreign key column the primary key??? - final ForeignKey foreignKey = bindForeignKey( - quoteIdentifierIfNonEmpty( secondaryTableSource.getExplicitForeignKeyName() ), - extractColumnsFromRelationalValueBindings( joinRelationalValueBindings ), - determineForeignKeyTargetColumns( entityBinding, secondaryTableSource ) - ); - SecondaryTable secondaryTable = new SecondaryTable( table, foreignKey ); - secondaryTable.setFetchStyle( secondaryTableSource.getFetchStyle() ); - secondaryTable.setInverse( secondaryTableSource.isInverse() ); - secondaryTable.setOptional( secondaryTableSource.isOptional() ); - secondaryTable.setCascadeDeleteEnabled( secondaryTableSource.isCascadeDeleteEnabled() ); - entityBinding.addSecondaryTable( secondaryTable ); - } - } - - 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 ) + HibernateTypeHelper.defaultCollectionElementJavaTypeName( reflectedCollectionJavaTypes ) ); } - 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 SingularAttributeBinding bindSingularAttribute( - final AttributeBindingContainer attributeBindingContainer, - final SingularAttributeSource attributeSource, - boolean isIdentifierAttribute) { - final SingularAttributeSource.Nature nature = attributeSource.getNature(); - final SingularAttribute attribute = - attributeBindingContainer.getAttributeContainer().locateSingularAttribute( attributeSource.getName() ); - switch ( nature ) { - case BASIC: - return bindBasicAttribute( attributeBindingContainer, attributeSource, attribute ); - case ONE_TO_ONE: - return bindOneToOneAttribute( - attributeBindingContainer, - ToOneAttributeSource.class.cast( attributeSource ), - attribute - ); - case MANY_TO_ONE: - return bindManyToOneAttribute( - attributeBindingContainer, - ToOneAttributeSource.class.cast( attributeSource ), - attribute - ); - case COMPOSITE: - return bindAggregatedCompositeAttribute( - attributeBindingContainer, - ComponentAttributeSource.class.cast( attributeSource ), - attribute, - isIdentifierAttribute - ); - default: - throw new NotYetImplementedException( nature.toString() ); - } - } private void bindSortingAndOrdering( final AbstractPluralAttributeBinding attributeBinding, - final PluralAttributeSource attributeSource ) { + final PluralAttributeSource attributeSource) { if ( Sortable.class.isInstance( attributeSource ) ) { - final Sortable sortable = ( Sortable ) attributeSource; + final Sortable sortable = (Sortable) attributeSource; attributeBinding.setSorted( sortable.isSorted() ); if ( sortable.isSorted() && !sortable.getComparatorName().equalsIgnoreCase( "natural" ) ) { - Class< Comparator< ? > > comparatorClass = + Class> comparatorClass = bindingContext().locateClassByName( sortable.getComparatorName() ); try { attributeBinding.setComparator( comparatorClass.newInstance() ); - } catch ( Exception error ) { + } + catch ( Exception error ) { throw bindingContext().makeMappingException( String.format( "Unable to create comparator [%s] for attribute [%s]", sortable.getComparatorName(), - attributeSource.getName() ), - error ); + attributeSource.getName() + ), + error + ); } } } @@ -2241,7 +1994,7 @@ public class Binder { sb.append( column.getColumnName().getText() ); sb.append( " asc ," ); } - orderBy = sb.substring( 0, sb.length()-2 ); + orderBy = sb.substring( 0, sb.length() - 2 ); } attributeBinding.setOrderBy( orderBy ); @@ -2249,13 +2002,428 @@ public class Binder { } } - private void bindSubEntities( final EntityBinding entityBinding, final EntitySource entitySource ) { - for ( final SubclassEntitySource subEntitySource : entitySource.subclassEntitySources() ) { - bindSubEntity( subEntitySource, entityBinding ); + private SingularAttributeBinding determinePluralAttributeKeyReferencedBinding( + final AttributeBindingContainer attributeBindingContainer, + final PluralAttributeSource attributeSource) { + final EntityBinding entityBinding = attributeBindingContainer.seekEntityBinding(); + final JoinColumnResolutionDelegate resolutionDelegate = + attributeSource.getKeySource().getForeignKeyTargetColumnResolutionDelegate(); + + if ( resolutionDelegate == null ) { + return entityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); + } + + AttributeBinding referencedAttributeBinding; + final String referencedAttributeName = resolutionDelegate.getReferencedAttributeName(); + if ( referencedAttributeName == null ) { + referencedAttributeBinding = attributeBindingContainer.locateAttributeBinding( + resolutionDelegate.getJoinColumns( + new JoinColumnResolutionContext() { + @Override + public List resolveRelationalValuesForAttribute(String attributeName) { + return null; + } + + @Override + public Column resolveColumn(String logicalColumnName, String logicalTableName, String logicalSchemaName, String logicalCatalogName) { + for ( AttributeBinding attributeBinding : attributeBindingContainer.attributeBindings() ) { + if ( SingularAttributeBinding.class.isInstance( attributeBinding ) ) { + SingularAttributeBinding singularAttributeBinding = SingularAttributeBinding.class + .cast( + attributeBinding + ); + for ( RelationalValueBinding relationalValueBinding : singularAttributeBinding + .getRelationalValueBindings() ) { + if ( Column.class.isInstance( relationalValueBinding.getValue() ) ) { + Identifier columnIdentifier = Identifier.toIdentifier( + quotedIdentifier( + logicalColumnName + ) + ); + Column column = Column.class.cast( relationalValueBinding.getValue() ); + if ( column.getColumnName().equals( columnIdentifier ) ) { + return column; + } + } + } + } + } + return null; + } + } + ) + ); + } + else { + referencedAttributeBinding = attributeBindingContainer.locateAttributeBinding( referencedAttributeName ); + } + + + if ( referencedAttributeBinding == null ) { + referencedAttributeBinding = attributeBinding( + entityBinding.getEntity().getName(), + referencedAttributeName + ); + } + if ( referencedAttributeBinding == null ) { + throw bindingContext().makeMappingException( + "Plural attribute key references an attribute binding that does not exist: " + + referencedAttributeBinding + ); + } + if ( !referencedAttributeBinding.getAttribute().isSingular() ) { + throw bindingContext().makeMappingException( + "Plural attribute key references a plural attribute; it must not be plural: " + + referencedAttributeName + ); + } + return (SingularAttributeBinding) referencedAttributeBinding; + } + + private SingularAttributeBinding determineReferencedAttributeBinding( + final JoinColumnResolutionDelegate resolutionDelegate, + final JoinColumnResolutionContext resolutionContext, + final EntityBinding referencedEntityBinding) { + if ( resolutionDelegate == null ) { + return referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); + } + + final String explicitName = resolutionDelegate.getReferencedAttributeName(); + final AttributeBinding referencedAttributeBinding = explicitName != null + ? referencedEntityBinding.locateAttributeBindingByPath( explicitName, true ) + : referencedEntityBinding.locateAttributeBinding( + resolutionDelegate.getJoinColumns( resolutionContext ), + true + ); + + if ( referencedAttributeBinding == null ) { + if ( explicitName != null ) { + throw bindingContext().makeMappingException( + String.format( + "No attribute binding found with name: %s.%s", + referencedEntityBinding.getEntityName(), + explicitName + ) + ); + } + else { + throw new NotYetImplementedException( + "No support yet for referenced join columns unless they correspond with columns bound for an attribute binding." + ); + } + } + + if ( !referencedAttributeBinding.getAttribute().isSingular() ) { + throw bindingContext().makeMappingException( + String.format( + "Foreign key references a non-singular attribute [%s]", + referencedAttributeBinding.getAttribute().getName() + ) + ); + } + return (SingularAttributeBinding) referencedAttributeBinding; + } + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ relational binding relates methods + private void bindBasicPluralElementRelationalValues( + final RelationalValueSourceContainer relationalValueSourceContainer, + final BasicPluralAttributeElementBinding elementBinding) { + elementBinding.setRelationalValueBindings( + bindValues( + elementBinding.getPluralAttributeBinding().getContainer(), + relationalValueSourceContainer, + elementBinding.getPluralAttributeBinding().getAttribute(), + elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().getCollectionTable(), + false + ) + ); + } + + private void bindBasicSetCollectionTablePrimaryKey(final SetBinding attributeBinding) { + final BasicPluralAttributeElementBinding elementBinding = + (BasicPluralAttributeElementBinding) attributeBinding.getPluralAttributeElementBinding(); + if ( elementBinding.getNature() != PluralAttributeElementBinding.Nature.BASIC ) { + throw bindingContext().makeMappingException( + String.format( + "Expected a SetBinding with an element of nature Nature.BASIC; instead was %s", + elementBinding.getNature() + ) + ); + } + if ( hasAnyNonNullableColumns( elementBinding.getRelationalValueBindings() ) ) { + bindSetCollectionTablePrimaryKey( attributeBinding ); + } + 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 bindUniqueConstraints( final EntityBinding entityBinding, final EntitySource entitySource ) { + private void bindSetCollectionTablePrimaryKey(final SetBinding attributeBinding) { + final PluralAttributeElementBinding elementBinding = attributeBinding.getPluralAttributeElementBinding(); + 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() ); + } + } + } + + private void bindCollectionTableForeignKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeKeySource keySource, + final TableSpecification collectionTable) { + + final AttributeBindingContainer attributeBindingContainer = attributeBinding.getContainer(); + final PluralAttributeKeyBinding keyBinding = attributeBinding.getPluralAttributeKeyBinding(); + + List sourceColumnBindings = + bindValues( + attributeBindingContainer, + keySource, + attributeBinding.getAttribute(), + collectionTable, + attributeBinding.getPluralAttributeElementBinding() + .getNature() != PluralAttributeElementBinding.Nature.ONE_TO_MANY + ); + // Determine if the foreign key (source) column is updatable and also extract the columns out + // of the RelationalValueBindings. + boolean isInsertable = false; + boolean isUpdatable = false; + List sourceColumns = new ArrayList( sourceColumnBindings.size() ); + for ( RelationalValueBinding relationalValueBinding : sourceColumnBindings ) { + 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." + ); + } + isInsertable = isInsertable || relationalValueBinding.isIncludeInInsert(); + isUpdatable = isUpdatable || relationalValueBinding.isIncludeInUpdate(); + sourceColumns.add( (Column) value ); + } + keyBinding.setInsertable( isInsertable ); + keyBinding.setUpdatable( isUpdatable ); + + List targetColumns = + determineForeignKeyTargetColumns( + attributeBindingContainer.seekEntityBinding(), + keySource + ); + + ForeignKey foreignKey = bindForeignKey( + quoteIdentifierIfNonEmpty( keySource.getExplicitForeignKeyName() ), + sourceColumns, + 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 fkColumnIterator = keyBinding.getForeignKey().getSourceColumns().iterator(); + if ( resolvedKeyType.isComponentType() ) { + ComponentType componentType = (ComponentType) resolvedKeyType; + for ( Type subType : componentType.getSubtypes() ) { + typeHelper.bindJdbcDataType( subType, fkColumnIterator.next() ); + } + } + else { + typeHelper.bindJdbcDataType( resolvedKeyType, fkColumnIterator.next() ); + } + } + + private void bindCollectionTablePrimaryKey( + final AbstractPluralAttributeBinding attributeBinding, + final PluralAttributeSource attributeSource) { + PluralAttributeSource.Nature pluralAttributeNature = attributeSource.getNature(); + if ( attributeSource.getElementSource().getNature() == PluralAttributeElementSource.Nature.ONE_TO_MANY + || pluralAttributeNature == PluralAttributeSource.Nature.BAG ) { + return; + } + if ( attributeBinding.getPluralAttributeElementBinding() + .getNature() == PluralAttributeElementBinding.Nature.BASIC ) { + if ( pluralAttributeNature == PluralAttributeSource.Nature.SET ) { + bindBasicSetCollectionTablePrimaryKey( (SetBinding) attributeBinding ); + } + else if ( + pluralAttributeNature == PluralAttributeSource.Nature.LIST + || pluralAttributeNature == PluralAttributeSource.Nature.MAP + || pluralAttributeNature == PluralAttributeSource.Nature.ARRAY ) { + bindIndexedCollectionTablePrimaryKey( (IndexedPluralAttributeBinding) attributeBinding ); + } + else { + throw new NotYetImplementedException( + String.format( "%s of basic elements is not supported yet.", pluralAttributeNature ) + ); + } + } + else if ( attributeBinding.getPluralAttributeElementBinding() + .getNature() == PluralAttributeElementBinding.Nature.MANY_TO_MANY ) { + if ( !attributeBinding.getPluralAttributeKeyBinding().isInverse() ) { + if ( pluralAttributeNature == PluralAttributeSource.Nature.SET ) { + bindSetCollectionTablePrimaryKey( (SetBinding) attributeBinding ); + } + else if ( + pluralAttributeNature == PluralAttributeSource.Nature.LIST + || pluralAttributeNature == PluralAttributeSource.Nature.MAP + || pluralAttributeNature == PluralAttributeSource.Nature.ARRAY ) { + bindIndexedCollectionTablePrimaryKey( (IndexedPluralAttributeBinding) attributeBinding ); + } + else { + throw new NotYetImplementedException( + String.format( "Many-to-many %s is not supported yet.", pluralAttributeNature ) + ); + } + } + } + } + + private void bindSubEntityPrimaryKey( + final EntityBinding entityBinding, + final EntitySource entitySource) { + final InheritanceType inheritanceType = entityBinding.getHierarchyDetails().getInheritanceType(); + final EntityBinding superEntityBinding = entityBinding.getSuperEntityBinding(); + if ( superEntityBinding == null ) { + throw new AssertionFailure( "super entitybinding is null " ); + } + if ( inheritanceType == InheritanceType.TABLE_PER_CLASS ) { + + } + if ( inheritanceType == InheritanceType.JOINED ) { + SubclassEntitySource subclassEntitySource = (SubclassEntitySource) entitySource; + ForeignKey fk = entityBinding.getPrimaryTable().createForeignKey( + superEntityBinding.getPrimaryTable(), + subclassEntitySource.getJoinedForeignKeyName() + ); + final List primaryKeyJoinColumnSources = subclassEntitySource.getPrimaryKeyJoinColumnSources(); + final boolean hasPrimaryKeyJoinColumns = CollectionHelper.isNotEmpty( primaryKeyJoinColumnSources ); + final List superEntityBindingPrimaryKeyColumns = superEntityBinding.getPrimaryTable() + .getPrimaryKey() + .getColumns(); + + for ( int i = 0, size = superEntityBindingPrimaryKeyColumns.size(); i < size; i++ ) { + Column superEntityBindingPrimaryKeyColumn = superEntityBindingPrimaryKeyColumns.get( i ); + PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource = hasPrimaryKeyJoinColumns && i < primaryKeyJoinColumnSources + .size() ? primaryKeyJoinColumnSources.get( i ) : null; + final String columnName; + if ( primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( primaryKeyJoinColumnSource.getColumnName() ) ) { + columnName = bindingContext().getNamingStrategy() + .columnName( primaryKeyJoinColumnSource.getColumnName() ); + } + else { + columnName = superEntityBindingPrimaryKeyColumn.getColumnName().getText(); + } + Column column = entityBinding.getPrimaryTable().locateOrCreateColumn( columnName ); + column.setCheckCondition( superEntityBindingPrimaryKeyColumn.getCheckCondition() ); + column.setComment( superEntityBindingPrimaryKeyColumn.getComment() ); + column.setDefaultValue( superEntityBindingPrimaryKeyColumn.getDefaultValue() ); + column.setIdentity( superEntityBindingPrimaryKeyColumn.isIdentity() ); + column.setNullable( superEntityBindingPrimaryKeyColumn.isNullable() ); + column.setReadFragment( superEntityBindingPrimaryKeyColumn.getReadFragment() ); + column.setWriteFragment( superEntityBindingPrimaryKeyColumn.getWriteFragment() ); + column.setUnique( superEntityBindingPrimaryKeyColumn.isUnique() ); + final String sqlType = getSqlTypeFromPrimaryKeyJoinColumnSourceIfExist( + superEntityBindingPrimaryKeyColumn, + primaryKeyJoinColumnSource + ); + column.setSqlType( sqlType ); + column.setSize( superEntityBindingPrimaryKeyColumn.getSize() ); + column.setJdbcDataType( superEntityBindingPrimaryKeyColumn.getJdbcDataType() ); + entityBinding.getPrimaryTable().getPrimaryKey().addColumn( column ); + //todo still need to figure out how to handle the referencedColumnName property + fk.addColumnMapping( column, superEntityBindingPrimaryKeyColumn ); + } + } + } + + + private ForeignKey bindForeignKey( + final String foreignKeyName, + final List sourceColumns, + final 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( + final ForeignKey foreignKey, + final List sourceColumns, + final 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().getText(), + sourceColumns.size(), + targetColumns.get( 0 ).getTable().getLogicalName().getText(), + targetColumns.size() + ) + ); + } + for ( int i = 0; i < sourceColumns.size(); i++ ) { + foreignKey.addColumnMapping( sourceColumns.get( i ), targetColumns.get( i ) ); + } + } + + private TableSpecification locateDefaultTableSpecificationForAttribute( + final AttributeBindingContainer attributeBindingContainer, + final SingularAttributeSource attributeSource) { + return attributeSource.getContainingTableName() == null ? + attributeBindingContainer.getPrimaryTable() : + attributeBindingContainer.seekEntityBinding().locateTable( attributeSource.getContainingTableName() ); + } + + private void bindUniqueConstraints( + final EntityBinding entityBinding, + final EntitySource entitySource) { int uniqueIndexPerTable = 0; for ( final ConstraintSource constraintSource : entitySource.getConstraints() ) { if ( UniqueConstraintSource.class.isInstance( constraintSource ) ) { @@ -2287,10 +2455,17 @@ public class Binder { } } ); - return bindValues( attributeBindingContainer, valueSourceContainer, attribute, defaultTable, list, forceNonNullable ); + return bindValues( + attributeBindingContainer, + valueSourceContainer, + attribute, + defaultTable, + list, + forceNonNullable + ); } - private List< RelationalValueBinding > bindValues( + private List bindValues( final AttributeBindingContainer attributeBindingContainer, final RelationalValueSourceContainer valueSourceContainer, final Attribute attribute, @@ -2303,7 +2478,7 @@ public class Binder { ) ? SingularAttributeSource.class.cast( valueSourceContainer ).getNaturalIdMutability() : NaturalIdMutability.NOT_NATURAL_ID; final boolean isNaturalId = naturalIdMutability != NaturalIdMutability.NOT_NATURAL_ID; - final boolean isImmutableNaturalId = isNaturalId && (naturalIdMutability == NaturalIdMutability.IMMUTABLE); + final boolean isImmutableNaturalId = isNaturalId && ( naturalIdMutability == NaturalIdMutability.IMMUTABLE ); final boolean reallyForceNonNullable = forceNonNullable || isNaturalId; if ( valueSourceContainer.relationalValueSources().isEmpty() ) { @@ -2324,23 +2499,27 @@ public class Binder { ); } - } else { + } + else { final String name = attribute.getName(); for ( final RelationalValueSource valueSource : valueSourceContainer.relationalValueSources() ) { final TableSpecification table = valueSource.getContainingTableName() == null ? defaultTable - : attributeBindingContainer.seekEntityBinding().locateTable( valueSource.getContainingTableName() ); + : attributeBindingContainer.seekEntityBinding() + .locateTable( valueSource.getContainingTableName() ); if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { - final ColumnSource columnSource = ( ColumnSource ) valueSource; + final ColumnSource columnSource = (ColumnSource) valueSource; final boolean isIncludedInInsert = TruthValue.toBoolean( columnSource.isIncludedInInsert(), - valueSourceContainer.areValuesIncludedInInsertByDefault() ); + valueSourceContainer.areValuesIncludedInInsertByDefault() + ); final boolean isIncludedInUpdate = TruthValue.toBoolean( columnSource.isIncludedInUpdate(), - valueSourceContainer.areValuesIncludedInUpdateByDefault() ); + valueSourceContainer.areValuesIncludedInUpdateByDefault() + ); Column column = createColumn( table, columnSource, @@ -2349,13 +2528,20 @@ public class Binder { valueSourceContainer.areValuesNullableByDefault(), true ); - if(isNaturalId){ + if ( isNaturalId ) { addUniqueConstraintForNaturalIdColumn( table, column ); } - valueBindings.add( new RelationalValueBinding( column, isIncludedInInsert, !isImmutableNaturalId && isIncludedInUpdate ) ); - } else { + valueBindings.add( + new RelationalValueBinding( + column, + isIncludedInInsert, + !isImmutableNaturalId && isIncludedInUpdate + ) + ); + } + else { final DerivedValue derivedValue = - table.locateOrCreateDerivedValue( ( ( DerivedValueSource ) valueSource ).getExpression() ); + table.locateOrCreateDerivedValue( ( (DerivedValueSource) valueSource ).getExpression() ); valueBindings.add( new RelationalValueBinding( derivedValue ) ); } } @@ -2363,30 +2549,14 @@ public class Binder { return valueBindings; } - private void bindVersion( final EntityBinding rootEntityBinding, final VersionAttributeSource versionAttributeSource ) { - if ( versionAttributeSource == null ) { - return; - } - final EntityVersion version = rootEntityBinding.getHierarchyDetails().getEntityVersion(); - version.setVersioningAttributeBinding( ( BasicAttributeBinding ) bindAttribute( rootEntityBinding, versionAttributeSource ) ); - // ensure version is non-nullable - for ( RelationalValueBinding valueBinding : version.getVersioningAttributeBinding().getRelationalValueBindings() ) { - if ( valueBinding.getValue() instanceof Column ) { - ( (Column) valueBinding.getValue() ).setNullable( false ); - } - } - version.setUnsavedValue( - versionAttributeSource.getUnsavedValue() == null - ? "undefined" - : versionAttributeSource.getUnsavedValue() - ); - } - private Value buildRelationValue(RelationalValueSource valueSource, TableSpecification table) { + private Value buildRelationValue( + final RelationalValueSource valueSource, + final TableSpecification table) { if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { return createColumn( table, - ( ColumnSource ) valueSource, + (ColumnSource) valueSource, bindingContext().getMappingDefaults().getDiscriminatorColumnName(), false, false, @@ -2394,16 +2564,10 @@ public class Binder { ); } else { - return table.locateOrCreateDerivedValue( ( ( DerivedValueSource ) valueSource ).getExpression() ); + return table.locateOrCreateDerivedValue( ( (DerivedValueSource) valueSource ).getExpression() ); } } - private static String createAttributePath(final AttributeBinding attributeBinding) { - return new StringBuffer( attributeBinding.getContainer().getPathBase() ) - .append( '.' ) - .append( attributeBinding.getAttribute().getName() ) - .toString(); - } private TableSpecification createBasicCollectionTable( final AbstractPluralAttributeBinding pluralAttributeBinding, @@ -2415,7 +2579,7 @@ public class Binder { final EntityBinding owner = pluralAttributeBinding.getContainer().seekEntityBinding(); final String ownerTableLogicalName = Table.class.isInstance( owner.getPrimaryTable() ) - ? ( ( Table ) owner.getPrimaryTable() ).getPhysicalName().getText() + ? ( (Table) owner.getPrimaryTable() ).getPhysicalName().getText() : null; return bindingContext().getNamingStrategy().collectionTableName( owner.getEntity().getName(), @@ -2450,11 +2614,11 @@ public class Binder { } final String ownerTableLogicalName = Table.class.isInstance( ownerEntityBinding.getPrimaryTable() ) - ? ( ( Table ) ownerEntityBinding.getPrimaryTable() ).getPhysicalName().getText() + ? ( (Table) ownerEntityBinding.getPrimaryTable() ).getPhysicalName().getText() : null; final String inverseTableLogicalName = Table.class.isInstance( inverseEntityBinding.getPrimaryTable() ) - ? ( ( Table ) inverseEntityBinding.getPrimaryTable() ).getPhysicalName().getText() + ? ( (Table) inverseEntityBinding.getPrimaryTable() ).getPhysicalName().getText() : null; return bindingContext().getNamingStrategy().collectionTableName( ownerEntityBinding.getEntity().getName(), @@ -2474,9 +2638,11 @@ public class Binder { final String defaultName, final boolean forceNotNull, final boolean isNullableByDefault, - final boolean isDefaultAttributeName ) { + final boolean isDefaultAttributeName) { if ( columnSource.getName() == null && defaultName == null ) { - throw bindingContext().makeMappingException( "Cannot resolve name for column because no name was specified and default name is null." ); + throw bindingContext().makeMappingException( + "Cannot resolve name for column because no name was specified and default name is null." + ); } final String name = resolveColumnName( columnSource, defaultName, isDefaultAttributeName ); final String resolvedColumnName = quotedIdentifier( name ); @@ -2494,11 +2660,20 @@ public class Binder { return column; } - private void resolveColumnNullabl(ColumnSource columnSource, boolean forceNotNull, boolean isNullableByDefault, Column column) { + private void resolveColumnNullabl( + final ColumnSource columnSource, + final boolean forceNotNull, + final boolean isNullableByDefault, + final Column column) { if ( forceNotNull ) { column.setNullable( false ); - if(columnSource.isNullable() == TruthValue.TRUE){ - log.warn( String.format( "Natural Id column[%s] has explicit set to allow nullable, we have to make it force not null ", columnSource.getName() ) ); + if ( columnSource.isNullable() == TruthValue.TRUE ) { + log.warn( + String.format( + "Natural Id column[%s] has explicit set to allow nullable, we have to make it force not null ", + columnSource.getName() + ) + ); } } else { @@ -2509,107 +2684,393 @@ public class Binder { } } - private String resolveColumnName(ColumnSource columnSource, String defaultName, boolean isDefaultAttributeName) { + private String resolveColumnName( + final ColumnSource columnSource, + final String defaultName, + final boolean isDefaultAttributeName) { final String name; if ( StringHelper.isNotEmpty( columnSource.getName() ) ) { name = bindingContext().getNamingStrategy().columnName( columnSource.getName() ); - } else if ( isDefaultAttributeName ) { + } + else if ( isDefaultAttributeName ) { name = bindingContext().getNamingStrategy().propertyToColumnName( defaultName ); - } else { + } + else { name = bindingContext().getNamingStrategy().columnName( defaultName ); } return name; } - private EntityBinding createEntityBinding( final EntitySource entitySource, final EntityBinding superEntityBinding ) { - // Create binding - final InheritanceType inheritanceType = inheritanceTypes.peek(); - final EntityMode entityMode = entityModes.peek(); - final EntityBinding entityBinding = - entitySource instanceof RootEntitySource ? new EntityBinding( inheritanceType, entityMode ) : new EntityBinding( - superEntityBinding ); - // Create domain entity - final String entityClassName = entityMode == EntityMode.POJO ? entitySource.getClassName() : null; - LocalBindingContext bindingContext = bindingContext(); - entityBinding.setEntity( new Entity( - entitySource.getEntityName(), - entityClassName, - bindingContext.makeClassReference( entityClassName ), - superEntityBinding == null ? null : superEntityBinding.getEntity() ) ); - entityBinding.setEntityName( entitySource.getEntityName() ); - entityBinding.setJpaEntityName( entitySource.getJpaEntityName() ); //must before creating primary table - entityBinding.setDynamicUpdate( entitySource.isDynamicUpdate() ); - entityBinding.setDynamicInsert( entitySource.isDynamicInsert() ); - entityBinding.setBatchSize( entitySource.getBatchSize() ); - entityBinding.setSelectBeforeUpdate( entitySource.isSelectBeforeUpdate() ); - entityBinding.setAbstract( entitySource.isAbstract() ); - entityBinding.setCustomLoaderName( entitySource.getCustomLoaderName() ); - entityBinding.setCustomInsert( entitySource.getCustomSqlInsert() ); - entityBinding.setCustomUpdate( entitySource.getCustomSqlUpdate() ); - entityBinding.setCustomDelete( entitySource.getCustomSqlDelete() ); - entityBinding.setJpaCallbackClasses( entitySource.getJpaCallbackClasses() ); + private TableSpecification createTable( + final TableSpecificationSource tableSpecSource, + final DefaultNamingStrategy defaultNamingStrategy) { + return createTable( tableSpecSource, defaultNamingStrategy, null ); - // todo: deal with joined and unioned subclass bindings - // todo: bind fetch profiles - // Configure rest of binding - final String customTuplizerClassName = entitySource.getCustomTuplizerClassName(); - if ( customTuplizerClassName != null ) { - entityBinding.setCustomEntityTuplizerClass( bindingContext.< EntityTuplizer >locateClassByName( customTuplizerClassName ) ); - } - final String customPersisterClassName = entitySource.getCustomPersisterClassName(); - if ( customPersisterClassName != null ) { - entityBinding.setCustomEntityPersisterClass( bindingContext.< EntityPersister >locateClassByName( customPersisterClassName ) ); - } - entityBinding.setMetaAttributeContext( createMetaAttributeContext( - entitySource.getMetaAttributeSources(), - true, - metadata.getGlobalMetaAttributeContext() ) ); - - if ( entitySource.getSynchronizedTableNames() != null ) { - entityBinding.addSynchronizedTableNames( entitySource.getSynchronizedTableNames() ); - } - resolveEntityLaziness( entitySource, entityMode, entityBinding, bindingContext ); - // Register binding with metadata - metadata.addEntity( entityBinding ); - return entityBinding; } - private void resolveEntityLaziness(EntitySource entitySource, EntityMode entityMode, EntityBinding entityBinding, LocalBindingContext bindingContext) { - if ( entityMode == EntityMode.POJO ) { - final String proxy = entitySource.getProxy(); - if ( proxy == null ) { - if ( entitySource.isLazy() ) { - entityBinding.setProxyInterfaceType( entityBinding.getEntity().getClassReferenceUnresolved() ); - entityBinding.setLazy( true ); - } - } else { - entityBinding.setProxyInterfaceType( bindingContext.makeClassReference( bindingContext.qualifyClassName( proxy ) ) ); - entityBinding.setLazy( true ); + private TableSpecification createTable( + final TableSpecificationSource tableSpecSource, + final DefaultNamingStrategy defaultNamingStrategy, + final Table includedTable) { + + final LocalBindingContext bindingContext = bindingContext(); + final MappingDefaults mappingDefaults = bindingContext.getMappingDefaults(); + final boolean isTableSourceNull = tableSpecSource == null; + final String explicitCatalogName = isTableSourceNull ? null : tableSpecSource.getExplicitCatalogName(); + final String explicitSchemaName = isTableSourceNull ? null : tableSpecSource.getExplicitSchemaName(); + final Schema.Name schemaName = + new Schema.Name( + createIdentifier( explicitCatalogName, mappingDefaults.getCatalogName() ), + createIdentifier( explicitSchemaName, mappingDefaults.getSchemaName() ) + ); + final Schema schema = bindingContext.getMetadataImplementor().getDatabase().locateSchema( schemaName ); + + TableSpecification tableSpec; + if ( isTableSourceNull ) { + if ( defaultNamingStrategy == null ) { + throw bindingContext().makeMappingException( "An explicit name must be specified for the table" ); } - } else { - entityBinding.setProxyInterfaceType( null ); - entityBinding.setLazy( entitySource.isLazy() ); + String tableName = defaultNamingStrategy.defaultName(); + tableSpec = createTableSpecification( bindingContext, schema, tableName, includedTable ); + } + else if ( tableSpecSource instanceof TableSource ) { + final TableSource tableSource = (TableSource) tableSpecSource; + String tableName = tableSource.getExplicitTableName(); + if ( tableName == null ) { + if ( defaultNamingStrategy == null ) { + throw bindingContext().makeMappingException( "An explicit name must be specified for the table" ); + } + tableName = defaultNamingStrategy.defaultName(); + } + tableSpec = createTableSpecification( bindingContext, schema, tableName, includedTable ); + } + else { + final InLineViewSource inLineViewSource = (InLineViewSource) tableSpecSource; + tableSpec = schema.createInLineView( + Identifier.toIdentifier( inLineViewSource.getLogicalName() ), + inLineViewSource.getSelectStatement() + ); + } + + return tableSpec; + } + + private TableSpecification createTableSpecification( + final LocalBindingContext bindingContext, + final Schema schema, + final String tableName, + final Table includedTable) { + String name = quotedIdentifier( tableName ); + final Identifier logicalTableId = Identifier.toIdentifier( name ); + name = quotedIdentifier( bindingContext.getNamingStrategy().tableName( name ) ); + final Identifier physicalTableId = Identifier.toIdentifier( name ); + final Table table = schema.locateTable( logicalTableId ); + if ( table != null ) { + return table; + } + TableSpecification tableSpec; + if ( includedTable == null ) { + tableSpec = schema.createTable( logicalTableId, physicalTableId ); + } + else { + tableSpec = schema.createDenormalizedTable( logicalTableId, physicalTableId, includedTable ); + } + return tableSpec; + } + + + private List determineForeignKeyTargetColumns( + final EntityBinding entityBinding, + final ForeignKeyContributingSource foreignKeyContributingSource) { + + // TODO: This method, JoinColumnResolutionContext, + // and JoinColumnResolutionDelegate need re-worked. There is currently + // no way to bind to a collection's inverse foreign key. + + final JoinColumnResolutionDelegate fkColumnResolutionDelegate = + foreignKeyContributingSource.getForeignKeyTargetColumnResolutionDelegate(); + + if ( fkColumnResolutionDelegate == null ) { + return entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); + } + else { + final List columns = new ArrayList(); + final JoinColumnResolutionContext resolutionContext = + new 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; + } + + @Override + public List resolveRelationalValuesForAttribute(String attributeName) { + if ( attributeName == null ) { + List values = new ArrayList(); + for ( Column column : entityBinding.getPrimaryTable().getPrimaryKey().getColumns() ) { + values.add( column ); + } + return values; + } + final AttributeBinding referencedAttributeBinding = + entityBinding.locateAttributeBindingByPath( attributeName, true ); + if ( referencedAttributeBinding == null ) { + throw bindingContext().makeMappingException( + String.format( + "Could not resolve named referenced property [%s] against entity [%s]", + attributeName, + entityBinding.getEntity().getName() + ) + ); + } + if ( !referencedAttributeBinding.getAttribute().isSingular() ) { + throw bindingContext().makeMappingException( + String.format( + "Referenced property [%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; } } - private Identifier createIdentifier( String name, final String defaultName ) { - if ( StringHelper.isEmpty( name ) ) { - name = defaultName; + + private ForeignKey locateAndBindForeignKeyByName( + final String foreignKeyName, + final List sourceColumns, + final List targetColumns) { + if ( foreignKeyName == null ) { + throw new AssertionFailure( "foreignKeyName must be non-null." ); } - name = quotedIdentifier( name ); - return Identifier.toIdentifier( name ); + 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; + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ simple instance helper methods + private void mapSourcesByName(final EntitySource rootEntitySource) { + String entityName = rootEntitySource.getEntityName(); + entitySourcesByName.put( entityName, rootEntitySource ); + log.debugf( "Mapped entity source \"%s\"", entityName ); + for ( final AttributeSource attributeSource : rootEntitySource.attributeSources() ) { + String key = attributeSourcesByNameKey( entityName, attributeSource.getName() ); + attributeSourcesByName.put( key, attributeSource ); + log.debugf( + "Mapped attribute source \"%s\" for entity source \"%s\"", + key, + rootEntitySource.getEntityName() + ); + } + for ( final SubclassEntitySource subclassEntitySource : rootEntitySource.subclassEntitySources() ) { + mapSourcesByName( subclassEntitySource ); + } + } + + private void cleanupBindingContext() { + bindingContexts.pop(); + inheritanceTypes.pop(); + entityModes.pop(); + } + + public LocalBindingContext bindingContext() { + return bindingContexts.peek(); + } + + + private void setupBindingContext( + final EntityHierarchy entityHierarchy, + final RootEntitySource rootEntitySource) { + // Save inheritance type and entity mode that will apply to entire hierarchy + inheritanceTypes.push( entityHierarchy.getHierarchyInheritanceType() ); + entityModes.push( rootEntitySource.getEntityMode() ); + bindingContexts.push( rootEntitySource.getLocalBindingContext() ); + } + + private String propertyAccessorName(final AttributeSource attributeSource) { + return propertyAccessorName( attributeSource.getPropertyAccessorName() ); + } + + private String propertyAccessorName(final String propertyAccessorName) { + return propertyAccessorName == null + ? bindingContext().getMappingDefaults().getPropertyAccessorName() + : propertyAccessorName; + } + + private String quoteIdentifierIfNonEmpty(final String name) { + return StringHelper.isEmpty( name ) ? null : quotedIdentifier( name ); + } + + private String quotedIdentifier(final String name) { + return bindingContext().isGloballyQuotedIdentifiers() ? StringHelper.quote( name ) : name; + } + + private Identifier createIdentifier(final String name, final String defaultName) { + String identifier = StringHelper.isEmpty( name ) ? defaultName : name; + + identifier = quotedIdentifier( identifier ); + return Identifier.toIdentifier( identifier ); + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static methods + private static PluralAttributeElementBinding.Nature pluralAttributeElementNature( + final PluralAttributeSource attributeSource) { + return PluralAttributeElementBinding.Nature.valueOf( attributeSource.getElementSource().getNature().name() ); + } + + private static PluralAttributeIndexBinding.Nature pluralAttributeIndexNature( + final PluralAttributeSource attributeSource) { + if ( !IndexedPluralAttributeSource.class.isInstance( attributeSource ) ) { + return null; + } + return PluralAttributeIndexBinding.Nature.valueOf( + ( (IndexedPluralAttributeSource) attributeSource ).getIndexSource().getNature().name() + ); + } + + private static void bindIndexedCollectionTablePrimaryKey( + final 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 ); + } + } + + + private static void markSuperEntityTableAbstractIfNecessary( + final EntityBinding superEntityBinding) { + if ( superEntityBinding == null ) { + return; + } + if ( superEntityBinding.getHierarchyDetails().getInheritanceType() != InheritanceType.TABLE_PER_CLASS ) { + return; + } + if ( superEntityBinding.isAbstract() != Boolean.TRUE ) { + return; + } + if ( !Table.class.isInstance( superEntityBinding.getPrimaryTable() ) ) { + return; + } + Table.class.cast( superEntityBinding.getPrimaryTable() ).setPhysicalTable( false ); + } + + private static String getRelativePathFromEntityName( + final AttributeBinding attributeBinding) { + final String fullPath = attributeBinding.getContainer().getPathBase() + "." + attributeBinding.getAttribute() + .getName(); + return fullPath.substring( attributeBinding.getContainer().seekEntityBinding().getEntityName().length() + 1 ); + } + + private static boolean hasDerivedValue( + final List relationalValueBindings) { + for ( RelationalValueBinding relationalValueBinding : relationalValueBindings ) { + if ( DerivedValue.class.isInstance( relationalValueBinding.getValue() ) ) { + return true; + } + } + return false; + } + + // TODO: should this be moved to CascadeStyles as a static method? + // TODO: sources already factor in default cascade; should that be done here instead? + private static CascadeStyle determineCascadeStyle( + final Iterable cascadeStyles) { + CascadeStyle cascadeStyleResult; + List cascadeStyleList = new ArrayList(); + for ( CascadeStyle style : cascadeStyles ) { + if ( style != CascadeStyles.NONE ) { + cascadeStyleList.add( style ); + } + } + if ( cascadeStyleList.isEmpty() ) { + cascadeStyleResult = CascadeStyles.NONE; + } + else if ( cascadeStyleList.size() == 1 ) { + cascadeStyleResult = cascadeStyleList.get( 0 ); + } + else { + cascadeStyleResult = new CascadeStyles.MultipleCascadeStyle( + cascadeStyleList.toArray( new CascadeStyle[cascadeStyleList.size()] ) + ); + } + return cascadeStyleResult; } private static MetaAttributeContext createMetaAttributeContext( final AttributeBindingContainer attributeBindingContainer, - final AttributeSource attributeSource ) { + final AttributeSource attributeSource) { return createMetaAttributeContext( attributeBindingContainer, attributeSource.getMetaAttributeSources() ); } private static MetaAttributeContext createMetaAttributeContext( final AttributeBindingContainer attributeBindingContainer, - final Iterable metaAttributeSources ) { + final Iterable metaAttributeSources) { return createMetaAttributeContext( metaAttributeSources, false, @@ -2617,10 +3078,10 @@ public class Binder { ); } - private static MetaAttributeContext createMetaAttributeContext( - final Iterable metaAttributeSources, + private static MetaAttributeContext createMetaAttributeContext( + final Iterable metaAttributeSources, final boolean onlyInheritable, - final MetaAttributeContext parentContext ) { + final MetaAttributeContext parentContext) { final MetaAttributeContext subContext = new MetaAttributeContext( parentContext ); for ( final MetaAttributeSource metaAttributeSource : metaAttributeSources ) { if ( onlyInheritable && !metaAttributeSource.isInheritable() ) { @@ -2639,406 +3100,22 @@ public class Binder { private static SingularAttribute createSingularAttribute( final AttributeBindingContainer attributeBindingContainer, - final SingularAttributeSource attributeSource ) { + final SingularAttributeSource attributeSource) { return attributeSource.isVirtualAttribute() - ? attributeBindingContainer.getAttributeContainer().createSyntheticSingularAttribute( attributeSource.getName() ) - : attributeBindingContainer.getAttributeContainer().createSingularAttribute( attributeSource.getName() ); - } - private TableSpecification createTable(final TableSpecificationSource tableSpecSource, - final DefaultNamingStrategy defaultNamingStrategy){ - return createTable( tableSpecSource, defaultNamingStrategy, null ); - - } - private TableSpecification createTable( - final TableSpecificationSource tableSpecSource, - final DefaultNamingStrategy defaultNamingStrategy, - final Table includedTable) { - - final LocalBindingContext bindingContext = bindingContext(); - final MappingDefaults mappingDefaults = bindingContext.getMappingDefaults(); - final boolean isTableSourceNull = tableSpecSource == null; - final String explicitCatalogName = isTableSourceNull ? null : tableSpecSource.getExplicitCatalogName(); - final String explicitSchemaName = isTableSourceNull ? null : tableSpecSource.getExplicitSchemaName(); - final Schema.Name schemaName = - new Schema.Name( - createIdentifier( explicitCatalogName, mappingDefaults.getCatalogName() ), - createIdentifier( explicitSchemaName, mappingDefaults.getSchemaName() ) - ); - final Schema schema = bindingContext.getMetadataImplementor().getDatabase().locateSchema( schemaName ); - - TableSpecification tableSpec = null; - if ( isTableSourceNull ) { - if ( defaultNamingStrategy == null ) { - throw bindingContext().makeMappingException( "An explicit name must be specified for the table" ); - } - String tableName = defaultNamingStrategy.defaultName(); - tableSpec = createTableSpecification( bindingContext, schema, tableName, includedTable ); - } - else if ( tableSpecSource instanceof TableSource ) { - final TableSource tableSource = ( TableSource ) tableSpecSource; - String tableName = tableSource.getExplicitTableName(); - if ( tableName == null ) { - if ( defaultNamingStrategy == null ) { - throw bindingContext().makeMappingException( "An explicit name must be specified for the table" ); - } - tableName = defaultNamingStrategy.defaultName(); - } - tableSpec = createTableSpecification( bindingContext, schema, tableName, includedTable ); - } - else { - final InLineViewSource inLineViewSource = ( InLineViewSource ) tableSpecSource; - tableSpec = schema.createInLineView( - Identifier.toIdentifier( inLineViewSource.getLogicalName() ), - inLineViewSource.getSelectStatement() - ); - } - - return tableSpec; - } - - private TableSpecification createTableSpecification(LocalBindingContext bindingContext, Schema schema, String tableName, Table includedTable) { - tableName = quotedIdentifier( tableName ); - final Identifier logicalTableId = Identifier.toIdentifier( tableName ); - tableName = quotedIdentifier( bindingContext.getNamingStrategy().tableName( tableName ) ); - final Identifier physicalTableId = Identifier.toIdentifier( tableName ); - final Table table = schema.locateTable( logicalTableId ); - if ( table != null ) { - return table; - } - TableSpecification tableSpec; - if ( includedTable == null ) { - tableSpec = schema.createTable( logicalTableId, physicalTableId ); - } - else { - tableSpec = schema.createDenormalizedTable( logicalTableId, physicalTableId, includedTable ); - } - return tableSpec; - } - - private static String defaultCollectionElementJavaTypeName( - HibernateTypeHelper.ReflectedCollectionJavaTypes reflectedCollectionJavaTypes ) { - return reflectedCollectionJavaTypes != null && reflectedCollectionJavaTypes.getCollectionElementType() != null - ? reflectedCollectionJavaTypes.getCollectionElementType().getName() - : null; - } - - private static String defaultCollectionIndexJavaTypeName( - HibernateTypeHelper.ReflectedCollectionJavaTypes reflectedCollectionJavaTypes ) { - return reflectedCollectionJavaTypes != null && reflectedCollectionJavaTypes.getCollectionIndexType() != null - ? reflectedCollectionJavaTypes.getCollectionIndexType().getName() - : null; - } - - private static String defaultCollectionJavaTypeName( - HibernateTypeHelper.ReflectedCollectionJavaTypes reflectedCollectionJavaTypes, - PluralAttributeSource attributeSource ) { - return reflectedCollectionJavaTypes != null && reflectedCollectionJavaTypes.getCollectionType() != null - ? reflectedCollectionJavaTypes.getCollectionType().getName() - : attributeSource.getNature().reportedJavaType().getName(); - } - - private List< Column > determineForeignKeyTargetColumns( - final EntityBinding entityBinding, - ForeignKeyContributingSource foreignKeyContributingSource ) { - - // TODO: This method, JoinColumnResolutionContext, - // and JoinColumnResolutionDelegate need re-worked. There is currently - // no way to bind to a collection's inverse foreign key. - - final JoinColumnResolutionDelegate fkColumnResolutionDelegate = - foreignKeyContributingSource.getForeignKeyTargetColumnResolutionDelegate(); - - if ( fkColumnResolutionDelegate == null ) { - return entityBinding.getPrimaryTable().getPrimaryKey().getColumns(); - } else { - final List< Column > columns = new ArrayList< Column >(); - final JoinColumnResolutionContext resolutionContext = - new 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; - } - - @Override - public List< Value > resolveRelationalValuesForAttribute( String attributeName ) { - if ( attributeName == null ) { - List< Value > values = new ArrayList< Value >(); - for ( Column column : entityBinding.getPrimaryTable().getPrimaryKey().getColumns() ) { - values.add( column ); - } - return values; - } - final AttributeBinding referencedAttributeBinding = - entityBinding.locateAttributeBindingByPath( attributeName, true ); - if ( referencedAttributeBinding == null ) { - throw bindingContext().makeMappingException( - String.format( - "Could not resolve named referenced property [%s] against entity [%s]", - attributeName, - entityBinding.getEntity().getName() ) ); - } - if ( ! referencedAttributeBinding.getAttribute().isSingular() ) { - throw bindingContext().makeMappingException( - String.format( - "Referenced property [%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; - } - } - - private SingularAttributeBinding determinePluralAttributeKeyReferencedBinding( - final AttributeBindingContainer attributeBindingContainer, - final PluralAttributeSource attributeSource ) { - final EntityBinding entityBinding = attributeBindingContainer.seekEntityBinding(); - final JoinColumnResolutionDelegate resolutionDelegate = - attributeSource.getKeySource().getForeignKeyTargetColumnResolutionDelegate(); - - if ( resolutionDelegate == null ) { - return entityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); - } - - AttributeBinding referencedAttributeBinding; - final String referencedAttributeName = resolutionDelegate.getReferencedAttributeName(); - if ( referencedAttributeName == null ) { - referencedAttributeBinding = attributeBindingContainer.locateAttributeBinding( - resolutionDelegate.getJoinColumns( new JoinColumnResolutionContext() { - @Override - public List resolveRelationalValuesForAttribute(String attributeName) { - return null; - } - - @Override - public Column resolveColumn(String logicalColumnName, String logicalTableName, String logicalSchemaName, String logicalCatalogName) { - for ( AttributeBinding attributeBinding : attributeBindingContainer.attributeBindings() ) { - if ( SingularAttributeBinding.class.isInstance( attributeBinding ) ) { - SingularAttributeBinding singularAttributeBinding = SingularAttributeBinding.class.cast( - attributeBinding - ); - for ( RelationalValueBinding relationalValueBinding : singularAttributeBinding.getRelationalValueBindings() ) { - if ( Column.class.isInstance( relationalValueBinding.getValue() ) ) { - Identifier columnIdentifier = Identifier.toIdentifier( - quotedIdentifier( - logicalColumnName - ) - ); - Column column = Column.class.cast( relationalValueBinding.getValue() ); - if ( column.getColumnName().equals( columnIdentifier ) ) { - return column; - } - } - } - } - } - return null; - } - } ) ); - } else { - referencedAttributeBinding = attributeBindingContainer.locateAttributeBinding( referencedAttributeName ); - } - - - if ( referencedAttributeBinding == null ) { - referencedAttributeBinding = attributeBinding( entityBinding.getEntity().getName(), referencedAttributeName ); - } - if ( referencedAttributeBinding == null ) { - throw bindingContext().makeMappingException( "Plural attribute key references an attribute binding that does not exist: " - + referencedAttributeBinding ); - } - if ( !referencedAttributeBinding.getAttribute().isSingular() ) { - throw bindingContext().makeMappingException( "Plural attribute key references a plural attribute; it must not be plural: " - + referencedAttributeName ); - } - return ( SingularAttributeBinding ) referencedAttributeBinding; - } - - private SingularAttributeBinding determineReferencedAttributeBinding( - JoinColumnResolutionDelegate resolutionDelegate, - JoinColumnResolutionContext resolutionContext, - EntityBinding referencedEntityBinding) { - if ( resolutionDelegate == null ) { - return referencedEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(); - } - - final String explicitName = resolutionDelegate.getReferencedAttributeName(); - final AttributeBinding referencedAttributeBinding = explicitName != null - ? referencedEntityBinding.locateAttributeBindingByPath( explicitName, true ) - : referencedEntityBinding.locateAttributeBinding( resolutionDelegate.getJoinColumns( resolutionContext ), true ); - - if ( referencedAttributeBinding == null ) { - if ( explicitName != null ) { - throw bindingContext().makeMappingException( - String.format( - "No attribute binding found with name: %s.%s", - referencedEntityBinding.getEntityName(), - explicitName - ) - ); - } - else { - throw new NotYetImplementedException( - "No support yet for referenced join columns unless they correspond with columns bound for an attribute binding." - ); - } - } - - if ( !referencedAttributeBinding.getAttribute().isSingular() ) { - throw bindingContext().makeMappingException( - String.format( - "Foreign key references a non-singular attribute [%s]", - referencedAttributeBinding.getAttribute().getName() ) ); - } - return (SingularAttributeBinding) referencedAttributeBinding; - } - - private EntityBinding findOrBindEntityBinding(ValueHolder< Class< ? >> entityJavaTypeValue, String explicitEntityName) { - final String referencedEntityName = - explicitEntityName != null - ? explicitEntityName - : entityJavaTypeValue.getValue().getName(); - return findOrBindEntityBinding( referencedEntityName ); - } - - /** - * - * @param entityName - * @return - */ - private EntityBinding findOrBindEntityBinding(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 ); - if ( entitySource == null ) { - String msg = log.missingEntitySource( entityName ); - throw bindingContext().makeMappingException( msg ); - } - - // Get super entity binding (creating it if necessary using recursive call to this method) - if ( SubclassEntitySource.class.isInstance( entitySource ) ) { - String superEntityName = ( (SubclassEntitySource) entitySource ).superclassEntitySource() - .getEntityName(); - EntityBinding superEntityBinding = findOrBindEntityBinding( superEntityName ); - entityBinding = bindSubEntity( entitySource, superEntityBinding ); - } - else { - EntityHierarchy entityHierarchy = entityHierarchiesByRootEntitySource.get( entitySource ); - entityBinding = bindEntityHierarchy( entityHierarchy ); - } - } - return entityBinding; - } - - // TODO: try to get rid of this... - private static 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 static 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; + ? attributeBindingContainer.getAttributeContainer() + .createSyntheticSingularAttribute( attributeSource.getName() ) + : attributeBindingContainer.getAttributeContainer() + .createSingularAttribute( attributeSource.getName() ); } private static ForeignKey locateForeignKeyByColumnMapping( - List sourceColumns, - List targetColumns) { + final List sourceColumns, + final 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 ); + Iterable possibleForeignKeys = sourceTable.locateForeignKey( targetTable ); if ( possibleForeignKeys != null ) { for ( ForeignKey possibleFK : possibleForeignKeys ) { if ( possibleFK.getSourceColumns().equals( sourceColumns ) && @@ -3052,122 +3129,160 @@ public class Binder { 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 static String attributeSourcesByNameKey( + final String entityName, + final String attributeName) { + return entityName + "." + attributeName; } - private static PluralAttributeElementBinding.Nature pluralAttributeElementNature(PluralAttributeSource attributeSource) { - return PluralAttributeElementBinding.Nature.valueOf( attributeSource.getElementSource().getNature().name() ); - } - - private static PluralAttributeIndexBinding.Nature pluralAttributeIndexNature(PluralAttributeSource attributeSource) { - if ( ! IndexedPluralAttributeSource.class.isInstance( attributeSource ) ) { - return null; - } - return PluralAttributeIndexBinding.Nature.valueOf( - ( (IndexedPluralAttributeSource) attributeSource ).getIndexSource().getNature().name() + private static String getSqlTypeFromPrimaryKeyJoinColumnSourceIfExist( + final Column superEntityBindingPrimaryKeyColumn, + final PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource) { + final boolean isColumnDefOverrided = primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( + primaryKeyJoinColumnSource.getColumnDefinition() ); - } - - private String propertyAccessorName( final AttributeSource attributeSource ) { - return propertyAccessorName( attributeSource.getPropertyAccessorName() ); - } - - private String propertyAccessorName(final String propertyAccessorName) { - return propertyAccessorName == null - ? bindingContext().getMappingDefaults().getPropertyAccessorName() - : propertyAccessorName; - } - - private String quoteIdentifierIfNonEmpty(String name) { - return StringHelper.isEmpty( name ) ? null : quotedIdentifier( name ); - } - - private String quotedIdentifier( final String name ) { - return bindingContext().isGloballyQuotedIdentifiers() ? StringHelper.quote( name ) : name; + return isColumnDefOverrided ? primaryKeyJoinColumnSource.getColumnDefinition() : superEntityBindingPrimaryKeyColumn + .getSqlType(); } - private static String getReferencedPropertyNameIfNotId(PluralAttributeBinding pluralAttributeBinding) { - EntityIdentifier entityIdentifier = - pluralAttributeBinding.getContainer().seekEntityBinding().getHierarchyDetails().getEntityIdentifier(); - final String idAttributeName = - entityIdentifier.getAttributeBinding().getAttribute().getName(); - return pluralAttributeBinding.getReferencedPropertyName().equals( idAttributeName ) ? - null : - pluralAttributeBinding.getReferencedPropertyName(); + // TODO: try to get rid of this... + private static List extractColumnsFromRelationalValueBindings( + final 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 Type resolveCustomCollectionType( PluralAttributeBinding pluralAttributeBinding ) { - final HibernateTypeDescriptor hibernateTypeDescriptor = pluralAttributeBinding.getHibernateTypeDescriptor(); - Properties typeParameters = new Properties(); - typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() ); - return metadata.getTypeResolver().getTypeFactory().customCollection( - hibernateTypeDescriptor.getExplicitTypeName(), - typeParameters, - pluralAttributeBinding.getAttribute().getName(), - getReferencedPropertyNameIfNotId( pluralAttributeBinding ), - pluralAttributeBinding.getPluralAttributeElementBinding() - .getNature() == PluralAttributeElementBinding.Nature.AGGREGATE - ); - } - - private Type resolvePluralType( - PluralAttributeBinding pluralAttributeBinding, - PluralAttributeSource pluralAttributeSource, - PluralAttributeSource.Nature nature){ - if ( pluralAttributeBinding.getHibernateTypeDescriptor().getExplicitTypeName() != null ) { - return resolveCustomCollectionType( pluralAttributeBinding ); - } else { - final TypeFactory typeFactory = metadata.getTypeResolver().getTypeFactory(); - final String role = pluralAttributeBinding.getAttribute().getRole(); - final String propertyRef = getReferencedPropertyNameIfNotId( pluralAttributeBinding ); - final boolean embedded = pluralAttributeBinding.getPluralAttributeElementBinding().getNature() == PluralAttributeElementBinding.Nature.AGGREGATE; - switch ( nature ){ - case BAG: - return typeFactory.bag( role, propertyRef, embedded ); - case LIST: - return typeFactory.list( role, propertyRef, embedded ); - case ARRAY: - return typeFactory.array( role, propertyRef, embedded, pluralAttributeSource.getElementClassReference().getValue() ); - case MAP: - if ( pluralAttributeBinding.isSorted() ) { - return typeFactory.sortedMap( role, propertyRef, embedded, pluralAttributeBinding.getComparator() ); - } - // TODO: else if ( pluralAttributeBinding.hasOrder() ) { orderedMap... } - else { - return typeFactory.map( role, propertyRef, embedded ); - } - case SET: - if ( pluralAttributeBinding.isSorted() ) { - return typeFactory.sortedSet( role, propertyRef, embedded, pluralAttributeBinding.getComparator() ); - } - // TODO: else if ( pluralAttributeBinding.hasOrder() ) { orderedSet... } - else { - return typeFactory.set( role, propertyRef, embedded ); - } - default: - throw new NotYetImplementedException( nature + " is to be implemented" ); + private static boolean hasAnyNonNullableColumns( + final List relationalValueBindings) { + for ( RelationalValueBinding relationalValueBinding : relationalValueBindings ) { + if ( Column.class.isInstance( relationalValueBinding.getValue() ) && !relationalValueBinding.isNullable() ) { + return true; } } + return false; } + private static String createAttributePath( + final AttributeBinding attributeBinding) { + return new StringBuffer( attributeBinding.getContainer().getPathBase() ) + .append( '.' ) + .append( attributeBinding.getAttribute().getName() ) + .toString(); + } + + + private static ValueHolder> createSingularAttributeJavaType( + final Class attributeContainerClassReference, + final String attributeName) { + ValueHolder.DeferredInitializer> deferredInitializer = + new ValueHolder.DeferredInitializer>() { + public Class initialize() { + return ReflectHelper.reflectedPropertyClass( + attributeContainerClassReference, + attributeName + ); + } + }; + return new ValueHolder>( deferredInitializer ); + } + + private static ValueHolder> createSingularAttributeJavaType( + final SingularAttribute attribute) { + return createSingularAttributeJavaType( + attribute.getAttributeContainer().getClassReference(), + attribute.getName() + ); + } + + private static String interpretIdentifierUnsavedValue( + final IdentifierSource identifierSource, + final 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 NON_AGGREGATED_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 void addUniqueConstraintForNaturalIdColumn( + final TableSpecification table, + final Column column) { + final UniqueKey uniqueKey = table.getOrCreateUniqueKey( "natural_id_unique_key_" ); + uniqueKey.addColumn( column ); + } public static interface DefaultNamingStrategy { String defaultName(); } + private static interface ToOneAttributeBindingContext { + SingularAssociationAttributeBinding createToOneAttributeBinding( + EntityBinding referencedEntityBinding, + SingularAttributeBinding referencedAttributeBinding + ); + + EntityType resolveEntityType( + EntityBinding referencedEntityBinding, + SingularAttributeBinding referencedAttributeBinding + ); + } public class JoinColumnResolutionContextImpl implements JoinColumnResolutionContext { private final EntityBinding referencedEntityBinding; @@ -3182,16 +3297,16 @@ public class Binder { String logicalColumnName, String logicalTableName, String logicalSchemaName, - String logicalCatalogName ) { - Identifier tableIdentifier = Identifier.toIdentifier(logicalTableName); - if (tableIdentifier == null) { + String logicalCatalogName) { + Identifier tableIdentifier = Identifier.toIdentifier( logicalTableName ); + if ( tableIdentifier == null ) { tableIdentifier = referencedEntityBinding.getPrimaryTable().getLogicalName(); } Schema schema = metadata.getDatabase().getSchema( logicalCatalogName, logicalSchemaName ); - Table table = schema.locateTable(tableIdentifier ); + Table table = schema.locateTable( tableIdentifier ); - if(bindingContext().isGloballyQuotedIdentifiers() && !StringHelper.isQuoted(logicalColumnName)) { + if ( bindingContext().isGloballyQuotedIdentifiers() && !StringHelper.isQuoted( logicalColumnName ) ) { logicalColumnName = StringHelper.quote( logicalColumnName ); } @@ -3199,9 +3314,9 @@ public class Binder { } @Override - public List< Value > resolveRelationalValuesForAttribute( String attributeName ) { + public List resolveRelationalValuesForAttribute(String attributeName) { if ( attributeName == null ) { - List< Value > values = new ArrayList< Value >(); + List values = new ArrayList(); for ( Column column : referencedEntityBinding.getPrimaryTable().getPrimaryKey().getColumns() ) { values.add( column ); } @@ -3214,18 +3329,22 @@ public class Binder { String.format( "Could not resolve named property-ref [%s] against entity [%s]", attributeName, - referencedEntityBinding.getEntity().getName() ) ); + referencedEntityBinding.getEntity().getName() + ) + ); } - if ( ! referencedAttributeBinding.getAttribute().isSingular() ) { + 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, - referencedEntityBinding.getEntity().getName() ) ); + referencedEntityBinding.getEntity().getName() + ) + ); } - List< Value > values = new ArrayList< Value >(); + List values = new ArrayList(); SingularAttributeBinding referencedAttributeBindingAsSingular = - ( SingularAttributeBinding ) referencedAttributeBinding; + (SingularAttributeBinding) referencedAttributeBinding; for ( RelationalValueBinding valueBinding : referencedAttributeBindingAsSingular.getRelationalValueBindings() ) { values.add( valueBinding.getValue() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java index 7bdecd128b..b16a4e0e57 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java @@ -33,15 +33,20 @@ import java.util.Properties; import org.hibernate.AssertionFailure; import org.hibernate.EntityMode; +import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.internal.util.ValueHolder; import org.hibernate.internal.util.beans.BeanInfoHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.spi.MetadataImplementor; import org.hibernate.metamodel.spi.binding.AttributeBinding; import org.hibernate.metamodel.spi.binding.BasicAttributeBinding; +import org.hibernate.metamodel.spi.binding.BasicPluralAttributeElementBinding; import org.hibernate.metamodel.spi.binding.CompositeAttributeBinding; +import org.hibernate.metamodel.spi.binding.EntityIdentifier; import org.hibernate.metamodel.spi.binding.HibernateTypeDescriptor; import org.hibernate.metamodel.spi.binding.PluralAttributeBinding; +import org.hibernate.metamodel.spi.binding.PluralAttributeElementBinding; import org.hibernate.metamodel.spi.binding.RelationalValueBinding; import org.hibernate.metamodel.spi.binding.SingularAttributeBinding; import org.hibernate.metamodel.spi.binding.TypeDefinition; @@ -51,62 +56,334 @@ import org.hibernate.metamodel.spi.relational.AbstractValue; import org.hibernate.metamodel.spi.relational.JdbcDataType; import org.hibernate.metamodel.spi.relational.Value; import org.hibernate.metamodel.spi.source.AttributeSource; +import org.hibernate.metamodel.spi.source.BasicPluralAttributeElementSource; import org.hibernate.metamodel.spi.source.ComponentAttributeSource; import org.hibernate.metamodel.spi.source.ExplicitHibernateTypeSource; +import org.hibernate.metamodel.spi.source.PluralAttributeSource; import org.hibernate.metamodel.spi.source.SingularAttributeSource; import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; +import org.hibernate.type.TypeFactory; + import org.jboss.logging.Logger; /** * Delegate for handling:
    - *
  1. - * binding of Hibernate type information ({@link ExplicitHibernateTypeSource} -> - * {@link HibernateTypeDescriptor} - *
  2. - *
  3. - * attempt to resolve the actual {@link Type} instance - *
  4. - *
  5. - * push java type and JDBC type information reported by the {@link Type} instance to relational/ - * domain models. - *
  6. + *
  7. + * binding of Hibernate type information ({@link ExplicitHibernateTypeSource} -> + * {@link HibernateTypeDescriptor} + *
  8. + *
  9. + * attempt to resolve the actual {@link Type} instance + *
  10. + *
  11. + * push java type and JDBC type information reported by the {@link Type} instance to relational/ + * domain models. + *
  12. *
*

* Methods intended as entry points are:

    - *
  • {@link #bindSingularAttributeTypeInformation}
  • + *
  • {@link #bindSingularAttributeTypeInformation}
  • *
*

* Currently the following methods are also required to be non-private because of handling discriminators which * are currently not modeled using attributes:

    - *
  • {@link #bindJdbcDataType(org.hibernate.type.Type, org.hibernate.metamodel.spi.relational.Value)}
  • + *
  • {@link #bindJdbcDataType(org.hibernate.type.Type, org.hibernate.metamodel.spi.relational.Value)}
  • *
* * @author Steve Ebersole * @author Gail Badner * @author Brett Meyer */ -public class HibernateTypeHelper { +class HibernateTypeHelper { private static final Logger log = Logger.getLogger( HibernateTypeHelper.class ); + /* package-protected */ + static class ReflectedCollectionJavaTypes { + private final String collectionTypeName; + private final String collectionElementTypeName; + private final String collectionIndexTypeName; + + private ReflectedCollectionJavaTypes( + Class collectionType, + Class collectionElementType, + Class collectionIndexType) { + this.collectionTypeName = collectionType != null ? collectionType.getName() : null; + this.collectionElementTypeName = collectionElementType != null ? collectionElementType.getName() : null; + this.collectionIndexTypeName = collectionIndexType != null ? collectionIndexType.getName() : null; + } + + String getCollectionElementTypeName() { + return collectionElementTypeName; + } + + String getCollectionIndexTypeName() { + return collectionIndexTypeName; + } + + String getCollectionTypeName() { + return collectionTypeName; + } + } + + static String defaultCollectionElementJavaTypeName( + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + return reflectedCollectionJavaTypes != null ? reflectedCollectionJavaTypes.getCollectionElementTypeName() : null; + } + + static String defaultCollectionIndexJavaTypeName( + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes) { + return reflectedCollectionJavaTypes != null ? reflectedCollectionJavaTypes.getCollectionIndexTypeName() : null; + } + + static String defaultCollectionJavaTypeName( + final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes, + final PluralAttributeSource attributeSource) { + return reflectedCollectionJavaTypes != null ? reflectedCollectionJavaTypes.getCollectionTypeName() : attributeSource + .getNature() + .reportedJavaType() + .getName(); + } + + static 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 final Binder binder; private final MetadataImplementor metadata; - public HibernateTypeHelper( Binder binder, - MetadataImplementor metadata ) { + //package scope methods + HibernateTypeHelper(Binder binder, + MetadataImplementor metadata) { this.binder = binder; this.metadata = metadata; } - private org.hibernate.metamodel.spi.domain.Type makeJavaType(String name) { - return binder.bindingContext().makeJavaType( name ); + /** + * Bind relational types using hibernate type just resolved. + * + * @param resolvedHibernateType The hibernate type resolved from metadata. + * @param value The relational value to be binded. + */ + void bindJdbcDataType( + final Type resolvedHibernateType, + final Value value) { + if ( value.getJdbcDataType() == null && resolvedHibernateType != null && value != null ) { + final Type resolvedRelationalType = + resolvedHibernateType.isEntityType() + ? EntityType.class.cast( resolvedHibernateType ).getIdentifierOrUniqueKeyType( metadata ) + : resolvedHibernateType; + if ( AbstractValue.class.isInstance( value ) ) { + ( (AbstractValue) value ).setJdbcDataType( + new JdbcDataType( + resolvedRelationalType.sqlTypes( metadata )[0], + resolvedRelationalType.getName(), + resolvedRelationalType.getReturnedClass() + ) + ); + } + } } - public void bindSingularAttributeTypeInformation( - SingularAttributeSource attributeSource, - SingularAttributeBinding attributeBinding) { + void bindJdbcDataType( + final Type resolvedHibernateType, + final List relationalValueBindings) { + if ( relationalValueBindings.size() <= 1 ) { + bindJdbcDataType( resolvedHibernateType, relationalValueBindings.get( 0 ).getValue() ); + return; + } + final Type resolvedRelationalType = + resolvedHibernateType.isEntityType() + ? EntityType.class.cast( resolvedHibernateType ).getIdentifierOrUniqueKeyType( metadata ) + : resolvedHibernateType; + if ( !CompositeType.class.isInstance( resolvedRelationalType ) ) { + throw binder.bindingContext() + .makeMappingException( "Column number mismatch" ); // todo refine the exception message + } + Type[] subTypes = CompositeType.class.cast( resolvedRelationalType ).getSubtypes(); + for ( int i = 0; i < subTypes.length; i++ ) { + bindJdbcDataType( subTypes[i], relationalValueBindings.get( i ).getValue() ); + } + } + + void bindBasicCollectionElementType( + final BasicPluralAttributeElementBinding elementBinding, + final BasicPluralAttributeElementSource elementSource, + final String defaultElementJavaTypeName) { + bindHibernateTypeDescriptor( + elementBinding.getHibernateTypeDescriptor(), + elementSource.getExplicitHibernateTypeSource(), + defaultElementJavaTypeName + ); + Type resolvedElementType = heuristicType( elementBinding.getHibernateTypeDescriptor() ); + bindHibernateResolvedType( elementBinding.getHibernateTypeDescriptor(), resolvedElementType ); + bindJdbcDataType( + resolvedElementType, + elementBinding.getRelationalValueBindings() + ); + } + + Type resolvePluralType( + final PluralAttributeBinding pluralAttributeBinding, + final PluralAttributeSource pluralAttributeSource, + final PluralAttributeSource.Nature nature) { + if ( pluralAttributeBinding.getHibernateTypeDescriptor().getExplicitTypeName() != null ) { + return resolveCustomCollectionType( pluralAttributeBinding ); + } + else { + final TypeFactory typeFactory = metadata.getTypeResolver().getTypeFactory(); + final String role = pluralAttributeBinding.getAttribute().getRole(); + final String propertyRef = getReferencedPropertyNameIfNotId( pluralAttributeBinding ); + final boolean embedded = pluralAttributeBinding.getPluralAttributeElementBinding() + .getNature() == PluralAttributeElementBinding.Nature.AGGREGATE; + switch ( nature ) { + case BAG: + return typeFactory.bag( role, propertyRef, embedded ); + case LIST: + return typeFactory.list( role, propertyRef, embedded ); + case ARRAY: + return typeFactory.array( + role, + propertyRef, + embedded, + pluralAttributeSource.getElementClassReference().getValue() + ); + case MAP: + if ( pluralAttributeBinding.isSorted() ) { + return typeFactory.sortedMap( + role, + propertyRef, + embedded, + pluralAttributeBinding.getComparator() + ); + } + // TODO: else if ( pluralAttributeBinding.hasOrder() ) { orderedMap... } + else { + return typeFactory.map( role, propertyRef, embedded ); + } + case SET: + if ( pluralAttributeBinding.isSorted() ) { + return typeFactory.sortedSet( + role, + propertyRef, + embedded, + pluralAttributeBinding.getComparator() + ); + } + // TODO: else if ( pluralAttributeBinding.hasOrder() ) { orderedSet... } + else { + return typeFactory.set( role, propertyRef, embedded ); + } + default: + throw new NotYetImplementedException( nature + " is to be implemented" ); + } + } + } + + Type heuristicType( + final 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 ); + } + // TODO: The following 3 methods should eventually be replaced w/ + // typeHelper use. + + void bindHibernateTypeDescriptor( + final HibernateTypeDescriptor hibernateTypeDescriptor, + final ExplicitHibernateTypeSource explicitTypeSource, + final ValueHolder> 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 + ); + } + + 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 + ); + } + } + + void bindHibernateTypeDescriptor( + final HibernateTypeDescriptor hibernateTypeDescriptor, + final String explicitTypeName, + final Map explictTypeParameters, + final String defaultJavaTypeName) { + if ( explicitTypeName == null ) { + if ( hibernateTypeDescriptor.getJavaTypeName() != null ) { + throw binder.bindingContext().makeMappingException( + String.format( + "Attempt to re-initialize (non-explicit) Java type name; current=%s new=%s", + hibernateTypeDescriptor.getJavaTypeName(), + defaultJavaTypeName + ) + ); + } + 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 binder.bindingContext().makeMappingException( + String.format( + "Attempt to re-initialize explicity-mapped Java type name; current=%s new=%s", + hibernateTypeDescriptor.getExplicitTypeName(), + explicitTypeName + ) + ); + } + if ( typeDef == null ) { + hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName ); + } + else { + hibernateTypeDescriptor.setExplicitTypeName( typeDef.getTypeImplementorClass().getName() ); + // Don't use set() -- typeDef#parameters is unmodifiable + hibernateTypeDescriptor.getTypeParameters().putAll( typeDef.getParameters() ); + } + if ( explictTypeParameters != null ) { + hibernateTypeDescriptor.getTypeParameters().putAll( explictTypeParameters ); + } + } + } + + void bindSingularAttributeTypeInformation( + final SingularAttributeSource attributeSource, + final SingularAttributeBinding attributeBinding) { final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding .getHibernateTypeDescriptor(); @@ -117,26 +394,35 @@ public class HibernateTypeHelper { attributeBinding.getAttribute() ) : null; if ( attributeJavaType != null ) { - attributeBinding.getAttribute().resolveType( makeJavaType( - attributeJavaType.getName() ) ); + attributeBinding.getAttribute().resolveType( + makeJavaType( + attributeJavaType.getName() + ) + ); if ( hibernateTypeDescriptor.getJavaTypeName() == null ) { - hibernateTypeDescriptor.setJavaTypeName( - attributeJavaType.getName() ); + hibernateTypeDescriptor.setJavaTypeName( + attributeJavaType.getName() + ); } } - bindHibernateTypeInformation( attributeSource.getTypeInformation(), - hibernateTypeDescriptor ); + bindHibernateTypeInformation( + attributeSource.getTypeInformation(), + hibernateTypeDescriptor + ); - processSingularAttributeTypeInformation( attributeSource, - attributeBinding ); + processSingularAttributeTypeInformation( + attributeSource, + attributeBinding + ); } - public ReflectedCollectionJavaTypes getReflectedCollectionJavaTypes( - PluralAttributeBinding attributeBinding) { + ReflectedCollectionJavaTypes getReflectedCollectionJavaTypes( + final PluralAttributeBinding attributeBinding) { return determineJavaType( attributeBinding.getAttribute() ); } + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private scope methods private Class determineJavaType(final SingularAttribute attribute) { try { final Class ownerClass = attribute.getAttributeContainer().getClassReference(); @@ -181,29 +467,33 @@ public class HibernateTypeHelper { private void bindHibernateTypeInformation( final ExplicitHibernateTypeSource typeSource, final HibernateTypeDescriptor hibernateTypeDescriptor) { - + final String explicitTypeName = typeSource.getName(); - + if ( explicitTypeName != null ) { - final TypeDefinition typeDefinition = metadata.getTypeDefinition( - explicitTypeName ); + final TypeDefinition typeDefinition = metadata.getTypeDefinition( + explicitTypeName + ); if ( typeDefinition != null ) { - hibernateTypeDescriptor.setExplicitTypeName( - typeDefinition.getTypeImplementorClass().getName() ); + hibernateTypeDescriptor.setExplicitTypeName( + typeDefinition.getTypeImplementorClass().getName() + ); // Don't use set() -- typeDef#parameters is unmodifiable - hibernateTypeDescriptor.getTypeParameters().putAll( - typeDefinition.getParameters() ); + hibernateTypeDescriptor.getTypeParameters().putAll( + typeDefinition.getParameters() + ); } else { hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName ); } - + // TODO: Should type parameters be used for @TypeDefs? final Map parameters = typeSource.getParameters(); if ( parameters != null ) { // Don't use set() -- typeDef#parameters is unmodifiable - hibernateTypeDescriptor.getTypeParameters().putAll( - parameters ); + hibernateTypeDescriptor.getTypeParameters().putAll( + parameters + ); } } } @@ -216,8 +506,8 @@ public class HibernateTypeHelper { * @param attributeBinding The attribute. */ private void processSingularAttributeTypeInformation( - SingularAttributeSource attributeSource, - SingularAttributeBinding attributeBinding) { + final SingularAttributeSource attributeSource, + final SingularAttributeBinding attributeBinding) { Type resolvedType = attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping(); if ( resolvedType == null ) { @@ -235,7 +525,8 @@ public class HibernateTypeHelper { } } - private Type determineHibernateTypeFromDescriptor(HibernateTypeDescriptor hibernateTypeDescriptor) { + private Type determineHibernateTypeFromDescriptor( + final HibernateTypeDescriptor hibernateTypeDescriptor) { if ( hibernateTypeDescriptor.getResolvedTypeMapping() != null ) { return hibernateTypeDescriptor.getResolvedTypeMapping(); } @@ -246,12 +537,14 @@ public class HibernateTypeHelper { return type; } - private Type getHeuristicType(String typeName, Properties typeParameters) { + private Type getHeuristicType( + final String typeName, + final Properties typeParameters) { if ( typeName != null ) { try { return metadata.getTypeResolver().heuristicType( typeName, typeParameters ); } - catch (Exception ignore) { + catch ( Exception ignore ) { } } @@ -260,7 +553,8 @@ public class HibernateTypeHelper { private static final Properties EMPTY_PROPERTIES = new Properties(); - private Type determineHibernateTypeFromAttributeJavaType(SingularAttribute singularAttribute) { + private Type determineHibernateTypeFromAttributeJavaType( + final SingularAttribute singularAttribute) { if ( singularAttribute.getSingularAttributeType() != null ) { return getHeuristicType( singularAttribute.getSingularAttributeType().getClassName(), @@ -270,27 +564,10 @@ public class HibernateTypeHelper { return null; } - private static String determineTypeName(HibernateTypeDescriptor hibernateTypeDescriptor) { - return hibernateTypeDescriptor.getExplicitTypeName() != null - ? hibernateTypeDescriptor.getExplicitTypeName() - : hibernateTypeDescriptor.getJavaTypeName(); - } - - private static Properties getTypeParameters(HibernateTypeDescriptor hibernateTypeDescriptor) { - if ( CollectionHelper.isEmpty( hibernateTypeDescriptor.getTypeParameters() ) ) { - return EMPTY_PROPERTIES; - } - else { - Properties typeParameters = new Properties(); - typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() ); - return typeParameters; - } - } - private void pushHibernateTypeInformationDown( - SingularAttributeSource attributeSource, - SingularAttributeBinding attributeBinding, - Type resolvedHibernateType) { + final SingularAttributeSource attributeSource, + final SingularAttributeBinding attributeBinding, + final Type resolvedHibernateType) { // sql type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ( BasicAttributeBinding.class.isInstance( attributeBinding ) ) { @@ -309,14 +586,14 @@ public class HibernateTypeHelper { } private void pushHibernateTypeInformationDown( - BasicAttributeBinding attributeBinding, - Type resolvedHibernateType) { + final BasicAttributeBinding attributeBinding, + final Type resolvedHibernateType) { final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor(); final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() ); if ( hibernateTypeDescriptor.getResolvedTypeMapping() != null && hibernateTypeDescriptor.getJavaTypeName() == null ) { hibernateTypeDescriptor.setJavaTypeName( resolvedHibernateType.getReturnedClass().getName() ); } - if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) { + if ( !singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) { singularAttribute.resolveType( makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) ); } pushHibernateTypeInformationDown( @@ -326,14 +603,14 @@ public class HibernateTypeHelper { ); } - @SuppressWarnings( {"UnusedParameters"}) + @SuppressWarnings({ "UnusedParameters" }) private void pushHibernateTypeInformationDown( - ComponentAttributeSource attributeSource, - CompositeAttributeBinding attributeBinding, - Type resolvedHibernateType) { + final ComponentAttributeSource attributeSource, + final CompositeAttributeBinding attributeBinding, + final Type resolvedHibernateType) { final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor(); final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() ); - if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) { + if ( !singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) { singularAttribute.resolveType( makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) ); } @@ -342,20 +619,23 @@ public class HibernateTypeHelper { AttributeSource subAttributeSource = subAttributeSourceIterator.next(); if ( SingularAttributeBinding.class.isInstance( subAttributeBinding ) ) { processSingularAttributeTypeInformation( - ( SingularAttributeSource ) subAttributeSource, + (SingularAttributeSource) subAttributeSource, SingularAttributeBinding.class.cast( subAttributeBinding ) ); } else { - throw new AssertionFailure( "Unknown type of AttributeBinding: " + attributeBinding.getClass().getName() ); + throw new AssertionFailure( + "Unknown type of AttributeBinding: " + attributeBinding.getClass() + .getName() + ); } } } private void pushHibernateTypeInformationDown( - HibernateTypeDescriptor hibernateTypeDescriptor, - List relationalValueBindings, - Type resolvedHibernateType) { + final HibernateTypeDescriptor hibernateTypeDescriptor, + final List relationalValueBindings, + final Type resolvedHibernateType) { if ( resolvedHibernateType == null ) { return; } @@ -368,84 +648,66 @@ public class HibernateTypeHelper { if ( hibernateTypeDescriptor.getJavaTypeName() == null ) { hibernateTypeDescriptor.setJavaTypeName( resolvedHibernateType.getReturnedClass().getName() ); } - + hibernateTypeDescriptor.setToOne( resolvedHibernateType.isEntityType() ); - + bindJdbcDataType( resolvedHibernateType, relationalValueBindings ); } - /** - * Bind relational types using hibernate type just resolved. - * - * @param resolvedHibernateType The hibernate type resolved from metadata. - * @param value The relational value to be binded. - */ - public void bindJdbcDataType(Type resolvedHibernateType, Value value) { - if ( value.getJdbcDataType() == null && resolvedHibernateType != null && value != null ) { - final Type resolvedRelationalType = - resolvedHibernateType.isEntityType() - ? EntityType.class.cast( resolvedHibernateType ).getIdentifierOrUniqueKeyType( metadata ) - : resolvedHibernateType; - if ( AbstractValue.class.isInstance( value ) ) { - ( (AbstractValue) value ).setJdbcDataType( - new JdbcDataType( - resolvedRelationalType.sqlTypes( metadata )[0], - resolvedRelationalType.getName(), - resolvedRelationalType.getReturnedClass() - ) - ); - } + private org.hibernate.metamodel.spi.domain.Type makeJavaType(String name) { + return binder.bindingContext().makeJavaType( name ); + } + + private Type resolveCustomCollectionType( + final PluralAttributeBinding pluralAttributeBinding) { + final HibernateTypeDescriptor hibernateTypeDescriptor = pluralAttributeBinding.getHibernateTypeDescriptor(); + Properties typeParameters = new Properties(); + typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() ); + return metadata.getTypeResolver().getTypeFactory().customCollection( + hibernateTypeDescriptor.getExplicitTypeName(), + typeParameters, + pluralAttributeBinding.getAttribute().getName(), + getReferencedPropertyNameIfNotId( pluralAttributeBinding ), + pluralAttributeBinding.getPluralAttributeElementBinding() + .getNature() == PluralAttributeElementBinding.Nature.AGGREGATE + ); + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private static methods + private static String determineTypeName( + final HibernateTypeDescriptor hibernateTypeDescriptor) { + return hibernateTypeDescriptor.getExplicitTypeName() != null + ? hibernateTypeDescriptor.getExplicitTypeName() + : hibernateTypeDescriptor.getJavaTypeName(); + } + + private static Properties getTypeParameters( + final HibernateTypeDescriptor hibernateTypeDescriptor) { + if ( CollectionHelper.isEmpty( hibernateTypeDescriptor.getTypeParameters() ) ) { + return EMPTY_PROPERTIES; + } + else { + Properties typeParameters = new Properties(); + typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() ); + return typeParameters; } } - public void bindJdbcDataType(Type resolvedHibernateType, List relationalValueBindings) { - if ( relationalValueBindings.size() <= 1 ) { - bindJdbcDataType( resolvedHibernateType, relationalValueBindings.get( 0 ).getValue() ); - return; - } - final Type resolvedRelationalType = - resolvedHibernateType.isEntityType() - ? EntityType.class.cast( resolvedHibernateType ).getIdentifierOrUniqueKeyType( metadata ) - : resolvedHibernateType; - if ( !CompositeType.class.isInstance( resolvedRelationalType ) ) { - throw binder.bindingContext().makeMappingException( "Column number mismatch" ); // todo refine the exception message - } - Type[] subTypes = CompositeType.class.cast( resolvedRelationalType ).getSubtypes(); - for ( int i = 0; i < subTypes.length; i++ ) { - bindJdbcDataType( subTypes[i], relationalValueBindings.get( i ).getValue() ); - } - } - - /* package-protected */ - static class ReflectedCollectionJavaTypes { - private final Class collectionType; - private final Class collectionElementType; - private final Class collectionIndexType; - - private ReflectedCollectionJavaTypes( - Class collectionType, - Class collectionElementType, - Class collectionIndexType) { - this.collectionType = collectionType; - this.collectionElementType = collectionElementType; - this.collectionIndexType = collectionIndexType; - } - - public Class getCollectionType() { - return collectionType; - } - public Class getCollectionElementType() { - return collectionElementType; - } - public Class getCollectionIndexType() { - return collectionIndexType; - } + private static String getReferencedPropertyNameIfNotId( + final PluralAttributeBinding pluralAttributeBinding) { + EntityIdentifier entityIdentifier = + pluralAttributeBinding.getContainer().seekEntityBinding().getHierarchyDetails().getEntityIdentifier(); + final String idAttributeName = + entityIdentifier.getAttributeBinding().getAttribute().getName(); + return pluralAttributeBinding.getReferencedPropertyName().equals( idAttributeName ) ? + null : + pluralAttributeBinding.getReferencedPropertyName(); } /** * @see HibernateTypeHelper#determineJavaType(PluralAttribute) */ - private class PluralAttributeJavaTypeDeterminerDelegate implements BeanInfoHelper.BeanInfoDelegate { + private static class PluralAttributeJavaTypeDeterminerDelegate implements BeanInfoHelper.BeanInfoDelegate { private final Class ownerClass; private final String attributeName;