public Class<? extends EntityPersister> getEntityPersisterClass(EntityBinding metadata) {
   if (metadata.isRoot()) {
     Iterator<EntityBinding> subEntityBindingIterator =
         metadata.getDirectSubEntityBindings().iterator();
     if (subEntityBindingIterator.hasNext()) {
       // If the class has children, we need to find of which kind
       metadata = subEntityBindingIterator.next();
     } else {
       return singleTableEntityPersister();
     }
   }
   switch (metadata.getHierarchyDetails().getInheritanceType()) {
     case JOINED:
       {
         return joinedSubclassEntityPersister();
       }
     case SINGLE_TABLE:
       {
         return singleTableEntityPersister();
       }
     case TABLE_PER_CLASS:
       {
         return unionSubclassEntityPersister();
       }
     default:
       {
         throw new UnknownPersisterException(
             "Could not determine persister implementation for entity ["
                 + metadata.getEntity().getName()
                 + "]");
       }
   }
 }
  @Override
  public void bindMappingMetadata(MetadataSources sources, List<String> processedEntityNames) {
    AnnotationBindingContext context =
        new AnnotationBindingContext(index, metadata.getServiceRegistry());
    // need to order our annotated entities into an order we can process
    Set<ConfiguredClassHierarchy<EntityClass>> hierarchies =
        ConfiguredClassHierarchyBuilder.createEntityHierarchies(context);

    // now we process each hierarchy one at the time
    Hierarchical parent = null;
    for (ConfiguredClassHierarchy<EntityClass> hierarchy : hierarchies) {
      for (EntityClass entityClass : hierarchy) {
        // for classes annotated w/ @Entity we create a EntityBinding
        if (ConfiguredClassType.ENTITY.equals(entityClass.getConfiguredClassType())) {
          LOG.bindingEntityFromAnnotatedClass(entityClass.getName());
          EntityBinder entityBinder = new EntityBinder(metadata, entityClass, parent);
          EntityBinding binding = entityBinder.bind();
          parent = binding.getEntity();
        }
        // for classes annotated w/ @MappedSuperclass we just create the domain instance
        // the attribute bindings will be part of the first entity subclass
        else if (ConfiguredClassType.MAPPED_SUPERCLASS.equals(
            entityClass.getConfiguredClassType())) {
          parent = new Superclass(entityClass.getName(), parent);
        }
        // for classes which are not annotated at all we create the NonEntity domain class
        // todo - not sure whether this is needed. It might be that we don't need this information
        // (HF)
        else {
          parent = new NonEntity(entityClass.getName(), parent);
        }
      }
    }
  }
 public static Callback[] resolveCallbacks(
     Class<?> entityClass,
     Class<?> callbackClass,
     ClassLoaderService classLoaderService,
     EntityBinding binding) {
   List<Callback> callbacks = new ArrayList<Callback>();
   for (JpaCallbackClass jpaCallbackClass : binding.getJpaCallbackClasses()) {
     Object listener = classLoaderService.classForName(jpaCallbackClass.getName());
     String methodName = jpaCallbackClass.getCallbackMethod(callbackClass);
     Callback callback =
         jpaCallbackClass.isListener()
             ? createListenerCallback(entityClass, callbackClass, listener, methodName)
             : createBeanCallback(callbackClass, methodName);
     LOG.debugf(
         "Adding %s as %s callback for entity %s",
         methodName, callbackClass.getName(), entityClass.getName());
     assert callback != null;
     callbacks.add(callback);
   }
   return callbacks.toArray(new Callback[callbacks.size()]);
 }
  public EntityMetamodel(EntityBinding entityBinding, SessionFactoryImplementor sessionFactory) {
    this.sessionFactory = sessionFactory;

    name = entityBinding.getEntity().getName();

    rootName = entityBinding.getHierarchyDetails().getRootEntityBinding().getEntity().getName();
    entityType = sessionFactory.getTypeResolver().getTypeFactory().manyToOne(name);

    identifierProperty =
        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 StandardProperty[propertySpan];
    List naturalIdNumbers = new ArrayList();
    // temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    propertyNames = new String[propertySpan];
    propertyTypes = new Type[propertySpan];
    propertyUpdateability = new boolean[propertySpan];
    propertyInsertability = new boolean[propertySpan];
    insertInclusions = new ValueInclusion[propertySpan];
    updateInclusions = new ValueInclusion[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];
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    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(
                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();
      insertInclusions[i] = determineInsertValueGenerationType(attributeBinding, properties[i]);
      updateInclusions[i] = determineUpdateValueGenerationType(attributeBinding, properties[i]);
      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;
      }

      if (insertInclusions[i] != ValueInclusion.NONE) {
        foundInsertGeneratedValue = true;
      }

      if (updateInclusions[i] != ValueInclusion.NONE) {
        foundUpdateGeneratedValue = 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
    }

    hasInsertGeneratedValues = foundInsertGeneratedValue;
    hasUpdateGeneratedValues = foundUpdateGeneratedValue;

    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);
    }
  }
 @Test
 @Resources(annotatedClasses = RowIdEntity.class)
 public void testRowId() {
   EntityBinding binding = getEntityBinding(RowIdEntity.class);
   assertEquals("Wrong row id", "rowid", binding.getRowId());
 }