1. 程式人生 > >hibernate3.6.10 註解配置新增表字段註釋

hibernate3.6.10 註解配置新增表字段註釋

如果使用的是xml方式配置,則使用comment即可:

<property name="name"  >
      <column name="name_col">
      <comment>我是註釋</comment>
      </column>
    </property>

下面的方法是修改hibernate3.6.10原始碼、添加註解配置的處理資訊:

package org.hibernate.cfg;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.FIELD})  
@Retention(RetentionPolicy.RUNTIME)  
public @interface Comment {  
    String value() default "";  
}  
package org.hibernate.cfg;

  
import org.hibernate.annotations.common.reflection.XClass;  
import org.hibernate.annotations.common.reflection.XProperty;  
import org.hibernate.cfg.Ejb3Column;  
import org.hibernate.mapping.PersistentClass;  
  

public class CommentBinder {  
	
    public static void bindTableComment(XClass clazzToProcess, PersistentClass persistentClass) {  
    	//System.out.println("處理表名註釋!");
        if (clazzToProcess.isAnnotationPresent(Comment.class)) {  
            String tableComment = clazzToProcess.getAnnotation(Comment.class).value();  
            persistentClass.getTable().setComment(tableComment);  
  
        }  
    }  
  
    public static void bindColumnComment(XProperty property, Ejb3Column[] columns) {  
    	//System.out.println("處理多欄位註釋"+property+columns);
        if (null != columns)  
            if (property.isAnnotationPresent(Comment.class)) {  
                String comment = property.getAnnotation(Comment.class).value();  
                for (Ejb3Column column : columns) {  
                    column.getMappingColumn().setComment(comment);  
                }  
  
            }  
    }  
  
    public static void bindColumnComment(XProperty property, Ejb3Column column) {  
    	//System.out.println("處理欄位註釋");
        if (null != column)  
            if (property.isAnnotationPresent(Comment.class)) {  
                String comment = property.getAnnotation(Comment.class).value();  
  
                column.getMappingColumn().setComment(comment);  
  
            }  
    }  
}  
下面這個類路徑不能變、覆蓋hibernate對應的原始碼。
/*
 * 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.cfg;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.MapKeyJoinColumns;
import javax.persistence.MappedSuperclass;
import javax.persistence.MapsId;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderColumn;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SequenceGenerator;
import javax.persistence.SharedCacheMode;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.DiscriminatorOptions;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchProfile;
import org.hibernate.annotations.FetchProfiles;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.ForceDiscriminator;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.GenericGenerators;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.OrderBy;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Parent;
import org.hibernate.annotations.Proxy;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.Source;
import org.hibernate.annotations.Tuplizer;
import org.hibernate.annotations.Tuplizers;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.hibernate.annotations.Where;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.annotations.common.reflection.XPackage;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.MapKeyColumnDelegator;
import org.hibernate.cfg.annotations.MapKeyJoinColumnDelegator;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.cfg.annotations.PropertyBinder;
import org.hibernate.cfg.annotations.QueryBinder;
import org.hibernate.cfg.annotations.SimpleValueBinder;
import org.hibernate.cfg.annotations.TableBinder;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.engine.Versioning;
import org.hibernate.id.MultipleHiLoPerTableGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceHiLoGenerator;
import org.hibernate.id.TableHiLoGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.IdGenerator;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;

/**
 * JSR 175 annotation binder which reads the annotations from classes, applies the
 * principles of the EJB3 spec and produces the Hibernate configuration-time metamodel
 * (the classes in the {@code org.hibernate.mapping} package)
 *
 * @author Emmanuel Bernard
 * @author Hardy Ferentschik
 */
@SuppressWarnings("unchecked")
public final class AnnotationBinder {

	/*
	 * Some design description
	 * I tried to remove any link to annotation except from the 2 first level of
	 * method call.
	 * It'll enable to:
	 *   - facilitate annotation overriding
	 *   - mutualize one day xml and annotation binder (probably a dream though)
	 *   - split this huge class in smaller mapping oriented classes
	 *
	 * bindSomething usually create the mapping container and is accessed by one of the 2 first level method
	 * makeSomething usually create the mapping container and is accessed by bindSomething[else]
	 * fillSomething take the container into parameter and fill it.
	 */

	private AnnotationBinder() {
	}

	private static final Logger log = LoggerFactory.getLogger( AnnotationBinder.class );

	public static void bindDefaults(Mappings mappings) {
		Map defaults = mappings.getReflectionManager().getDefaults();
		{
			List<SequenceGenerator> anns = ( List<SequenceGenerator> ) defaults.get( SequenceGenerator.class );
			if ( anns != null ) {
				for ( SequenceGenerator ann : anns ) {
					IdGenerator idGen = buildIdGenerator( ann, mappings );
					if ( idGen != null ) {
						mappings.addDefaultGenerator( idGen );
					}
				}
			}
		}
		{
			List<TableGenerator> anns = ( List<TableGenerator> ) defaults.get( TableGenerator.class );
			if ( anns != null ) {
				for ( TableGenerator ann : anns ) {
					IdGenerator idGen = buildIdGenerator( ann, mappings );
					if ( idGen != null ) {
						mappings.addDefaultGenerator( idGen );
					}
				}
			}
		}
		{
			List<NamedQuery> anns = ( List<NamedQuery> ) defaults.get( NamedQuery.class );
			if ( anns != null ) {
				for ( NamedQuery ann : anns ) {
					QueryBinder.bindQuery( ann, mappings, true );
				}
			}
		}
		{
			List<NamedNativeQuery> anns = ( List<NamedNativeQuery> ) defaults.get( NamedNativeQuery.class );
			if ( anns != null ) {
				for ( NamedNativeQuery ann : anns ) {
					QueryBinder.bindNativeQuery( ann, mappings, true );
				}
			}
		}
		{
			List<SqlResultSetMapping> anns = ( List<SqlResultSetMapping> ) defaults.get( SqlResultSetMapping.class );
			if ( anns != null ) {
				for ( SqlResultSetMapping ann : anns ) {
					QueryBinder.bindSqlResultsetMapping( ann, mappings, true );
				}
			}
		}
	}

	public static void bindPackage(String packageName, Mappings mappings) {
		XPackage pckg;
		try {
			pckg = mappings.getReflectionManager().packageForName( packageName );
		}
		catch ( ClassNotFoundException cnf ) {
			log.warn( "Package not found or wo package-info.java: {}", packageName );
			return;
		}
		if ( pckg.isAnnotationPresent( SequenceGenerator.class ) ) {
			SequenceGenerator ann = pckg.getAnnotation( SequenceGenerator.class );
			IdGenerator idGen = buildIdGenerator( ann, mappings );
			mappings.addGenerator( idGen );
			log.trace( "Add sequence generator with name: {}", idGen.getName() );
		}
		if ( pckg.isAnnotationPresent( TableGenerator.class ) ) {
			TableGenerator ann = pckg.getAnnotation( TableGenerator.class );
			IdGenerator idGen = buildIdGenerator( ann, mappings );
			mappings.addGenerator( idGen );

		}
		bindGenericGenerators( pckg, mappings );
		bindQueries( pckg, mappings );
		bindFilterDefs( pckg, mappings );
		bindTypeDefs( pckg, mappings );
		bindFetchProfiles( pckg, mappings );
		BinderHelper.bindAnyMetaDefs( pckg, mappings );
		
	}

	private static void bindGenericGenerators(XAnnotatedElement annotatedElement, Mappings mappings) {
		GenericGenerator defAnn = annotatedElement.getAnnotation( GenericGenerator.class );
		GenericGenerators defsAnn = annotatedElement.getAnnotation( GenericGenerators.class );
		if ( defAnn != null ) {
			bindGenericGenerator( defAnn, mappings );
		}
		if ( defsAnn != null ) {
			for ( GenericGenerator def : defsAnn.value() ) {
				bindGenericGenerator( def, mappings );
			}
		}
	}

	private static void bindGenericGenerator(GenericGenerator def, Mappings mappings) {
		IdGenerator idGen = buildIdGenerator( def, mappings );
		mappings.addGenerator( idGen );
	}

	private static void bindQueries(XAnnotatedElement annotatedElement, Mappings mappings) {
		{
			SqlResultSetMapping ann = annotatedElement.getAnnotation( SqlResultSetMapping.class );
			QueryBinder.bindSqlResultsetMapping( ann, mappings, false );
		}
		{
			SqlResultSetMappings ann = annotatedElement.getAnnotation( SqlResultSetMappings.class );
			if ( ann != null ) {
				for ( SqlResultSetMapping current : ann.value() ) {
					QueryBinder.bindSqlResultsetMapping( current, mappings, false );
				}
			}
		}
		{
			NamedQuery ann = annotatedElement.getAnnotation( NamedQuery.class );
			QueryBinder.bindQuery( ann, mappings, false );
		}
		{
			org.hibernate.annotations.NamedQuery ann = annotatedElement.getAnnotation(
					org.hibernate.annotations.NamedQuery.class
			);
			QueryBinder.bindQuery( ann, mappings );
		}
		{
			NamedQueries ann = annotatedElement.getAnnotation( NamedQueries.class );
			QueryBinder.bindQueries( ann, mappings, false );
		}
		{
			org.hibernate.annotations.NamedQueries ann = annotatedElement.getAnnotation(
					org.hibernate.annotations.NamedQueries.class
			);
			QueryBinder.bindQueries( ann, mappings );
		}
		{
			NamedNativeQuery ann = annotatedElement.getAnnotation( NamedNativeQuery.class );
			QueryBinder.bindNativeQuery( ann, mappings, false );
		}
		{
			org.hibernate.annotations.NamedNativeQuery ann = annotatedElement.getAnnotation(
					org.hibernate.annotations.NamedNativeQuery.class
			);
			QueryBinder.bindNativeQuery( ann, mappings );
		}
		{
			NamedNativeQueries ann = annotatedElement.getAnnotation( NamedNativeQueries.class );
			QueryBinder.bindNativeQueries( ann, mappings, false );
		}
		{
			org.hibernate.annotations.NamedNativeQueries ann = annotatedElement.getAnnotation(
					org.hibernate.annotations.NamedNativeQueries.class
			);
			QueryBinder.bindNativeQueries( ann, mappings );
		}
	}

	private static IdGenerator buildIdGenerator(java.lang.annotation.Annotation ann, Mappings mappings) {
		IdGenerator idGen = new IdGenerator();
		if ( mappings.getSchemaName() != null ) {
			idGen.addParam( PersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
		}
		if ( mappings.getCatalogName() != null ) {
			idGen.addParam( PersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
		}
		final boolean useNewGeneratorMappings = mappings.useNewGeneratorMappings();
		if ( ann == null ) {
			idGen = null;
		}
		else if ( ann instanceof TableGenerator ) {
			TableGenerator tabGen = ( TableGenerator ) ann;
			idGen.setName( tabGen.name() );
			if ( useNewGeneratorMappings ) {
				idGen.setIdentifierGeneratorStrategy( org.hibernate.id.enhanced.TableGenerator.class.getName() );
				idGen.addParam( org.hibernate.id.enhanced.TableGenerator.CONFIG_PREFER_SEGMENT_PER_ENTITY, "true" );

				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.catalog() ) ) {
					idGen.addParam( org.hibernate.id.enhanced.TableGenerator.CATALOG, tabGen.catalog() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.schema() ) ) {
					idGen.addParam( org.hibernate.id.enhanced.TableGenerator.SCHEMA, tabGen.schema() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.table() ) ) {
					idGen.addParam( org.hibernate.id.enhanced.TableGenerator.TABLE_PARAM, tabGen.table() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.pkColumnName() ) ) {
					idGen.addParam(
							org.hibernate.id.enhanced.TableGenerator.SEGMENT_COLUMN_PARAM, tabGen.pkColumnName()
					);
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.pkColumnValue() ) ) {
					idGen.addParam(
							org.hibernate.id.enhanced.TableGenerator.SEGMENT_VALUE_PARAM, tabGen.pkColumnValue()
					);
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.valueColumnName() ) ) {
					idGen.addParam(
							org.hibernate.id.enhanced.TableGenerator.VALUE_COLUMN_PARAM, tabGen.valueColumnName()
					);
				}
				idGen.addParam(
						org.hibernate.id.enhanced.TableGenerator.INCREMENT_PARAM,
						String.valueOf( tabGen.allocationSize() )
				);
				// See comment on HHH-4884 wrt initialValue.  Basically initialValue is really the stated value + 1
				idGen.addParam(
						org.hibernate.id.enhanced.TableGenerator.INITIAL_PARAM,
						String.valueOf( tabGen.initialValue() + 1 )
				);
				if ( tabGen.uniqueConstraints() != null && tabGen.uniqueConstraints().length > 0 ) {
					log.warn( "Ignoring unique constraints specified on table generator [{}]", tabGen.name() );
				}
			}
			else {
				idGen.setIdentifierGeneratorStrategy( MultipleHiLoPerTableGenerator.class.getName() );

				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.table() ) ) {
					idGen.addParam( MultipleHiLoPerTableGenerator.ID_TABLE, tabGen.table() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.catalog() ) ) {
					idGen.addParam( MultipleHiLoPerTableGenerator.CATALOG, tabGen.catalog() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.schema() ) ) {
					idGen.addParam( MultipleHiLoPerTableGenerator.SCHEMA, tabGen.schema() );
				}
				//FIXME implement uniqueconstrains
				if ( tabGen.uniqueConstraints() != null && tabGen.uniqueConstraints().length > 0 ) {
					log.warn( "Ignoring unique constraints specified on table generator [{}]", tabGen.name() );
				}

				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.pkColumnName() ) ) {
					idGen.addParam( MultipleHiLoPerTableGenerator.PK_COLUMN_NAME, tabGen.pkColumnName() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.valueColumnName() ) ) {
					idGen.addParam( MultipleHiLoPerTableGenerator.VALUE_COLUMN_NAME, tabGen.valueColumnName() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( tabGen.pkColumnValue() ) ) {
					idGen.addParam( MultipleHiLoPerTableGenerator.PK_VALUE_NAME, tabGen.pkColumnValue() );
				}
				idGen.addParam( TableHiLoGenerator.MAX_LO, String.valueOf( tabGen.allocationSize() - 1 ) );
			}
			log.trace( "Add table generator with name: {}", idGen.getName() );
		}
		else if ( ann instanceof SequenceGenerator ) {
			SequenceGenerator seqGen = ( SequenceGenerator ) ann;
			idGen.setName( seqGen.name() );
			if ( useNewGeneratorMappings ) {
				idGen.setIdentifierGeneratorStrategy( SequenceStyleGenerator.class.getName() );

				if ( !BinderHelper.isEmptyAnnotationValue( seqGen.catalog() ) ) {
					idGen.addParam( SequenceStyleGenerator.CATALOG, seqGen.catalog() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( seqGen.schema() ) ) {
					idGen.addParam( SequenceStyleGenerator.SCHEMA, seqGen.schema() );
				}
				if ( !BinderHelper.isEmptyAnnotationValue( seqGen.sequenceName() ) ) {
					idGen.addParam( SequenceStyleGenerator.SEQUENCE_PARAM, seqGen.sequenceName() );
				}
				idGen.addParam( SequenceStyleGenerator.INCREMENT_PARAM, String.valueOf( seqGen.allocationSize() ) );
				idGen.addParam( SequenceStyleGenerator.INITIAL_PARAM, String.valueOf( seqGen.initialValue() ) );
			}
			else {
				idGen.setIdentifierGeneratorStrategy( "seqhilo" );

				if ( !BinderHelper.isEmptyAnnotationValue( seqGen.sequenceName() ) ) {
					idGen.addParam( org.hibernate.id.SequenceGenerator.SEQUENCE, seqGen.sequenceName() );
				}
				//FIXME: work on initialValue() through SequenceGenerator.PARAMETERS
				//		steve : or just use o.h.id.enhanced.SequenceStyleGenerator
				if ( seqGen.initialValue() != 1 ) {
					log.warn(
							"Hibernate does not support SequenceGenerator.initialValue() unless '{}' set",
							Configuration.USE_NEW_ID_GENERATOR_MAPPINGS
					);
				}
				idGen.addParam( SequenceHiLoGenerator.MAX_LO, String.valueOf( seqGen.allocationSize() - 1 ) );
				log.trace( "Add sequence generator with name: {}", idGen.getName() );
			}
		}
		else if ( ann instanceof GenericGenerator ) {
			GenericGenerator genGen = ( GenericGenerator ) ann;
			idGen.setName( genGen.name() );
			idGen.setIdentifierGeneratorStrategy( genGen.strategy() );
			Parameter[] params = genGen.parameters();
			for ( Parameter parameter : params ) {
				idGen.addParam( parameter.name(), parameter.value() );
			}
			log.trace( "Add generic generator with name: {}", idGen.getName() );
		}
		else {
			throw new AssertionFailure( "Unknown Generator annotation: " + ann );
		}
		return idGen;
	}

	/**
	 * Bind a class having JSR175 annotations. Subclasses <b>have to</b> be bound after its parent class.
	 *
	 * @param clazzToProcess entity to bind as {@code XClass} instance
	 * @param inheritanceStatePerClass Meta data about the inheritance relationships for all mapped classes
	 * @param mappings Mapping meta data
	 *
	 * @throws MappingException in case there is an configuration error
	 */
	public static void bindClass(
			XClass clazzToProcess,
			Map<XClass, InheritanceState> inheritanceStatePerClass,
			Mappings mappings) throws MappingException {
		//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
		if ( clazzToProcess.isAnnotationPresent( Entity.class ) 
				&&  clazzToProcess.isAnnotationPresent( MappedSuperclass.class ) ) {
			throw new AnnotationException( "An entity cannot be annotated with both @Entity and @MappedSuperclass: "
					+ clazzToProcess.getName() );
		}

		//TODO: be more strict with secondarytable allowance (not for ids, not for secondary table join columns etc)
		InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
		AnnotatedClassType classType = mappings.getClassType( clazzToProcess );

		//Queries declared in MappedSuperclass should be usable in Subclasses
		if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) ) {
			bindQueries( clazzToProcess, mappings );
			bindTypeDefs( clazzToProcess, mappings );
			bindFilterDefs( clazzToProcess, mappings );
		}

		if ( !isEntityClassType( clazzToProcess, classType ) ) {
			return;
		}

		log.info( "Binding entity from annotated class: {}", clazzToProcess.getName() );

		PersistentClass superEntity = getSuperEntity(
				clazzToProcess, inheritanceStatePerClass, mappings, inheritanceState
		);

		PersistentClass persistentClass = makePersistentClass( inheritanceState, superEntity );
		Entity entityAnn = clazzToProcess.getAnnotation( Entity.class );
		org.hibernate.annotations.Entity hibEntityAnn = clazzToProcess.getAnnotation(
				org.hibernate.annotations.Entity.class
		);
		EntityBinder entityBinder = new EntityBinder(
				entityAnn, hibEntityAnn, clazzToProcess, persistentClass, mappings
		);
		entityBinder.setInheritanceState( inheritanceState );

		bindQueries( clazzToProcess, mappings );
		bindFilterDefs( clazzToProcess, mappings );
		bindTypeDefs( clazzToProcess, mappings );
		bindFetchProfiles( clazzToProcess, mappings );
		BinderHelper.bindAnyMetaDefs( clazzToProcess, mappings );
		
		
		String schema = "";
		String table = ""; //might be no @Table annotation on the annotated class
		String catalog = "";
		List<UniqueConstraintHolder> uniqueConstraints = new ArrayList<UniqueConstraintHolder>();
		if ( clazzToProcess.isAnnotationPresent( javax.persistence.Table.class ) ) {
			javax.persistence.Table tabAnn = clazzToProcess.getAnnotation( javax.persistence.Table.class );
			table = tabAnn.name();
			schema = tabAnn.schema();
			catalog = tabAnn.catalog();
			uniqueConstraints = TableBinder.buildUniqueConstraintHolders( tabAnn.uniqueConstraints() );
		}

		Ejb3JoinColumn[] inheritanceJoinedColumns = makeInheritanceJoinColumns(
				clazzToProcess, mappings, inheritanceState, superEntity
		);
		Ejb3DiscriminatorColumn discriminatorColumn = null;
		if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
			discriminatorColumn = processDiscriminatorProperties(
					clazzToProcess, mappings, inheritanceState, entityBinder
			);
		}

		entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) );
		entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
		entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) );
	    entityBinder.setCache( determineCacheSettings( clazzToProcess, mappings ) );

		//Filters are not allowed on subclasses
		if ( !inheritanceState.hasParents() ) {
			bindFilters( clazzToProcess, entityBinder, mappings );
		}

		entityBinder.bindEntity();

		if ( inheritanceState.hasTable() ) {
			Check checkAnn = clazzToProcess.getAnnotation( Check.class );
			String constraints = checkAnn == null ?
					null :
					checkAnn.constraints();
			entityBinder.bindTable(
					schema, catalog, table, uniqueConstraints,
					constraints, inheritanceState.hasDenormalizedTable() ?
							superEntity.getTable() :
							null
			);
		}
		else {
			if ( clazzToProcess.isAnnotationPresent( Table.class ) ) {
				log.warn(
						"Illegal use of @Table in a subclass of a SINGLE_TABLE hierarchy: " + clazzToProcess
								.getName()
				);
			}
		}

		PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(
				clazzToProcess,
				persistentClass,
				entityBinder, mappings, inheritanceStatePerClass
		);

		javax.persistence.SecondaryTable secTabAnn = clazzToProcess.getAnnotation(
				javax.persistence.SecondaryTable.class
		);
		javax.persistence.SecondaryTables secTabsAnn = clazzToProcess.getAnnotation(
				javax.persistence.SecondaryTables.class
		);
		entityBinder.firstLevelSecondaryTablesBinding( secTabAnn, secTabsAnn );

		OnDelete onDeleteAnn = clazzToProcess.getAnnotation( OnDelete.class );
		boolean onDeleteAppropriate = false;
		if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) && inheritanceState.hasParents() ) {
			onDeleteAppropriate = true;
			final JoinedSubclass jsc = ( JoinedSubclass ) persistentClass;
			if ( persistentClass.getEntityPersisterClass() == null ) {
				persistentClass.getRootClass().setEntityPersisterClass( JoinedSubclassEntityPersister.class );
			}
			SimpleValue key = new DependantValue( mappings, jsc.getTable(), jsc.getIdentifier() );
			jsc.setKey( key );
			ForeignKey fk = clazzToProcess.getAnnotation( ForeignKey.class );
			if ( fk != null && !BinderHelper.isEmptyAnnotationValue( fk.name() ) ) {
				key.setForeignKeyName( fk.name() );
			}
			if ( onDeleteAnn != null ) {
				key.setCascadeDeleteEnabled( OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) );
			}
			else {
				key.setCascadeDeleteEnabled( false );
			}
			//we are never in a second pass at that stage, so queue it
			SecondPass sp = new JoinedSubclassFkSecondPass( jsc, inheritanceJoinedColumns, key, mappings );
			mappings.addSecondPass( sp );
			mappings.addSecondPass( new CreateKeySecondPass( jsc ) );

		}
		else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
			if ( inheritanceState.hasParents() ) {
				if ( persistentClass.getEntityPersisterClass() == null ) {
					persistentClass.getRootClass().setEntityPersisterClass( SingleTableEntityPersister.class );
				}
			}
			else {
				if ( inheritanceState.hasSiblings() || !discriminatorColumn.isImplicit() ) {
					//need a discriminator column
					bindDiscriminatorToPersistentClass(
							( RootClass ) persistentClass,
							discriminatorColumn,
							entityBinder.getSecondaryTables(),
							propertyHolder,
							mappings
					);
					entityBinder.bindDiscriminatorValue();//bind it again since the type might have changed
				}
			}
		}
		else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.getType() ) ) {
			if ( inheritanceState.hasParents() ) {
				if ( persistentClass.getEntityPersisterClass() == null ) {
					persistentClass.getRootClass().setEntityPersisterClass( UnionSubclassEntityPersister.class );
				}
			}
		}
		if ( onDeleteAnn != null && !onDeleteAppropriate ) {
			log.warn(
					"Inapropriate use of @OnDelete on entity, annotation ignored: {}", propertyHolder.getEntityName()
			);
		}

		// try to find class level generators
		HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );

		// check properties
		final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
		inheritanceState.postProcess( persistentClass, entityBinder );

		final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
				&& inheritanceState.hasParents();
		Set<String> idPropertiesIfIdClass = new HashSet<String>();
		boolean isIdClass = mapAsIdClass(
				inheritanceStatePerClass,
				inheritanceState,
				persistentClass,
				entityBinder,
				propertyHolder,
				elementsToProcess,
				idPropertiesIfIdClass,
				mappings
		);

		if ( !isIdClass ) {
			entityBinder.setWrapIdsInEmbeddedComponents( elementsToProcess.getIdPropertyCount() > 1 );
		}

		processIdPropertiesIfNotAlready(
				inheritanceStatePerClass,
				mappings,
				persistentClass,
				entityBinder,
				propertyHolder,
				classGenerators,
				elementsToProcess,
				subclassAndSingleTableStrategy,
				idPropertiesIfIdClass
		);

		if ( !inheritanceState.hasParents() ) {
			final RootClass rootClass = ( RootClass ) persistentClass;
			mappings.addSecondPass( new CreateKeySecondPass( rootClass ) );
		}
		else {
			superEntity.addSubclass( ( Subclass ) persistentClass );
		}

		mappings.addClass( persistentClass );

		//Process secondary tables and complementary definitions (ie o.h.a.Table)
		mappings.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, clazzToProcess ) );

		//add process complementary Table definition (index & all)
		entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
		entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
		
		CommentBinder.bindTableComment(clazzToProcess, persistentClass);//繫結表名註釋
		

	}

	// parse everything discriminator column relevant in case of single table inheritance
	private static Ejb3DiscriminatorColumn processDiscriminatorProperties(XClass clazzToProcess, Mappings mappings, InheritanceState inheritanceState, EntityBinder entityBinder) {
		Ejb3DiscriminatorColumn discriminatorColumn = null;
		javax.persistence.DiscriminatorColumn discAnn = clazzToProcess.getAnnotation(
				javax.persistence.DiscriminatorColumn.class
		);
		DiscriminatorType discriminatorType = discAnn != null ?
				discAnn.discriminatorType() :
				DiscriminatorType.STRING;

		org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = clazzToProcess.getAnnotation(
				org.hibernate.annotations.DiscriminatorFormula.class
		);
		if ( !inheritanceState.hasParents() ) {
			discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
					discriminatorType, discAnn, discFormulaAnn, mappings
			);
		}
		if ( discAnn != null && inheritanceState.hasParents() ) {
			log.warn(
					"Discriminator column has to be defined in the root entity, it will be ignored in subclass: {}",
					clazzToProcess.getName()
			);
		}

		String discrimValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class ) ?
				clazzToProcess.getAnnotation( DiscriminatorValue.class ).value() :
				null;
		entityBinder.setDiscriminatorValue( discrimValue );

		if ( clazzToProcess.isAnnotationPresent( ForceDiscriminator.class ) ) {
			log.warn( "@ForceDiscriminator is deprecated use @DiscriminatorOptions instead." );
			entityBinder.setForceDiscriminator( true );
		}

		DiscriminatorOptions discriminatorOptions = clazzToProcess.getAnnotation( DiscriminatorOptions.class );
		if ( discriminatorOptions != null) {
			entityBinder.setForceDiscriminator( discriminatorOptions.force() );
			entityBinder.setInsertableDiscriminator( discriminatorOptions.insert() );
		}

		return discriminatorColumn;
	}

	private static void processIdPropertiesIfNotAlready(
			Map<XClass, InheritanceState> inheritanceStatePerClass,
			Mappings mappings,
			PersistentClass persistentClass,
			EntityBinder entityBinder,
			PropertyHolder propertyHolder,
			HashMap<String, IdGenerator> classGenerators,
			InheritanceState.ElementsToProcess elementsToProcess,
			boolean subclassAndSingleTableStrategy,
			Set<String> idPropertiesIfIdClass) {
		Set<String> missingIdProperties = new HashSet<String>( idPropertiesIfIdClass );
		for ( PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) {
			String propertyName = propertyAnnotatedElement.getPropertyName();
			if ( !idPropertiesIfIdClass.contains( propertyName ) ) {
				processElementAnnotations(
						propertyHolder,
						subclassAndSingleTableStrategy ?
								Nullability.FORCED_NULL :
								Nullability.NO_CONSTRAINT,
						propertyAnnotatedElement, classGenerators, entityBinder,
						false, false, false, mappings, inheritanceStatePerClass
				);
			}
			else {
				missingIdProperties.remove( propertyName );
			}
		}

		if ( missingIdProperties.size() != 0 ) {
			StringBuilder missings = new StringBuilder();
			for ( String property : missingIdProperties ) {
				missings.append( property ).append( ", " );
			}
			throw new AnnotationException(
					"Unable to find properties ("
							+ missings.substring( 0, missings.length() - 2 )
							+ ") in entity annotated with @IdClass:" + persistentClass.getEntityName()
			);
		}
	}

	private static boolean mapAsIdClass(
			Map<XClass, InheritanceState> inheritanceStatePerClass,
			InheritanceState inheritanceState,
			PersistentClass persistentClass,
			EntityBinder entityBinder,
			PropertyHolder propertyHolder,
			InheritanceState.ElementsToProcess elementsToProcess,
			Set<String> idPropertiesIfIdClass,
			Mappings mappings) {
		/*
		 * We are looking for @IdClass
		 * In general we map the id class as identifier using the mapping metadata of the main entity's properties
		 * and we create an identifier mapper containing the id properties of the main entity
		 *
		 * In JPA 2, there is a shortcut if the id class is the Pk of the associated class pointed to by the id
		 * it ought to be treated as an embedded and not a real IdClass (at least in the Hibernate's internal way
		 */
		XClass classWithIdClass = inheritanceState.getClassWithIdClass( false );
		if ( classWithIdClass != null ) {
			IdClass idClass = classWithIdClass.getAnnotation( IdClass.class );
			XClass compositeClass = mappings.getReflectionManager().toXClass( idClass.value() );
			PropertyData inferredData = new PropertyPreloadedData(
					entityBinder.getPropertyAccessType(), "id", compositeClass
			);
			PropertyData baseInferredData = new PropertyPreloadedData(
					entityBinder.getPropertyAccessType(), "id", classWithIdClass
			);
			AccessType propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
			//In JPA 2, there is a shortcut if the IdClass is the Pk of the associated class pointed to by the id
			//it ought to be treated as an embedded and not a real IdClass (at least in the Hibernate's internal way
			final boolean isFakeIdClass = isIdClassPkOfTheAssociatedEntity(
					elementsToProcess,
					compositeClass,
					inferredData,
					baseInferredData,
					propertyAccessor,
					inheritanceStatePerClass,
					mappings
			);

			if ( isFakeIdClass ) {
				return false;
			}

			boolean isComponent = true;
			String generatorType = "assigned";
			String generator = BinderHelper.ANNOTATION_STRING_DEFAULT;

			boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
			entityBinder.setIgnoreIdAnnotations( true );
			propertyHolder.setInIdClass( true );
			bindIdClass(
					generatorType,
					generator,
					inferredData,
					baseInferredData,
					null,
					propertyHolder,
					isComponent,
					propertyAccessor,
					entityBinder,
					true,
					false,
					mappings,
					inheritanceStatePerClass
			);
			propertyHolder.setInIdClass( null );
			inferredData = new PropertyPreloadedData(
					propertyAccessor, "_identifierMapper", compositeClass
			);
			Component mapper = fillComponent(
					propertyHolder,
					inferredData,
					baseInferredData,
					propertyAccessor,
					false,
					entityBinder,
					true,
					true,
					false,
					mappings,
					inheritanceStatePerClass
			);
			entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations );
			persistentClass.setIdentifierMapper( mapper );

			//If id definition is on a mapped superclass, update the mapping
			final org.hibernate.mapping.MappedSuperclass superclass =
					BinderHelper.getMappedSuperclassOrNull(
							inferredData.getDeclaringClass(),
							inheritanceStatePerClass,
							mappings
					);
			if ( superclass != null ) {
				superclass.setDeclaredIdentifierMapper( mapper );
			}
			else {
				//we are for sure on the entity
				persistentClass.setDeclaredIdentifierMapper( mapper );
			}

			Property property = new Property();
			property.setName( "_identifierMapper" );
			property.setNodeName( "id" );
			property.setUpdateable( false );
			property.setInsertable( false );
			property.setValue( mapper );
			property.setPropertyAccessorName( "embedded" );
			persistentClass.addProperty( property );
			entityBinder.setIgnoreIdAnnotations( true );

			Iterator properties = mapper.getPropertyIterator();
			while ( properties.hasNext() ) {
				idPropertiesIfIdClass.add( ( ( Property ) properties.next() ).getName() );
			}
			return true;
		}
		else {
			return false;
		}
	}

	private static boolean isIdClassPkOfTheAssociatedEntity(
			InheritanceState.ElementsToProcess elementsToProcess,
			XClass compositeClass,
			PropertyData inferredData,
			PropertyData baseInferredData,
			AccessType propertyAccessor,
			Map<XClass, InheritanceState> inheritanceStatePerClass,
			Mappings mappings) {
		if ( elementsToProcess.getIdPropertyCount() == 1 ) {
			final PropertyData idPropertyOnBaseClass = getUniqueIdPropertyFromBaseClass(
					inferredData, baseInferredData, propertyAccessor, mappings
			);
			final InheritanceState state = inheritanceStatePerClass.get( idPropertyOnBaseClass.getClassOrElement() );
			if ( state == null ) {
				return false; //while it is likely a user error, let's consider it is something that might happen
			}
			final XClass associatedClassWithIdClass = state.getClassWithIdClass( true );
			if ( associatedClassWithIdClass == null ) {
				//we cannot know for sure here unless we try and find the @EmbeddedId
				//Let's not do this thorough checking but do some extra validation
				final XProperty property = idPropertyOnBaseClass.getProperty();
				return property.isAnnotationPresent( ManyToOne.class )
						|| property.isAnnotationPresent( OneToOne.class );

			}
			else {
				final XClass idClass = mappings.getReflectionManager().toXClass(
						associatedClassWithIdClass.getAnnotation( IdClass.class ).value()
				);
				return idClass.equals( compositeClass );
			}
		}
		else {
			return false;
		}
	}

	private static Cache determineCacheSettings(XClass clazzToProcess, Mappings mappings) {
		Cache cacheAnn = clazzToProcess.getAnnotation( Cache.class );
		if ( cacheAnn != null ) {
			return cacheAnn;
		}

		Cacheable cacheableAnn = clazzToProcess.getAnnotation( Cacheable.class );
		SharedCacheMode mode = determineSharedCacheMode( mappings );
		switch ( mode ) {
			case ALL: {
				cacheAnn = buildCacheMock( clazzToProcess.getName(), mappings );
				break;
			}
			case ENABLE_SELECTIVE: {
				if ( cacheableAnn != null && cacheableAnn.value() ) {
					cacheAnn = buildCacheMock( clazzToProcess.getName(), mappings );
				}
				break;
			}
			case DISABLE_SELECTIVE: {
				if ( cacheableAnn == null || cacheableAnn.value() ) {
					cacheAnn = buildCacheMock( clazzToProcess.getName(), mappings );
				}
				break;
			}
			default: {
				// treat both NONE and UNSPECIFIED the same
				break;
			}
		}
		return cacheAnn;
	}

	private static SharedCacheMode determineSharedCacheMode(Mappings mappings) {
		SharedCacheMode mode;
		final Object value = mappings.getConfigurationProperties().get( "javax.persistence.sharedCache.mode" );
		if ( value == null ) {
			log.debug( "no value specified for 'javax.persistence.sharedCache.mode'; using UNSPECIFIED" );
			mode = SharedCacheMode.UNSPECIFIED;
		}
		else {
			if ( SharedCacheMode.class.isInstance( value ) ) {
				mode = ( SharedCacheMode ) value;
			}
			else {
				try {
					mode = SharedCacheMode.valueOf( value.toString() );
				}
				catch ( Exception e ) {
					log.debug(
							"Unable to resolve given mode name [" + value.toString()
									+ "]; using UNSPECIFIED : " + e.toString()
					);
					mode = SharedCacheMode.UNSPECIFIED;
				}
			}
		}
		return mode;
	}

	private static Cache buildCacheMock(String region, Mappings mappings) {
		return new LocalCacheAnnotationImpl( region, determineCacheConcurrencyStrategy( mappings ) );
	}

	private static CacheConcurrencyStrategy DEFAULT_CACHE_CONCURRENCY_STRATEGY;

	static void prepareDefaultCacheConcurrencyStrategy(Properties properties) {
		if ( DEFAULT_CACHE_CONCURRENCY_STRATEGY != null ) {
			log.trace( "Default cache concurrency strategy already defined" );
			return;
		}

		if ( !properties.containsKey( Configuration.DEFAULT_CACHE_CONCURRENCY_STRATEGY ) ) {
			log.trace( "Given properties did not contain any default cache concurrency strategy setting" );
			return;
		}

		final String strategyName = properties.getProperty( Configuration.DEFAULT_CACHE_CONCURRENCY_STRATEGY );
		log.trace( "Discovered default cache concurrency strategy via config [" + strategyName + "]" );
		CacheConcurrencyStrategy strategy = CacheConcurrencyStrategy.parse( strategyName );
		if ( strategy == null ) {
			log.trace( "Discovered default cache concurrency strategy specified nothing" );
			return;
		}

		log.debug( "Setting default cache concurrency strategy via config [" + strategy.name() + "]" );
		DEFAULT_CACHE_CONCURRENCY_STRATEGY = strategy;
	}

	private static CacheConcurrencyStrategy determineCacheConcurrencyStrategy(Mappings mappings) {
		if ( DEFAULT_CACHE_CONCURRENCY_STRATEGY == null ) {
			final RegionFactory cacheRegionFactory = SettingsFactory.createRegionFactory(
					mappings.getConfigurationProperties(), true
			);
			DEFAULT_CACHE_CONCURRENCY_STRATEGY = CacheConcurrencyStrategy.fromAccessType( cacheRegionFactory.getDefaultAccessType() );
		}
		return DEFAULT_CACHE_CONCURRENCY_STRATEGY;
	}

	@SuppressWarnings({ "ClassExplicitlyAnnotation" })
	private static class LocalCacheAnnotationImpl implements Cache {
		private final String region;
		private final CacheConcurrencyStrategy usage;

		private LocalCacheAnnotationImpl(String region, CacheConcurrencyStrategy usage) {
			this.region = region;
			this.usage = usage;
		}

		public CacheConcurrencyStrategy usage() {
			return usage;
		}

		public String region() {
			return region;
		}

		public String include() {
			return "all";
		}

		public Class<? extends Annotation> annotationType() {
			return Cache.class;
		}
	}

	private static PersistentClass makePersistentClass(InheritanceState inheritanceState, PersistentClass superEntity) {
		//we now know what kind of persistent entity it is
		PersistentClass persistentClass;
		//create persistent class
		if ( !inheritanceState.hasParents() ) {
			persistentClass = new RootClass();
		}
		else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
			persistentClass = new SingleTableSubclass( superEntity );
		}
		else if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
			persistentClass = new JoinedSubclass( superEntity );
		}
		else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.getType() ) ) {
			persistentClass = new UnionSubclass( superEntity );
		}
		else {
			throw new AssertionFailure( "Unknown inheritance type: " + inheritanceState.getType() );
		}
		return persistentClass;
	}

	private static Ejb3JoinColumn[] makeInheritanceJoinColumns(
			XClass clazzToProcess,
			Mappings mappings,
			InheritanceState inheritanceState,
			PersistentClass superEntity) {
		Ejb3JoinColumn[] inheritanceJoinedColumns = null;
		final boolean hasJoinedColumns = inheritanceState.hasParents()
				&& InheritanceType.JOINED.equals( inheritanceState.getType() );
		if ( hasJoinedColumns ) {
			//@Inheritance(JOINED) subclass need to link back to the super entity
			PrimaryKeyJoinColumns jcsAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
			boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
			if ( explicitInheritanceJoinedColumns ) {
				int nbrOfInhJoinedColumns = jcsAnn.value().length;
				PrimaryKeyJoinColumn jcAnn;
				inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
				for ( int colIndex = 0; colIndex < nbrOfInhJoinedColumns; colIndex++ ) {
					jcAnn = jcsAnn.value()[colIndex];
					inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
							jcAnn, null, superEntity.getIdentifier(),
							( Map<String, Join> ) null, ( PropertyHolder ) null, mappings
					);
				}
			}
			else {
				PrimaryKeyJoinColumn jcAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
				inheritanceJoinedColumns = new Ejb3JoinColumn[1];
				inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(
						jcAnn, null, superEntity.getIdentifier(),
						( Map<String, Join> ) null, ( PropertyHolder ) null, mappings
				);
			}
			log.trace( "Subclass joined column(s) created" );
		}
		else {
			if ( clazzToProcess.isAnnotationPresent( PrimaryKeyJoinColumns.class )
					|| clazzToProcess.isAnnotationPresent( PrimaryKeyJoinColumn.class ) ) {
				log.warn( "Root entity should not hold an PrimaryKeyJoinColum(s), will be ignored" );
			}
		}
		return inheritanceJoinedColumns;
	}

	private static PersistentClass getSuperEntity(XClass clazzToProcess, Map<XClass, InheritanceState> inheritanceStatePerClass, Mappings mappings, InheritanceState inheritanceState) {
		InheritanceState superEntityState = InheritanceState.getInheritanceStateOfSuperEntity(
				clazzToProcess, inheritanceStatePerClass
		);
		PersistentClass superEntity = superEntityState != null ?
				mappings.getClass(
						superEntityState.getClazz().getName()
				) :
				null;
		if ( superEntity == null ) {
			//check if superclass is not a potential persistent class
			if ( inheritanceState.hasParents() ) {
				throw new AssertionFailure(
						"Subclass has to be binded after it's mother class: "
								+ superEntityState.getClazz().getName()
				);
			}
		}
		return superEntity;
	}

	private static boolean isEntityClassType(XClass clazzToProcess, AnnotatedClassType classType) {
		if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) //will be processed by their subentities
				|| AnnotatedClassType.NONE.equals( classType ) //to be ignored
				|| AnnotatedClassType.EMBEDDABLE.equals( classType ) //allow embeddable element declaration
				) {
			if ( AnnotatedClassType.NONE.equals( classType )
					&& clazzToProcess.isAnnotationPresent( org.hibernate.annotations.Entity.class ) ) {
				log.warn(
						"Class annotated @org.hibernate.annotations.Entity but not javax.persistence.Entity "
								+ "(most likely a user error): {}", clazzToProcess.getName()
				);
			}
			return false;
		}

		if ( !classType.equals( AnnotatedClassType.ENTITY ) ) {
			throw new AnnotationException(
					"Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: " + clazzToProcess
							.getName()
			);
		}

		return true;
	}

	/*
	 * Process the filters defined on the given class, as well as all filters defined
	 * on the MappedSuperclass(s) in the inheritance hierarchy
	 */

	private static void bindFilters(XClass annotatedClass, EntityBinder entityBinder,
									Mappings mappings) {

		bindFilters( annotatedClass, entityBinder );

		XClass classToProcess = annotatedClass.getSuperclass();
		while ( classToProcess != null ) {
			AnnotatedClassType classType = mappings.getClassType( classToProcess );
			if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) ) {
				bindFilters( classToProcess, entityBinder );
			}
			classToProcess = classToProcess.getSuperclass();
		}

	}

	private static void bindFilters(XAnnotatedElement annotatedElement, EntityBinder entityBinder) {

		Filters filtersAnn = annotatedElement.getAnnotation( Filters.class );
		if ( filtersAnn != null ) {
			for ( Filter filter : filtersAnn.value() ) {
				entityBinder.addFilter( filter.name(), filter.condition() );
			}
		}

		Filter filterAnn = annotatedElement.getAnnotation( Filter.class );
		if ( filterAnn != null ) {
			entityBinder.addFilter( filterAnn.name(), filterAnn.condition() );
		}
	}

	private static void bindFilterDefs(XAnnotatedElement annotatedElement, Mappings mappings) {
		FilterDef defAnn = annotatedElement.getAnnotation( FilterDef.class );
		FilterDefs defsAnn = annotatedElement.getAnnotation( FilterDefs.class );
		if ( defAnn != null ) {
			bindFilterDef( defAnn, mappings );
		}
		if ( defsAnn != null ) {
			for ( FilterDef def : defsAnn.value() ) {
				bindFilterDef( def, mappings );
			}
		}
	}

	private static void bindFilterDef(FilterDef defAnn, Mappings mappings) {
		Map<String, org.hibernate.type.Type> params = new HashMap<String, org.hibernate.type.Type>();
		for ( ParamDef param : defAnn.parameters() ) {
			params.put( param.name(), mappings.getTypeResolver().heuristicType( param.type() ) );
		}
		FilterDefinition def = new FilterDefinition( defAnn.name(), defAnn.defaultCondition(), params );
		log.info( "Binding filter definition: {}", def.getFilterName() );
		mappings.addFilterDefinition( def );
	}

	private static void bindTypeDefs(XAnnotatedElement annotatedElement, Mappings mappings) {
		TypeDef defAnn = annotatedElement.getAnnotation( TypeDef.class );
		TypeDefs defsAnn = annotatedElement.getAnnotation( TypeDefs.class );
		if ( defAnn != null ) {
			bindTypeDef( defAnn, mappings );
		}
		if ( defsAnn != null ) {
			for ( TypeDef def : defsAnn.value() ) {
				bindTypeDef( def, mappings );
			}
		}
	}

	private static void bindFetchProfiles(XAnnotatedElement annotatedElement, Mappings mappings) {
		FetchProfile fetchProfileAnnotation = annotatedElement.getAnnotation( FetchProfile.class );
		FetchProfiles fetchProfileAnnotations = annotatedElement.getAnnotation( FetchProfiles.class );
		if ( fetchProfileAnnotation != null ) {
			bindFetchProfile( fetchProfileAnnotation, mappings );
		}
		if ( fetchProfileAnnotations != null ) {
			for ( FetchProfile profile : fetchProfileAnnotations.value() ) {
				bindFetchProfile( profile, mappings );
			}
		}
	}

	private static void bindFetchProfile(FetchProfile fetchProfileAnnotation, Mappings mappings) {
		for ( FetchProfile.FetchOverride fetch : fetchProfileAnnotation.fetchOverrides() ) {
			org.hibernate.annotations.FetchMode mode = fetch.mode();
			if ( !mode.equals( org.hibernate.annotations.FetchMode.JOIN ) ) {
				throw new MappingException( "Only FetchMode.JOIN is currently supported" );
			}

			SecondPass sp = new VerifyFetchProfileReferenceSecondPass( fetchProfileAnnotation.name(), fetch, mappings );
			mappings.addSecondPass( sp );
		}
	}

	private static void bindTypeDef(TypeDef defAnn, Mappings mappings) {
		Properties params = new Properties();
		for ( Parameter param : defAnn.parameters() ) {
			params.setProperty( param.name(), param.value() );
		}

		if ( BinderHelper.isEmptyAnnotationValue( defAnn.name() ) && defAnn.defaultForType().equals( void.class ) ) {
			throw new AnnotationException(
					"Either name or defaultForType (or both) attribute should be set in TypeDef having typeClass " +
							defAnn.typeClass().getName()
			);
		}

		if ( !BinderHelper.isEmptyAnnotationValue( defAnn.name() ) ) {
			log.info( "Binding type definition: {}", defAnn.name() );
			mappings.addTypeDef( defAnn.name(), defAnn.typeClass().getName(), params );
		}
		if ( !defAnn.defaultForType().equals( void.class ) ) {
			log.info( "Binding type definition: {}", defAnn.defaultForType().getName() );
			mappings.addTypeDef( defAnn.defaultForType().getName(), defAnn.typeClass().getName(), params );
		}

	}


	private static void bindDiscriminatorToPersistentClass(
			RootClass rootClass,
			Ejb3DiscriminatorColumn discriminatorColumn,
			Map<String, Join> secondaryTables,
			PropertyHolder propertyHolder,
			Mappings mappings) {
		if ( rootClass.getDiscriminator() == null ) {
			if ( discriminatorColumn == null ) {
				throw new AssertionFailure( "discriminator column should have been built" );
			}
			discriminatorColumn.setJoins( secondaryTables );
			discriminatorColumn.setPropertyHolder( propertyHolder );
			SimpleValue discrim = new SimpleValue( mappings, rootClass.getTable() );
			rootClass.setDiscriminator( discrim );
			discriminatorColumn.linkWithValue( discrim );
			discrim.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
			rootClass.setPolymorphic( true );
			log.trace( "Setting discriminator for entity {}", rootClass.getEntityName() );
		}
	}

	/**
	 * @param elements List of {@code ProperyData} instances
	 * @param defaultAccessType The default value access strategy which has to be used in case no explicit local access
	 * strategy is used
	 * @param propertyContainer Metadata about a class and its properties
	 * @param mappings Mapping meta data
	 *
	 * @return the number of id properties found while iterating the elements of {@code annotatedClass} using
	 *         the determined access strategy, {@code false} otherwise.
	 */
	static int addElementsOfClass(
			List<PropertyData> elements,
			AccessType defaultAccessType,
			PropertyContainer propertyContainer,
			Mappings mappings) {
		int idPropertyCounter = 0;
		AccessType accessType = defaultAccessType;

		if ( propertyContainer.hasExplicitAccessStrategy() ) {
			accessType = propertyContainer.getExplicitAccessStrategy();
		}

		Collection<XProperty> properties = propertyContainer.getProperties( accessType );
		for ( XProperty p : properties ) {
			final int currentIdPropertyCounter = addProperty(
					propertyContainer, p, elements, accessType.getType(), mappings
			);
			idPropertyCounter += currentIdPropertyCounter;
		}
		return idPropertyCounter;
	}

	private static int addProperty(
			PropertyContainer propertyContainer,
			XProperty property,
			List<PropertyData> annElts,
			String propertyAccessor,
			Mappings mappings) {
		final XClass declaringClass = propertyContainer.getDeclaringClass();
		final XClass entity = propertyContainer.getEntityAtStake();
		int idPropertyCounter = 0;
		PropertyData propertyAnnotatedElement = new PropertyInferredData(
				declaringClass, property, propertyAccessor,
				mappings.getReflectionManager()
		);

		/*
		 * put element annotated by @Id in front
		 * since it has to be parsed before any association by Hibernate
		 */
		final XAnnotatedElement element = propertyAnnotatedElement.getProperty();
		if ( element.isAnnotationPresent( Id.class ) || element.isAnnotationPresent( EmbeddedId.class ) ) {
			annElts.add( 0, propertyAnnotatedElement );
			/**
			 * The property must be put in hibernate.properties as it's a system wide property. Fixable?
			 * TODO support true/false/default on the property instead of present / not present
			 * TODO is @Column mandatory?
			 * TODO add method support
			 */
			if ( mappings.isSpecjProprietarySyntaxEnabled() ) {
				if ( element.isAnnotationPresent( Id.class ) && element.isAnnotationPresent( Column.class ) ) {
					String columnName = element.getAnnotation( Column.class ).name();
					for ( XProperty prop : declaringClass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
						if ( prop.isAnnotationPresent( JoinColumn.class )
								&& prop.getAnnotation( JoinColumn.class ).name().equals( columnName )
								&& !prop.isAnnotationPresent( MapsId.class ) ) {
							//create a PropertyData fpr the specJ property holding the mapping
							PropertyData specJPropertyData = new PropertyInferredData(
									declaringClass,  //same dec
									prop, // the actual @XToOne property
									propertyAccessor, //TODO we should get the right accessor but the same as id would do
									mappings.getReflectionManager()
							);
							mappings.addPropertyAnnotatedWithMapsIdSpecj( entity, specJPropertyData, element.toString() );
						}
					}
				}
			}

			if ( element.isAnnotationPresent( ManyToOne.class ) || element.isAnnotationPresent( OneToOne.class ) ) {
				mappings.addToOneAndIdProperty( entity, propertyAnnotatedElement );
			}
			idPropertyCounter++;
		}
		else {
			annElts.add( propertyAnnotatedElement );
		}
		if ( element.isAnnotationPresent( MapsId.class ) ) {
			mappings.addPropertyAnnotatedWithMapsId( entity, propertyAnnotatedElement );
		}

		return idPropertyCounter;
	}

	/*
	 * Process annotation of a particular property
	 */

	private static void processElementAnnotations(
			PropertyHolder propertyHolder,
			Nullability nullability,
			PropertyData inferredData,
			HashMap<String, IdGenerator> classGenerators,
			EntityBinder entityBinder,
			boolean isIdentifierMapper,
			boolean isComponentEmbedded,
			boolean inSecondPass,
			Mappings mappings,
			Map<XClass, InheritanceState> inheritanceStatePerClass) throws MappingException {
		/**
		 * inSecondPass can only be used to apply right away the second pass of a composite-element
		 * Because it's a value type, there is no bidirectional association, hence second pass
		 * ordering does not matter
		 */

		log.trace(
				"Processing annotations of {}.{}", propertyHolder.getEntityName(), inferredData.getPropertyName()
		);

		final XProperty property = inferredData.getProperty();
		if ( property.isAnnotationPresent( Parent.class ) ) {
			if ( propertyHolder.isComponent() ) {
				propertyHolder.setParentProperty( property.getName() );
			}
			else {
				throw new AnnotationException(
						"@Parent cannot be applied outside an embeddable object: "
								+ BinderHelper.getPath( propertyHolder, inferredData )
				);
			}
			return;
		}

		ColumnsBuilder columnsBuilder = new ColumnsBuilder(
				propertyHolder, nullability, property, inferredData, entityBinder, mappings
		).extractMetadata();
		Ejb3Column[] columns = columnsBuilder.getColumns();
		Ejb3JoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
		
		
		final XClass returnedClass = inferredData.getClassOrElement();

		//prepare PropertyBinder
		PropertyBinder propertyBinder = new PropertyBinder();
		propertyBinder.setName( inferredData.getPropertyName() );
		propertyBinder.setReturnedClassName( inferredData.getTypeName() );
		propertyBinder.setAccessType( inferredData.getDefaultAccess() );
		propertyBinder.setHolder( propertyHolder );
		propertyBinder.setProperty( property );
		propertyBinder.setReturnedClass( inferredData.getPropertyClass() );
		propertyBinder.setMappings( mappings );
		if ( isIdentifierMapper ) {
			propertyBinder.setInsertable( false );
			propertyBinder.setUpdatable( false );
		}
		propertyBinder.setDeclaringClass( inferredData.getDeclaringClass() );
		propertyBinder.setEntityBinder( entityBinder );
		propertyBinder.setInheritanceStatePerClass( inheritanceStatePerClass );

		boolean isId = !entityBinder.isIgnoreIdAnnotations() &&
				( property.isAnnotationPresent( Id.class )
						|| property.isAnnotationPresent( EmbeddedId.class ) );
		propertyBinder.setId( isId );

		if ( property.isAnnotationPresent( Version.class ) ) {
			if ( isIdentifierMapper ) {
				throw new AnnotationException(
						"@IdClass class should not have @Version property"
				);
			}
			if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
				throw new AnnotationException(
						"Unable to define/override @Version on a subclass: "
								+ propertyHolder.getEntityName()
				);
			}
			if ( !propertyHolder.isEntity() ) {
				throw new AnnotationException(
						"Unable to define @Version on an embedded class: "
								+ propertyHolder.getEntityName()
				);
			}
			log.trace( "{} is a version property", inferredData.getPropertyName() );
			RootClass rootClass = ( RootClass ) propertyHolder.getPersistentClass();
			propertyBinder.setColumns( columns );
			Property prop = propertyBinder.makePropertyValueAndBind();
			setVersionInformation( property, propertyBinder );
			rootClass.setVersion( prop );

			//If version is on a mapped superclass, update the mapping
			final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
					inferredData.getDeclaringClass(),
					inheritanceStatePerClass,
					mappings
			);
			if ( superclass != null ) {
				superclass.setDeclaredVersion( prop );
			}
			else {
				//we know the property is on the actual entity
				rootClass.setDeclaredVersion( prop );
			}

			SimpleValue simpleValue = ( SimpleValue ) prop.getValue();
			simpleValue.setNullValue( "undefined" );
			rootClass.setOptimisticLockMode( Versioning.OPTIMISTIC_LOCK_VERSION );
			log.trace(
					"Version name: {}, unsavedValue: {}", rootClass.getVersion().getName(),
					( ( SimpleValue ) rootClass.getVersion().getValue() ).getNullValue()
			);
		}
		else {
			final boolean forcePersist = property.isAnnotationPresent( MapsId.class )
					|| property.isAnnotationPresent( Id.class );
			if ( property.isAnnotationPresent( ManyToOne.class ) ) {
				ManyToOne ann = property.getAnnotation( ManyToOne.class );

				//check validity
				if ( property.isAnnotationPresent( Column.class )
						|| property.isAnnotationPresent( Columns.class ) ) {
					throw new AnnotationException(
							"@Column(s) not allowed on a @ManyToOne property: "
									+ BinderHelper.getPath( propertyHolder, inferredData )
					);
				}

				Cascade hibernateCascade = property.getAnnotation( Cascade.class );
				NotFound notFound = property.getAnnotation( NotFound.class );
				boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
				OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
				boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
				JoinTable assocTable = propertyHolder.getJoinTable( property );
				if ( assocTable != null ) {
					Join join = propertyHolder.addJoin( assocTable, false );
					for ( Ejb3JoinColumn joinColumn : joinColumns ) {
						joinColumn.setSecondaryTableName( join.getTable().getName() );
					}
				}
				final boolean mandatory = !ann.optional() || forcePersist;
				bindManyToOne(
						getCascadeStrategy( ann.cascade(), hibernateCascade, false, forcePersist ),
						joinColumns,
						!mandatory,
						ignoreNotFound, onDeleteCascade,
						ToOneBinder.getTargetEntity( inferredData, mappings ),
						propertyHolder,
						inferredData, false, isIdentifierMapper,
						inSecondPass, propertyBinder, mappings
				);
			}
			else if ( property.isAnnotationPresent( OneToOne.class ) ) {
				OneToOne ann = property.getAnnotation( OneToOne.class );

				//check validity
				if ( property.isAnnotationPresent( Column.class )
						|| property.isAnnotationPresent( Columns.class ) ) {
					throw new AnnotationException(
							"@Column(s) not allowed on a @OneToOne property