Package org.hibernate.tuple.entity

Source Code of org.hibernate.tuple.entity.EntityMetamodel$GenerationStrategyPair

/*
* 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];
    nonlazyPropertyUpdateability = new boolean[propertySpan];
    propertyCheckability = new boolean[propertySpan];
    propertyNullability = new boolean[propertySpan];
    propertyVersionability = new boolean[propertySpan];
    propertyLaziness = new boolean[propertySpan];
    cascadeStyles = new CascadeStyle[propertySpan];
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    // todo : handle value generations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    this.hasPreInsertGeneratedValues = false;
    this.hasPreUpdateGeneratedValues = false;
    this.hasInsertGeneratedValues = false;
    this.hasUpdateGeneratedValues = false;
    this.inMemoryValueGenerationStrategies = new InMemoryValueGenerationStrategy[propertySpan];
    Arrays.fill( this.inMemoryValueGenerationStrategies, NoInMemoryValueGenerationStrategy.INSTANCE );
    this.inDatabaseValueGenerationStrategies = new InDatabaseValueGenerationStrategy[propertySpan];
    Arrays.fill( this.inDatabaseValueGenerationStrategies, NoInDatabaseValueGenerationStrategy.INSTANCE );
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    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;

    for ( AttributeBinding attributeBinding : entityBinding.getAttributeBindingClosure() ) {
      if ( attributeBinding == rootEntityIdentifier ) {
        // skip the identifier attribute binding
        continue;
      }

      if ( attributeBinding == entityBinding.getHierarchyDetails().getVersioningAttributeBinding() ) {
        tempVersionProperty = i;
        properties[i] = PropertyFactory.buildVersionProperty(
            persister,
            entityBinding.getHierarchyDetails().getVersioningAttributeBinding(),
            instrumentationMetadata.isInstrumented()
        );
      }
      else {
        properties[i] = PropertyFactory.buildStandardProperty( attributeBinding, instrumentationMetadata.isInstrumented() );
      }

      // TODO: fix when natural IDs are added (HHH-6354)
      //if ( attributeBinding.isNaturalIdentifier() ) {
      //  naturalIdNumbers.add( i );
      //  if ( attributeBinding.isUpdateable() ) {
      //    foundUpdateableNaturalIdProperty = true;
      //  }
      //}

      if ( "id".equals( attributeBinding.getAttribute().getName() ) ) {
        foundNonIdentifierPropertyNamedId = true;
      }

      // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      boolean lazy = attributeBinding.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();
      // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      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(attributeBinding.getAttribute(), i);
      i++;
    }

    if (naturalIdNumbers.size()==0) {
      naturalIdPropertyNumbers = null;
      hasImmutableNaturalId = false;
      hasCacheableNaturalId = false;
    }
    else {
      naturalIdPropertyNumbers = ArrayHelper.toIntArray(naturalIdNumbers);
      hasImmutableNaturalId = !foundUpdateableNaturalIdProperty;
      hasCacheableNaturalId = false; //See previous TODO and HHH-6354
    }

    hasCascades = foundCascade;
    hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
    versionPropertyIndex = tempVersionProperty;
    hasLazyProperties = hasLazy;
    if (hasLazyProperties) {
      LOG.lazyPropertyFetchingAvailable( name );
    }

    lazy = entityBinding.isLazy() && (
        // TODO: this disables laziness even in non-pojo entity modes:
        ! hasPojoRepresentation ||
        ! ReflectHelper.isFinalClass( proxyInterfaceClass )
    );
    mutable = entityBinding.isMutable();
    if ( entityBinding.isAbstract() == null ) {
      // legacy behavior (with no abstract attribute specified)
      isAbstract = hasPojoRepresentation &&
                   ReflectHelper.isAbstractClass( mappedClass );
    }
    else {
      isAbstract = entityBinding.isAbstract().booleanValue();
      if ( !isAbstract && hasPojoRepresentation &&
          ReflectHelper.isAbstractClass( mappedClass ) ) {
        LOG.entityMappedAsNonAbstract(name);
      }
    }
    selectBeforeUpdate = entityBinding.isSelectBeforeUpdate();
    dynamicUpdate = entityBinding.isDynamicUpdate();
    dynamicInsert = entityBinding.isDynamicInsert();

    hasSubclasses = entityBinding.hasSubEntityBindings();
    polymorphic = entityBinding.isPolymorphic();

    explicitPolymorphism = entityBinding.getHierarchyDetails().isExplicitPolymorphism();
    inherited = ! entityBinding.isRoot();
    superclass = inherited ?
        entityBinding.getEntity().getSuperType().getName() :
        null;

    optimisticLockStyle = entityBinding.getHierarchyDetails().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;

    for ( EntityBinding subEntityBinding : entityBinding.getPostOrderSubEntityBindingClosure() ) {
      subclassEntityNames.add( subEntityBinding.getEntity().getName() );
      if ( subEntityBinding.getEntity().getClassReference() != null ) {
        entityNameByInheritenceClassMap.put(
            subEntityBinding.getEntity().getClassReference(),
            subEntityBinding.getEntity().getName() );
      }
    }
    subclassEntityNames.add( name );
    if ( mappedClass != null ) {
      entityNameByInheritenceClassMap.put( mappedClass, name );
    }

    entityMode = hasPojoRepresentation ? EntityMode.POJO : EntityMode.MAP;
    final EntityTuplizerFactory entityTuplizerFactory = sessionFactory.getSettings().getEntityTuplizerFactory();
    Class<? extends EntityTuplizer> tuplizerClass = entityBinding.getCustomEntityTuplizerClass();

    if ( tuplizerClass == null ) {
      entityTuplizer = entityTuplizerFactory.constructDefaultTuplizer( entityMode, this, entityBinding );
    }
    else {
      entityTuplizer = entityTuplizerFactory.constructTuplizer( tuplizerClass, this, entityBinding );
    }
  }

  private ValueInclusion determineInsertValueGenerationType(AttributeBinding mappingProperty, NonIdentifierAttribute runtimeProperty) {
    if ( isInsertGenerated( runtimeProperty ) ) {
      return ValueInclusion.FULL;
    }
    // TODO: fix the following when components are working (HHH-6173)
    //else if ( mappingProperty.getValue() instanceof ComponentAttributeBinding ) {
    //  if ( hasPartialInsertComponentGeneration( ( ComponentAttributeBinding ) mappingProperty.getValue() ) ) {
    //    return ValueInclusion.PARTIAL;
    //  }
    //}
    return ValueInclusion.NONE;
  }

  private boolean hasPartialInsertComponentGeneration(Component component) {
    Iterator subProperties = component.getPropertyIterator();
    while ( subProperties.hasNext() ) {
      final Property prop = ( Property ) subProperties.next();
      if ( isInsertGenerated( prop ) ) {
        return true;
      }
      else if ( prop.getValue() instanceof Component ) {
        if ( hasPartialInsertComponentGeneration( (Component) prop.getValue() ) ) {
          return true;
        }
      }
    }
    return false;
  }

  private ValueInclusion determineUpdateValueGenerationType(Property mappingProperty, NonIdentifierAttribute runtimeProperty) {
    if ( isUpdateGenerated( runtimeProperty ) ) {
      return ValueInclusion.FULL;
    }
    else if ( mappingProperty.getValue() instanceof Component ) {
      if ( hasPartialUpdateComponentGeneration( ( Component ) mappingProperty.getValue() ) ) {
        return ValueInclusion.PARTIAL;
      }
    }
    return ValueInclusion.NONE;
  }

  private static boolean isUpdateGenerated(Property property) {
    return property.getValueGenerationStrategy() != null
        && property.getValueGenerationStrategy().getGenerationTiming() == GenerationTiming.ALWAYS;
  }

  private static boolean isUpdateGenerated(NonIdentifierAttribute property) {
    return property.getValueGenerationStrategy() != null
        && property.getValueGenerationStrategy().getGenerationTiming() == GenerationTiming.ALWAYS;
  }

  private ValueInclusion determineUpdateValueGenerationType(AttributeBinding mappingProperty, NonIdentifierAttribute runtimeProperty) {
    if ( isUpdateGenerated( runtimeProperty ) ) {
      return ValueInclusion.FULL;
    }
    // TODO: fix the following when components are working (HHH-6173)
    //else if ( mappingProperty.getValue() instanceof ComponentAttributeBinding ) {
    //  if ( hasPartialUpdateComponentGeneration( ( ComponentAttributeBinding ) mappingProperty.getValue() ) ) {
    //    return ValueInclusion.PARTIAL;
    //  }
    //}
    return ValueInclusion.NONE;
  }

  private boolean hasPartialUpdateComponentGeneration(Component component) {
    Iterator subProperties = component.getPropertyIterator();
    while ( subProperties.hasNext() ) {
      Property prop = (Property) subProperties.next();
      if ( isUpdateGenerated( prop ) ) {
        return true;
      }
      else if ( prop.getValue() instanceof Component ) {
        if ( hasPartialUpdateComponentGeneration( ( Component ) prop.getValue() ) ) {
          return true;
        }
      }
    }
    return false;
  }

  private void mapPropertyToIndex(Property prop, int i) {
    propertyIndexes.put( prop.getName(), i );
    if ( prop.getValue() instanceof Component ) {
      Iterator iter = ( (Component) prop.getValue() ).getPropertyIterator();
      while ( iter.hasNext() ) {
        Property subprop = (Property) iter.next();
        propertyIndexes.put(
            prop.getName() + '.' + subprop.getName(),
            i
          );
      }
    }
  }

  private void mapPropertyToIndex(Attribute attribute, int i) {
    propertyIndexes.put( attribute.getName(), i );
    if ( attribute.isSingular() &&
        ( ( SingularAttribute ) attribute ).getSingularAttributeType().isComponent() ) {
      org.hibernate.metamodel.domain.Component component =
          ( org.hibernate.metamodel.domain.Component ) ( ( SingularAttribute ) attribute ).getSingularAttributeType();
      for ( Attribute subAttribute : component.attributes() ) {
        propertyIndexes.put(
            attribute.getName() + '.' + subAttribute.getName(),
            i
          );
      }
    }
  }

  public EntityTuplizer getTuplizer() {
    return entityTuplizer;
  }

  public boolean isNaturalIdentifierInsertGenerated() {
    // the intention is for this call to replace the usage of the old ValueInclusion stuff (as exposed from
    // persister) in SelectGenerator to determine if it is safe to use the natural identifier to find the
    // insert-generated identifier.  That wont work if the natural-id is also insert-generated.
    //
    // Assumptions:
    //    * That code checks that there is a natural identifier before making this call, so we assume the same here
    //     * That code assumes a non-composite natural-id, so we assume the same here
    final InDatabaseValueGenerationStrategy strategy = inDatabaseValueGenerationStrategies[ naturalIdPropertyNumbers[0] ];
    return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
  }

  public boolean isVersionGenerated() {
    final InDatabaseValueGenerationStrategy strategy = inDatabaseValueGenerationStrategies[ versionPropertyIndex ];
    return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER;
  }

  public int[] getNaturalIdentifierProperties() {
    return naturalIdPropertyNumbers;
  }

  public boolean hasNaturalIdentifier() {
    return naturalIdPropertyNumbers!=null;
  }
 
  public boolean isNaturalIdentifierCached() {
    return hasNaturalIdentifier() && hasCacheableNaturalId;
  }

  public boolean hasImmutableNaturalId() {
    return hasImmutableNaturalId;
  }

  public Set getSubclassEntityNames() {
    return subclassEntityNames;
  }

  private boolean indicatesCollection(Type type) {
    if ( type.isCollectionType() ) {
      return true;
    }
    else if ( type.isComponentType() ) {
      Type[] subtypes = ( (CompositeType) type ).getSubtypes();
      for ( int i = 0; i < subtypes.length; i++ ) {
        if ( indicatesCollection( subtypes[i] ) ) {
          return true;
        }
      }
    }
    return false;
  }

  public SessionFactoryImplementor getSessionFactory() {
    return sessionFactory;
  }

  public String getName() {
    return name;
  }

  public String getRootName() {
    return rootName;
  }

  public EntityType getEntityType() {
    return entityType;
  }

  public IdentifierProperty getIdentifierProperty() {
    return identifierAttribute;
  }

  public int getPropertySpan() {
    return propertySpan;
  }

  public int getVersionPropertyIndex() {
    return versionPropertyIndex;
  }

  public VersionProperty getVersionProperty() {
    if ( NO_VERSION_INDX == versionPropertyIndex ) {
      return null;
    }
    else {
      return ( VersionProperty ) properties[ versionPropertyIndex ];
    }
  }

  public NonIdentifierAttribute[] getProperties() {
    return properties;
  }

  public int getPropertyIndex(String propertyName) {
    Integer index = getPropertyIndexOrNull(propertyName);
    if ( index == null ) {
      throw new HibernateException("Unable to resolve property: " + propertyName);
    }
    return index;
  }

  public Integer getPropertyIndexOrNull(String propertyName) {
    return propertyIndexes.get( propertyName );
  }

  public boolean hasCollections() {
    return hasCollections;
  }

  public boolean hasMutableProperties() {
    return hasMutableProperties;
  }

  public boolean hasNonIdentifierPropertyNamedId() {
    return hasNonIdentifierPropertyNamedId;
  }

  public boolean hasLazyProperties() {
    return hasLazyProperties;
  }

  public boolean hasCascades() {
    return hasCascades;
  }

  public boolean isMutable() {
    return mutable;
  }

  public boolean isSelectBeforeUpdate() {
    return selectBeforeUpdate;
  }

  public boolean isDynamicUpdate() {
    return dynamicUpdate;
  }

  public boolean isDynamicInsert() {
    return dynamicInsert;
  }

  public OptimisticLockStyle getOptimisticLockStyle() {
    return optimisticLockStyle;
  }

  public boolean isPolymorphic() {
    return polymorphic;
  }

  public String getSuperclass() {
    return superclass;
  }

  public boolean isExplicitPolymorphism() {
    return explicitPolymorphism;
  }

  public boolean isInherited() {
    return inherited;
  }

  public boolean hasSubclasses() {
    return hasSubclasses;
  }

  public boolean isLazy() {
    return lazy;
  }

  public void setLazy(boolean lazy) {
    this.lazy = lazy;
  }

  public boolean isVersioned() {
    return versioned;
  }

  public boolean isAbstract() {
    return isAbstract;
  }

  /**
   * Return the entity-name mapped to the given class within our inheritance hierarchy, if any.
   *
   * @param inheritenceClass The class for which to resolve the entity-name.
   * @return The mapped entity-name, or null if no such mapping was found.
   */
  public String findEntityNameByEntityClass(Class inheritenceClass) {
    return ( String ) entityNameByInheritenceClassMap.get( inheritenceClass );
  }

  @Override
    public String toString() {
    return "EntityMetamodel(" + name + ':' + ArrayHelper.toString(properties) + ')';
  }

  // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  public String[] getPropertyNames() {
    return propertyNames;
  }

  public Type[] getPropertyTypes() {
    return propertyTypes;
  }

  public boolean[] getPropertyLaziness() {
    return propertyLaziness;
  }

  public boolean[] getPropertyUpdateability() {
    return propertyUpdateability;
  }

  public boolean[] getPropertyCheckability() {
    return propertyCheckability;
  }

  public boolean[] getNonlazyPropertyUpdateability() {
    return nonlazyPropertyUpdateability;
  }

  public boolean[] getPropertyInsertability() {
    return propertyInsertability;
  }

  public boolean[] getPropertyNullability() {
    return propertyNullability;
  }

  public boolean[] getPropertyVersionability() {
    return propertyVersionability;
  }

  public CascadeStyle[] getCascadeStyles() {
    return cascadeStyles;
  }

  public boolean hasPreInsertGeneratedValues() {
    return hasPreInsertGeneratedValues;
  }

  public boolean hasPreUpdateGeneratedValues() {
    return hasPreUpdateGeneratedValues;
  }

  public boolean hasInsertGeneratedValues() {
    return hasInsertGeneratedValues;
  }

  public boolean hasUpdateGeneratedValues() {
    return hasUpdateGeneratedValues;
  }

  public InMemoryValueGenerationStrategy[] getInMemoryValueGenerationStrategies() {
    return inMemoryValueGenerationStrategies;
  }

  public InDatabaseValueGenerationStrategy[] getInDatabaseValueGenerationStrategies() {
    return inDatabaseValueGenerationStrategies;
  }

  public EntityMode getEntityMode() {
    return entityMode;
  }

  /**
   * Whether or not this class can be lazy (ie intercepted)
   */
  public boolean isInstrumented() {
    return instrumentationMetadata.isInstrumented();
  }

  public EntityInstrumentationMetadata getInstrumentationMetadata() {
    return instrumentationMetadata;
  }
}
TOP

Related Classes of org.hibernate.tuple.entity.EntityMetamodel$GenerationStrategyPair

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.