gt: 		nonlazyPropertyUpdateability = new boolean[propertySpan];
context: /*
   * Hibernate, Relational Persistence for Idiomatic Java
   *
   * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
   * indicated by the @author tags or express copyright attribution
   * statements applied by the authors.  All third-party contributions are
   * distributed under license by Red Hat Inc.
   *
   * This copyrighted material is made available to anyone wishing to use, modify,
   * copy, or redistribute it subject to the terms and conditions of the GNU
   * Lesser General Public License, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   * for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with this distribution; if not, write to:
   * Free Software Foundation, Inc.
   * 51 Franklin Street, Fifth Floor
   * Boston, MA  02110-1301  USA
   */
  package org.hibernate.tuple.entity;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.hibernate.EntityMode;
  import org.hibernate.HibernateException;
  import org.hibernate.MappingException;
  import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
  import org.hibernate.cfg.Environment;
  import org.hibernate.cfg.NotYetImplementedException;
  import org.hibernate.engine.OptimisticLockStyle;
  import org.hibernate.engine.spi.CascadeStyle;
  import org.hibernate.engine.spi.CascadeStyles;
  import org.hibernate.engine.spi.SessionFactoryImplementor;
  import org.hibernate.engine.spi.ValueInclusion;
  import org.hibernate.internal.CoreMessageLogger;
  import org.hibernate.internal.util.ReflectHelper;
  import org.hibernate.internal.util.collections.ArrayHelper;
  import org.hibernate.mapping.Component;
  import org.hibernate.mapping.PersistentClass;
  import org.hibernate.mapping.Property;
  import org.hibernate.metamodel.binding.AttributeBinding;
  import org.hibernate.metamodel.binding.BasicAttributeBinding;
  import org.hibernate.metamodel.binding.EntityBinding;
  import org.hibernate.metamodel.domain.Attribute;
  import org.hibernate.metamodel.domain.SingularAttribute;
  import org.hibernate.persister.entity.AbstractEntityPersister;
  import org.hibernate.tuple.GenerationTiming;
  import org.hibernate.tuple.IdentifierProperty;
  import org.hibernate.tuple.InDatabaseValueGenerationStrategy;
  import org.hibernate.tuple.InMemoryValueGenerationStrategy;
  import org.hibernate.tuple.NonIdentifierAttribute;
  import org.hibernate.tuple.PropertyFactory;
  import org.hibernate.tuple.ValueGeneration;
  import org.hibernate.tuple.ValueGenerator;
  import org.hibernate.type.AssociationType;
  import org.hibernate.type.CompositeType;
  import org.hibernate.type.EntityType;
  import org.hibernate.type.Type;
  
  import org.jboss.logging.Logger;
  
  /**
   * Centralizes metamodel information about an entity.
   *
   * @author Steve Ebersole
   */
  public class EntityMetamodel implements Serializable {
  
  	private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, EntityMetamodel.class.getName());
  
  	private static final int NO_VERSION_INDX = -66;
  
  	private final SessionFactoryImplementor sessionFactory;
  	private final AbstractEntityPersister persister;
  
  	private final String name;
  	private final String rootName;
  	private final EntityType entityType;
  
  	private final IdentifierProperty identifierAttribute;
  	private final boolean versioned;
  
  	private final int propertySpan;
  	private final int versionPropertyIndex;
  	private final NonIdentifierAttribute[] properties;
  	// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  	private final String[] propertyNames;
  	private final Type[] propertyTypes;
  	private final boolean[] propertyLaziness;
  	private final boolean[] propertyUpdateability;
  	private final boolean[] nonlazyPropertyUpdateability;
  	private final boolean[] propertyCheckability;
  	private final boolean[] propertyInsertability;
  	private final boolean[] propertyNullability;
  	private final boolean[] propertyVersionability;
  	private final CascadeStyle[] cascadeStyles;
  	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  	// value generations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  	private final boolean hasPreInsertGeneratedValues;
  	private final boolean hasPreUpdateGeneratedValues;
  	private final boolean hasInsertGeneratedValues;
  	private final boolean hasUpdateGeneratedValues;
  
  	private final InMemoryValueGenerationStrategy[] inMemoryValueGenerationStrategies;
  	private final InDatabaseValueGenerationStrategy[] inDatabaseValueGenerationStrategies;
  	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  	private final Map<String, Integer> propertyIndexes = new HashMap<String, Integer>();
  	private final boolean hasCollections;
  	private final boolean hasMutableProperties;
  	private final boolean hasLazyProperties;
  	private final boolean hasNonIdentifierPropertyNamedId;
  
  	private final int[] naturalIdPropertyNumbers;
  	private final boolean hasImmutableNaturalId;
  	private final boolean hasCacheableNaturalId;
  
  	private boolean lazy; //not final because proxy factory creation can fail
  	private final boolean hasCascades;
  	private final boolean mutable;
  	private final boolean isAbstract;
  	private final boolean selectBeforeUpdate;
  	private final boolean dynamicUpdate;
  	private final boolean dynamicInsert;
  	private final OptimisticLockStyle optimisticLockStyle;
  
  	private final boolean polymorphic;
  	private final String superclass;  // superclass entity-name
  	private final boolean explicitPolymorphism;
  	private final boolean inherited;
  	private final boolean hasSubclasses;
  	private final Set subclassEntityNames = new HashSet();
  	private final Map entityNameByInheritenceClassMap = new HashMap();
  
  	private final EntityMode entityMode;
  	private final EntityTuplizer entityTuplizer;
  	private final EntityInstrumentationMetadata instrumentationMetadata;
  
  	public EntityMetamodel(
  			PersistentClass persistentClass,
  			AbstractEntityPersister persister,
  			SessionFactoryImplementor sessionFactory) {
  		this.sessionFactory = sessionFactory;
  		this.persister = persister;
  
  		name = persistentClass.getEntityName();
  		rootName = persistentClass.getRootClass().getEntityName();
  		entityType = sessionFactory.getTypeResolver().getTypeFactory().manyToOne( name );
  
  		identifierAttribute = PropertyFactory.buildIdentifierAttribute(
  				persistentClass,
  				sessionFactory.getIdentifierGenerator( rootName )
  		);
  
  		versioned = persistentClass.isVersioned();
  
  		instrumentationMetadata = persistentClass.hasPojoRepresentation()
  				? Environment.getBytecodeProvider().getEntityInstrumentationMetadata( persistentClass.getMappedClass() )
  				: new NonPojoInstrumentationMetadata( persistentClass.getEntityName() );
  
  		boolean hasLazy = false;
  
  		propertySpan = persistentClass.getPropertyClosureSpan();
  		properties = new NonIdentifierAttribute[propertySpan];
  		List<Integer> naturalIdNumbers = new ArrayList<Integer>();
  		// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  		propertyNames = new String[propertySpan];
  		propertyTypes = new Type[propertySpan];
  		propertyUpdateability = new boolean[propertySpan];
  		propertyInsertability = new boolean[propertySpan];
  		nonlazyPropertyUpdateability = new boolean[propertySpan];
  		propertyCheckability = new boolean[propertySpan];
  		propertyNullability = new boolean[propertySpan];
  		propertyVersionability = new boolean[propertySpan];
  		propertyLaziness = new boolean[propertySpan];
  		cascadeStyles = new CascadeStyle[propertySpan];
  		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  		// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  		this.inMemoryValueGenerationStrategies = new InMemoryValueGenerationStrategy[propertySpan];
  		this.inDatabaseValueGenerationStrategies = new InDatabaseValueGenerationStrategy[propertySpan];
  
  		boolean foundPreInsertGeneratedValues = false;
  		boolean foundPreUpdateGeneratedValues = false;
  		boolean foundPostInsertGeneratedValues = false;
  		boolean foundPostUpdateGeneratedValues = false;
  		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  		Iterator iter = persistentClass.getPropertyClosureIterator();
  		int i = 0;
  		int tempVersionProperty = NO_VERSION_INDX;
  		boolean foundCascade = false;
  		boolean foundCollection = false;
  		boolean foundMutable = false;
  		boolean foundNonIdentifierPropertyNamedId = false;
  		boolean foundInsertGeneratedValue = false;
  		boolean foundUpdateGeneratedValue = false;
  		boolean foundUpdateableNaturalIdProperty = false;
  
  		while ( iter.hasNext() ) {
  			Property prop = ( Property ) iter.next();
  
  			if ( prop == persistentClass.getVersion() ) {
  				tempVersionProperty = i;
  				properties[i] = PropertyFactory.buildVersionProperty(
  						persister,
  						sessionFactory,
  						i,
  						prop,
  						instrumentationMetadata.isInstrumented()
  				);
  			}
  			else {
  				properties[i] = PropertyFactory.buildEntityBasedAttribute(
  						persister,
  						sessionFactory,
  						i,
  						prop,
  						instrumentationMetadata.isInstrumented()
  				);
  			}
  
  			if ( prop.isNaturalIdentifier() ) {
  				naturalIdNumbers.add( i );
  				if ( prop.isUpdateable() ) {
  					foundUpdateableNaturalIdProperty = true;
  				}
  			}
  
  			if ( "id".equals( prop.getName() ) ) {
  				foundNonIdentifierPropertyNamedId = true;
  			}
  
  			// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  			boolean lazy = prop.isLazy() && instrumentationMetadata.isInstrumented();
  			if ( lazy ) hasLazy = true;
  			propertyLaziness[i] = lazy;
  
  			propertyNames[i] = properties[i].getName();
  			propertyTypes[i] = properties[i].getType();
  			propertyNullability[i] = properties[i].isNullable();
  			propertyUpdateability[i] = properties[i].isUpdateable();
  			propertyInsertability[i] = properties[i].isInsertable();
  			propertyVersionability[i] = properties[i].isVersionable();
  			nonlazyPropertyUpdateability[i] = properties[i].isUpdateable() && !lazy;
  			propertyCheckability[i] = propertyUpdateability[i] ||
  					( propertyTypes[i].isAssociationType() && ( (AssociationType) propertyTypes[i] ).isAlwaysDirtyChecked() );
  
  			cascadeStyles[i] = properties[i].getCascadeStyle();
  			// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  			// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  			GenerationStrategyPair pair = buildGenerationStrategyPair( sessionFactory, prop );
  			inMemoryValueGenerationStrategies[i] = pair.getInMemoryStrategy();
  			inDatabaseValueGenerationStrategies[i] = pair.getInDatabaseStrategy();
  
  			if ( pair.getInMemoryStrategy() != null ) {
  				final GenerationTiming timing = pair.getInMemoryStrategy().getGenerationTiming();
  				if ( timing != GenerationTiming.NEVER ) {
  					final ValueGenerator generator = pair.getInMemoryStrategy().getValueGenerator();
  					if ( generator != null ) {
  						// we have some level of generation indicated
  						if ( timing == GenerationTiming.INSERT ) {
  							foundPreInsertGeneratedValues = true;
  						}
  						else if ( timing == GenerationTiming.ALWAYS ) {
  							foundPreInsertGeneratedValues = true;
  							foundPreUpdateGeneratedValues = true;
  						}
  					}
  				}
  			}
  			if (  pair.getInDatabaseStrategy() != null ) {
  				final GenerationTiming timing =  pair.getInDatabaseStrategy().getGenerationTiming();
  				if ( timing == GenerationTiming.INSERT ) {
  					foundPostInsertGeneratedValues = true;
  				}
  				else if ( timing == GenerationTiming.ALWAYS ) {
  					foundPostInsertGeneratedValues = true;
  					foundPostUpdateGeneratedValues = true;
  				}
  			}
  			// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  			if ( properties[i].isLazy() ) {
  				hasLazy = true;
  			}
  
  			if ( properties[i].getCascadeStyle() != CascadeStyles.NONE ) {
  				foundCascade = true;
  			}
  
  			if ( indicatesCollection( properties[i].getType() ) ) {
  				foundCollection = true;
  			}
  
  			if ( propertyTypes[i].isMutable() && propertyCheckability[i] ) {
  				foundMutable = true;
  			}
  
  			mapPropertyToIndex(prop, i);
  			i++;
  		}
  
  		if (naturalIdNumbers.size()==0) {
  			naturalIdPropertyNumbers = null;
  			hasImmutableNaturalId = false;
  			hasCacheableNaturalId = false;
  		}
  		else {
  			naturalIdPropertyNumbers = ArrayHelper.toIntArray(naturalIdNumbers);
  			hasImmutableNaturalId = !foundUpdateableNaturalIdProperty;
  			hasCacheableNaturalId = persistentClass.getNaturalIdCacheRegionName() != null;
  		}
  
  		this.hasPreInsertGeneratedValues = foundPreInsertGeneratedValues;
  		this.hasPreUpdateGeneratedValues = foundPreUpdateGeneratedValues;
  		this.hasInsertGeneratedValues = foundPostInsertGeneratedValues;
  		this.hasUpdateGeneratedValues = foundPostUpdateGeneratedValues;
  
  		hasCascades = foundCascade;
  		hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
  		versionPropertyIndex = tempVersionProperty;
  		hasLazyProperties = hasLazy;
          if (hasLazyProperties) LOG.lazyPropertyFetchingAvailable(name);
  
  		lazy = persistentClass.isLazy() && (
  				// TODO: this disables laziness even in non-pojo entity modes:
  				!persistentClass.hasPojoRepresentation() ||
  				!ReflectHelper.isFinalClass( persistentClass.getProxyInterface() )
  		);
  		mutable = persistentClass.isMutable();
  		if ( persistentClass.isAbstract() == null ) {
  			// legacy behavior (with no abstract attribute specified)
  			isAbstract = persistentClass.hasPojoRepresentation() &&
  			             ReflectHelper.isAbstractClass( persistentClass.getMappedClass() );
  		}
  		else {
  			isAbstract = persistentClass.isAbstract().booleanValue();
  			if ( !isAbstract && persistentClass.hasPojoRepresentation() &&
  			     ReflectHelper.isAbstractClass( persistentClass.getMappedClass() ) ) {
                  LOG.entityMappedAsNonAbstract(name);
  			}
  		}
  		selectBeforeUpdate = persistentClass.hasSelectBeforeUpdate();
  		dynamicUpdate = persistentClass.useDynamicUpdate();
  		dynamicInsert = persistentClass.useDynamicInsert();
  
  		polymorphic = persistentClass.isPolymorphic();
  		explicitPolymorphism = persistentClass.isExplicitPolymorphism();
  		inherited = persistentClass.isInherited();
  		superclass = inherited ?
  				persistentClass.getSuperclass().getEntityName() :
  				null;
  		hasSubclasses = persistentClass.hasSubclasses();
  
  		optimisticLockStyle = persistentClass.getOptimisticLockStyle();
  		final boolean isAllOrDirty =
  				optimisticLockStyle == OptimisticLockStyle.ALL
  						|| optimisticLockStyle == OptimisticLockStyle.DIRTY;
  		if ( isAllOrDirty && !dynamicUpdate ) {
  			throw new MappingException( "optimistic-lock=all|dirty requires dynamic-update=\"true\": " + name );
  		}
  		if ( versionPropertyIndex != NO_VERSION_INDX && isAllOrDirty ) {
  			throw new MappingException( "version and optimistic-lock=all|dirty are not a valid combination : " + name );
  		}
  
  		hasCollections = foundCollection;
  		hasMutableProperties = foundMutable;
  
  		iter = persistentClass.getSubclassIterator();
  		while ( iter.hasNext() ) {
  			subclassEntityNames.add( ( (PersistentClass) iter.next() ).getEntityName() );
  		}
  		subclassEntityNames.add( name );
  
  		if ( persistentClass.hasPojoRepresentation() ) {
  			entityNameByInheritenceClassMap.put( persistentClass.getMappedClass(), persistentClass.getEntityName() );
  			iter = persistentClass.getSubclassIterator();
  			while ( iter.hasNext() ) {
  				final PersistentClass pc = ( PersistentClass ) iter.next();
  				entityNameByInheritenceClassMap.put( pc.getMappedClass(), pc.getEntityName() );
  			}
  		}
  
  		entityMode = persistentClass.hasPojoRepresentation() ? EntityMode.POJO : EntityMode.MAP;
  		final EntityTuplizerFactory entityTuplizerFactory = sessionFactory.getSettings().getEntityTuplizerFactory();
  		final String tuplizerClassName = persistentClass.getTuplizerImplClassName( entityMode );
  		if ( tuplizerClassName == null ) {
  			entityTuplizer = entityTuplizerFactory.constructDefaultTuplizer( entityMode, this, persistentClass );
  		}
  		else {
  			entityTuplizer = entityTuplizerFactory.constructTuplizer( tuplizerClassName, this, persistentClass );
  		}
  	}
  
  	private static GenerationStrategyPair buildGenerationStrategyPair(
  			final SessionFactoryImplementor sessionFactory,
  			final Property mappingProperty) {
  		final ValueGeneration valueGeneration = mappingProperty.getValueGenerationStrategy();
  		if ( valueGeneration != null && valueGeneration.getGenerationTiming() != GenerationTiming.NEVER ) {
  			// the property is generated in full. build the generation strategy pair.
  			if ( valueGeneration.getValueGenerator() != null ) {
  				// in-memory generation
  				return new GenerationStrategyPair(
  						FullInMemoryValueGenerationStrategy.create( valueGeneration )
  				);
  			}
  			else {
  				// in-db generation
  				return new GenerationStrategyPair(
  						create(
  								sessionFactory,
  								mappingProperty,
  								valueGeneration
  						)
  				);
  			}
  		}
  		else if ( mappingProperty.getValue() instanceof Component ) {
  			final CompositeGenerationStrategyPairBuilder builder = new CompositeGenerationStrategyPairBuilder( mappingProperty );
  			interpretPartialCompositeValueGeneration( sessionFactory, (Component) mappingProperty.getValue(), builder );
  			return builder.buildPair();
  		}
  
  		return NO_GEN_PAIR;
  	}
  
  	private static final GenerationStrategyPair NO_GEN_PAIR = new GenerationStrategyPair();
  
  	private static void interpretPartialCompositeValueGeneration(
  			SessionFactoryImplementor sessionFactory,
  			Component composite,
  			CompositeGenerationStrategyPairBuilder builder) {
  		Iterator subProperties = composite.getPropertyIterator();
  		while ( subProperties.hasNext() ) {
  			final Property subProperty = (Property) subProperties.next();
  			builder.addPair( buildGenerationStrategyPair( sessionFactory, subProperty ) );
  		}
  	}
  
  	public static InDatabaseValueGenerationStrategyImpl create(
  			SessionFactoryImplementor sessionFactoryImplementor,
  			Property mappingProperty,
  			ValueGeneration valueGeneration) {
  		final int numberOfMappedColumns = mappingProperty.getType().getColumnSpan( sessionFactoryImplementor );
  		if ( numberOfMappedColumns == 1 ) {
  			return new InDatabaseValueGenerationStrategyImpl(
  					valueGeneration.getGenerationTiming(),
  					valueGeneration.referenceColumnInSql(),
  					new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue() }
  
  			);
  		}
  		else {
  			if ( valueGeneration.getDatabaseGeneratedReferencedColumnValue() != null ) {
  				LOG.debugf(
  						"Value generator specified column value in reference to multi-column attribute [%s -> %s]; ignoring",
  						mappingProperty.getPersistentClass(),
  						mappingProperty.getName()
  				);
  			}
  			return new InDatabaseValueGenerationStrategyImpl(
  					valueGeneration.getGenerationTiming(),
  					valueGeneration.referenceColumnInSql(),
  					new String[numberOfMappedColumns]
  			);
  		}
  	}
  
  	public static class GenerationStrategyPair {
  		private final InMemoryValueGenerationStrategy inMemoryStrategy;
  		private final InDatabaseValueGenerationStrategy inDatabaseStrategy;
  
  		public GenerationStrategyPair() {
  			this( NoInMemoryValueGenerationStrategy.INSTANCE, NoInDatabaseValueGenerationStrategy.INSTANCE );
  		}
  
  		public GenerationStrategyPair(FullInMemoryValueGenerationStrategy inMemoryStrategy) {
  			this( inMemoryStrategy, NoInDatabaseValueGenerationStrategy.INSTANCE );
  		}
  
  		public GenerationStrategyPair(InDatabaseValueGenerationStrategyImpl inDatabaseStrategy) {
  			this( NoInMemoryValueGenerationStrategy.INSTANCE, inDatabaseStrategy );
  		}
  
  		public GenerationStrategyPair(
  				InMemoryValueGenerationStrategy inMemoryStrategy,
  				InDatabaseValueGenerationStrategy inDatabaseStrategy) {
  			// perform some normalization.  Also check that only one (if any) strategy is specified
  			if ( inMemoryStrategy == null ) {
  				inMemoryStrategy = NoInMemoryValueGenerationStrategy.INSTANCE;
  			}
  			if ( inDatabaseStrategy == null ) {
  				inDatabaseStrategy = NoInDatabaseValueGenerationStrategy.INSTANCE;
  			}
  
  			if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER
  					&& inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) {
  				throw new ValueGenerationStrategyException(
  						"in-memory and in-database value generation are mutually exclusive"
  				);
  			}
  
  			this.inMemoryStrategy = inMemoryStrategy;
  			this.inDatabaseStrategy = inDatabaseStrategy;
  		}
  
  		public InMemoryValueGenerationStrategy getInMemoryStrategy() {
  			return inMemoryStrategy;
  		}
  
  		public InDatabaseValueGenerationStrategy getInDatabaseStrategy() {
  			return inDatabaseStrategy;
  		}
  	}
  
  	public static class ValueGenerationStrategyException extends HibernateException {
  		public ValueGenerationStrategyException(String message) {
  			super( message );
  		}
  
  		public ValueGenerationStrategyException(String message, Throwable cause) {
  			super( message, cause );
  		}
  	}
  
  	private static class CompositeGenerationStrategyPairBuilder {
  		private final Property mappingProperty;
  
  		private boolean hadInMemoryGeneration;
  		private boolean hadInDatabaseGeneration;
  
  		private List<InMemoryValueGenerationStrategy> inMemoryStrategies;
  		private List<InDatabaseValueGenerationStrategy> inDatabaseStrategies;
  
  		public CompositeGenerationStrategyPairBuilder(Property mappingProperty) {
  			this.mappingProperty = mappingProperty;
  		}
  
  		public void addPair(GenerationStrategyPair generationStrategyPair) {
  			add( generationStrategyPair.getInMemoryStrategy() );
  			add( generationStrategyPair.getInDatabaseStrategy() );
  		}
  
  		private void add(InMemoryValueGenerationStrategy inMemoryStrategy) {
  			if ( inMemoryStrategies == null ) {
  				inMemoryStrategies = new ArrayList<InMemoryValueGenerationStrategy>();
  			}
  			inMemoryStrategies.add( inMemoryStrategy );
  
  			if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER ) {
  				hadInMemoryGeneration = true;
  			}
  		}
  
  		private void add(InDatabaseValueGenerationStrategy inDatabaseStrategy) {
  			if ( inDatabaseStrategies == null ) {
  				inDatabaseStrategies = new ArrayList<InDatabaseValueGenerationStrategy>();
  			}
  			inDatabaseStrategies.add( inDatabaseStrategy );
  
  			if ( inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) {
  				hadInDatabaseGeneration = true;
  			}
  		}
  
  		public GenerationStrategyPair buildPair() {
  			if ( hadInMemoryGeneration && hadInDatabaseGeneration ) {
  				throw new ValueGenerationStrategyException(
  						"Composite attribute [" + mappingProperty.getName() + "] contained both in-memory"
  								+ " and in-database value generation"
  				);
  			}
  			else if ( hadInMemoryGeneration ) {
  				throw new NotYetImplementedException( "Still need to wire in composite in-memory value generation" );
  
  			}
  			else if ( hadInDatabaseGeneration ) {
  				final Component composite = (Component) mappingProperty.getValue();
  
  				// we need the numbers to match up so we can properly handle 'referenced sql column values'
  				if ( inDatabaseStrategies.size() != composite.getPropertySpan() ) {
  					throw new ValueGenerationStrategyException(
  							"Internal error : mismatch between number of collected in-db generation strategies" +
  									" and number of attributes for composite attribute : " + mappingProperty.getName()
  					);
  				}
  
  				// the base-line values for the aggregated InDatabaseValueGenerationStrategy we will build here.
  				GenerationTiming timing = GenerationTiming.INSERT;
  				boolean referenceColumns = false;
  				String[] columnValues = new String[ composite.getColumnSpan() ];
  
  				// start building the aggregate values
  				int propertyIndex = -1;
  				int columnIndex = 0;
  				Iterator subProperties = composite.getPropertyIterator();
  				while ( subProperties.hasNext() ) {
  					propertyIndex++;
  					final Property subProperty = (Property) subProperties.next();
  					final InDatabaseValueGenerationStrategy subStrategy = inDatabaseStrategies.get( propertyIndex );
  
  					if ( subStrategy.getGenerationTiming() == GenerationTiming.ALWAYS ) {
  						// override the base-line to the more often "ALWAYS"...
  						timing = GenerationTiming.ALWAYS;
  
  					}
  					if ( subStrategy.referenceColumnsInSql() ) {
  						// override base-line value
  						referenceColumns = true;
  					}
  					if ( subStrategy.getReferencedColumnValues() != null ) {
  						if ( subStrategy.getReferencedColumnValues().length != subProperty.getColumnSpan() ) {
  							throw new ValueGenerationStrategyException(
  									"Internal error : mismatch between number of collected 'referenced column values'" +
  											" and number of columns for composite attribute : " + mappingProperty.getName() +
  											'.' + subProperty.getName()
  							);
  						}
  						System.arraycopy(
  								subStrategy.getReferencedColumnValues(),
  								0,
  								columnValues,
  								columnIndex,
  								subProperty.getColumnSpan()
  						);
  					}
  				}
  
  				// then use the aggregated values to build the InDatabaseValueGenerationStrategy
  				return new GenerationStrategyPair(
  						new InDatabaseValueGenerationStrategyImpl( timing, referenceColumns, columnValues )
  				);
  			}
  			else {
  				return NO_GEN_PAIR;
  			}
  		}
  	}
  
  	private static class NoInMemoryValueGenerationStrategy implements InMemoryValueGenerationStrategy {
  		/**
  		 * Singleton access
  		 */
  		public static final NoInMemoryValueGenerationStrategy INSTANCE = new NoInMemoryValueGenerationStrategy();
  
  		@Override
  		public GenerationTiming getGenerationTiming() {
  			return GenerationTiming.NEVER;
  		}
  
  		@Override
  		public ValueGenerator getValueGenerator() {
  			return null;
  		}
  	}
  
  	private static class FullInMemoryValueGenerationStrategy implements InMemoryValueGenerationStrategy {
  		private final GenerationTiming timing;
  		private final ValueGenerator generator;
  
  		private FullInMemoryValueGenerationStrategy(GenerationTiming timing, ValueGenerator generator) {
  			this.timing = timing;
  			this.generator = generator;
  		}
  
  		public static FullInMemoryValueGenerationStrategy create(ValueGeneration valueGeneration) {
  			return new FullInMemoryValueGenerationStrategy(
  					valueGeneration.getGenerationTiming(),
  					valueGeneration.getValueGenerator()
  			);
  		}
  
  		@Override
  		public GenerationTiming getGenerationTiming() {
  			return timing;
  		}
  
  		@Override
  		public ValueGenerator getValueGenerator() {
  			return generator;
  		}
  	}
  
  	private static class NoInDatabaseValueGenerationStrategy implements InDatabaseValueGenerationStrategy {
  		/**
  		 * Singleton access
  		 */
  		public static final NoInDatabaseValueGenerationStrategy INSTANCE = new NoInDatabaseValueGenerationStrategy();
  
  		@Override
  		public GenerationTiming getGenerationTiming() {
  			return GenerationTiming.NEVER;
  		}
  
  		@Override
  		public boolean referenceColumnsInSql() {
  			return true;
  		}
  
  		@Override
  		public String[] getReferencedColumnValues() {
  			return null;
  		}
  	}
  
  	private static class InDatabaseValueGenerationStrategyImpl implements InDatabaseValueGenerationStrategy {
  		private final GenerationTiming timing;
  		private final boolean referenceColumnInSql;
  		private final String[] referencedColumnValues;
  
  		private InDatabaseValueGenerationStrategyImpl(
  				GenerationTiming timing,
  				boolean referenceColumnInSql,
  				String[] referencedColumnValues) {
  			this.timing = timing;
  			this.referenceColumnInSql = referenceColumnInSql;
  			this.referencedColumnValues = referencedColumnValues;
  		}
  
  		@Override
  		public GenerationTiming getGenerationTiming() {
  			return timing;
  		}
  
  		@Override
  		public boolean referenceColumnsInSql() {
  			return referenceColumnInSql;
  		}
  
  		@Override
  		public String[] getReferencedColumnValues() {
  			return referencedColumnValues;
  		}
  	}
  
  	private ValueInclusion determineInsertValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) {
  		if ( isInsertGenerated( runtimeProperty ) ) {
  			return ValueInclusion.FULL;
  		}
  		else if ( mappingProperty.getValue() instanceof Component ) {
  			if ( hasPartialInsertComponentGeneration( ( Component ) mappingProperty.getValue() ) ) {
  				return ValueInclusion.PARTIAL;
  			}
  		}
  		return ValueInclusion.NONE;
  	}
  
  	private boolean isInsertGenerated(NonIdentifierAttribute property) {
  		return property.getValueGenerationStrategy() != null
  				&& property.getValueGenerationStrategy().getGenerationTiming() != GenerationTiming.NEVER;
  	}
  
  	private boolean isInsertGenerated(Property property) {
  		return property.getValueGenerationStrategy() != null
  				&& property.getValueGenerationStrategy().getGenerationTiming() != GenerationTiming.NEVER;
  	}
  
  	public EntityMetamodel(
  			EntityBinding entityBinding,
  			AbstractEntityPersister persister,
  			SessionFactoryImplementor sessionFactory) {
  		this.sessionFactory = sessionFactory;
  		this.persister = persister;
  
  		name = entityBinding.getEntity().getName();
  
  		rootName = entityBinding.getHierarchyDetails().getRootEntityBinding().getEntity().getName();
  		entityType = sessionFactory.getTypeResolver().getTypeFactory().manyToOne( name );
  
  		identifierAttribute = PropertyFactory.buildIdentifierProperty(
  				entityBinding,
  				sessionFactory.getIdentifierGenerator( rootName )
  		);
  
  		versioned = entityBinding.isVersioned();
  
  		boolean hasPojoRepresentation = false;
  		Class<?> mappedClass = null;
  		Class<?> proxyInterfaceClass = null;
  		if ( entityBinding.getEntity().getClassReferenceUnresolved() != null ) {
  			hasPojoRepresentation = true;
  			mappedClass = entityBinding.getEntity().getClassReference();
  			proxyInterfaceClass = entityBinding.getProxyInterfaceType().getValue();
  		}
  		instrumentationMetadata = Environment.getBytecodeProvider().getEntityInstrumentationMetadata( mappedClass );
  
  		boolean hasLazy = false;
  
  		// TODO: Fix after HHH-6337 is fixed; for now assume entityBinding is the root binding
  		BasicAttributeBinding rootEntityIdentifier = entityBinding.getHierarchyDetails().getEntityIdentifier().getValueBinding();
  		// entityBinding.getAttributeClosureSpan() includes the identifier binding;
  		// "properties" here excludes the ID, so subtract 1 if the identifier binding is non-null
  		propertySpan = rootEntityIdentifier == null ?
  				entityBinding.getAttributeBindingClosureSpan() :
  				entityBinding.getAttributeBindingClosureSpan() - 1;
  
  		properties = new NonIdentifierAttribute[propertySpan];
  		List naturalIdNumbers = new ArrayList();
  		// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  		propertyNames = new String[propertySpan];
  		propertyTypes = new Type[propertySpan];
  		propertyUpdateability = new boolean[propertySpan];
  		propertyInsertability = new boolean[propertySpan];
  