Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]
    1   //$Id: AnnotationBinder.java 11264 2007-03-09 04:24:33Z epbernard $
    2   package org.hibernate.cfg;
    3   
    4   import java.util.ArrayList;
    5   import java.util.EnumSet;
    6   import java.util.HashMap;
    7   import java.util.HashSet;
    8   import java.util.Iterator;
    9   import java.util.List;
   10   import java.util.Map;
   11   import java.util.Properties;
   12   import java.util.Set;
   13   import java.util.Collections;
   14   import java.util.Comparator;
   15   import javax.persistence.Basic;
   16   import javax.persistence.Column;
   17   import javax.persistence.DiscriminatorType;
   18   import javax.persistence.DiscriminatorValue;
   19   import javax.persistence.Embeddable;
   20   import javax.persistence.Embedded;
   21   import javax.persistence.EmbeddedId;
   22   import javax.persistence.Entity;
   23   import javax.persistence.FetchType;
   24   import javax.persistence.GeneratedValue;
   25   import javax.persistence.GenerationType;
   26   import javax.persistence.Id;
   27   import javax.persistence.IdClass;
   28   import javax.persistence.InheritanceType;
   29   import javax.persistence.JoinColumn;
   30   import javax.persistence.JoinColumns;
   31   import javax.persistence.JoinTable;
   32   import javax.persistence.ManyToMany;
   33   import javax.persistence.ManyToOne;
   34   import javax.persistence.MapKey;
   35   import javax.persistence.MappedSuperclass;
   36   import javax.persistence.NamedNativeQueries;
   37   import javax.persistence.NamedNativeQuery;
   38   import javax.persistence.NamedQueries;
   39   import javax.persistence.NamedQuery;
   40   import javax.persistence.OneToMany;
   41   import javax.persistence.OneToOne;
   42   import javax.persistence.PrimaryKeyJoinColumn;
   43   import javax.persistence.PrimaryKeyJoinColumns;
   44   import javax.persistence.SequenceGenerator;
   45   import javax.persistence.SqlResultSetMapping;
   46   import javax.persistence.SqlResultSetMappings;
   47   import javax.persistence.Table;
   48   import javax.persistence.TableGenerator;
   49   import javax.persistence.Transient;
   50   import javax.persistence.Version;
   51   
   52   import org.apache.commons.logging.Log;
   53   import org.apache.commons.logging.LogFactory;
   54   import org.hibernate.AnnotationException;
   55   import org.hibernate.AssertionFailure;
   56   import org.hibernate.FetchMode;
   57   import org.hibernate.MappingException;
   58   import org.hibernate.EntityMode;
   59   import org.hibernate.annotations.AccessType;
   60   import org.hibernate.annotations.BatchSize;
   61   import org.hibernate.annotations.Cache;
   62   import org.hibernate.annotations.Cascade;
   63   import org.hibernate.annotations.CascadeType;
   64   import org.hibernate.annotations.Check;
   65   import org.hibernate.annotations.CollectionId;
   66   import org.hibernate.annotations.CollectionOfElements;
   67   import org.hibernate.annotations.Columns;
   68   import org.hibernate.annotations.Fetch;
   69   import org.hibernate.annotations.Filter;
   70   import org.hibernate.annotations.FilterDef;
   71   import org.hibernate.annotations.FilterDefs;
   72   import org.hibernate.annotations.Filters;
   73   import org.hibernate.annotations.ForeignKey;
   74   import org.hibernate.annotations.Formula;
   75   import org.hibernate.annotations.GenericGenerator;
   76   import org.hibernate.annotations.LazyToOne;
   77   import org.hibernate.annotations.LazyToOneOption;
   78   import org.hibernate.annotations.MapKeyManyToMany;
   79   import org.hibernate.annotations.NotFound;
   80   import org.hibernate.annotations.NotFoundAction;
   81   import org.hibernate.annotations.OnDelete;
   82   import org.hibernate.annotations.OnDeleteAction;
   83   import org.hibernate.annotations.OrderBy;
   84   import org.hibernate.annotations.ParamDef;
   85   import org.hibernate.annotations.Parameter;
   86   import org.hibernate.annotations.Parent;
   87   import org.hibernate.annotations.Proxy;
   88   import org.hibernate.annotations.Sort;
   89   import org.hibernate.annotations.Type;
   90   import org.hibernate.annotations.TypeDef;
   91   import org.hibernate.annotations.TypeDefs;
   92   import org.hibernate.annotations.Where;
   93   import org.hibernate.annotations.Index;
   94   import org.hibernate.annotations.Target;
   95   import org.hibernate.annotations.Tuplizers;
   96   import org.hibernate.annotations.Tuplizer;
   97   import org.hibernate.cfg.annotations.CollectionBinder;
   98   import org.hibernate.cfg.annotations.EntityBinder;
   99   import org.hibernate.cfg.annotations.Nullability;
  100   import org.hibernate.cfg.annotations.PropertyBinder;
  101   import org.hibernate.cfg.annotations.QueryBinder;
  102   import org.hibernate.cfg.annotations.SimpleValueBinder;
  103   import org.hibernate.cfg.annotations.TableBinder;
  104   import org.hibernate.engine.FilterDefinition;
  105   import org.hibernate.engine.Versioning;
  106   import org.hibernate.id.MultipleHiLoPerTableGenerator;
  107   import org.hibernate.id.PersistentIdentifierGenerator;
  108   import org.hibernate.id.SequenceHiLoGenerator;
  109   import org.hibernate.id.TableHiLoGenerator;
  110   import org.hibernate.mapping.Component;
  111   import org.hibernate.mapping.DependantValue;
  112   import org.hibernate.mapping.IdGenerator;
  113   import org.hibernate.mapping.Join;
  114   import org.hibernate.mapping.JoinedSubclass;
  115   import org.hibernate.mapping.PersistentClass;
  116   import org.hibernate.mapping.Property;
  117   import org.hibernate.mapping.RootClass;
  118   import org.hibernate.mapping.SimpleValue;
  119   import org.hibernate.mapping.SingleTableSubclass;
  120   import org.hibernate.mapping.Subclass;
  121   import org.hibernate.mapping.ToOne;
  122   import org.hibernate.mapping.UnionSubclass;
  123   import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
  124   import org.hibernate.persister.entity.SingleTableEntityPersister;
  125   import org.hibernate.persister.entity.UnionSubclassEntityPersister;
  126   import org.hibernate.annotations.common.reflection.ReflectionManager;
  127   import org.hibernate.annotations.common.reflection.XAnnotatedElement;
  128   import org.hibernate.annotations.common.reflection.XClass;
  129   import org.hibernate.annotations.common.reflection.XPackage;
  130   import org.hibernate.annotations.common.reflection.XProperty;
  131   import org.hibernate.type.TypeFactory;
  132   import org.hibernate.util.StringHelper;
  133   
  134   /**
  135    * JSR 175 annotation binder
  136    * Will read the annotation from classes, apply the
  137    * principles of the EJB3 spec and produces the Hibernate
  138    * configuration-time metamodel (the classes in the <tt>mapping</tt>
  139    * package)
  140    *
  141    * @author Emmanuel Bernard
  142    */
  143   public final class AnnotationBinder {
  144   
  145   	/*
  146   	 * Some design description
  147   	 * I tried to remove any link to annotation except from the 2 first level of
  148   	 * method call.
  149   	 * It'll enable to:
  150   	 *   - facilitate annotation overriding
  151   	 *   - mutualize one day xml and annotation binder (probably a dream though)
  152   	 *   - split this huge class in smaller mapping oriented classes
  153   	 *
  154   	 * bindSomething usually create the mapping container and is accessed by one of the 2 first level method
  155   	 * makeSomething usually create the mapping container and is accessed by bindSomething[else]
  156   	 * fillSomething take the container into parameter and fill it.
  157   	 *
  158   	 *
  159   	 */
  160   	private AnnotationBinder() {
  161   	}
  162   
  163   	private static final Log log = LogFactory.getLog( AnnotationBinder.class );
  164   
  165   	public static void bindDefaults(ExtendedMappings mappings) {
  166   		Map defaults = mappings.getReflectionManager().getDefaults();
  167   		{
  168   			List<SequenceGenerator> anns = (List<SequenceGenerator>) defaults.get( SequenceGenerator.class );
  169   			if ( anns != null ) {
  170   				for ( SequenceGenerator ann : anns ) {
  171   					IdGenerator idGen = buildIdGenerator( ann, mappings );
  172   					if ( idGen != null ) mappings.addDefaultGenerator( idGen );
  173   				}
  174   			}
  175   		}
  176   		{
  177   			List<TableGenerator> anns = (List<TableGenerator>) defaults.get( TableGenerator.class );
  178   			if ( anns != null ) {
  179   				for ( TableGenerator ann : anns ) {
  180   					IdGenerator idGen = buildIdGenerator( ann, mappings );
  181   					if ( idGen != null ) mappings.addDefaultGenerator( idGen );
  182   				}
  183   			}
  184   		}
  185   		{
  186   			List<NamedQuery> anns = (List<NamedQuery>) defaults.get( NamedQuery.class );
  187   			if ( anns != null ) {
  188   				for ( NamedQuery ann : anns ) {
  189   					QueryBinder.bindQuery( ann, mappings, true );
  190   				}
  191   			}
  192   		}
  193   		{
  194   			List<NamedNativeQuery> anns = (List<NamedNativeQuery>) defaults.get( NamedNativeQuery.class );
  195   			if ( anns != null ) {
  196   				for ( NamedNativeQuery ann : anns ) {
  197   					QueryBinder.bindNativeQuery( ann, mappings, true );
  198   				}
  199   			}
  200   		}
  201   		{
  202   			List<SqlResultSetMapping> anns = (List<SqlResultSetMapping>) defaults.get( SqlResultSetMapping.class );
  203   			if ( anns != null ) {
  204   				for ( SqlResultSetMapping ann : anns ) {
  205   					QueryBinder.bindSqlResultsetMapping( ann, mappings, true );
  206   				}
  207   			}
  208   		}
  209   	}
  210   
  211   	public static void bindPackage(String packageName, ExtendedMappings mappings) {
  212   		XPackage pckg = null;
  213   		try {
  214   			pckg = mappings.getReflectionManager().packageForName( packageName );
  215   		}
  216   		catch (ClassNotFoundException cnf) {
  217   			log.warn( "Package not found or wo package-info.java: " + packageName );
  218   			return;
  219   		}
  220   		if ( pckg.isAnnotationPresent( SequenceGenerator.class ) ) {
  221   			SequenceGenerator ann = pckg.getAnnotation( SequenceGenerator.class );
  222   			IdGenerator idGen = buildIdGenerator( ann, mappings );
  223   			mappings.addGenerator( idGen );
  224   			log.debug( "Add sequence generator with name: " + idGen.getName() );
  225   		}
  226   		if ( pckg.isAnnotationPresent( TableGenerator.class ) ) {
  227   			TableGenerator ann = pckg.getAnnotation( TableGenerator.class );
  228   			IdGenerator idGen = buildIdGenerator( ann, mappings );
  229   			mappings.addGenerator( idGen );
  230   
  231   		}
  232   		if ( pckg.isAnnotationPresent( GenericGenerator.class ) ) {
  233   			GenericGenerator ann = pckg.getAnnotation( GenericGenerator.class );
  234   			IdGenerator idGen = buildIdGenerator( ann, mappings );
  235   			mappings.addGenerator( idGen );
  236   		}
  237   		bindQueries( pckg, mappings );
  238   		bindFilterDefs( pckg, mappings );
  239   		bindTypeDefs( pckg, mappings );
  240   	}
  241   
  242   	private static void bindQueries(XAnnotatedElement annotatedElement, ExtendedMappings mappings) {
  243   		{
  244   			SqlResultSetMapping ann = annotatedElement.getAnnotation( SqlResultSetMapping.class );
  245   			QueryBinder.bindSqlResultsetMapping( ann, mappings, false );
  246   		}
  247   		{
  248   			SqlResultSetMappings ann = annotatedElement.getAnnotation( SqlResultSetMappings.class );
  249   			if ( ann != null ) {
  250   				for ( SqlResultSetMapping current : ann.value() ) {
  251   					QueryBinder.bindSqlResultsetMapping( current, mappings, false );
  252   				}
  253   			}
  254   		}
  255   		{
  256   			NamedQuery ann = annotatedElement.getAnnotation( NamedQuery.class );
  257   			QueryBinder.bindQuery( ann, mappings, false );
  258   		}
  259   		{
  260   			org.hibernate.annotations.NamedQuery ann = annotatedElement.getAnnotation(
  261   					org.hibernate.annotations.NamedQuery.class
  262   			);
  263   			QueryBinder.bindQuery( ann, mappings );
  264   		}
  265   		{
  266   			NamedQueries ann = annotatedElement.getAnnotation( NamedQueries.class );
  267   			QueryBinder.bindQueries( ann, mappings, false );
  268   		}
  269   		{
  270   			org.hibernate.annotations.NamedQueries ann = annotatedElement.getAnnotation(
  271   					org.hibernate.annotations.NamedQueries.class
  272   			);
  273   			QueryBinder.bindQueries( ann, mappings );
  274   		}
  275   		{
  276   			NamedNativeQuery ann = annotatedElement.getAnnotation( NamedNativeQuery.class );
  277   			QueryBinder.bindNativeQuery( ann, mappings, false );
  278   		}
  279   		{
  280   			org.hibernate.annotations.NamedNativeQuery ann = annotatedElement.getAnnotation(
  281   					org.hibernate.annotations.NamedNativeQuery.class
  282   			);
  283   			QueryBinder.bindNativeQuery( ann, mappings );
  284   		}
  285   		{
  286   			NamedNativeQueries ann = annotatedElement.getAnnotation( NamedNativeQueries.class );
  287   			QueryBinder.bindNativeQueries( ann, mappings, false );
  288   		}
  289   		{
  290   			org.hibernate.annotations.NamedNativeQueries ann = annotatedElement.getAnnotation(
  291   					org.hibernate.annotations.NamedNativeQueries.class
  292   			);
  293   			QueryBinder.bindNativeQueries( ann, mappings );
  294   		}
  295   	}
  296   
  297   	private static IdGenerator buildIdGenerator(java.lang.annotation.Annotation ann, Mappings mappings) {
  298   		IdGenerator idGen = new IdGenerator();
  299   		if ( mappings.getSchemaName() != null ) {
  300   			idGen.addParam( PersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
  301   		}
  302   		if ( mappings.getCatalogName() != null ) {
  303   			idGen.addParam( PersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
  304   		}
  305   		if ( ann == null ) {
  306   			idGen = null;
  307   		}
  308   		else if ( ann instanceof TableGenerator ) {
  309   			TableGenerator tabGen = (TableGenerator) ann;
  310   			idGen.setName( tabGen.name() );
  311   			idGen.setIdentifierGeneratorStrategy( MultipleHiLoPerTableGenerator.class.getName() );
  312   
  313   			if ( !BinderHelper.isDefault( tabGen.table() ) ) {
  314   				idGen.addParam( MultipleHiLoPerTableGenerator.ID_TABLE, tabGen.table() );
  315   			}
  316   			if ( !BinderHelper.isDefault( tabGen.catalog() ) ) {
  317   				idGen.addParam( MultipleHiLoPerTableGenerator.CATALOG, tabGen.catalog() );
  318   			}
  319   			if ( !BinderHelper.isDefault( tabGen.schema() ) ) {
  320   				idGen.addParam( MultipleHiLoPerTableGenerator.SCHEMA, tabGen.schema() );
  321   			}
  322   			//FIXME implements uniqueconstrains
  323   
  324   			if ( !BinderHelper.isDefault( tabGen.pkColumnName() ) ) {
  325   				idGen.addParam( MultipleHiLoPerTableGenerator.PK_COLUMN_NAME, tabGen.pkColumnName() );
  326   			}
  327   			if ( !BinderHelper.isDefault( tabGen.valueColumnName() ) ) {
  328   				idGen.addParam( MultipleHiLoPerTableGenerator.VALUE_COLUMN_NAME, tabGen.valueColumnName() );
  329   			}
  330   			if ( !BinderHelper.isDefault( tabGen.pkColumnValue() ) ) {
  331   				idGen.addParam( MultipleHiLoPerTableGenerator.PK_VALUE_NAME, tabGen.pkColumnValue() );
  332   			}
  333   			idGen.addParam( TableHiLoGenerator.MAX_LO, String.valueOf( tabGen.allocationSize() - 1 ) );
  334   			log.debug( "Add table generator with name: " + idGen.getName() );
  335   		}
  336   		else if ( ann instanceof SequenceGenerator ) {
  337   			SequenceGenerator seqGen = (SequenceGenerator) ann;
  338   			idGen.setName( seqGen.name() );
  339   			idGen.setIdentifierGeneratorStrategy( "seqhilo" );
  340   
  341   			if ( !BinderHelper.isDefault( seqGen.sequenceName() ) ) {
  342   				idGen.addParam( org.hibernate.id.SequenceGenerator.SEQUENCE, seqGen.sequenceName() );
  343   			}
  344   			//FIXME: work on initialValue() through SequenceGenerator.PARAMETERS
  345   			if ( seqGen.initialValue() != 1 ) {
  346   				log.warn(
  347   						"Hibernate does not support SequenceGenerator.initialValue()"
  348   				);
  349   			}
  350   			idGen.addParam( SequenceHiLoGenerator.MAX_LO, String.valueOf( seqGen.allocationSize() - 1 ) );
  351   			log.debug( "Add sequence generator with name: " + idGen.getName() );
  352   		}
  353   		else if ( ann instanceof GenericGenerator ) {
  354   			GenericGenerator genGen = (GenericGenerator) ann;
  355   			idGen.setName( genGen.name() );
  356   			idGen.setIdentifierGeneratorStrategy( genGen.strategy() );
  357   			Parameter[] params = genGen.parameters();
  358   			for ( Parameter parameter : params ) {
  359   				idGen.addParam( parameter.name(), parameter.value() );
  360   			}
  361   			log.debug( "Add generic generator with name: " + idGen.getName() );
  362   		}
  363   		else {
  364   			throw new AssertionFailure( "Unknown Generator annotation: " + ann );
  365   		}
  366   		return idGen;
  367   	}
  368   
  369   	/**
  370   	 * Bind a class having JSR175 annotations
  371   	 * The subclasses <b>have to</b> be binded after its mother class
  372   	 */
  373   	public static void bindClass(
  374   			XClass clazzToProcess, Map<XClass, InheritanceState> inheritanceStatePerClass, ExtendedMappings mappings
  375   	) throws MappingException {
  376   		//TODO: be more strict with secondarytable allowance (not for ids, not for secondary table join columns etc)
  377   		InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
  378   		AnnotatedClassType classType = mappings.getClassType( clazzToProcess );
  379   		if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) //will be processed by their subentities
  380   				|| AnnotatedClassType.NONE.equals( classType ) //to be ignored
  381   				|| AnnotatedClassType.EMBEDDABLE.equals( classType ) //allow embeddable element declaration
  382   				) {
  383   			if ( AnnotatedClassType.NONE.equals( classType )
  384   					&& clazzToProcess.isAnnotationPresent( org.hibernate.annotations.Entity.class ) ) {
  385   				log.warn("Class annotated @org.hibernate.annotations.Entity but not javax.persistence.Entity "
  386   						+ "(most likely a user error): " + clazzToProcess.getName() );
  387   			}
  388   			return;
  389   		}
  390   		if ( !classType.equals( AnnotatedClassType.ENTITY ) ) {
  391   			//TODO make this test accurate by removing the none elements artifically added
  392   			throw new AnnotationException(
  393   					"Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: " + clazzToProcess
  394   							.getName()
  395   			);
  396   		}
  397   		XAnnotatedElement annotatedClass = clazzToProcess;
  398   		if ( log.isInfoEnabled() ) log.info( "Binding entity from annotated class: " + clazzToProcess.getName() );
  399   		InheritanceState superEntityState =
  400   				InheritanceState.getSuperEntityInheritanceState(
  401   						clazzToProcess, inheritanceStatePerClass, mappings.getReflectionManager()
  402   				);
  403   		PersistentClass superEntity = superEntityState != null ?
  404   				mappings.getClass(
  405   						superEntityState.clazz.getName()
  406   				) :
  407   				null;
  408   		if ( superEntity == null ) {
  409   			//check if superclass is not a potential persistent class
  410   			if ( inheritanceState.hasParents ) {
  411   				throw new AssertionFailure(
  412   						"Subclass has to be binded after it's mother class: "
  413   								+ superEntityState.clazz.getName()
  414   				);
  415   			}
  416   		}
  417   		bindQueries( annotatedClass, mappings );
  418   		bindFilterDefs( annotatedClass, mappings );
  419   		bindTypeDefs( annotatedClass, mappings );
  420   
  421   		String schema = "";
  422   		String table = ""; //might be no @Table annotation on the annotated class
  423   		String catalog = "";
  424   		String discrimValue = null;
  425   		List<String[]> uniqueConstraints = new ArrayList<String[]>();
  426   		Ejb3DiscriminatorColumn discriminatorColumn = null;
  427   		Ejb3JoinColumn[] inheritanceJoinedColumns = null;
  428   
  429   		if ( annotatedClass.isAnnotationPresent( javax.persistence.Table.class ) ) {
  430   			javax.persistence.Table tabAnn = annotatedClass.getAnnotation( javax.persistence.Table.class );
  431   			table = tabAnn.name();
  432   			schema = tabAnn.schema();
  433   			catalog = tabAnn.catalog();
  434   			uniqueConstraints = TableBinder.buildUniqueConstraints( tabAnn.uniqueConstraints() );
  435   		}
  436   		final boolean hasJoinedColumns = inheritanceState.hasParents
  437   				&& InheritanceType.JOINED.equals( inheritanceState.type );
  438   		if ( hasJoinedColumns ) {
  439   			//@Inheritance(JOINED) subclass need to link back to the super entity
  440   			PrimaryKeyJoinColumns jcsAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumns.class );
  441   			boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
  442   			if ( explicitInheritanceJoinedColumns ) {
  443   				int nbrOfInhJoinedColumns = jcsAnn.value().length;
  444   				PrimaryKeyJoinColumn jcAnn;
  445   				inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
  446   				for ( int colIndex = 0; colIndex < nbrOfInhJoinedColumns; colIndex++ ) {
  447   					jcAnn = jcsAnn.value()[colIndex];
  448   					inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
  449   							jcAnn, null, superEntity.getIdentifier(),
  450   							(Map<String, Join>) null, (PropertyHolder) null, mappings
  451   					);
  452   				}
  453   			}
  454   			else {
  455   				PrimaryKeyJoinColumn jcAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumn.class );
  456   				inheritanceJoinedColumns = new Ejb3JoinColumn[1];
  457   				inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(
  458   						jcAnn, null, superEntity.getIdentifier(),
  459   						(Map<String, Join>) null, (PropertyHolder) null, mappings
  460   				);
  461   			}
  462   			log.debug( "Subclass joined column(s) created" );
  463   		}
  464   		else {
  465   			if ( annotatedClass.isAnnotationPresent( javax.persistence.PrimaryKeyJoinColumns.class )
  466   					|| annotatedClass.isAnnotationPresent( javax.persistence.PrimaryKeyJoinColumn.class ) ) {
  467   				log.warn( "Root entity should not hold an PrimaryKeyJoinColum(s), will be ignored" );
  468   			}
  469   		}
  470   
  471   		if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
  472   			javax.persistence.Inheritance inhAnn = annotatedClass.getAnnotation( javax.persistence.Inheritance.class );
  473   			javax.persistence.DiscriminatorColumn discAnn = annotatedClass.getAnnotation(
  474   					javax.persistence.DiscriminatorColumn.class
  475   			);
  476   			DiscriminatorType discriminatorType = discAnn != null ?
  477   					discAnn.discriminatorType() :
  478   					DiscriminatorType.STRING;
  479   
  480   			org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = annotatedClass.getAnnotation(
  481   					org.hibernate.annotations.DiscriminatorFormula.class
  482   			);
  483   			if ( !inheritanceState.hasParents ) {
  484   				discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
  485   						discriminatorType, discAnn, discFormulaAnn, mappings
  486   				);
  487   			}
  488   			if ( discAnn != null && inheritanceState.hasParents ) {
  489   				log.warn(
  490   						"Discriminator column has to be defined in the root entity, it will be ignored in subclass: "
  491   								+ clazzToProcess.getName()
  492   				);
  493   			}
  494   			discrimValue = annotatedClass.isAnnotationPresent( DiscriminatorValue.class ) ?
  495   					annotatedClass.getAnnotation( DiscriminatorValue.class ).value() :
  496   					null;
  497   		}
  498   
  499   		//we now know what kind of persistent entity it is
  500   		PersistentClass persistentClass;
  501   		//create persistent class
  502   		if ( !inheritanceState.hasParents ) {
  503   			persistentClass = new RootClass();
  504   		}
  505   		else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
  506   			persistentClass = new SingleTableSubclass( superEntity );
  507   		}
  508   		else if ( InheritanceType.JOINED.equals( inheritanceState.type ) ) {
  509   			persistentClass = new JoinedSubclass( superEntity );
  510   		}
  511   		else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.type ) ) {
  512   			persistentClass = new UnionSubclass( superEntity );
  513   		}
  514   		else {
  515   			throw new AssertionFailure( "Unknown inheritance type: " + inheritanceState.type );
  516   		}
  517   		Proxy proxyAnn = annotatedClass.getAnnotation( Proxy.class );
  518   		BatchSize sizeAnn = annotatedClass.getAnnotation( BatchSize.class );
  519   		Where whereAnn = annotatedClass.getAnnotation( Where.class );
  520   		Entity entityAnn = annotatedClass.getAnnotation( Entity.class );
  521   		org.hibernate.annotations.Entity hibEntityAnn = annotatedClass.getAnnotation(
  522   				org.hibernate.annotations.Entity.class
  523   		);
  524   		org.hibernate.annotations.Cache cacheAnn = annotatedClass.getAnnotation(
  525   				org.hibernate.annotations.Cache.class
  526   		);
  527   		EntityBinder entityBinder = new EntityBinder(
  528   				entityAnn, hibEntityAnn, clazzToProcess, persistentClass, mappings
  529   		);
  530   		entityBinder.setDiscriminatorValue( discrimValue );
  531   		entityBinder.setBatchSize( sizeAnn );
  532   		entityBinder.setProxy( proxyAnn );
  533   		entityBinder.setWhere( whereAnn );
  534   		entityBinder.setCache( cacheAnn );
  535   		entityBinder.setInheritanceState( inheritanceState );
  536   		Filter filterAnn = annotatedClass.getAnnotation( Filter.class );
  537   		if ( filterAnn != null ) {
  538   			entityBinder.addFilter( filterAnn.name(), filterAnn.condition() );
  539   		}
  540   		Filters filtersAnn = annotatedClass.getAnnotation( Filters.class );
  541   		if ( filtersAnn != null ) {
  542   			for ( Filter filter : filtersAnn.value() ) {
  543   				entityBinder.addFilter( filter.name(), filter.condition() );
  544   			}
  545   		}
  546   		entityBinder.bindEntity();
  547   
  548   		if ( inheritanceState.hasTable() ) {
  549   			Check checkAnn = annotatedClass.getAnnotation( Check.class );
  550   			String constraints = checkAnn == null ?
  551   					null :
  552   					checkAnn.constraints();
  553   			entityBinder.bindTable(
  554   					schema, catalog, table, uniqueConstraints,
  555   					constraints, inheritanceState.hasDenormalizedTable() ?
  556   					superEntity.getTable() :
  557   					null
  558   			);
  559   		}
  560   		else {
  561   			if ( annotatedClass.isAnnotationPresent( Table.class ) ) {
  562   				log.warn( "Illegal use of @Table in a subclass of a SINGLE_TABLE hierarchy: " + clazzToProcess
  563   						.getName() );
  564   			}
  565   		}
  566   //		Map<String, Column[]> columnOverride = PropertyHolderBuilder.buildHierarchyColumnOverride(
  567   //				clazzToProcess,
  568   //				persistentClass.getClassName()
  569   //		);
  570   		PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(
  571   				clazzToProcess,
  572   				persistentClass,
  573   				entityBinder, mappings
  574   		);
  575   
  576   		javax.persistence.SecondaryTable secTabAnn = annotatedClass.getAnnotation(
  577   				javax.persistence.SecondaryTable.class
  578   		);
  579   		javax.persistence.SecondaryTables secTabsAnn = annotatedClass.getAnnotation(
  580   				javax.persistence.SecondaryTables.class
  581   		);
  582   		entityBinder.firstLevelSecondaryTablesBinding( secTabAnn, secTabsAnn );
  583   
  584   		OnDelete onDeleteAnn = annotatedClass.getAnnotation( OnDelete.class );
  585   		boolean onDeleteAppropriate = false;
  586   		if ( InheritanceType.JOINED.equals( inheritanceState.type ) && inheritanceState.hasParents ) {
  587   			onDeleteAppropriate = true;
  588   			final JoinedSubclass jsc = (JoinedSubclass) persistentClass;
  589   			if ( persistentClass.getEntityPersisterClass() == null ) {
  590   				persistentClass.getRootClass().setEntityPersisterClass( JoinedSubclassEntityPersister.class );
  591   			}
  592   			SimpleValue key = new DependantValue( jsc.getTable(), jsc.getIdentifier() );
  593   			jsc.setKey( key );
  594   			ForeignKey fk = annotatedClass.getAnnotation( ForeignKey.class );
  595   			if (fk != null && ! BinderHelper.isDefault( fk.name() ) ) {
  596   				key.setForeignKeyName( fk.name() );
  597   			}
  598   			if ( onDeleteAnn != null ) {
  599   				key.setCascadeDeleteEnabled( OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) );
  600   			}
  601   			else {
  602   				key.setCascadeDeleteEnabled( false );
  603   			}
  604   			TableBinder.bindFk( jsc.getSuperclass(), jsc, inheritanceJoinedColumns, key, false, mappings );
  605   			//no need to handle inSecondPass this is an Etntiy related work
  606   			mappings.addSecondPass( new CreateKeySecondPass( jsc ) );
  607   
  608   		}
  609   		else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
  610   			if ( inheritanceState.hasParents ) {
  611   				if ( persistentClass.getEntityPersisterClass() == null ) {
  612   					persistentClass.getRootClass().setEntityPersisterClass( SingleTableEntityPersister.class );
  613   				}
  614   			}
  615   			else {
  616   				if ( inheritanceState.hasSons || !discriminatorColumn.isImplicit() ) {
  617   					//need a discriminator column
  618   					bindDiscriminatorToPersistentClass(
  619   							(RootClass) persistentClass,
  620   							discriminatorColumn,
  621   							entityBinder.getSecondaryTables(),
  622   							propertyHolder
  623   					);
  624   					entityBinder.bindDiscriminatorValue();//bind it again since the type might have changed
  625   				}
  626   			}
  627   		}
  628   		else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.type ) ) {
  629   			if ( inheritanceState.hasParents ) {
  630   				if ( persistentClass.getEntityPersisterClass() == null ) {
  631   					persistentClass.getRootClass().setEntityPersisterClass( UnionSubclassEntityPersister.class );
  632   				}
  633   			}
  634   		}
  635   		if ( onDeleteAnn != null && !onDeleteAppropriate ) {
  636   			log.warn(
  637   					"Inapropriate use of @OnDelete on entity, annotation ignored: " + propertyHolder.getEntityName()
  638   			);
  639   		}
  640   
  641   		//try to find class level generators
  642   		HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( annotatedClass, mappings );
  643   
  644   		// check properties
  645   		List<PropertyData> elements =
  646   				getElementsToProcess(
  647   						clazzToProcess, inheritanceStatePerClass, propertyHolder, entityBinder, mappings
  648   				);
  649   		if ( elements == null ) {
  650   			throw new AnnotationException( "No identifier specified for entity: " + propertyHolder.getEntityName() );
  651   		}
  652   		final boolean subclassAndSingleTableStrategy = inheritanceState.type == InheritanceType.SINGLE_TABLE
  653   				&& inheritanceState.hasParents;
  654   		//process idclass if any
  655   		Set<String> idProperties = new HashSet<String>();
  656   		IdClass idClass = null;
  657   		if ( !inheritanceState.hasParents ) {
  658   			//look for idClass
  659   			XClass current = inheritanceState.clazz;
  660   			InheritanceState state = inheritanceState;
  661   			do {
  662   				current = state.clazz;
  663   				if ( current.isAnnotationPresent( IdClass.class ) ) {
  664   					idClass = current.getAnnotation( IdClass.class );
  665   					break;
  666   				}
  667   				state = InheritanceState.getSuperclassInheritanceState(
  668   						current, inheritanceStatePerClass, mappings.getReflectionManager()
  669   				);
  670   			}
  671   			while ( state != null );
  672   		}
  673   		if ( idClass != null ) {
  674   			XClass compositeClass = mappings.getReflectionManager().toXClass( idClass.value() );
  675   			boolean isComponent = true;
  676   			boolean propertyAnnotated = entityBinder.isPropertyAnnotated( compositeClass );
  677   			String propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
  678   			String generatorType = "assigned";
  679   			String generator = BinderHelper.ANNOTATION_STRING_DEFAULT;
  680   			PropertyData inferredData = new PropertyPreloadedData(
  681   					entityBinder.getPropertyAccessor(), "id", compositeClass
  682   			);
  683   			HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
  684   			boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
  685   			entityBinder.setIgnoreIdAnnotations( true );
  686   			bindId(
  687   					generatorType,
  688   					generator,
  689   					inferredData,
  690   					null,
  691   					propertyHolder,
  692   					localGenerators,
  693   					isComponent,
  694   					propertyAnnotated,
  695   					propertyAccessor, entityBinder,
  696   					null,
  697   					true,
  698   					false, mappings
  699   			);
  700   			inferredData = new PropertyPreloadedData(
  701   					propertyAccessor, "_identifierMapper", compositeClass
  702   			);
  703   			Component mapper = fillComponent(
  704   					propertyHolder,
  705   					inferredData,
  706   					propertyAnnotated,
  707   					propertyAccessor, false,
  708   					entityBinder,
  709   					true, true,
  710   					false, mappings
  711   			);
  712   			entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations );
  713   			persistentClass.setIdentifierMapper( mapper );
  714   			Property property = new Property();
  715   			property.setName( "_identifierMapper" );
  716   			property.setNodeName( "id" );
  717   			property.setUpdateable( false );
  718   			property.setInsertable( false );
  719   			property.setValue( mapper );
  720   			property.setPropertyAccessorName( "embedded" );
  721   			persistentClass.addProperty( property );
  722   			entityBinder.setIgnoreIdAnnotations( true );
  723   
  724   			Iterator properties = mapper.getPropertyIterator();
  725   			while ( properties.hasNext() ) {
  726   				idProperties.add( ( (Property) properties.next() ).getName() );
  727   			}
  728   		}
  729   		Set<String> missingIdProperties = new HashSet<String>( idProperties );
  730   		for ( PropertyData propertyAnnotatedElement : elements ) {
  731   			String propertyName = propertyAnnotatedElement.getPropertyName();
  732   			if ( !idProperties.contains( propertyName ) ) {
  733   				processElementAnnotations(
  734   						propertyHolder,
  735   						subclassAndSingleTableStrategy ?
  736   								Nullability.FORCED_NULL :
  737   								Nullability.NO_CONSTRAINT,
  738   						propertyAnnotatedElement.getProperty(),
  739   						propertyAnnotatedElement, classGenerators, entityBinder,
  740   						false, false, false, mappings
  741   				);
  742   			}
  743   			else {
  744   				missingIdProperties.remove( propertyName );
  745   			}
  746   		}
  747   
  748   		if ( missingIdProperties.size() != 0 ) {
  749   			StringBuilder missings = new StringBuilder();
  750   			for ( String property : missingIdProperties ) {
  751   				missings.append( property ).append( ", " );
  752   			}
  753   			throw new AnnotationException(
  754   					"Unable to find properties ("
  755   							+ missings.substring( 0, missings.length() - 2 )
  756   							+ ") in entity annotated with @IdClass:" + persistentClass.getEntityName()
  757   			);
  758   		}
  759   
  760   		if ( !inheritanceState.hasParents ) {
  761   			final RootClass rootClass = (RootClass) persistentClass;
  762   			//no need to handle inSecondPass this is an Entity related work
  763   			mappings.addSecondPass( new CreateKeySecondPass( rootClass ) );
  764   		}
  765   		else {
  766   			superEntity.addSubclass( (Subclass) persistentClass );
  767   		}
  768   
  769   		mappings.addClass( persistentClass );
  770   		entityBinder.finalSecondaryTableBinding( propertyHolder );
  771   
  772   		//add process complementary Table definition (index & all)
  773   		entityBinder.processComplementaryTableDefinitions( annotatedClass.getAnnotation( org.hibernate.annotations.Table.class ) );
  774   		entityBinder.processComplementaryTableDefinitions( annotatedClass.getAnnotation( org.hibernate.annotations.Tables.class ) );
  775   
  776   	}
  777   
  778   	/**
  779   	 * Get the annotated elements
  780   	 * Guess the annotated element from @Id or @EmbeddedId presence
  781   	 * Change EntityBinder by side effect
  782   	 */
  783   	private static List<PropertyData> getElementsToProcess(
  784   			XClass clazzToProcess, Map<XClass, InheritanceState> inheritanceStatePerClass,
  785   			PropertyHolder propertyHolder, EntityBinder entityBinder, ExtendedMappings mappings
  786   	) {
  787   		InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
  788   		List<XClass> classesToProcess = orderClassesToBeProcessed(
  789   				clazzToProcess, inheritanceStatePerClass, inheritanceState, mappings
  790   		);
  791   		List<PropertyData> elements = new ArrayList<PropertyData>();
  792   		int deep = classesToProcess.size();
  793   		boolean hasIdentifier = false;
  794   
  795   		assert !inheritanceState.isEmbeddableSuperclass;
  796   		Boolean isExplicitPropertyAnnotated = null;
  797   		String explicitAccessType = null;
  798   		if ( inheritanceState.hasParents ) {
  799   			InheritanceState superEntityState =
  800   					InheritanceState.getSuperEntityInheritanceState(
  801   							clazzToProcess, inheritanceStatePerClass, mappings.getReflectionManager()
  802   					);
  803   			isExplicitPropertyAnnotated = superEntityState != null ?
  804   					superEntityState.isPropertyAnnotated :
  805   					null;
  806   			explicitAccessType = superEntityState != null ?
  807   					superEntityState.accessType :
  808   					null;
  809   		}
  810   		else {
  811   			AccessType access = clazzToProcess.getAnnotation( AccessType.class );
  812   			explicitAccessType = access != null ?
  813   					access.value() :
  814   					null;
  815   			if ( "property".equals( explicitAccessType ) ) {
  816   				isExplicitPropertyAnnotated = Boolean.TRUE;
  817   			}
  818   			else if ( "field".equals( explicitAccessType ) ) {
  819   				isExplicitPropertyAnnotated = Boolean.FALSE;
  820   			}
  821   		}
  822   		Boolean isPropertyAnnotated = isExplicitPropertyAnnotated == null ?
  823   				Boolean.TRUE :
  824   				//default to property and fallback if needed
  825   				isExplicitPropertyAnnotated;
  826   		String accessType = explicitAccessType != null ?
  827   				explicitAccessType :
  828   				"property";
  829   
  830   		for ( int index = 0; index < deep; index++ ) {
  831   			XClass clazz = classesToProcess.get( index );
  832   
  833   			boolean currentHasIdentifier = addElementsOfAClass(
  834   					elements, propertyHolder, isPropertyAnnotated,
  835   					accessType, clazz, mappings
  836   			);
  837   			hasIdentifier = hasIdentifier || currentHasIdentifier;
  838   		}
  839   
  840   		if ( !hasIdentifier && !inheritanceState.hasParents ) {
  841   			if ( isExplicitPropertyAnnotated != null ) return null; //explicit but no @Id
  842   			isPropertyAnnotated = !isPropertyAnnotated;
  843   			accessType = "field";
  844   			elements.clear();
  845   			for ( int index = 0; index < deep; index++ ) {
  846   				XClass clazz = classesToProcess.get( index );
  847   				boolean currentHasIdentifier = addElementsOfAClass(
  848   						elements, propertyHolder, isPropertyAnnotated,
  849   						accessType, clazz, mappings
  850   				);
  851   				hasIdentifier = hasIdentifier || currentHasIdentifier;
  852   			}
  853   		}
  854   		//TODO set the access type here?
  855   		entityBinder.setPropertyAnnotated( isPropertyAnnotated );
  856   		entityBinder.setPropertyAccessor( accessType );
  857   		inheritanceState.isPropertyAnnotated = isPropertyAnnotated;
  858   		inheritanceState.accessType = accessType;
  859   		return hasIdentifier || inheritanceState.hasParents ?
  860   				elements :
  861   				null;
  862   	}
  863   
  864   	private static List<XClass> orderClassesToBeProcessed(
  865   			XClass annotatedClass, Map<XClass, InheritanceState> inheritanceStatePerClass,
  866   			InheritanceState inheritanceState, ExtendedMappings mappings
  867   	) {
  868   		//ordered to allow proper messages on properties subclassing
  869   		List<XClass> classesToProcess = new ArrayList<XClass>();
  870   		XClass currentClassInHierarchy = annotatedClass;
  871   		InheritanceState superclassState;
  872   		do {
  873   			classesToProcess.add( 0, currentClassInHierarchy );
  874   			XClass superClass = currentClassInHierarchy;
  875   			do {
  876   				superClass = superClass.getSuperclass();
  877   				superclassState = inheritanceStatePerClass.get( superClass );
  878   			}
  879   			while ( superClass != null && !mappings.getReflectionManager()
  880   					.equals( superClass, Object.class ) && superclassState == null );
  881   
  882   			currentClassInHierarchy = superClass;
  883   		}
  884   		while ( superclassState != null && superclassState.isEmbeddableSuperclass );
  885   
  886   		return classesToProcess;
  887   	}
  888   
  889   	private static void bindFilterDefs(XAnnotatedElement annotatedElement, ExtendedMappings mappings) {
  890   		FilterDef defAnn = annotatedElement.getAnnotation( FilterDef.class );
  891   		FilterDefs defsAnn = annotatedElement.getAnnotation( FilterDefs.class );
  892   		if ( defAnn != null ) {
  893   			bindFilterDef( defAnn, mappings );
  894   		}
  895   		if ( defsAnn != null ) {
  896   			for ( FilterDef def : defsAnn.value() ) {
  897   				bindFilterDef( def, mappings );
  898   			}
  899   		}
  900   	}
  901   
  902   	private static void bindFilterDef(FilterDef defAnn, ExtendedMappings mappings) {
  903   		Map<String, org.hibernate.type.Type> params = new HashMap<String, org.hibernate.type.Type>();
  904   		for ( ParamDef param : defAnn.parameters() ) {
  905   			params.put( param.name(), TypeFactory.heuristicType( param.type() ) );
  906   		}
  907   		FilterDefinition def = new FilterDefinition( defAnn.name(), defAnn.defaultCondition(), params );
  908   		if ( log.isInfoEnabled() ) log.info( "Binding filter definition: " + def.getFilterName() );
  909   		mappings.addFilterDefinition( def );
  910   	}
  911   
  912   	private static void bindTypeDefs(XAnnotatedElement annotatedElement, ExtendedMappings mappings) {
  913   		TypeDef defAnn = annotatedElement.getAnnotation( TypeDef.class );
  914   		TypeDefs defsAnn = annotatedElement.getAnnotation( TypeDefs.class );
  915   		if ( defAnn != null ) {
  916   			bindTypeDef( defAnn, mappings );
  917   		}
  918   		if ( defsAnn != null ) {
  919   			for ( TypeDef def : defsAnn.value() ) {
  920   				bindTypeDef( def, mappings );
  921   			}
  922   		}
  923   	}
  924   
  925   	private static void bindTypeDef(TypeDef defAnn, ExtendedMappings mappings) {
  926   		Properties params = new Properties();
  927   		for ( Parameter param : defAnn.parameters() ) {
  928   			params.setProperty( param.name(), param.value() );
  929   		}
  930   		if ( log.isInfoEnabled() ) log.info( "Binding type definition: " + defAnn.name() );
  931   		mappings.addTypeDef( defAnn.name(), defAnn.typeClass().getName(), params );
  932   	}
  933   
  934   	private static void bindDiscriminatorToPersistentClass(
  935   			RootClass rootClass,
  936   			Ejb3DiscriminatorColumn discriminatorColumn, Map<String, Join> secondaryTables,
  937   			PropertyHolder propertyHolder
  938   	) {
  939   		if ( rootClass.getDiscriminator() == null ) {
  940   			if ( discriminatorColumn == null ) {
  941   				throw new AssertionFailure( "discriminator column should have been built" );
  942   			}
  943   			discriminatorColumn.setJoins( secondaryTables );
  944   			discriminatorColumn.setPropertyHolder( propertyHolder );
  945   			SimpleValue discrim = new SimpleValue( rootClass.getTable() );
  946   			rootClass.setDiscriminator( discrim );
  947   			discriminatorColumn.linkWithValue( discrim );
  948   			discrim.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
  949   			rootClass.setPolymorphic( true );
  950   			log.debug( "Setting discriminator for entity " + rootClass.getEntityName() );
  951   		}
  952   	}
  953   
  954   	/**
  955   	 * Add elements of a class
  956   	 */
  957   	private static boolean addElementsOfAClass(
  958   			List<PropertyData> elements, PropertyHolder propertyHolder, boolean isPropertyAnnotated,
  959   			String propertyAccessor, final XClass annotatedClass, ExtendedMappings mappings
  960   	) {
  961   		boolean hasIdentifier = false;
  962   		AccessType access = annotatedClass.getAnnotation( AccessType.class );
  963   		String localPropertyAccessor = access != null ?
  964   				access.value() :
  965   				null;
  966   		String accessType = null;
  967   		if ( "property".equals( localPropertyAccessor ) || "field".equals( localPropertyAccessor ) ) {
  968   			accessType = localPropertyAccessor;
  969   		}
  970   		else {
  971   			if ( localPropertyAccessor == null ) {
  972   				localPropertyAccessor = propertyAccessor;
  973   			}
  974   
  975   			if ( isPropertyAnnotated ) {
  976   				accessType = "property";
  977   			}
  978   			else {
  979   				accessType = "field";
  980   			}
  981   		}
  982   
  983   		log.debug( "Processing " + propertyHolder.getEntityName() + " " + accessType + " annotation" );
  984   		List<XProperty> properties = annotatedClass.getDeclaredProperties( accessType );
  985   		//order so that property are used int he same order when binding native query
  986   		Collections.sort( properties, new Comparator<XProperty>() {
  987   			public int compare(XProperty property1, XProperty property2) {
  988   				return property1.getName().compareTo( property2.getName() );
  989   			}
  990   		} );
  991   		for ( XProperty p : properties ) {
  992   			if ( !p.isTypeResolved() && !discoverTypeWithoutReflection( p ) && !mustBeSkipped( p, mappings ) ) {
  993   				throw new AnnotationException(
  994   						"Property " + StringHelper.qualify( propertyHolder.getEntityName(), p.getName() ) +
  995   								" has an unbound type and no explicit target entity. Resolve this Generic usage issue" +
  996   								" or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type"
  997   				);
  998   			}
  999   			final boolean currentHasIdentifier = addProperty( p, elements, localPropertyAccessor, mappings );
 1000   			hasIdentifier = hasIdentifier || currentHasIdentifier;
 1001   		}
 1002   		return hasIdentifier;
 1003   	}
 1004   
 1005   	private static boolean discoverTypeWithoutReflection(XProperty p) {
 1006   		if ( p.isAnnotationPresent( OneToOne.class ) && !p.getAnnotation( OneToOne.class )
 1007   				.targetEntity()
 1008   				.equals( void.class ) ) {
 1009   			return true;
 1010   		}
 1011   		else if ( p.isAnnotationPresent( OneToMany.class ) && !p.getAnnotation( OneToMany.class )
 1012   				.targetEntity()
 1013   				.equals( void.class ) ) {
 1014   			return true;
 1015   		}
 1016   		else if ( p.isAnnotationPresent( ManyToOne.class ) && !p.getAnnotation( ManyToOne.class )
 1017   				.targetEntity()
 1018   				.equals( void.class ) ) {
 1019   			return true;
 1020   		}
 1021   		else if ( p.isAnnotationPresent( ManyToMany.class ) && !p.getAnnotation( ManyToMany.class )
 1022   				.targetEntity()
 1023   				.equals( void.class ) ) {
 1024   			return true;
 1025   		}
 1026   		else if ( p.isAnnotationPresent( Type.class ) ) {
 1027   			return true;
 1028   		}
 1029   		else if ( p.isAnnotationPresent( Target.class ) ) {
 1030   			return true;
 1031   		}
 1032   		return false;
 1033   	}
 1034   
 1035   	private static boolean addProperty(
 1036   			XProperty property, List<PropertyData> annElts,
 1037   			String propertyAccessor, ExtendedMappings mappings
 1038   	) {
 1039   		boolean hasIdentifier = false;
 1040   		PropertyData propertyAnnotatedElement = new PropertyInferredData(
 1041   				property, propertyAccessor,
 1042   				mappings.getReflectionManager() );
 1043   		if ( !mustBeSkipped( propertyAnnotatedElement.getProperty(), mappings ) ) {
 1044   			/*
 1045   			 * put element annotated by @Id in front
 1046   			 * since it has to be parsed before any assoctation by Hibernate
 1047   			 */
 1048   			final XAnnotatedElement element = propertyAnnotatedElement.getProperty();
 1049   			if ( element.isAnnotationPresent( Id.class ) || element.isAnnotationPresent( EmbeddedId.class ) ) {
 1050   				annElts.add( 0, propertyAnnotatedElement );
 1051   				hasIdentifier = true;
 1052   			}
 1053   			else {
 1054   				annElts.add( propertyAnnotatedElement );
 1055   				hasIdentifier = false;
 1056   			}
 1057   		}
 1058   		return hasIdentifier;
 1059   	}
 1060   
 1061   	private static boolean mustBeSkipped(XProperty property, ExtendedMappings mappings) {
 1062   		//TODO make those hardcoded tests more portable (through the bytecode provider?)
 1063   		return property.isAnnotationPresent( Transient.class )
 1064   				|| "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() )
 1065   				|| "org.hibernate.tool.instrument.javassist.FieldHandler".equals( property.getType().getName() );
 1066   	}
 1067   
 1068   	/**
 1069   	 * Process annotation of a particular property
 1070   	 */
 1071   	private static void processElementAnnotations(
 1072   			PropertyHolder propertyHolder, Nullability nullability, XProperty property,
 1073   			PropertyData inferredData, HashMap<String, IdGenerator> classGenerators,
 1074   			EntityBinder entityBinder, boolean isIdentifierMapper,
 1075   			boolean isComponentEmbedded, boolean inSecondPass, ExtendedMappings mappings
 1076   	)
 1077   			throws MappingException {
 1078   		/**
 1079   		 * inSecondPass can only be used to apply right away the second pass of a composite-element
 1080   		 * Because it's a value type, there is no bidirectional association, hence second pass
 1081   		 * ordering does not matter
 1082   		 */
 1083   		Ejb3Column[] columns = null;
 1084   		Ejb3JoinColumn[] joinColumns = null;
 1085   		if ( log.isDebugEnabled() ) {
 1086   			log.debug(
 1087   					"Processing annotations of " + propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
 1088   			);
 1089   		}
 1090   
 1091   		if ( property.isAnnotationPresent( Parent.class ) ) {
 1092   			if ( propertyHolder.isComponent() ) {
 1093   				propertyHolder.setParentProperty( property.getName() );
 1094   			}
 1095   			else {
 1096   				throw new AnnotationException(
 1097   						"@Parent cannot be applied outside an embeddable object: "
 1098   								+ StringHelper.qualify( propertyHolder.getPath(), property.getName() )
 1099   				);
 1100   			}
 1101   			return;
 1102   		}
 1103   
 1104   		//process @JoinColumn(s) before @Column(s) to handle collection of elements properly
 1105   		{
 1106   			JoinColumn[] anns = null;
 1107   			if ( property.isAnnotationPresent( JoinColumn.class ) ) {
 1108   				anns = new JoinColumn[]{property.getAnnotation( JoinColumn.class )};
 1109   			}
 1110   			else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
 1111   				JoinColumns ann = property.getAnnotation( JoinColumns.class );
 1112   				anns = ann.value();
 1113   				int length = anns.length;
 1114   				if ( length == 0 ) {
 1115   					throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
 1116   				}
 1117   			}
 1118   			if ( anns != null ) {
 1119   				joinColumns = Ejb3JoinColumn.buildJoinColumns(
 1120   						anns, null, entityBinder.getSecondaryTables(),
 1121   						propertyHolder, inferredData.getPropertyName(), mappings
 1122   				);
 1123   			}
 1124   		}
 1125   		if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent( Formula.class ) ) {
 1126   			Column ann = property.getAnnotation( Column.class );
 1127   			Formula formulaAnn = property.getAnnotation( Formula.class );
 1128   			columns = Ejb3Column.buildColumnFromAnnotation(
 1129   					new Column[]{ann}, formulaAnn, nullability, propertyHolder, inferredData,
 1130   					entityBinder.getSecondaryTables(), mappings
 1131   			);
 1132   		}
 1133   		else if ( property.isAnnotationPresent( Columns.class ) ) {
 1134   			Columns anns = property.getAnnotation( Columns.class );
 1135   			columns = Ejb3Column.buildColumnFromAnnotation(
 1136   					anns.columns(), null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(),
 1137   					mappings
 1138   			);
 1139   		}
 1140   
 1141   		//set default values if needed
 1142   		if ( joinColumns == null &&
 1143   				( property.isAnnotationPresent( ManyToOne.class )
 1144   						|| property.isAnnotationPresent( OneToOne.class ) )
 1145   				) {
 1146   			if ( property.isAnnotationPresent( JoinTable.class ) ) {
 1147   				JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
 1148   				joinColumns = Ejb3JoinColumn.buildJoinColumns(
 1149   						joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
 1150   						propertyHolder, inferredData.getPropertyName(), mappings
 1151   				);
 1152   				if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
 1153   					throw new AnnotationException(
 1154   							"JoinTable.name() on a @ToOne association has to be explicit: "
 1155   									+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() )
 1156   					);
 1157   				}
 1158   			}
 1159   			else {
 1160   				OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
 1161   				String mappedBy = oneToOneAnn != null ?
 1162   						oneToOneAnn.mappedBy() :
 1163   						null;
 1164   				joinColumns = Ejb3JoinColumn.buildJoinColumns(
 1165   						(JoinColumn[]) null,
 1166   						mappedBy, entityBinder.getSecondaryTables(),
 1167   						propertyHolder, inferredData.getPropertyName(), mappings
 1168   				);
 1169   			}
 1170   		}
 1171   		else if ( joinColumns == null &&
 1172   				( property.isAnnotationPresent( OneToMany.class )
 1173   						|| property.isAnnotationPresent( CollectionOfElements.class ) ) ) {
 1174   			OneToMany oneToMany = property.getAnnotation( OneToMany.class );
 1175   			String mappedBy = oneToMany != null ?
 1176   					oneToMany.mappedBy() :
 1177   					"";
 1178   			joinColumns = Ejb3JoinColumn.buildJoinColumns(
 1179   					(JoinColumn[]) null,
 1180   					mappedBy, entityBinder.getSecondaryTables(),
 1181   					propertyHolder, inferredData.getPropertyName(), mappings
 1182   			);
 1183   		}
 1184   		if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
 1185   			//useful for collection of embedded elements
 1186   			columns = Ejb3Column.buildColumnFromAnnotation(
 1187   					null, null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings
 1188   			);
 1189   		}
 1190   
 1191   		if ( nullability == Nullability.FORCED_NOT_NULL ) {
 1192   			//force columns to not null
 1193   			for ( Ejb3Column col : columns ) {
 1194   				col.forceNotNull();
 1195   			}
 1196   		}
 1197   
 1198   		final XClass returnedClass = inferredData.getClassOrElement();
 1199   		if ( !entityBinder.isIgnoreIdAnnotations() &&
 1200   				( property.isAnnotationPresent( Id.class )
 1201   						|| property.isAnnotationPresent( EmbeddedId.class ) ) ) {
 1202   			if ( isIdentifierMapper ) {
 1203   				throw new AnnotationException(
 1204   						"@IdClass class should not have @Id nor @EmbeddedId properties"
 1205   				);
 1206   			}
 1207   			log.debug( inferredData.getPropertyName() + " is an id" );
 1208   			//clone classGenerator and override with local values
 1209   			HashMap<String, IdGenerator> localGenerators = (HashMap<String, IdGenerator>) classGenerators.clone();
 1210   			localGenerators.putAll( buildLocalGenerators( property, mappings ) );
 1211   
 1212   			//manage composite related metadata
 1213   			//guess if its a component and find id data access (property, field etc)
 1214   			final boolean isComponent = returnedClass.isAnnotationPresent( Embeddable.class )
 1215   					|| property.isAnnotationPresent( EmbeddedId.class );
 1216   			boolean propertyAnnotated = entityBinder.isPropertyAnnotated( returnedClass );
 1217   			String propertyAccessor = entityBinder.getPropertyAccessor( returnedClass );
 1218   			//if ( isComponent && embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
 1219   
 1220   			GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
 1221   			String generatorType = generatedValue != null ?
 1222   					generatorType( generatedValue.strategy() ) :
 1223   					"assigned";
 1224   			String generator = generatedValue != null ?
 1225   					generatedValue.generator() :
 1226   					BinderHelper.ANNOTATION_STRING_DEFAULT;
 1227   			if ( isComponent ) generatorType = "assigned"; //a component must not have any generator
 1228   			Type typeAnn = property.getAnnotation( Type.class );
 1229   			bindId(
 1230   					generatorType,
 1231   					generator,
 1232   					inferredData,
 1233   					columns,
 1234   					propertyHolder,
 1235   					localGenerators,
 1236   					isComponent,
 1237   					propertyAnnotated,
 1238   					propertyAccessor, entityBinder,
 1239   					typeAnn,
 1240   					false,
 1241   					isIdentifierMapper, mappings
 1242   			);
 1243   			if ( log.isDebugEnabled() ) {
 1244   				log.debug(
 1245   						"Bind " + ( isComponent ?
 1246   								"@EmbeddedId" :
 1247   								"@Id" ) + " on " + inferredData.getPropertyName()
 1248   				);
 1249   			}
 1250   		}
 1251   		else if ( property.isAnnotationPresent( Version.class ) ) {
 1252   			if ( isIdentifierMapper ) {
 1253   				throw new AnnotationException(
 1254   						"@IdClass class should not have @Version property"
 1255   				);
 1256   			}
 1257   			if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
 1258   				throw new AnnotationException(
 1259   						"Unable to define/override @Version on a subclass: "
 1260   								+ propertyHolder.getEntityName()
 1261   				);
 1262   			}
 1263   			log.debug( inferredData.getPropertyName() + " is a version property" );
 1264   			RootClass rootClass = (RootClass) propertyHolder.getPersistentClass();
 1265   			boolean lazy = false;
 1266   			PropertyBinder propBinder = new PropertyBinder();
 1267   			propBinder.setName( inferredData.getPropertyName() );
 1268   			propBinder.setReturnedClassName( inferredData.getTypeName() );
 1269   			propBinder.setLazy( lazy );
 1270   			propBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
 1271   			propBinder.setColumns( columns );
 1272   			propBinder.setHolder( propertyHolder ); //PropertyHolderBuilder.buildPropertyHolder(rootClass)
 1273   			propBinder.setProperty( property );
 1274   			propBinder.setReturnedClass( inferredData.getPropertyClass() );
 1275   
 1276   			propBinder.setMappings( mappings );
 1277   			Property prop = propBinder.bind();
 1278   			rootClass.setVersion( prop );
 1279   			SimpleValue simpleValue = (SimpleValue) prop.getValue();
 1280   			if ( !simpleValue.isTypeSpecified() ) simpleValue.setTypeName( "integer" );
 1281   			simpleValue.setNullValue( "undefined" );
 1282   			rootClass.setOptimisticLockMode( Versioning.OPTIMISTIC_LOCK_VERSION );
 1283   			log.debug(
 1284   					"Version name: " + rootClass.getVersion().getName() + ", unsavedValue: " + ( (SimpleValue) rootClass
 1285   							.getVersion()
 1286   							.getValue() ).getNullValue()
 1287   			);
 1288   		}
 1289   		else if ( property.isAnnotationPresent( ManyToOne.class ) ) {
 1290   			ManyToOne ann = property.getAnnotation( ManyToOne.class );
 1291   
 1292   			//check validity
 1293   			if ( property.isAnnotationPresent( Column.class )
 1294   					|| property.isAnnotationPresent( Columns.class ) ) {
 1295   				throw new AnnotationException( "@Column(s) not allowed on a @ManyToOne property: "
 1296   						+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() ) );
 1297   			}
 1298   
 1299   			Cascade hibernateCascade = property.getAnnotation( Cascade.class );
 1300   			NotFound notFound = property.getAnnotation( NotFound.class );
 1301   			boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
 1302   			OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
 1303   			boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
 1304   			JoinTable assocTable = property.getAnnotation( JoinTable.class );
 1305   			if ( assocTable != null ) {
 1306   				Join join = propertyHolder.addJoin( assocTable, false );
 1307   				for ( Ejb3JoinColumn joinColumn : joinColumns ) {
 1308   					joinColumn.setSecondaryTableName( join.getTable().getName() );
 1309   				}
 1310   			}
 1311   			bindManyToOne(
 1312   					getCascadeStrategy( ann.cascade(), hibernateCascade ),
 1313   					joinColumns,
 1314   					ann.optional(),
 1315   					ignoreNotFound, onDeleteCascade,
 1316   					mappings.getReflectionManager().toXClass( ann.targetEntity() ),
 1317   					propertyHolder,
 1318   					inferredData, false, isIdentifierMapper, inSecondPass, mappings
 1319   			);
 1320   		}
 1321   		else if ( property.isAnnotationPresent( OneToOne.class ) ) {
 1322   			OneToOne ann = property.getAnnotation( OneToOne.class );
 1323   
 1324   			//check validity
 1325   			if ( property.isAnnotationPresent( Column.class )
 1326   					|| property.isAnnotationPresent( Columns.class ) ) {
 1327   				throw new AnnotationException( "@Column(s) not allowed on a @OneToOne property: "
 1328   						+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() ) );
 1329   			}
 1330   
 1331   			//FIXME support a proper PKJCs
 1332   			boolean trueOneToOne = property.isAnnotationPresent( PrimaryKeyJoinColumn.class )
 1333   					|| property.isAnnotationPresent( PrimaryKeyJoinColumns.class );
 1334   			Cascade hibernateCascade = property.getAnnotation( Cascade.class );
 1335   			NotFound notFound = property.getAnnotation( NotFound.class );
 1336   			boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
 1337   			OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
 1338   			boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
 1339   			JoinTable assocTable = property.getAnnotation( JoinTable.class );
 1340   			if ( assocTable != null ) {
 1341   				Join join = propertyHolder.addJoin( assocTable, false );
 1342   				for ( Ejb3JoinColumn joinColumn : joinColumns ) {
 1343   					joinColumn.setSecondaryTableName( join.getTable().getName() );
 1344   				}
 1345   			}
 1346   			bindOneToOne(
 1347   					getCascadeStrategy( ann.cascade(), hibernateCascade ),
 1348   					joinColumns,
 1349   					ann.optional(),
 1350   					getFetchMode( ann.fetch() ),
 1351   					ignoreNotFound, onDeleteCascade,
 1352   					mappings.getReflectionManager().toXClass( ann.targetEntity() ),
 1353   					propertyHolder,
 1354   					inferredData, ann.mappedBy(), trueOneToOne, isIdentifierMapper, inSecondPass, mappings
 1355   			);
 1356   		}
 1357   		else if ( property.isAnnotationPresent( OneToMany.class )
 1358   				|| property.isAnnotationPresent( ManyToMany.class )
 1359   				|| property.isAnnotationPresent( CollectionOfElements.class ) ) {
 1360   			OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
 1361   			ManyToMany manyToManyAnn = property.getAnnotation( ManyToMany.class );
 1362   			CollectionOfElements collectionOfElementsAnn = property.getAnnotation( CollectionOfElements.class );
 1363   			org.hibernate.annotations.IndexColumn indexAnn = property.getAnnotation(
 1364   					org.hibernate.annotations.IndexColumn.class
 1365   			);
 1366   			JoinTable assocTable = property.getAnnotation( JoinTable.class );
 1367   
 1368   			IndexColumn indexColumn = IndexColumn.buildColumnFromAnnotation(
 1369   					indexAnn, propertyHolder, inferredData, mappings
 1370   			);
 1371   			CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
 1372   					propertyHolder.getEntityName(),
 1373   					property,
 1374   					!indexColumn.isImplicit()
 1375   			);
 1376   			collectionBinder.setIndexColumn( indexColumn );
 1377   			MapKey mapKeyAnn = property.getAnnotation( MapKey.class );
 1378   			collectionBinder.setMapKey( mapKeyAnn );
 1379   			collectionBinder.setPropertyName( inferredData.getPropertyName() );
 1380   			BatchSize batchAnn = property.getAnnotation( BatchSize.class );
 1381   			collectionBinder.setBatchSize( batchAnn );
 1382   			javax.persistence.OrderBy ejb3OrderByAnn = property.getAnnotation( javax.persistence.OrderBy.class );
 1383   			OrderBy orderByAnn = property.getAnnotation( OrderBy.class );
 1384   			collectionBinder.setEjb3OrderBy( ejb3OrderByAnn );
 1385   			collectionBinder.setSqlOrderBy( orderByAnn );
 1386   			Sort sortAnn = property.getAnnotation( Sort.class );
 1387   			collectionBinder.setSort( sortAnn );
 1388   			Cache cachAnn = property.getAnnotation( Cache.class );
 1389   			collectionBinder.setCache( cachAnn );
 1390   			collectionBinder.setPropertyHolder( propertyHolder );
 1391   			Cascade hibernateCascade = property.getAnnotation( Cascade.class );
 1392   			NotFound notFound = property.getAnnotation( NotFound.class );
 1393   			boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
 1394   			collectionBinder.setIgnoreNotFound( ignoreNotFound );
 1395   			collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
 1396   			collectionBinder.setMappings( mappings );
 1397   			collectionBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
 1398   
 1399   			Ejb3Column[] elementColumns = null;
 1400   			PropertyData virtualProperty = new WrappedInferredData( inferredData, "element" );
 1401   			if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent(
 1402   					Formula.class
 1403   			) ) {
 1404   				Column ann = property.getAnnotation( Column.class );
 1405   				Formula formulaAnn = property.getAnnotation( Formula.class );
 1406   				elementColumns = Ejb3Column.buildColumnFromAnnotation(
 1407   						new Column[]{ann},
 1408   						formulaAnn,
 1409   						nullability,
 1410   						propertyHolder,
 1411   						virtualProperty,
 1412   						entityBinder.getSecondaryTables(),
 1413   						mappings
 1414   				);
 1415   			}
 1416   			else if ( property.isAnnotationPresent( Columns.class ) ) {
 1417   				Columns anns = property.getAnnotation( Columns.class );
 1418   				elementColumns = Ejb3Column.buildColumnFromAnnotation(
 1419   						anns.columns(), null, nullability, propertyHolder, virtualProperty,
 1420   						entityBinder.getSecondaryTables(), mappings
 1421   				);
 1422   			}
 1423   			else {
 1424   				elementColumns = Ejb3Column.buildColumnFromAnnotation(
 1425   						null,
 1426   						null,
 1427   						nullability,
 1428   						propertyHolder,
 1429   						virtualProperty,
 1430   						entityBinder.getSecondaryTables(),
 1431   						mappings
 1432   				);
 1433   			}
 1434   
 1435   			org.hibernate.annotations.MapKey hibMapKeyAnn = property.getAnnotation(
 1436   					org.hibernate.annotations.MapKey.class
 1437   			);
 1438   			PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
 1439   			Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
 1440   					hibMapKeyAnn != null && hibMapKeyAnn.columns().length > 0 ?
 1441   							hibMapKeyAnn.columns() :
 1442   							null,
 1443   					null,
 1444   					Nullability.FORCED_NOT_NULL,
 1445   					propertyHolder,
 1446   					mapKeyVirtualProperty,
 1447   					entityBinder.getSecondaryTables(),
 1448   					mappings
 1449   			);
 1450   			collectionBinder.setMapKeyColumns( mapColumns );
 1451   
 1452   			MapKeyManyToMany mapKeyManyToMany = property.getAnnotation( MapKeyManyToMany.class );
 1453   			Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumns(
 1454   					mapKeyManyToMany != null ?
 1455   							mapKeyManyToMany.joinColumns() :
 1456   							null,
 1457   					null, entityBinder.getSecondaryTables(),
 1458   					propertyHolder, mapKeyVirtualProperty.getPropertyName(), mappings
 1459   			);
 1460   			collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
 1461   
 1462   			//potential element
 1463   			collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
 1464   			collectionBinder.setElementColumns( elementColumns );
 1465   			collectionBinder.setProperty( property );
 1466   
 1467   			if ( oneToManyAnn != null && manyToManyAnn != null ) {
 1468   				throw new AnnotationException(
 1469   						"@OneToMany and @ManyToMany on the same property is not allowed: "
 1470   								+ propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
 1471   				);
 1472   			}
 1473   			String mappedBy = null;
 1474   			if ( oneToManyAnn != null ) {
 1475   				for ( Ejb3JoinColumn column : joinColumns ) {
 1476   					if ( column.isSecondary() ) {
 1477   						throw new NotYetImplementedException( "Collections having FK in secondary table" );
 1478   					}
 1479   				}
 1480   				collectionBinder.setFkJoinColumns( joinColumns );
 1481   				mappedBy = oneToManyAnn.mappedBy();
 1482   				collectionBinder.setTargetEntity(
 1483   						mappings.getReflectionManager().toXClass( oneToManyAnn.targetEntity() )
 1484   				);
 1485   				collectionBinder.setCascadeStrategy( getCascadeStrategy( oneToManyAnn.cascade(), hibernateCascade ) );
 1486   				collectionBinder.setOneToMany( true );
 1487   			}
 1488   			else if ( collectionOfElementsAnn != null ) {
 1489   				for ( Ejb3JoinColumn column : joinColumns ) {
 1490   					if ( column.isSecondary() ) {
 1491   						throw new NotYetImplementedException( "Collections having FK in secondary table" );
 1492   					}
 1493   				}
 1494   				collectionBinder.setFkJoinColumns( joinColumns );
 1495   				mappedBy = "";
 1496   				collectionBinder.setTargetEntity(
 1497   						mappings.getReflectionManager().toXClass( collectionOfElementsAnn.targetElement() )
 1498   				);
 1499   				//collectionBinder.setCascadeStrategy( getCascadeStrategy( embeddedCollectionAnn.cascade(), hibernateCascade ) );
 1500   				collectionBinder.setOneToMany( true );
 1501   			}
 1502   			else if ( manyToManyAnn != null ) {
 1503   				mappedBy = manyToManyAnn.mappedBy();
 1504   				collectionBinder.setTargetEntity(
 1505   						mappings.getReflectionManager().toXClass( manyToManyAnn.targetEntity() )
 1506   				);
 1507   				collectionBinder.setCascadeStrategy( getCascadeStrategy( manyToManyAnn.cascade(), hibernateCascade ) );
 1508   				collectionBinder.setOneToMany( false );
 1509   			}
 1510   			collectionBinder.setMappedBy( mappedBy );
 1511   			bindJoinedTableAssociation(
 1512   					assocTable, mappings, entityBinder, collectionBinder, propertyHolder, inferredData, mappedBy
 1513   			);
 1514   
 1515   			OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
 1516   			boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
 1517   			collectionBinder.setCascadeDeleteEnabled( onDeleteCascade );
 1518   			if ( isIdentifierMapper ) {
 1519   				collectionBinder.setInsertable( false );
 1520   				collectionBinder.setUpdatable( false );
 1521   			}
 1522   			if ( property.isAnnotationPresent( CollectionId.class ) ) { //do not compute the generators unless necessary
 1523   				HashMap<String, IdGenerator> localGenerators = (HashMap<String, IdGenerator>) classGenerators.clone();
 1524   				localGenerators.putAll( buildLocalGenerators( property, mappings ) );
 1525   				collectionBinder.setLocalGenerators( localGenerators );
 1526   
 1527   			}
 1528   			collectionBinder.bind();
 1529   
 1530   		}
 1531   		else {
 1532   			//define whether the type is a component or not
 1533   			boolean isComponent = false;
 1534   			Embeddable embeddableAnn = (Embeddable) returnedClass.getAnnotation( Embeddable.class );
 1535   			Embedded embeddedAnn = (Embedded) property.getAnnotation( Embedded.class );
 1536   			isComponent = embeddedAnn != null || embeddableAnn != null;
 1537   
 1538   			if ( isComponent ) {
 1539   				//process component object
 1540   				//boolean propertyAccess = true;
 1541   				//if ( embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
 1542   				boolean propertyAnnotated = entityBinder.isPropertyAnnotated( property );
 1543   				String propertyAccessor = entityBinder.getPropertyAccessor( property );
 1544   				bindComponent(
 1545   						inferredData, propertyHolder, propertyAnnotated, propertyAccessor, entityBinder,
 1546   						isIdentifierMapper,
 1547   						mappings, isComponentEmbedded
 1548   				);
 1549   			}
 1550   			else {
 1551   				//provide the basic property mapping
 1552   				boolean optional = true;
 1553   				boolean lazy = false;
 1554   				if ( property.isAnnotationPresent( Basic.class ) ) {
 1555   					Basic ann = property.getAnnotation( Basic.class );
 1556   					optional = ann.optional();
 1557   					lazy = ann.fetch() == FetchType.LAZY;
 1558   				}
 1559   				//implicit type will check basic types and Serializable classes
 1560   				if ( !optional && nullability != Nullability.FORCED_NULL ) {
 1561   					//force columns to not null
 1562   					for ( Ejb3Column col : columns ) {
 1563   						col.forceNotNull();
 1564   					}
 1565   				}
 1566   
 1567   				PropertyBinder propBinder = new PropertyBinder();
 1568   				propBinder.setName( inferredData.getPropertyName() );
 1569   				propBinder.setReturnedClassName( inferredData.getTypeName() );
 1570   				propBinder.setLazy( lazy );
 1571   				propBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
 1572   				propBinder.setColumns( columns );
 1573   				propBinder.setHolder( propertyHolder );
 1574   				propBinder.setProperty( property );
 1575   				propBinder.setReturnedClass( inferredData.getPropertyClass() );
 1576   				propBinder.setMappings( mappings );
 1577   				if ( isIdentifierMapper ) {
 1578   					propBinder.setInsertable( false );
 1579   					propBinder.setUpdatable( false );
 1580   				}
 1581   				propBinder.bind();
 1582   			}
 1583   		}
 1584   		//init index
 1585   		//process indexes after everything: in second pass, many to one has to be done before indexes
 1586   		Index index = property.getAnnotation( Index.class );
 1587   		if ( index != null ) {
 1588   			if ( joinColumns != null ) {
 1589   				for ( Ejb3Column column : joinColumns ) {
 1590   					column.addIndex( index, inSecondPass );
 1591   				}
 1592   			}
 1593   			else {
 1594   				for ( Ejb3Column column : columns ) {
 1595   					column.addIndex( index, inSecondPass );
 1596   				}
 1597   			}
 1598   		}
 1599   	}
 1600   
 1601   	//TODO move that to collection binder?
 1602   	private static void bindJoinedTableAssociation(
 1603   			JoinTable joinTableAnn, ExtendedMappings mappings, EntityBinder entityBinder,
 1604   			CollectionBinder collectionBinder, PropertyHolder propertyHolder, PropertyData inferredData,
 1605   			String mappedBy
 1606   	) {
 1607   		TableBinder associationTableBinder = new TableBinder();
 1608   		JoinColumn[] annJoins;
 1609   		JoinColumn[] annInverseJoins;
 1610   		if ( joinTableAnn != null ) {
 1611   			collectionBinder.setExplicitAssociationTable( true );
 1612   			if ( !BinderHelper.isDefault( joinTableAnn.schema() ) )
 1613   				associationTableBinder.setSchema( joinTableAnn.schema() );
 1614   			if ( !BinderHelper.isDefault( joinTableAnn.catalog() ) )
 1615   				associationTableBinder.setCatalog( joinTableAnn.catalog() );
 1616   			if ( !BinderHelper.isDefault( joinTableAnn.name() ) ) associationTableBinder.setName( joinTableAnn.name() );
 1617   			associationTableBinder.setUniqueConstraints( joinTableAnn.uniqueConstraints() );
 1618   
 1619   			//set check constaint in the second pass
 1620   
 1621   			JoinColumn[] joins = joinTableAnn.joinColumns();
 1622   
 1623   			if ( joins.length == 0 ) {
 1624   				annJoins = null;
 1625   			}
 1626   			else {
 1627   				annJoins = joins;
 1628   			}
 1629   
 1630   			JoinColumn[] inverseJoins = joinTableAnn.inverseJoinColumns();
 1631   
 1632   			if ( inverseJoins.length == 0 ) {
 1633   				annInverseJoins = null;
 1634   			}
 1635   			else {
 1636   				annInverseJoins = inverseJoins;
 1637   			}
 1638   		}
 1639   		else {
 1640   			annJoins = null;
 1641   			annInverseJoins = null;
 1642   		}
 1643   		Ejb3JoinColumn[] joinColumns = Ejb3JoinColumn.buildJoinTableJoinColumns(
 1644   				annJoins, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), mappedBy,
 1645   				mappings
 1646   		);
 1647   		Ejb3JoinColumn[] inverseJoinColumns = Ejb3JoinColumn.buildJoinTableJoinColumns(
 1648   				annInverseJoins, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(),
 1649   				mappedBy, mappings
 1650   		);
 1651   		associationTableBinder.setMappings( mappings );
 1652   		collectionBinder.setTableBinder( associationTableBinder );
 1653   		collectionBinder.setJoinColumns( joinColumns );
 1654   		collectionBinder.setInverseJoinColumns( inverseJoinColumns );
 1655   	}
 1656   
 1657   	private static void bindComponent(
 1658   			PropertyData inferredData,
 1659   			PropertyHolder propertyHolder,
 1660   			boolean propertyAnnotated,
 1661   			String propertyAccessor, EntityBinder entityBinder,
 1662   			boolean isIdentifierMapper,
 1663   			ExtendedMappings mappings, boolean isComponentEmbedded
 1664   	) {
 1665   		Component comp = fillComponent(
 1666   				propertyHolder, inferredData, propertyAnnotated, propertyAccessor, true, entityBinder,
 1667   				isComponentEmbedded, isIdentifierMapper,
 1668   				false, mappings
 1669   		);
 1670   		XProperty property = inferredData.getProperty();
 1671   		setupComponentTuplizer( property, comp );
 1672   
 1673   		PropertyBinder binder = new PropertyBinder();
 1674   		binder.setName( inferredData.getPropertyName() );
 1675   		binder.setValue( comp );
 1676   		binder.setProperty( inferredData.getProperty() );
 1677   		binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
 1678   		Property prop = binder.make();
 1679   		propertyHolder.addProperty( prop );
 1680   	}
 1681   
 1682   	public static Component fillComponent(
 1683   			PropertyHolder propertyHolder, PropertyData inferredData,
 1684   			boolean propertyAnnotated, String propertyAccessor, boolean isNullable,
 1685   			EntityBinder entityBinder,
 1686   			boolean isComponentEmbedded, boolean isIdentifierMapper, boolean inSecondPass, ExtendedMappings mappings
 1687   	) {
 1688   		/**
 1689   		 * inSecondPass can only be used to apply right away the second pass of a composite-element
 1690   		 * Because it's a value type, there is no bidirectional association, hence second pass
 1691   		 * ordering does not matter
 1692   		 */
 1693   		Component comp = new Component( propertyHolder.getPersistentClass() );
 1694   		comp.setEmbedded( isComponentEmbedded );
 1695   		//yuk
 1696   		comp.setTable( propertyHolder.getTable() );
 1697   		if ( !isIdentifierMapper ) {
 1698   			comp.setComponentClassName( inferredData.getClassOrElementName() );
 1699   		}
 1700   		else {
 1701   			comp.setComponentClassName( comp.getOwner().getClassName() );
 1702   		}
 1703   		comp.setNodeName( inferredData.getPropertyName() );
 1704   		String subpath = StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() );
 1705   		log.debug( "Binding component with path: " + subpath );
 1706   		PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(
 1707   				comp, subpath,
 1708   				inferredData, propertyHolder, mappings
 1709   		);
 1710   		List<PropertyData> classElements = new ArrayList<PropertyData>();
 1711   		XClass returnedClassOrElement = inferredData.getClassOrElement();
 1712   		addElementsOfAClass(
 1713   				classElements,
 1714   				subHolder,
 1715   				propertyAnnotated,
 1716   				propertyAccessor, returnedClassOrElement, mappings
 1717   		);
 1718   		//add elements of the embeddable superclass
 1719   		XClass superClass = inferredData.getPropertyClass().getSuperclass();
 1720   		while ( superClass != null && superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
 1721   			//FIXME: proper support of typevariables incl var resolved at upper levels
 1722   			addElementsOfAClass(
 1723   					classElements,
 1724   					subHolder,
 1725   					entityBinder.isPropertyAnnotated( superClass ),
 1726   					propertyAccessor, superClass, mappings
 1727   			);
 1728   			superClass = superClass.getSuperclass();
 1729   		}
 1730   		for ( PropertyData propertyAnnotatedElement : classElements ) {
 1731   			processElementAnnotations(
 1732   					subHolder, isNullable ?
 1733   					Nullability.NO_CONSTRAINT :
 1734   					Nullability.FORCED_NOT_NULL,
 1735   					propertyAnnotatedElement.getProperty(), propertyAnnotatedElement,
 1736   					new HashMap<String, IdGenerator>(), entityBinder, isIdentifierMapper, isComponentEmbedded,
 1737   					inSecondPass, mappings
 1738   			);
 1739   		}
 1740   		return comp;
 1741   	}
 1742   
 1743   	private static void bindId(
 1744   			String generatorType, String generatorName,
 1745   			PropertyData inferredData, Ejb3Column[] columns, PropertyHolder propertyHolder,
 1746   			Map<String, IdGenerator> localGenerators,
 1747   			boolean isComposite,
 1748   			boolean isPropertyAnnotated,
 1749   			String propertyAccessor, EntityBinder entityBinder, Type typeAnn, boolean isEmbedded,
 1750   			boolean isIdentifierMapper, ExtendedMappings mappings
 1751   	) {
 1752   		/*
 1753   		 * Fill simple value and property since and Id is a property
 1754   		 */
 1755   		PersistentClass persistentClass = propertyHolder.getPersistentClass();
 1756   		if ( !( persistentClass instanceof RootClass ) ) {
 1757   			throw new AnnotationException(
 1758   					"Unable to define/override @Id(s) on a subclass: "
 1759   							+ propertyHolder.getEntityName()
 1760   			);
 1761   		}
 1762   		RootClass rootClass = (RootClass) persistentClass;
 1763   		String persistentClassName = rootClass == null ?
 1764   				null :
 1765   				rootClass.getClassName();
 1766   		SimpleValue id;
 1767   		if ( isComposite ) {
 1768   			id = fillComponent(
 1769   					propertyHolder, inferredData, isPropertyAnnotated, propertyAccessor,
 1770   					false, entityBinder, isEmbedded, isIdentifierMapper, false, mappings
 1771   			);
 1772   			Component componentId = (Component) id;
 1773   			componentId.setKey( true );
 1774   			if ( componentId.getPropertySpan() == 0 ) {
 1775   				throw new AnnotationException( componentId.getComponentClassName() + " has no persistent id property" );
 1776   			}
 1777   			//tuplizers
 1778   			XProperty property = inferredData.getProperty();
 1779   			setupComponentTuplizer( property, componentId );
 1780   		}
 1781   		else {
 1782   			for ( Ejb3Column column : columns ) {
 1783   				column.forceNotNull(); //this is an id
 1784   			}
 1785   			SimpleValueBinder value = new SimpleValueBinder();
 1786   			value.setPropertyName( inferredData.getPropertyName() );
 1787   			value.setReturnedClassName( inferredData.getTypeName() );
 1788   			value.setColumns( columns );
 1789   			value.setPersistentClassName( persistentClassName );
 1790   			value.setMappings( mappings );
 1791   			value.setExplicitType( typeAnn );
 1792   			id = value.make();
 1793   		}
 1794   		rootClass.setIdentifier( id );
 1795   		BinderHelper.makeIdGenerator( id, generatorType, generatorName, mappings, localGenerators );
 1796   		if ( isEmbedded ) {
 1797   			rootClass.setEmbeddedIdentifier( inferredData.getPropertyClass() == null );
 1798   		}
 1799   		else {
 1800   			PropertyBinder binder = new PropertyBinder();
 1801   			binder.setName( inferredData.getPropertyName() );
 1802   			binder.setValue( id );
 1803   			binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
 1804   			binder.setProperty( inferredData.getProperty() );
 1805   			Property prop = binder.make();
 1806   			rootClass.setIdentifierProperty( prop );
 1807   		}
 1808   	}
 1809   
 1810   	private static void setupComponentTuplizer(XProperty property, Component component) {
 1811   		if ( property == null ) return;
 1812   		if ( property.isAnnotationPresent( Tuplizers.class ) ) {
 1813   			for ( Tuplizer tuplizer : property.getAnnotation( Tuplizers.class ).value() ) {
 1814   				EntityMode mode = EntityMode.parse( tuplizer.entityMode() );
 1815   				component.addTuplizer( mode, tuplizer.impl().getName() );
 1816   			}
 1817   		}
 1818   		if ( property.isAnnotationPresent( Tuplizer.class ) ) {
 1819   			Tuplizer tuplizer = property.getAnnotation( Tuplizer.class );
 1820   			EntityMode mode = EntityMode.parse( tuplizer.entityMode() );
 1821   			component.addTuplizer( mode, tuplizer.impl().getName() );
 1822   		}
 1823   	}
 1824   
 1825   	private static void bindManyToOne(
 1826   			String cascadeStrategy, Ejb3JoinColumn[] columns, boolean optional,
 1827   			boolean ignoreNotFound, boolean cascadeOnDelete,
 1828   			XClass targetEntity, PropertyHolder propertyHolder,
 1829   			PropertyData inferredData, boolean unique, boolean isIdentifierMapper, boolean inSecondPass,
 1830   			ExtendedMappings mappings
 1831   	) {
 1832   		//All FK columns should be in the same table
 1833   		org.hibernate.mapping.ManyToOne value = new org.hibernate.mapping.ManyToOne( columns[0].getTable() );
 1834   		if ( isDefault( targetEntity, mappings ) ) {
 1835   			value.setReferencedEntityName( inferredData.getClassOrElementName() );
 1836   		}
 1837   		else {
 1838   			value.setReferencedEntityName( targetEntity.getName() );
 1839   		}
 1840   		defineFetchingStrategy( value, inferredData.getProperty() );
 1841   		//value.setFetchMode( fetchMode );
 1842   		value.setIgnoreNotFound( ignoreNotFound );
 1843   		value.setCascadeDeleteEnabled( cascadeOnDelete );
 1844   		//value.setLazy( fetchMode != FetchMode.JOIN );
 1845   		if ( !optional ) {
 1846   			for ( Ejb3JoinColumn column : columns ) {
 1847   				column.setNullable( false );
 1848   			}
 1849   		}
 1850   		value.setTypeName( inferredData.getClassOrElementName() );
 1851   		final String propertyName = inferredData.getPropertyName();
 1852   		value.setTypeUsingReflection( propertyHolder.getClassName(), propertyName );
 1853   
 1854   		ForeignKey fk = inferredData.getProperty().getAnnotation( ForeignKey.class );
 1855   		String fkName = fk != null ?
 1856   				fk.name() :
 1857   				"";
 1858   		if ( !BinderHelper.isDefault( fkName ) ) value.setForeignKeyName( fkName );
 1859   
 1860   		String path = propertyHolder.getPath() + "." + propertyName;
 1861   		FkSecondPass secondPass = new FkSecondPass(
 1862   				value, columns,
 1863   				!optional && unique, //cannot have nullabe and unique on certain DBs like Derby
 1864   				propertyHolder.getEntityOwnerClassName(),
 1865   				path, mappings
 1866   		);
 1867   		if ( inSecondPass ) {
 1868   			secondPass.doSecondPass( mappings.getClasses() );
 1869   		}
 1870   		else {
 1871   			mappings.addSecondPass(
 1872   					secondPass
 1873   			);
 1874   		}
 1875   		Ejb3Column.checkPropertyConsistency( columns, propertyHolder.getEntityName() + propertyName );
 1876   		PropertyBinder binder = new PropertyBinder();
 1877   		binder.setName( propertyName );
 1878   		binder.setValue( value );
 1879   		//binder.setCascade(cascadeStrategy);
 1880   		if ( isIdentifierMapper ) {
 1881   			binder.setInsertable( false );
 1882   			binder.setInsertable( false );
 1883   		}
 1884   		else {
 1885   			binder.setInsertable( columns[0].isInsertable() );
 1886   			binder.setUpdatable( columns[0].isUpdatable() );
 1887   		}
 1888   		binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
 1889   		binder.setCascade( cascadeStrategy );
 1890   		Property prop = binder.make();
 1891   		//composite FK columns are in the same table so its OK
 1892   		propertyHolder.addProperty( prop, columns );
 1893   	}
 1894   
 1895   	protected static void defineFetchingStrategy(ToOne toOne, XProperty property) {
 1896   		LazyToOne lazy = property.getAnnotation( LazyToOne.class );
 1897   		Fetch fetch = property.getAnnotation( Fetch.class );
 1898   		ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
 1899   		OneToOne oneToOne = property.getAnnotation( OneToOne.class );
 1900   		FetchType fetchType;
 1901   		if ( manyToOne != null ) {
 1902   			fetchType = manyToOne.fetch();
 1903   		}
 1904   		else if ( oneToOne != null ) {
 1905   			fetchType = oneToOne.fetch();
 1906   		}
 1907   		else {
 1908   			throw new AssertionFailure(
 1909   					"Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne"
 1910   			);
 1911   		}
 1912   		if ( lazy != null ) {
 1913   			toOne.setLazy( !( lazy.value() == LazyToOneOption.FALSE ) );
 1914   			toOne.setUnwrapProxy( ( lazy.value() == LazyToOneOption.NO_PROXY ) );
 1915   		}
 1916   		else {
 1917   			toOne.setLazy( fetchType == FetchType.LAZY );
 1918   			toOne.setUnwrapProxy( false );
 1919   		}
 1920   		if ( fetch != null ) {
 1921   			if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
 1922   				toOne.setFetchMode( FetchMode.JOIN );
 1923   				toOne.setLazy( false );
 1924   				toOne.setUnwrapProxy( false );
 1925   			}
 1926   			else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
 1927   				toOne.setFetchMode( FetchMode.SELECT );
 1928   			}
 1929   			else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
 1930   				throw new AnnotationException( "Use of FetchMode.SUBSELECT not allowed on ToOne associations" );
 1931   			}
 1932   			else {
 1933   				throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
 1934   			}
 1935   		}
 1936   		else {
 1937   			toOne.setFetchMode( getFetchMode( fetchType ) );
 1938   		}
 1939   	}
 1940   
 1941   	private static void bindOneToOne(
 1942   			String cascadeStrategy,
 1943   			Ejb3JoinColumn[] joinColumns,
 1944   			boolean optional,
 1945   			FetchMode fetchMode,
 1946   			boolean ignoreNotFound,
 1947   			boolean cascadeOnDelete,
 1948   			XClass targetEntity,
 1949   			PropertyHolder propertyHolder,
 1950   			PropertyData inferredData, String mappedBy,
 1951   			boolean trueOneToOne,
 1952   			boolean isIdentifierMapper, boolean inSecondPass, ExtendedMappings mappings
 1953   	) {
 1954   		//column.getTable() => persistentClass.getTable()
 1955   		final String propertyName = inferredData.getPropertyName();
 1956   		log.debug( "Fetching " + propertyName + " with " + fetchMode );
 1957   		boolean mapToPK = true;
 1958   		if ( !trueOneToOne ) {
 1959   			//try to find a hidden true one to one (FK == PK columns)
 1960   			Iterator idColumns = propertyHolder.getIdentifier().getColumnIterator();
 1961   			List<String> idColumnNames = new ArrayList<String>();
 1962   			org.hibernate.mapping.Column currentColumn;
 1963   			while ( idColumns.hasNext() ) {
 1964   				currentColumn = (org.hibernate.mapping.Column) idColumns.next();
 1965   				idColumnNames.add( currentColumn.getName() );
 1966   			}
 1967   			for ( Ejb3JoinColumn col : joinColumns ) {
 1968   				if ( !idColumnNames.contains( col.getMappingColumn().getName() ) ) {
 1969   					mapToPK = false;
 1970   					break;
 1971   				}
 1972   			}
 1973   		}
 1974   		if ( trueOneToOne || mapToPK || !BinderHelper.isDefault( mappedBy ) ) {
 1975   			//is a true one-to-one
 1976   			//FIXME referencedColumnName ignored => ordering may fail.
 1977   			OneToOneSecondPass secondPass = new OneToOneSecondPass(
 1978   					mappedBy,
 1979   					propertyHolder.getEntityName(),
 1980   					propertyName,
 1981   					propertyHolder, inferredData, targetEntity, ignoreNotFound, cascadeOnDelete,
 1982   					optional, cascadeStrategy, joinColumns, mappings
 1983   			);
 1984   			if ( inSecondPass ) {
 1985   				secondPass.doSecondPass( mappings.getClasses() );
 1986   			}
 1987   			else {
 1988   				mappings.addSecondPass(
 1989   						secondPass, BinderHelper.isDefault( mappedBy )
 1990   				);
 1991   			}
 1992   		}
 1993   		else {
 1994   			//has a FK on the table
 1995   			bindManyToOne(
 1996   					cascadeStrategy, joinColumns, optional, ignoreNotFound, cascadeOnDelete,
 1997   					targetEntity,
 1998   					propertyHolder, inferredData, true, isIdentifierMapper, inSecondPass, mappings
 1999   			);
 2000   		}
 2001   	}
 2002   
 2003   	private static String generatorType(GenerationType generatorEnum) {
 2004   		switch (generatorEnum) {
 2005   			case IDENTITY:
 2006   				return "identity";
 2007   			case AUTO:
 2008   				return "native";
 2009   			case TABLE:
 2010   				return MultipleHiLoPerTableGenerator.class.getName();
 2011   			case SEQUENCE:
 2012   				return "seqhilo";
 2013   		}
 2014   		throw new AssertionFailure( "Unknown GeneratorType: " + generatorEnum );
 2015   	}
 2016   
 2017   	private static EnumSet<CascadeType> convertToHibernateCascadeType(javax.persistence.CascadeType[] ejbCascades) {
 2018   		EnumSet<CascadeType> hibernateCascadeSet = EnumSet.noneOf( CascadeType.class );
 2019   		if ( ejbCascades != null && ejbCascades.length > 0 ) {
 2020   			for ( javax.persistence.CascadeType cascade : ejbCascades ) {
 2021   				switch (cascade) {
 2022   					case ALL:
 2023   						hibernateCascadeSet.add( CascadeType.ALL );
 2024   						break;
 2025   					case PERSIST:
 2026   						hibernateCascadeSet.add( CascadeType.PERSIST );
 2027   						break;
 2028   					case MERGE:
 2029   						hibernateCascadeSet.add( CascadeType.MERGE );
 2030   						break;
 2031   					case REMOVE:
 2032   						hibernateCascadeSet.add( CascadeType.REMOVE );
 2033   						break;
 2034   					case REFRESH:
 2035   						hibernateCascadeSet.add( CascadeType.REFRESH );
 2036   						break;
 2037   				}
 2038   			}
 2039   		}
 2040   
 2041   		return hibernateCascadeSet;
 2042   	}
 2043   
 2044   	private static String getCascadeStrategy(
 2045   			javax.persistence.CascadeType[] ejbCascades, Cascade hibernateCascadeAnnotation
 2046   	) {
 2047   		EnumSet<CascadeType> hibernateCascadeSet = convertToHibernateCascadeType( ejbCascades );
 2048   		CascadeType[] hibernateCascades = hibernateCascadeAnnotation == null ?
 2049   				null :
 2050   				hibernateCascadeAnnotation.value();
 2051   
 2052   		if ( hibernateCascades != null && hibernateCascades.length > 0 ) {
 2053   			for ( CascadeType cascadeType : hibernateCascades ) {
 2054   				hibernateCascadeSet.add( cascadeType );
 2055   			}
 2056   		}
 2057   
 2058   		StringBuilder cascade = new StringBuilder();
 2059   		Iterator<CascadeType> cascadeType = hibernateCascadeSet.iterator();
 2060   		while ( cascadeType.hasNext() ) {
 2061   			switch (cascadeType.next()) {
 2062   				case ALL:
 2063   					cascade.append( "," ).append( "all" );
 2064   					break;
 2065   				case SAVE_UPDATE:
 2066   					cascade.append( "," ).append( "save-update" );
 2067   					break;
 2068   				case PERSIST:
 2069   					cascade.append( "," ).append( "persist" );
 2070   					break;
 2071   				case MERGE:
 2072   					cascade.append( "," ).append( "merge" );
 2073   					break;
 2074   				case LOCK:
 2075   					cascade.append( "," ).append( "lock" );
 2076   					break;
 2077   				case REFRESH:
 2078   					cascade.append( "," ).append( "refresh" );
 2079   					break;
 2080   				case REPLICATE:
 2081   					cascade.append( "," ).append( "replicate" );
 2082   					break;
 2083   				case EVICT:
 2084   					cascade.append( "," ).append( "evict" );
 2085   					break;
 2086   				case DELETE:
 2087   					cascade.append( "," ).append( "delete" );
 2088   					break;
 2089   				case DELETE_ORPHAN:
 2090   					cascade.append( "," ).append( "delete-orphan" );
 2091   					break;
 2092   				case REMOVE:
 2093   					cascade.append( "," ).append( "delete" );
 2094   					break;
 2095   			}
 2096   		}
 2097   		return cascade.length() > 0 ?
 2098   				cascade.substring( 1 ) :
 2099   				"none";
 2100   	}
 2101   
 2102   	public static FetchMode getFetchMode(FetchType fetch) {
 2103   		if ( fetch == FetchType.EAGER ) {
 2104   			return FetchMode.JOIN;
 2105   		}
 2106   		else {
 2107   			return FetchMode.SELECT;
 2108   		}
 2109   	}
 2110   
 2111   	private static HashMap<String, IdGenerator> buildLocalGenerators(XAnnotatedElement annElt, Mappings mappings) {
 2112   		HashMap<String, IdGenerator> generators = new HashMap<String, IdGenerator>();
 2113   		TableGenerator tabGen = annElt.getAnnotation( TableGenerator.class );
 2114   		SequenceGenerator seqGen = annElt.getAnnotation( SequenceGenerator.class );
 2115   		GenericGenerator genGen = annElt.getAnnotation( GenericGenerator.class );
 2116   		if ( tabGen != null ) {
 2117   			IdGenerator idGen = buildIdGenerator( tabGen, mappings );
 2118   			generators.put( idGen.getName(), idGen );
 2119   		}
 2120   		if ( seqGen != null ) {
 2121   			IdGenerator idGen = buildIdGenerator( seqGen, mappings );
 2122   			generators.put( idGen.getName(), idGen );
 2123   		}
 2124   		if ( genGen != null ) {
 2125   			IdGenerator idGen = buildIdGenerator( genGen, mappings );
 2126   			generators.put( idGen.getName(), idGen );
 2127   		}
 2128   		return generators;
 2129   	}
 2130   
 2131   	public static boolean isDefault(XClass clazz, ExtendedMappings mappings) {
 2132   		return mappings.getReflectionManager().equals( clazz, void.class );
 2133   	}
 2134   
 2135   	public static Map<XClass, InheritanceState> buildInheritanceStates(
 2136   			List<XClass> orderedClasses, ReflectionManager reflectionManager
 2137   	) {
 2138   		Map<XClass, InheritanceState> inheritanceStatePerClass = new HashMap<XClass, InheritanceState>(
 2139   				orderedClasses.size()
 2140   		);
 2141   		for ( XClass clazz : orderedClasses ) {
 2142   			InheritanceState superclassState = InheritanceState.getSuperclassInheritanceState(
 2143   					clazz, inheritanceStatePerClass,
 2144   					reflectionManager
 2145   			);
 2146   			InheritanceState state = new InheritanceState( clazz );
 2147   			if ( superclassState != null ) {
 2148   				//the classes are ordered thus preventing an NPE
 2149   				//FIXME if an entity has subclasses annotated @MappedSperclass wo sub @Entity this is wrong
 2150   				superclassState.hasSons = true;
 2151   				InheritanceState superEntityState = InheritanceState.getSuperEntityInheritanceState(
 2152   						clazz, inheritanceStatePerClass,
 2153   						reflectionManager
 2154   				);
 2155   				state.hasParents = superEntityState != null;
 2156   				final boolean nonDefault = state.type != null && !InheritanceType.SINGLE_TABLE.equals( state.type );
 2157   				if ( superclassState.type != null ) {
 2158   					final boolean mixingStrategy = state.type != null && !state.type.equals( superclassState.type );
 2159   					if ( nonDefault && mixingStrategy ) {
 2160   						log.warn(
 2161   								"Mixing inheritance strategy in a entity hierarchy is not allowed, ignoring sub strategy in: " + clazz
 2162   										.getName()
 2163   						);
 2164   					}
 2165   					state.type = superclassState.type;
 2166   				}
 2167   			}
 2168   			inheritanceStatePerClass.put( clazz, state );
 2169   		}
 2170   		return inheritanceStatePerClass;
 2171   	}
 2172   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]