KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > cfg > AnnotationBinder


1 //$Id: AnnotationBinder.java,v 1.122 2005/07/26 04:57:08 epbernard Exp $
2
package org.hibernate.cfg;
3
4 import java.lang.reflect.AnnotatedElement JavaDoc;
5 import java.lang.reflect.Field JavaDoc;
6 import java.lang.reflect.Method JavaDoc;
7 import java.util.ArrayList JavaDoc;
8 import java.util.EnumSet JavaDoc;
9 import java.util.HashMap JavaDoc;
10 import java.util.HashSet JavaDoc;
11 import java.util.Iterator JavaDoc;
12 import java.util.List JavaDoc;
13 import java.util.Map JavaDoc;
14 import java.util.Properties JavaDoc;
15 import java.util.ResourceBundle JavaDoc;
16 import java.util.Set JavaDoc;
17 import java.io.Serializable JavaDoc;
18 import javax.persistence.AccessType;
19 import javax.persistence.Basic;
20 import javax.persistence.Column;
21 import javax.persistence.DiscriminatorType;
22 import javax.persistence.Embeddable;
23 import javax.persistence.Embedded;
24 import javax.persistence.EmbeddedId;
25 import javax.persistence.Entity;
26 import javax.persistence.FetchType;
27 import javax.persistence.GeneratedIdTable;
28 import javax.persistence.GeneratorType;
29 import javax.persistence.Id;
30 import javax.persistence.IdClass;
31 import javax.persistence.InheritanceType;
32 import javax.persistence.JoinColumn;
33 import javax.persistence.JoinColumns;
34 import javax.persistence.JoinTable;
35 import javax.persistence.Lob;
36 import javax.persistence.LobType;
37 import javax.persistence.ManyToMany;
38 import javax.persistence.ManyToOne;
39 import javax.persistence.MapKey;
40 import javax.persistence.NamedNativeQueries;
41 import javax.persistence.NamedNativeQuery;
42 import javax.persistence.NamedQueries;
43 import javax.persistence.NamedQuery;
44 import javax.persistence.OneToMany;
45 import javax.persistence.OneToOne;
46 import javax.persistence.PrimaryKeyJoinColumn;
47 import javax.persistence.PrimaryKeyJoinColumns;
48 import javax.persistence.SequenceGenerator;
49 import javax.persistence.SqlResultSetMapping;
50 import javax.persistence.TableGenerator;
51 import javax.persistence.Transient;
52 import javax.persistence.UniqueConstraint;
53 import javax.persistence.Version;
54
55 import org.apache.commons.logging.Log;
56 import org.apache.commons.logging.LogFactory;
57 import org.hibernate.AnnotationException;
58 import org.hibernate.AssertionFailure;
59 import org.hibernate.FetchMode;
60 import org.hibernate.MappingException;
61 import org.hibernate.annotations.BatchSize;
62 import org.hibernate.annotations.Cache;
63 import org.hibernate.annotations.Cascade;
64 import org.hibernate.annotations.CascadeType;
65 import org.hibernate.annotations.Check;
66 import org.hibernate.annotations.Columns;
67 import org.hibernate.annotations.Filter;
68 import org.hibernate.annotations.FilterDef;
69 import org.hibernate.annotations.FilterDefs;
70 import org.hibernate.annotations.Filters;
71 import org.hibernate.annotations.Formula;
72 import org.hibernate.annotations.OnDelete;
73 import org.hibernate.annotations.OnDeleteAction;
74 import org.hibernate.annotations.OrderBy;
75 import org.hibernate.annotations.ParamDef;
76 import org.hibernate.annotations.Parameter;
77 import org.hibernate.annotations.Proxy;
78 import org.hibernate.annotations.Sort;
79 import org.hibernate.annotations.Type;
80 import org.hibernate.annotations.TypeDef;
81 import org.hibernate.annotations.TypeDefs;
82 import org.hibernate.annotations.Where;
83 import org.hibernate.annotations.GenericGenerator;
84 import org.hibernate.annotations.Index;
85 import org.hibernate.cfg.annotations.CollectionBinder;
86 import org.hibernate.cfg.annotations.EntityBinder;
87 import org.hibernate.cfg.annotations.Nullability;
88 import org.hibernate.cfg.annotations.PropertyBinder;
89 import org.hibernate.cfg.annotations.QueryBinder;
90 import org.hibernate.cfg.annotations.SimpleValueBinder;
91 import org.hibernate.cfg.annotations.TableBinder;
92 import org.hibernate.engine.FilterDefinition;
93 import org.hibernate.engine.Versioning;
94 import org.hibernate.id.MultipleHiLoPerTableGenerator;
95 import org.hibernate.id.PersistentIdentifierGenerator;
96 import org.hibernate.id.TableHiLoGenerator;
97 import org.hibernate.mapping.Component;
98 import org.hibernate.mapping.DependantValue;
99 import org.hibernate.mapping.IdGenerator;
100 import org.hibernate.mapping.Join;
101 import org.hibernate.mapping.JoinedSubclass;
102 import org.hibernate.mapping.PersistentClass;
103 import org.hibernate.mapping.Property;
104 import org.hibernate.mapping.RootClass;
105 import org.hibernate.mapping.SimpleValue;
106 import org.hibernate.mapping.Subclass;
107 import org.hibernate.mapping.Table;
108 import org.hibernate.mapping.UnionSubclass;
109 import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
110 import org.hibernate.persister.entity.SingleTableEntityPersister;
111 import org.hibernate.persister.entity.UnionSubclassEntityPersister;
112 import org.hibernate.type.ByteArrayBlobType;
113 import org.hibernate.type.CharacterArrayClobType;
114 import org.hibernate.type.EnumType;
115 import org.hibernate.type.ForeignKeyDirection;
116 import org.hibernate.type.PrimitiveByteArrayBlobType;
117 import org.hibernate.type.PrimitiveCharacterArrayClobType;
118 import org.hibernate.type.StringClobType;
119 import org.hibernate.type.TypeFactory;
120 import org.hibernate.type.SerializableToBlobType;
121 import org.hibernate.util.ReflectHelper;
122 import org.hibernate.util.StringHelper;
123 import org.hibernate.validator.ClassValidator;
124
125 /**
126  * JSR 175 annotation binder
127  * Will read the annotation from classes, apply the
128  * principles of the EJB3 spec and produces the Hibernate
129  * configuration-time metamodel (the classes in the <tt>mapping</tt>
130  * package)
131  *
132  * @author Emmanuel Bernard
133  */

134 public final class AnnotationBinder {
135     private static final String JavaDoc GENERATOR_TABLE_NAME_PARAM = "generatorTableName";
136     public static final String JavaDoc ANNOTATION_STRING_DEFAULT = "";
137
138     /*
139      * Some design description
140      * I tried to remove any link to annotation except from the 2 first level of
141      * method call.
142      * It'll enable to:
143      * - facilitate annotation overriding
144      * - mutualize one day xml and annotation binder (probably a dream though)
145      * - split this huge class in smaller mapping oriented classes
146      *
147      * bindSomething usually create the mapping container and is accessed by one of the 2 first level method
148      * makeSomething usually create the mapping container and is accessed by bindSomething[else]
149      * fillSomething take the container into parameter and fill it.
150      *
151      *
152      */

153     private AnnotationBinder() {}
154     
155     private static final Log log = LogFactory.getLog(AnnotationBinder.class);
156     
157     public static void bindPackage(String JavaDoc packageName, ExtendedMappings mappings) {
158         Package JavaDoc pckg = null;
159         try {
160             pckg = ReflectHelper.classForName(packageName + ".package-info").getPackage();
161         }
162         catch (ClassNotFoundException JavaDoc cnf) {
163             log.warn("Package not found or wo package-info.java: " + packageName);
164             return;
165         }
166         if ( pckg.isAnnotationPresent(SequenceGenerator.class) ) {
167             SequenceGenerator ann = pckg.getAnnotation(SequenceGenerator.class);
168             IdGenerator idGen = buildIdGenerator(ann);
169             mappings.addGenerator(idGen);
170             log.debug("Add sequence generator with name: " + idGen.getName() );
171         }
172         if ( pckg.isAnnotationPresent(TableGenerator.class) ) {
173             TableGenerator ann = pckg.getAnnotation(TableGenerator.class);
174             IdGenerator idGen = buildIdGenerator(ann);
175             mappings.addGenerator(idGen);
176
177         }
178         if ( pckg.isAnnotationPresent(GeneratedIdTable.class) ) {
179             GeneratedIdTable ann = pckg.getAnnotation(GeneratedIdTable.class);
180             Properties JavaDoc params = buildPropertiesFromGeneratorTable(ann);
181             mappings.addGeneratorTable(ann.name(), params);
182         }
183         if ( pckg.isAnnotationPresent(GenericGenerator.class) ) {
184             GenericGenerator ann = pckg.getAnnotation(GenericGenerator.class);
185             IdGenerator idGen = buildIdGenerator(ann);
186             mappings.addGenerator(idGen);
187         }
188         bindQueries(pckg, mappings);
189         bindFilterDefs(pckg, mappings);
190         bindTypeDefs(pckg, mappings);
191     }
192
193     private static void bindQueries(AnnotatedElement JavaDoc annotatedElement, ExtendedMappings mappings) {
194         {
195             SqlResultSetMapping ann = annotatedElement.getAnnotation(SqlResultSetMapping.class);
196             QueryBinder.bindSqlResultsetMapping(ann, mappings);
197         }
198         {
199             NamedQuery ann = annotatedElement.getAnnotation(NamedQuery.class);
200             QueryBinder.bindQuery(ann, mappings);
201         }
202         {
203             NamedQueries ann = annotatedElement.getAnnotation(NamedQueries.class);
204             QueryBinder.bindQueries(ann, mappings);
205         }
206         {
207             NamedNativeQuery ann = annotatedElement.getAnnotation(NamedNativeQuery.class);
208             QueryBinder.bindNativeQuery(ann, mappings);
209         }
210         {
211             NamedNativeQueries ann = annotatedElement.getAnnotation(NamedNativeQueries.class);
212             QueryBinder.bindNativeQueries(ann, mappings);
213         }
214     }
215
216     private static Properties JavaDoc buildPropertiesFromGeneratorTable(GeneratedIdTable ann) {
217         Properties JavaDoc params = new Properties JavaDoc();
218         if ( ! isDefault( ann.pkColumnName() ) )
219             params.setProperty( MultipleHiLoPerTableGenerator.PK_COLUMN_NAME, ann.pkColumnName() );
220         if ( ! isDefault( ann.valueColumnName() ) )
221             params.setProperty( MultipleHiLoPerTableGenerator.VALUE_COLUMN_NAME, ann.valueColumnName() );
222         if ( ann.table().specified() == true ) {
223             javax.persistence.Table table = ann.table();
224             if ( ! isDefault( table.name() ) )
225                 params.setProperty( MultipleHiLoPerTableGenerator.ID_TABLE, table.name() );
226             if ( ! isDefault( table.catalog() ) )
227                 params.setProperty( MultipleHiLoPerTableGenerator.CATALOG, table.catalog() );
228             if ( ! isDefault( table.schema() ) )
229                 params.setProperty( MultipleHiLoPerTableGenerator.SCHEMA, table.schema() );
230             //FIXME implements uniqueconstrains
231
}
232         else {
233             params.setProperty( MultipleHiLoPerTableGenerator.ID_TABLE, ann.name() );
234         }
235         return params;
236     }
237
238     private static IdGenerator buildIdGenerator(java.lang.annotation.Annotation JavaDoc ann) {
239         IdGenerator idGen = new IdGenerator();
240         if (ann == null) {
241             idGen = null;
242         }
243         else if (ann instanceof TableGenerator) {
244             TableGenerator tabGen = (TableGenerator) ann;
245             idGen.setName( tabGen.name() );
246             idGen.setIdentifierGeneratorStrategy( MultipleHiLoPerTableGenerator.class.getName() );
247             if ( !isDefault( tabGen.tableName() ) ) {
248                 idGen.addParam( GENERATOR_TABLE_NAME_PARAM, tabGen.tableName() );
249             }
250             if ( !isDefault( tabGen.pkColumnValue() ) ) {
251                 idGen.addParam( MultipleHiLoPerTableGenerator.PK_VALUE_NAME, tabGen.pkColumnValue() );
252             }
253             idGen.addParam( TableHiLoGenerator.MAX_LO, String.valueOf( tabGen.allocationSize() ) );
254             log.debug("Add table generator with name: " + idGen.getName() );
255         }
256         else if (ann instanceof SequenceGenerator) {
257             SequenceGenerator seqGen = (SequenceGenerator) ann;
258             idGen.setName( seqGen.name() );
259             idGen.setIdentifierGeneratorStrategy("sequence");
260             
261             if (! isDefault( seqGen.sequenceName() ) ) {
262                 idGen.addParam( org.hibernate.id.SequenceGenerator.SEQUENCE, seqGen.sequenceName() );
263             }
264             //FIXME: work on initialValue() and allocationSize() through SequenceGenerator.PARAMETERS
265
if (seqGen.initialValue() != 0 || seqGen.allocationSize() != 50) {
266                 log.warn("Hibernate does not support SequenceGenerator.initialValue() nor SequenceGenerator.allocationSize()");
267             }
268             log.debug("Add sequence generator with name: " + idGen.getName() );
269         }
270         else if (ann instanceof GenericGenerator) {
271             GenericGenerator genGen = (GenericGenerator) ann;
272             idGen.setName( genGen.name() );
273             idGen.setIdentifierGeneratorStrategy( genGen.strategy() );
274             Parameter[] params = genGen.parameters();
275             for ( Parameter parameter : params ) {
276                 idGen.addParam( parameter.name(), parameter.value() );
277             }
278             log.debug("Add generic generator with name: " + idGen.getName() );
279         }
280         else {
281             throw new AssertionFailure("Unknown Generator annotation: " + ann);
282         }
283         return idGen;
284     }
285     
286     /**
287      * Bind a class having JSR175 annotations
288      * The subclasses <b>have to</b> be binded after its mother class
289      */

290     public static void bindClass(Class JavaDoc clazzToProcess, Map JavaDoc<Class JavaDoc, InheritanceState> inheritanceStatePerClass, ExtendedMappings mappings) throws MappingException {
291         //TODO: be more strict with secondarytable allowance (not for ids, not for secondary table join columns etc)
292
InheritanceState inheritanceState = inheritanceStatePerClass.get(clazzToProcess);
293         AnnotatedClassType classType = mappings.getClassType(clazzToProcess);
294         if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) ) return; //will be processed when the top level entity is parsed
295
if ( AnnotatedClassType.EMBEDDABLE.equals( classType ) ) return; //allow embeded element declaration
296
//FIXME: think of removing from the inheritanceState all non EmbeddedSuperclass and Entity elements
297
if ( ! classType.equals(AnnotatedClassType.ENTITY) ) {
298             throw new AnnotationException( "Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: " + clazzToProcess.getName() );
299         }
300         AnnotatedElement JavaDoc annotatedClass = clazzToProcess;
301         bindQueries(annotatedClass, mappings);
302         bindFilterDefs(annotatedClass, mappings);
303         bindTypeDefs(annotatedClass, mappings);
304         Class JavaDoc superClass = clazzToProcess.getSuperclass();
305         PersistentClass superEntity = mappings.getClass( superClass.getName() );
306         if (superEntity == null) {
307             //check if superclass is not a potential persistent class
308
if ( inheritanceState.hasParents ) {
309                 throw new AnnotationException( "Subclass has to be binded after it's mother class: "
310                     + superClass.getName() );
311             }
312         }
313         String JavaDoc schema = "";
314         String JavaDoc table = ""; //might be no @Table annotation on the annotated class
315
String JavaDoc catalog = "";
316         String JavaDoc discrimValue = null;
317         List JavaDoc uniqueConstraints = new ArrayList JavaDoc();
318         Ejb3DiscriminatorColumn discriminatorColumn = null;
319         Ejb3JoinColumn[] inheritanceJoinedColumns = null;
320
321         if (annotatedClass.isAnnotationPresent(javax.persistence.Table.class) ) {
322             javax.persistence.Table tabAnn = annotatedClass.getAnnotation(javax.persistence.Table.class);
323             table = tabAnn.name();
324             schema = tabAnn.schema();
325             catalog = tabAnn.catalog();
326             if (tabAnn.uniqueConstraints().length != 0) {
327                 for( UniqueConstraint uc : tabAnn.uniqueConstraints() ) {
328                     uniqueConstraints.add( uc.columnNames() );
329                 }
330             }
331         }
332         final boolean hasJoinedColumns = InheritanceType.JOINED.equals( inheritanceState.type ) && inheritanceState.hasParents;
333         if ( hasJoinedColumns ) {
334             PrimaryKeyJoinColumns jcsAnn = annotatedClass.getAnnotation(PrimaryKeyJoinColumns.class);
335             boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
336             if (explicitInheritanceJoinedColumns) {
337                 int nbrOfInhJoinedColumns = jcsAnn.value().length;
338                 PrimaryKeyJoinColumn jcAnn;
339                 inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
340                 for (int colIndex = 0 ; colIndex < nbrOfInhJoinedColumns ; colIndex++) {
341                     jcAnn = jcsAnn.value()[colIndex];
342                     inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(jcAnn, superEntity.getIdentifier(),
343                             (Map JavaDoc<String JavaDoc, Join>) null, (PropertyHolder) null, mappings);
344                 }
345             }
346             else {
347                 PrimaryKeyJoinColumn jcAnn = annotatedClass.getAnnotation(PrimaryKeyJoinColumn.class);
348                 inheritanceJoinedColumns = new Ejb3JoinColumn[1];
349                 inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(jcAnn, superEntity.getIdentifier(),
350                         (Map JavaDoc<String JavaDoc, Join>) null, (PropertyHolder) null, mappings);
351             }
352             log.debug("Joined column(s) created" );
353         }
354         else {
355             if ( annotatedClass.isAnnotationPresent( javax.persistence.PrimaryKeyJoinColumns.class )
356                 || annotatedClass.isAnnotationPresent( javax.persistence.PrimaryKeyJoinColumn.class )) {
357                 log.warn("Root entity should not hold an InheritanceJoinColum(s), will be ignored");
358             }
359         }
360
361         if ( InheritanceType.SINGLE_TABLE.equals(inheritanceState.type) ) {
362             javax.persistence.Inheritance inhAnn = annotatedClass.getAnnotation(javax.persistence.Inheritance.class);
363             DiscriminatorType discriminatorType = inhAnn == null ? DiscriminatorType.STRING : inhAnn.discriminatorType();
364             javax.persistence.DiscriminatorColumn discAnn = annotatedClass.getAnnotation(javax.persistence.DiscriminatorColumn.class);
365             org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = annotatedClass.getAnnotation(org.hibernate.annotations.DiscriminatorFormula.class);
366             if ( ! inheritanceState.hasParents) {
367                 discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(discriminatorType, discAnn, discFormulaAnn, mappings);
368             }
369             if ( discAnn != null && inheritanceState.hasParents ) {
370                 log.warn(
371                     "Discriminator column has to be defined in the root entity, it will be ignored in subclass: "
372                     + clazzToProcess.getName()
373                 );
374             }
375             discrimValue = inhAnn == null ? null : inhAnn.discriminatorValue();
376         }
377
378         //we now know what kind of persistent entity it is
379
PersistentClass persistentClass;
380         //create persistent class
381
if ( ! inheritanceState.hasParents ) {
382             persistentClass = new RootClass();
383         }
384         else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
385             persistentClass = new Subclass(superEntity);
386         }
387         else if ( InheritanceType.JOINED.equals( inheritanceState.type ) ) {
388             persistentClass = new JoinedSubclass(superEntity);
389         }
390         else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.type ) ) {
391             persistentClass = new UnionSubclass(superEntity);
392         }
393         else {
394             throw new AssertionFailure("Unknown inheritance type: " + inheritanceState.type);
395         }
396         Proxy proxyAnn = annotatedClass.getAnnotation(Proxy.class);
397         BatchSize sizeAnn = annotatedClass.getAnnotation(BatchSize.class);
398         Where whereAnn = annotatedClass.getAnnotation(Where.class);
399         Entity entityAnn = annotatedClass.getAnnotation(Entity.class);
400         org.hibernate.annotations.Entity hibEntityAnn = annotatedClass.getAnnotation( org.hibernate.annotations.Entity.class );
401         org.hibernate.annotations.Cache cacheAnn = annotatedClass.getAnnotation( org.hibernate.annotations.Cache.class );
402         EntityBinder entityBinder = new EntityBinder(entityAnn, hibEntityAnn, clazzToProcess, persistentClass, mappings);
403         entityBinder.setDiscriminatorValue(discrimValue);
404         entityBinder.setBatchSize(sizeAnn);
405         entityBinder.setProxy(proxyAnn);
406         entityBinder.setWhere(whereAnn);
407         entityBinder.setCache(cacheAnn);
408         entityBinder.setInheritanceState(inheritanceState);
409         Filter filterAnn = annotatedClass.getAnnotation(Filter.class);
410         if (filterAnn != null) {
411             entityBinder.addFilter( filterAnn.name(), filterAnn.condition() );
412         }
413         Filters filtersAnn = annotatedClass.getAnnotation(Filters.class);
414         if (filtersAnn != null) {
415             for ( Filter filter : filtersAnn.value() ) {
416                 entityBinder.addFilter( filter.name(), filter.condition() );
417             }
418         }
419         entityBinder.bindEntity();
420
421         if ( inheritanceState.hasTable() ) {
422             Check checkAnn = annotatedClass.getAnnotation(Check.class);
423             String JavaDoc constraints = checkAnn == null ? null : checkAnn.constraints();
424             entityBinder.bindTable(
425                     schema, catalog, table, uniqueConstraints,
426                     constraints, inheritanceState.hasDenormalizedTable() ? superEntity.getTable() : null
427             );
428         }
429         Map JavaDoc<String JavaDoc, Column[]> columnOverride = PropertyHolderBuilder.buildColumnOverride(annotatedClass);
430         PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(persistentClass, columnOverride);
431
432         javax.persistence.SecondaryTable secTabAnn = annotatedClass.getAnnotation(javax.persistence.SecondaryTable.class);
433         javax.persistence.SecondaryTables secTabsAnn = annotatedClass.getAnnotation(javax.persistence.SecondaryTables.class);
434         JoinColumn joinColAnn = annotatedClass.getAnnotation(JoinColumn.class);
435         JoinColumns joinColsAnn = annotatedClass.getAnnotation(JoinColumns.class);
436         entityBinder.firstLevelSecondaryTablesBinding(secTabAnn, secTabsAnn, joinColAnn, joinColsAnn);
437
438         OnDelete onDeleteAnn = annotatedClass.getAnnotation(OnDelete.class);
439         boolean onDeleteAppropriate = false;
440         if ( InheritanceType.JOINED.equals( inheritanceState.type ) && inheritanceState.hasParents ) {
441             onDeleteAppropriate = true;
442             JoinedSubclass jsc = (JoinedSubclass) persistentClass;
443             if ( persistentClass.getEntityPersisterClass()==null ) {
444                 persistentClass.getRootClass().setEntityPersisterClass(JoinedSubclassEntityPersister.class);
445             }
446             SimpleValue key = new DependantValue( jsc.getTable(), jsc.getIdentifier() );
447             jsc.setKey(key);
448             if (onDeleteAnn != null) {
449                 key.setCascadeDeleteEnabled( OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) );
450             }
451             else {
452                 key.setCascadeDeleteEnabled(false);
453             }
454             TableBinder.bindFk(jsc, null, inheritanceJoinedColumns, key, false );
455             jsc.createPrimaryKey();
456             jsc.createForeignKey();
457
458         }
459         else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
460             if ( inheritanceState.hasParents ) {
461                 if ( persistentClass.getEntityPersisterClass() == null) {
462                     persistentClass.getRootClass().setEntityPersisterClass( SingleTableEntityPersister.class );
463                 }
464             }
465             else {
466                 if ( inheritanceState.hasSons || ! discriminatorColumn.isImplicit() ) {
467                     //need a discriminator column
468
bindDiscriminatorToPersistentClass(
469                             (RootClass) persistentClass,
470                             discriminatorColumn,
471                             entityBinder.getSecondaryTables(),
472                             propertyHolder);
473                 }
474             }
475         }
476         else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.type ) ) {
477             if ( inheritanceState.hasParents ) {
478                 if ( persistentClass.getEntityPersisterClass() == null) {
479                     persistentClass.getRootClass().setEntityPersisterClass( UnionSubclassEntityPersister.class );
480                 }
481             }
482         }
483         if (onDeleteAnn != null && onDeleteAppropriate == false) {
484             log.warn( "Inapropriate use of @OnDelete on entity, annotation ignored: " + propertyHolder.getEntityName() );
485         }
486
487         //try to find class level generators
488
HashMap JavaDoc<String JavaDoc, IdGenerator> classGenerators = buildLocalGenerators(annotatedClass);
489         HashMap JavaDoc<String JavaDoc, Properties JavaDoc> classGeneratorTables = buildLocalGeneratorTable(annotatedClass);
490         Set JavaDoc<String JavaDoc> idProperties = new HashSet JavaDoc<String JavaDoc>();
491         if ( annotatedClass.isAnnotationPresent( IdClass.class ) ) {
492             IdClass idClass = annotatedClass.getAnnotation(IdClass.class);
493             Class JavaDoc compositeClass = idClass.value();
494             Embeddable embeddableAnn = (Embeddable) compositeClass.getAnnotation(Embeddable.class);
495             boolean isComponent = true;
496             boolean propertyAccess = true;
497             if (embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD) propertyAccess = false;
498             String JavaDoc generatorType = "assigned";
499             String JavaDoc generator = ANNOTATION_STRING_DEFAULT;
500             PropertyInferredData inferredData = new PropertyInferredData(propertyAccess == true ? "property": "field", "id", compositeClass);
501             HashMap JavaDoc<String JavaDoc, IdGenerator> localGenerators = new HashMap JavaDoc<String JavaDoc, IdGenerator>();
502             HashMap JavaDoc<String JavaDoc, Properties JavaDoc> localGeneratorTables = new HashMap JavaDoc<String JavaDoc, Properties JavaDoc>();
503             bindId(
504                 generatorType,
505                 generator,
506                 inferredData,
507                 null,
508                 propertyHolder,
509                 localGenerators,
510                 localGeneratorTables,
511                 isComponent,
512                 columnOverride,
513                 propertyAccess,
514                 entityBinder,
515                 null,
516                 true,
517                 mappings);
518             inferredData = new PropertyInferredData(propertyAccess == true ? "property": "field", "_identifierMapper", compositeClass);
519             Component mapper = fillComponent(
520                     propertyHolder,
521                     inferredData,
522                     propertyAccess,
523                     false,
524                     entityBinder,
525                     true,
526                     columnOverride,
527                     mappings);
528             mapper.setEmbedded(true);
529             persistentClass.setIdentifierMapper(mapper);
530             Property property = new Property();
531             property.setName("_identifierMapper");
532             property.setNodeName("id");
533             property.setUpdateable(false);
534             property.setInsertable(false);
535             property.setValue(mapper);
536             property.setPropertyAccessorName( "embedded" );
537             persistentClass.addProperty(property);
538             entityBinder.setIgnoreIdAnnotations(true);
539
540             Iterator JavaDoc properties = mapper.getPropertyIterator();
541             while ( properties.hasNext() ) {
542                 idProperties.add( ( (Property) properties.next() ).getName() );
543             }
544         }
545
546         // check properties
547
List JavaDoc<Class JavaDoc> classesToProcess = orderClassesToBeProcessed(clazzToProcess, inheritanceStatePerClass, inheritanceState);
548         int deep = classesToProcess.size();
549         List JavaDoc<PropertyAnnotatedElement> elements = new ArrayList JavaDoc<PropertyAnnotatedElement>();
550         boolean hasIdentifier = false;
551         for ( int index = 0 ; index < deep ; index++ ) {
552             Class JavaDoc clazz = classesToProcess.get(index);
553             InheritanceState state = inheritanceStatePerClass.get(clazz);
554             boolean isPropertyAccess;
555             //yuk
556
if (state.isEmbeddableSuperclass) {
557                 isPropertyAccess = state.accessType == AccessType.PROPERTY;
558             }
559             else {
560                 isPropertyAccess = entityBinder.isPropertyAccess();
561             }
562             boolean currentHasIdentifier = addElementsOfAClass(elements, propertyHolder, isPropertyAccess, clazz);
563             hasIdentifier = hasIdentifier || currentHasIdentifier;
564         }
565         final boolean rootEntityWoId = !entityBinder.isIgnoreIdAnnotations() && !hasIdentifier && !inheritanceState.hasParents;
566         final boolean subclassAndSingleTableStrategy = inheritanceState.type == InheritanceType.SINGLE_TABLE
567                 && inheritanceState.hasParents;
568         if (rootEntityWoId) {
569             throw new AnnotationException( "No identifier specified for entity: " + propertyHolder.getEntityName() );
570         }
571         Set JavaDoc<String JavaDoc> missingIdProperties = new HashSet JavaDoc<String JavaDoc>(idProperties);
572         for (PropertyAnnotatedElement propertyAnnotatedElement : elements) {
573             String JavaDoc propertyName = propertyAnnotatedElement.inferredData.getPropertyName();
574             if ( ! idProperties.contains( propertyName ) ) {
575                 processElementAnnotations(propertyHolder,
576                     subclassAndSingleTableStrategy ? Nullability.FORCED_NULL : Nullability.NO_CONSTRAINT,
577                     propertyAnnotatedElement.element,
578                         propertyAnnotatedElement.inferredData, classGenerators, classGeneratorTables, entityBinder, false, mappings);
579             }
580             else {
581                 missingIdProperties.remove(propertyName);
582             }
583         }
584
585         if (missingIdProperties.size() != 0) {
586             StringBuffer JavaDoc missings = new StringBuffer JavaDoc();
587             for(String JavaDoc property : missingIdProperties) {
588                 missings.append(property).append(", ");
589             }
590             throw new AnnotationException("Unable to find properties ("
591                     + missings.substring(0, missings.length() - 2)
592                     + ") in entity annotated with @IdClass:" + persistentClass.getEntityName() );
593         }
594
595         if ( ! inheritanceState.hasParents ) {
596             ( (RootClass) persistentClass).createPrimaryKey();
597         }
598         else {
599             superEntity.addSubclass( (Subclass) persistentClass );
600         }
601
602         mappings.addClass(persistentClass);
603         entityBinder.finalSecondaryTableBinding(propertyHolder);
604
605         //add indexes
606
entityBinder.addIndexes( annotatedClass.getAnnotation( org.hibernate.annotations.Table.class ) );
607         entityBinder.addIndexes( annotatedClass.getAnnotation( org.hibernate.annotations.Tables.class ) );
608
609         //integrate the validate framework
610
ResourceBundle JavaDoc rb = null;
611         //TODO integrate a proper resource bundle
612
//ResourceBundle rb = ResourceBundle.getBundle("messages", Locale.ENGLISH);
613
new ClassValidator( clazzToProcess, rb ).apply( persistentClass );
614     }
615
616     private static List JavaDoc<Class JavaDoc> orderClassesToBeProcessed(
617             Class JavaDoc annotatedClass, Map JavaDoc<Class JavaDoc, InheritanceState> inheritanceStatePerClass,
618             InheritanceState inheritanceState
619             ) {
620         //ordered to allow proper messages on properties subclassing
621
List JavaDoc<Class JavaDoc> classesToProcess = new ArrayList JavaDoc<Class JavaDoc>();
622         Class JavaDoc currentClassInHierarchy = annotatedClass;
623         InheritanceState superclassState;
624         do {
625             classesToProcess.add(0, currentClassInHierarchy);
626             currentClassInHierarchy = currentClassInHierarchy.getSuperclass();
627             superclassState = inheritanceStatePerClass.get( currentClassInHierarchy );
628         }
629         while (! inheritanceState.hasParents && superclassState != null);
630         return classesToProcess;
631     }
632
633     private static void bindFilterDefs(AnnotatedElement JavaDoc annotatedElement, ExtendedMappings mappings) {
634         FilterDef defAnn = annotatedElement.getAnnotation(FilterDef.class);
635         FilterDefs defsAnn = annotatedElement.getAnnotation(FilterDefs.class);
636         if (defAnn != null) {
637             bindFilterDef(defAnn, mappings);
638         }
639         if (defsAnn != null) {
640             for ( FilterDef def : defsAnn.value() ) {
641                 bindFilterDef(def, mappings);
642             }
643         }
644     }
645
646     private static void bindFilterDef(FilterDef defAnn, ExtendedMappings mappings) {
647         FilterDefinition def = new FilterDefinition( defAnn.name() );
648         for ( ParamDef param : defAnn.parameters() ) {
649             TypeFactory.heuristicType( param.type() );
650             def.addParameterType( param.name(), TypeFactory.heuristicType( param.type() ) );
651         }
652         mappings.addFilterDefinition(def);
653     }
654
655     private static void bindTypeDefs(AnnotatedElement JavaDoc annotatedElement, ExtendedMappings mappings) {
656         TypeDef defAnn = annotatedElement.getAnnotation(TypeDef.class);
657         TypeDefs defsAnn = annotatedElement.getAnnotation(TypeDefs.class);
658         if (defAnn != null) {
659             bindTypeDef(defAnn, mappings);
660         }
661         if (defsAnn != null) {
662             for ( TypeDef def : defsAnn.value() ) {
663                 bindTypeDef(def, mappings);
664             }
665         }
666     }
667
668     private static void bindTypeDef(TypeDef defAnn, ExtendedMappings mappings) {
669         Properties JavaDoc params = new Properties JavaDoc();
670         for ( Parameter param : defAnn.parameters() ) {
671             params.setProperty( param.name(), param.value() );
672         }
673         mappings.addTypeDef( defAnn.name(), defAnn.typeClass().getName(), params );
674     }
675
676     private static HashMap JavaDoc<String JavaDoc, Properties JavaDoc> buildLocalGeneratorTable(AnnotatedElement JavaDoc annotatedClass) {
677         HashMap JavaDoc<String JavaDoc, Properties JavaDoc> result = new HashMap JavaDoc<String JavaDoc, Properties JavaDoc>();
678         GeneratedIdTable ann = (GeneratedIdTable) annotatedClass.getAnnotation(GeneratedIdTable.class);
679         if (ann != null) {
680             result.put( ann.name(), buildPropertiesFromGeneratorTable(ann) );
681         }
682         return result;
683     }
684
685     private static void bindDiscriminatorToPersistentClass(
686             RootClass rootClass,
687             Ejb3DiscriminatorColumn discriminatorColumn, Map JavaDoc<String JavaDoc, Join> secondaryTables,
688             PropertyHolder propertyHolder
689             ) {
690         if (rootClass.getDiscriminator() == null) {
691             if (discriminatorColumn == null) {
692                 throw new AssertionFailure("discriminator column should have been built");
693             }
694             discriminatorColumn.setJoins(secondaryTables);
695             discriminatorColumn.setPropertyHolder(propertyHolder);
696             SimpleValue discrim = new SimpleValue( rootClass.getTable() );
697             rootClass.setDiscriminator(discrim);
698             discriminatorColumn.linkWithValue(discrim);
699             discrim.setTypeName(discriminatorColumn.getDiscriminatorTypeName());
700             rootClass.setPolymorphic(true);
701             log.debug( "Setting discriminator for entity " + rootClass.getEntityName() );
702         }
703     }
704
705     /**
706      * Add elements of a class
707      */

708     private static boolean addElementsOfAClass(
709             List JavaDoc<PropertyAnnotatedElement> elements, PropertyHolder propertyHolder, boolean propertyAccess,
710             final Class JavaDoc annotatedClass
711             ) {
712         boolean hasIdentifier = false;
713         if (propertyAccess) {
714             log.debug("Processing " + propertyHolder.getEntityName() + " per property access");
715             Method JavaDoc[] methods = annotatedClass.getDeclaredMethods();
716             AnnotatedElement JavaDoc currentElt;
717             int index = 0;
718             while( index < methods.length) {
719                 currentElt = (AnnotatedElement JavaDoc) methods[index];
720                 final boolean currentHasIdentifier = addAnnotatedElement(currentElt, elements);
721                 hasIdentifier = hasIdentifier || currentHasIdentifier;
722                 index++;
723             }
724         }
725         else {
726             log.debug("Processing " + propertyHolder.getEntityName() + " per field access");
727             Field JavaDoc[] fields = annotatedClass.getDeclaredFields();
728             AnnotatedElement JavaDoc currentElt;
729             int index = 0;
730             while( index < fields.length) {
731                 currentElt = (AnnotatedElement JavaDoc) fields[index];
732                 final boolean currentHasIdentifier = addAnnotatedElement(currentElt, elements);
733                 hasIdentifier = hasIdentifier || currentHasIdentifier;
734                 index++;
735             }
736         }
737         return hasIdentifier;
738     }
739
740     private static boolean addAnnotatedElement(AnnotatedElement JavaDoc elt, List JavaDoc<PropertyAnnotatedElement> annElts) {
741         boolean hasIdentifier = false;
742         PropertyAnnotatedElement propertyAnnotatedElement = new PropertyAnnotatedElement(elt);
743         if ( ! propertyAnnotatedElement.inferredData.skip() ) {
744             /*
745              * put element annotated by @Id in front
746              * since it has to be parsed before any assoctation by Hibernate
747              */

748             final AnnotatedElement JavaDoc element = propertyAnnotatedElement.element;
749             if ( element.isAnnotationPresent(Id.class) || element.isAnnotationPresent(EmbeddedId.class) ) {
750                 annElts.add(0, propertyAnnotatedElement);
751                 hasIdentifier = true;
752             }
753             else {
754                 annElts.add(propertyAnnotatedElement);
755                 hasIdentifier = false;
756             }
757         }
758         return hasIdentifier;
759     }
760
761     /**
762      * Process annotation of a particular element
763      */

764     private static void processElementAnnotations(
765             PropertyHolder propertyHolder, Nullability nullability, AnnotatedElement JavaDoc annotatedElt,
766             PropertyInferredData inferredData, HashMap JavaDoc<String JavaDoc, IdGenerator> classGenerators,
767             HashMap JavaDoc<String JavaDoc, Properties JavaDoc> classGeneratorTables, EntityBinder entityBinder, boolean isIdentifierMapper,
768             ExtendedMappings mappings
769             )
770         throws MappingException {
771         Ejb3Column[] columns = null;
772         if ( annotatedElt.isAnnotationPresent(Transient.class) ) {
773             return;
774         }
775         if ( log.isDebugEnabled() ) {
776             log.debug( "Processing annotations of " + propertyHolder.getEntityName() + "." + inferredData.getPropertyName() );
777         }
778         if ( annotatedElt.isAnnotationPresent(Column.class) || annotatedElt.isAnnotationPresent(Formula.class) ) {
779             Column ann = (Column) annotatedElt.getAnnotation(Column.class);
780             Formula formulaAnn = (Formula) annotatedElt.getAnnotation(Formula.class);
781             columns = Ejb3Column.buildColumnFromAnnotation(new Column[] { ann }, formulaAnn, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings);
782             columns[0].addIndex( annotatedElt.getAnnotation( Index.class ) );
783         }
784         else if ( annotatedElt.isAnnotationPresent(Columns.class) ) {
785             Columns anns = annotatedElt.getAnnotation(Columns.class);
786             columns = Ejb3Column.buildColumnFromAnnotation(anns.columns(), null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings);
787         }
788         else if ( annotatedElt.isAnnotationPresent(JoinColumn.class) ) {
789             JoinColumn ann = (JoinColumn) annotatedElt.getAnnotation(JoinColumn.class);
790             columns = new Ejb3JoinColumn[1];
791             columns[0] = Ejb3JoinColumn.buildJoinColumn(ann,
792                     entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), mappings);
793         }
794         else if ( annotatedElt.isAnnotationPresent(JoinColumns.class) ) {
795             JoinColumns ann = annotatedElt.getAnnotation(JoinColumns.class);
796             JoinColumn[] annColumns = ann.value();
797             int length = annColumns.length;
798             if (length == 0) {
799                 throw new AnnotationException("Cannot bind an empty @JoinColumns");
800             }
801             columns = new Ejb3JoinColumn[length];
802             for (int index = 0 ; index < length ; index++) {
803                 columns[index] = Ejb3JoinColumn.buildJoinColumn(annColumns[index],
804                         entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), mappings);
805             }
806         }
807         else {
808             if ( annotatedElt.isAnnotationPresent(ManyToOne.class)
809                 || annotatedElt.isAnnotationPresent(OneToOne.class) ) {
810                 columns = new Ejb3JoinColumn[1];
811                 if ( annotatedElt.isAnnotationPresent(JoinTable.class) ) {
812                     JoinTable assocTable = annotatedElt.getAnnotation(JoinTable.class);
813                     //entityBinder.firstLevelSecondaryTablesBinding(assocTable);
814
throw new NotYetImplementedException("association table on a single ended association is not yet supported");
815                 }
816                 else {
817                     columns[0] = Ejb3JoinColumn.buildImplicitJoinColumn( (String JavaDoc) null, entityBinder.getSecondaryTables(),
818                             propertyHolder, inferredData.getPropertyName(), mappings);
819                 }
820             }
821             else if ( annotatedElt.isAnnotationPresent(OneToMany.class) ) {
822                 columns = new Ejb3JoinColumn[1];
823                 String JavaDoc mappedBy = ( (OneToMany) annotatedElt.getAnnotation(OneToMany.class) ).mappedBy();
824                 columns[0] = Ejb3JoinColumn.buildImplicitJoinColumn(mappedBy, entityBinder.getSecondaryTables(),
825                         propertyHolder, inferredData.getPropertyName(), mappings);
826             }
827             else {
828                 columns = Ejb3Column.buildColumnFromAnnotation(null, null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings);
829             }
830         }
831         if (nullability == Nullability.FORCED_NOT_NULL) {
832             //force columns to not null
833
for (Ejb3Column col : columns) {
834                 col.forceNotNull();
835             }
836         }
837
838         final Class JavaDoc returnedClass = inferredData.getReturnedClassOrElement();
839         if ( !entityBinder.isIgnoreIdAnnotations() &&
840                 (annotatedElt.isAnnotationPresent(Id.class) || annotatedElt.isAnnotationPresent(EmbeddedId.class) ) ) {
841             if (isIdentifierMapper) throw new AnnotationException("@IdClass class should not have @Id nor @EmbeddedId proeperties");
842             log.debug(inferredData.getPropertyName() + " is an id");
843             Id idAnn = (Id) annotatedElt.getAnnotation(Id.class);
844             EmbeddedId embeddedId = (EmbeddedId) annotatedElt.getAnnotation(EmbeddedId.class);
845
846             //clone classGenerator and override with local values
847
HashMap JavaDoc<String JavaDoc, IdGenerator> localGenerators = (HashMap JavaDoc<String JavaDoc, IdGenerator>) classGenerators.clone();
848             HashMap JavaDoc<String JavaDoc, Properties JavaDoc> localGeneratorTables = (HashMap JavaDoc<String JavaDoc, Properties JavaDoc>) classGeneratorTables.clone();
849             localGenerators.putAll( buildLocalGenerators(annotatedElt) );
850             localGeneratorTables.putAll( buildLocalGeneratorTable(annotatedElt) );
851
852             //manage composite related metadata
853
Embeddable embeddableAnn = (Embeddable) returnedClass.getAnnotation(Embeddable.class);
854             Map JavaDoc<String JavaDoc, Column[]> columnOverride = PropertyHolderBuilder.buildColumnOverride(annotatedElt);
855             //guess if its a component and find id data access (property, field etc)
856
final boolean isComponent = embeddableAnn != null || embeddedId != null;
857             boolean propertyAccess = true;
858             if (isComponent && embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD) propertyAccess = false;
859             //for now columns.length > 1 is impossible for @Id
860
String JavaDoc generatorType = idAnn != null ? generatorType( idAnn.generate() ) : "assigned";
861             String JavaDoc generator = idAnn != null ? idAnn.generator() : ANNOTATION_STRING_DEFAULT;
862             Type typeAnn = annotatedElt.getAnnotation(Type.class);
863             bindId(
864                 generatorType,
865                 generator,
866                 inferredData,
867                 columns[0],
868                 propertyHolder,
869                 localGenerators,
870                 localGeneratorTables,
871                 isComponent,
872                 columnOverride,
873                 propertyAccess,
874                 entityBinder,
875                 typeAnn,
876                 false,
877                 mappings);
878             if ( log.isDebugEnabled() ) {
879                 log.debug("Bind " + (isComponent ? "@Id":"@EmbeddedId") + " on " + inferredData.getPropertyName() );
880             }
881         }
882         else if ( annotatedElt.isAnnotationPresent(Version.class) ) {
883             if (isIdentifierMapper) throw new AnnotationException("@IdClass class should not have @Version proeperty");
884             if ( ! ( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
885                 throw new AnnotationException(
886                         "Unable to define/override @Version on a subclass: "
887                         + propertyHolder.getEntityName() );
888             }
889             log.debug(inferredData.getPropertyName() + " is a version property");
890             RootClass rootClass = (RootClass) propertyHolder.getPersistentClass();
891             boolean lazy = false;
892             //for now columns.length > 1 is impossible for @Version
893
Type annType = (Type) annotatedElt.getAnnotation(Type.class);
894             PropertyBinder propBinder = new PropertyBinder();
895             propBinder.setName( inferredData.getPropertyName() );
896             propBinder.setReturnedClassName( inferredData.getReturnedClassName() );
897             propBinder.setLazy( lazy );
898             propBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
899             propBinder.setColumns( columns );
900             propBinder.setHolder( propertyHolder ); //PropertyHolderBuilder.buildPropertyHolder(rootClass)
901
propBinder.setExplicitType(annType);
902             propBinder.setMappings(mappings);
903             Property prop = propBinder.bind();
904             rootClass.setVersion(prop);
905             SimpleValue simpleValue = (SimpleValue) prop.getValue();
906             if ( !simpleValue.isTypeSpecified() ) simpleValue.setTypeName("integer");
907             simpleValue.setNullValue("undefined");
908             rootClass.setOptimisticLockMode(Versioning.OPTIMISTIC_LOCK_VERSION);
909             log.debug( "Version name: " + rootClass.getVersion().getName() + ", unsavedValue: " + ( (SimpleValue) rootClass.getVersion().getValue() ).getNullValue() );
910         }
911         else if ( annotatedElt.isAnnotationPresent(ManyToOne.class) ) {
912             ManyToOne ann = annotatedElt.getAnnotation(ManyToOne.class);
913             Cascade hibernateCascade = annotatedElt.getAnnotation(Cascade.class);
914             Ejb3JoinColumn.checkIfJoinColumn(columns, propertyHolder, inferredData);
915             bindManyToOne( getCascadeStrategy( ann.cascade(), hibernateCascade ),
916                 (Ejb3JoinColumn[]) columns,
917                 ann.optional(),
918                 getFetchMode( ann.fetch() ),
919                 inferredData.getPropertyName(),
920                 inferredData.getReturnedClassOrElementName(),
921                 ann.targetEntity(),
922                 inferredData.getDefaultAccess(),
923                 propertyHolder,
924                 false, isIdentifierMapper, mappings
925             );
926         }
927         else if ( annotatedElt.isAnnotationPresent(OneToOne.class) ) {
928             OneToOne ann = annotatedElt.getAnnotation(OneToOne.class);
929             boolean trueOneToOne = annotatedElt.isAnnotationPresent(PrimaryKeyJoinColumn.class);
930             Cascade hibernateCascade = annotatedElt.getAnnotation(Cascade.class);
931             Ejb3JoinColumn.checkIfJoinColumn(columns, propertyHolder, inferredData);
932             bindOneToOne( getCascadeStrategy( ann.cascade(), hibernateCascade ),
933                 (Ejb3JoinColumn[]) columns,
934                 ann.optional(),
935                 getFetchMode( ann.fetch() ),
936                 inferredData.getPropertyName(),
937                 inferredData.getReturnedClassName(),
938                 ann.targetEntity(),
939                 inferredData.getDefaultAccess(),
940                 propertyHolder,
941                 ann.mappedBy(), trueOneToOne, isIdentifierMapper, mappings);
942         }
943         else if ( annotatedElt.isAnnotationPresent( OneToMany.class )
944             || annotatedElt.isAnnotationPresent( ManyToMany.class ) ) {
945             OneToMany oneToManyAnn = annotatedElt.getAnnotation(OneToMany.class);
946             ManyToMany manyToManyAnn = annotatedElt.getAnnotation(ManyToMany.class);
947             org.hibernate.annotations.IndexColumn indexAnn = annotatedElt.getAnnotation(
948                 org.hibernate.annotations.IndexColumn.class
949             );
950             JoinTable assocTable = annotatedElt.getAnnotation(JoinTable.class);
951             boolean isForeignKey =
952                     oneToManyAnn != null
953                     && assocTable == null
954                     && ( ( columns[0].isImplicit() && ! isDefault( oneToManyAnn.mappedBy() ) )
955                         || ! columns[0].isImplicit()
956                     );
957             IndexColumn indexColumn = IndexColumn.buildColumnFromAnnotation( indexAnn, propertyHolder, inferredData, mappings);
958             CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
959                     propertyHolder.getEntityName(),
960                     inferredData,
961                     ! indexColumn.isImplicit()
962             );
963             collectionBinder.setIndexColumn(indexColumn);
964             MapKey mapKeyAnn = annotatedElt.getAnnotation(MapKey.class);
965             collectionBinder.setMapKey(mapKeyAnn);
966             collectionBinder.setPropertyName( inferredData.getPropertyName() );
967             BatchSize batchAnn = annotatedElt.getAnnotation( BatchSize.class );
968             collectionBinder.setBatchSize(batchAnn);
969             javax.persistence.OrderBy ejb3OrderByAnn = annotatedElt.getAnnotation( javax.persistence.OrderBy.class );
970             OrderBy orderByAnn = annotatedElt.getAnnotation( OrderBy.class );
971             collectionBinder.setEjb3OrderBy(ejb3OrderByAnn);
972             collectionBinder.setSqlOrderBy(orderByAnn);
973             Sort sortAnn = annotatedElt.getAnnotation( Sort.class );
974             collectionBinder.setSort(sortAnn);
975             Cache cachAnn = annotatedElt.getAnnotation( Cache.class );
976             collectionBinder.setCache(cachAnn);
977             Filter filterAnn = annotatedElt.getAnnotation(Filter.class);
978             if (filterAnn != null) {
979                 collectionBinder.addFilter( filterAnn.name(), filterAnn.condition() );
980             }
981             Filters filtersAnn = annotatedElt.getAnnotation(Filters.class);
982             if (filtersAnn != null) {
983                 for ( Filter filter : filtersAnn.value() ) {
984                     collectionBinder.addFilter( filter.name(), filter.condition() );
985                 }
986             }
987             collectionBinder.setPropertyHolder(propertyHolder);
988             Where whereAnn = annotatedElt.getAnnotation( Where.class );
989             collectionBinder.setWhere(whereAnn);
990             Cascade hibernateCascade = annotatedElt.getAnnotation(Cascade.class);
991             collectionBinder.setCollectionType( inferredData.getCollectionType() );
992             collectionBinder.setMappings(mappings);
993             collectionBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
994             if (oneToManyAnn != null && manyToManyAnn != null) {
995                 throw new AnnotationException( "@OneToMany and @ManyToMany on the same property is not allowed: "
996                         + propertyHolder.getEntityName() + "." + inferredData.getPropertyName() );
997             }
998             String JavaDoc mappedBy = null;
999             if (oneToManyAnn != null) {
1000                Ejb3JoinColumn.checkIfJoinColumn(columns, propertyHolder, inferredData);
1001                for (Ejb3JoinColumn column : ( Ejb3JoinColumn[] ) columns) {
1002                    if ( column.isSecondary() ) {
1003                        throw new NotYetImplementedException("Collections having FK in secondary table");
1004                    }
1005                }
1006                collectionBinder.setJoinColumns( ( Ejb3JoinColumn[] ) columns );
1007                mappedBy = oneToManyAnn.mappedBy();
1008                collectionBinder.setUnique(true);
1009                collectionBinder.setTargetEntity( oneToManyAnn.targetEntity() );
1010                collectionBinder.setFetchType( oneToManyAnn.fetch() );
1011                collectionBinder.setCascadeStrategy( getCascadeStrategy( oneToManyAnn.cascade(), hibernateCascade ) );
1012            }
1013            else if (manyToManyAnn != null) {
1014                mappedBy = manyToManyAnn.mappedBy();
1015                collectionBinder.setUnique(false);
1016                collectionBinder.setTargetEntity( manyToManyAnn.targetEntity() );
1017                collectionBinder.setFetchType( manyToManyAnn.fetch() );
1018                collectionBinder.setCascadeStrategy( getCascadeStrategy( manyToManyAnn.cascade(), hibernateCascade ) );
1019            }
1020            collectionBinder.setMappedBy( mappedBy );
1021            if (isForeignKey) {
1022                for (Ejb3JoinColumn column : ( Ejb3JoinColumn[] ) columns) {
1023                    if ( column.isSecondary() ) {
1024                        throw new NotYetImplementedException("Collections having FK in secondary table");
1025                    }
1026                }
1027                collectionBinder.setOneToMany(true);
1028            }
1029            else {
1030                collectionBinder.setOneToMany(false);
1031                bindJoinedTableAssociation(assocTable, mappings, entityBinder, collectionBinder, propertyHolder, inferredData, mappedBy);
1032            }
1033            OnDelete onDeleteAnn = annotatedElt.getAnnotation(OnDelete.class);
1034            boolean onDeleteCascade = onDeleteAnn != null ? OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) : false;
1035            collectionBinder.setCascadeDeleteEnabled(onDeleteCascade);
1036            if (isIdentifierMapper) {
1037                collectionBinder.setInsertable(false);
1038                collectionBinder.setUpdatable(false);
1039            }
1040            collectionBinder.bind();
1041        }
1042        else {
1043            //define whether the type is a component or not
1044
boolean isComponent = false;
1045            Embeddable embeddableAnn = (Embeddable) returnedClass.getAnnotation(Embeddable.class);
1046            Embedded embeddedAnn = (Embedded) annotatedElt.getAnnotation(Embedded.class);
1047            if (embeddedAnn != null || embeddableAnn != null) isComponent = true;
1048
1049            if (isComponent) {
1050                //process component object
1051
boolean propertyAccess = true;
1052                if (embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD) propertyAccess = false;
1053                Map JavaDoc<String JavaDoc, Column[]> columnOverride = PropertyHolderBuilder.buildColumnOverride(annotatedElt);
1054                bindComponent(inferredData, propertyHolder, propertyAccess, entityBinder, isIdentifierMapper, columnOverride, mappings);
1055            }
1056            else {
1057                //provide the basic property mapping
1058
Properties JavaDoc typeParameters = null;
1059                boolean lazy = false;
1060                String JavaDoc type = ANNOTATION_STRING_DEFAULT;
1061                if ( annotatedElt.isAnnotationPresent(Basic.class) ) {
1062                    Basic ann = (Basic) annotatedElt.getAnnotation(Basic.class);
1063                    if (ann.optional() == false && nullability != Nullability.FORCED_NULL) {
1064                        //force columns to not null
1065
for (Ejb3Column col : columns) {
1066                            col.forceNotNull();
1067                        }
1068                    }
1069                    lazy = ann.fetch() == FetchType.LAZY;
1070                    switch ( ann.temporalType() ) {
1071                        case DATE: type = "date"; break;
1072                        case TIME: type = "time"; break;
1073                        case TIMESTAMP: type = "timestamp"; break;
1074                        case NONE: break;
1075                        default: throw new AssertionFailure( "Unknown temporal tyme: " + ann.temporalType() );
1076                    }
1077                }
1078                else if ( annotatedElt.isAnnotationPresent(Lob.class) ) {
1079                    Lob lob = annotatedElt.getAnnotation(Lob.class);
1080                    if (lob.optional() == false && nullability != Nullability.FORCED_NULL) {
1081                        //force columns to not null
1082
for (Ejb3Column col : columns) {
1083                            col.forceNotNull();
1084                        }
1085                    }
1086                    lazy = lob.fetch() == FetchType.LAZY;
1087                    LobType lobType = lob.type();
1088                    if ( LobType.CLOB.equals(lobType) ) {
1089                        if ( String JavaDoc.class.equals( returnedClass ) ) {
1090                            type = StringClobType.class.getName();
1091                        }
1092                        else if (Character JavaDoc.class.equals( returnedClass ) && inferredData.isArray() ) {
1093                            type = CharacterArrayClobType.class.getName();
1094                        }
1095                        else if (char.class.equals( returnedClass ) && inferredData.isArray() ) {
1096                            type = PrimitiveCharacterArrayClobType.class.getName();
1097                        }
1098                        else {
1099                            type = "clob";
1100                        }
1101                    }
1102                    if ( LobType.BLOB.equals(lobType ) ) {
1103                        if (Byte JavaDoc.class.equals( returnedClass ) && inferredData.isArray() ) {
1104                            type = ByteArrayBlobType.class.getName();
1105                        }
1106                        else if (byte.class.equals( returnedClass ) && inferredData.isArray() ) {
1107                            type = PrimitiveByteArrayBlobType.class.getName();
1108                        }
1109                        else if ( returnedClass instanceof Serializable JavaDoc ) {
1110                            type = SerializableToBlobType.class.getName();
1111                            typeParameters = new Properties JavaDoc();
1112                            typeParameters.setProperty(
1113                                    SerializableToBlobType.CLASS_NAME,
1114                                    returnedClass.getName()
1115                            );
1116                        }
1117                        else {
1118                            type = "blob";
1119                        }
1120                    }
1121                }
1122                else if (java.sql.Clob JavaDoc.class.equals( returnedClass ) ) {
1123                    type = "clob";
1124                }
1125                else if (java.sql.Blob JavaDoc.class.equals( returnedClass ) ) {
1126                    type = "blob";
1127                }
1128                //implicit type will check basic types and Serializable classes
1129

1130                if ( ANNOTATION_STRING_DEFAULT.equals(type) ) {
1131                    if ( returnedClass.isEnum() ) {
1132                        type = EnumType.class.getName();
1133                        typeParameters = new Properties JavaDoc();
1134                        typeParameters.setProperty(EnumType.ENUM, returnedClass.getName() );
1135                        String JavaDoc schema = columns[0].getTable().getSchema();
1136                        schema = schema == null ? "" : schema;
1137                        String JavaDoc catalog = columns[0].getTable().getCatalog();
1138                        catalog = catalog == null ? "" : catalog;
1139                        typeParameters.setProperty( EnumType.SCHEMA, schema );
1140                        typeParameters.setProperty( EnumType.CATALOG, catalog );
1141                        typeParameters.setProperty( EnumType.TABLE, columns[0].getTable().getName() );
1142                        typeParameters.setProperty( EnumType.COLUMN, columns[0].getName() );
1143                    }
1144                }
1145
1146                Type annType = (Type) annotatedElt.getAnnotation(Type.class);
1147                PropertyBinder propBinder = new PropertyBinder();
1148                propBinder.setName( inferredData.getPropertyName() );
1149                propBinder.setReturnedClassName( inferredData.getReturnedClassName() );
1150                propBinder.setLazy( lazy );
1151                propBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
1152                propBinder.setColumns( columns );
1153                propBinder.setHolder( propertyHolder );
1154                propBinder.setExplicitType(annType);
1155                propBinder.setExplicitType(type);
1156                propBinder.setTypeParameters(typeParameters);
1157                propBinder.setMappings(mappings);
1158                if (isIdentifierMapper) {
1159                    propBinder.setInsertable(false);
1160                    propBinder.setUpdatable(false);
1161                }
1162                propBinder.bind();
1163            }
1164        }
1165    }
1166
1167    private static void bindJoinedTableAssociation(JoinTable joinTableAnn, ExtendedMappings mappings, EntityBinder entityBinder, CollectionBinder collectionBinder, PropertyHolder propertyHolder, PropertyInferredData inferredData, String JavaDoc mappedBy) {
1168        Table assocTable;
1169        JoinColumn[] annJoins;
1170        JoinColumn[] annInverseJoins;
1171        if ( joinTableAnn != null ) {
1172            if (! joinTableAnn.table().specified() ) {
1173                assocTable = null;
1174            }
1175            else {
1176                assocTable = TableBinder.fillTable(joinTableAnn.table().schema(),
1177                    joinTableAnn.table().catalog(),
1178                    mappings.getNamingStrategy().tableName( joinTableAnn.table().name() ),
1179                    false, new ArrayList JavaDoc(), null, null, mappings);
1180            }
1181            //set check constaint in the second pass
1182

1183            JoinColumn[] joins = joinTableAnn.joinColumns();
1184
1185            if (joins.length == 0) {
1186                annJoins = null;
1187            }
1188            else {
1189                annJoins = joins;
1190            }
1191
1192            JoinColumn[] inverseJoins = joinTableAnn.inverseJoinColumns();
1193
1194            if (inverseJoins.length == 0) {
1195                annInverseJoins = null;
1196            }
1197            else {
1198                annInverseJoins = inverseJoins;
1199            }
1200        }
1201        else {
1202            assocTable = null;
1203            annJoins = null;
1204            annInverseJoins = null;
1205        }
1206        Ejb3JoinColumn[] joinColumns = buildJoinTableJoinColumns( annJoins, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), mappedBy, mappings);
1207        Ejb3JoinColumn[] inverseJoinColumns = buildJoinTableJoinColumns( annInverseJoins, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), mappedBy, mappings);
1208        collectionBinder.setTable(assocTable);
1209        collectionBinder.setJoinColumns(joinColumns);
1210        collectionBinder.setInverseJoinColumns(inverseJoinColumns);
1211    }
1212
1213    private static Ejb3JoinColumn[] buildJoinTableJoinColumns(JoinColumn[] annJoins, Map JavaDoc<String JavaDoc, Join> secondaryTables,
1214                                                               PropertyHolder propertyHolder, String JavaDoc propertyName, String JavaDoc mappedBy, ExtendedMappings mappings) {
1215        Ejb3JoinColumn[] joinColumns;
1216        if (annJoins == null) {
1217            Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
1218            currentJoinColumn.setImplicit(true);
1219            currentJoinColumn.setNullable(false); //I break the spec, but it's for good
1220
currentJoinColumn.setPropertyHolder(propertyHolder);
1221            currentJoinColumn.setJoins(secondaryTables);
1222            currentJoinColumn.setMappings(mappings);
1223            currentJoinColumn.setPropertyName(propertyName);
1224            currentJoinColumn.setMappedBy(mappedBy);
1225            currentJoinColumn.bind();
1226
1227            joinColumns = new Ejb3JoinColumn[] {
1228                currentJoinColumn
1229
1230            };
1231        }
1232        else {
1233            joinColumns = new Ejb3JoinColumn[annJoins.length];
1234            JoinColumn annJoin;
1235            int length = annJoins.length;
1236            for (int index = 0 ; index < length ; index++) {
1237                annJoin = annJoins[index];
1238                Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
1239                currentJoinColumn.setImplicit(true);
1240                currentJoinColumn.setPropertyHolder(propertyHolder);
1241                currentJoinColumn.setJoins(secondaryTables);
1242                currentJoinColumn.setMappings(mappings);
1243                currentJoinColumn.setPropertyName(propertyName);
1244                currentJoinColumn.setMappedBy(mappedBy);
1245                currentJoinColumn.setJoinAnnotation(annJoin, propertyName);
1246                currentJoinColumn.setNullable(false); //I break the spec, but it's for good
1247
//done after the annotation to override it
1248
currentJoinColumn.bind();
1249                joinColumns[index] = currentJoinColumn;
1250            }
1251        }
1252        return joinColumns;
1253    }
1254
1255    private static void bindComponent(
1256            PropertyInferredData inferredData,
1257            PropertyHolder propertyHolder,
1258            boolean propertyAccess,
1259            EntityBinder entityBinder,
1260            boolean isIdentifierMapper, Map JavaDoc<String JavaDoc, Column[]> columnOverride,
1261            ExtendedMappings mappings
1262            ) {
1263        Component comp = fillComponent(propertyHolder, inferredData, propertyAccess, true, entityBinder, isIdentifierMapper, columnOverride, mappings);
1264
1265        PropertyBinder binder = new PropertyBinder();
1266        binder.setName( inferredData.getPropertyName() );
1267        binder.setValue(comp);
1268        binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
1269        Property prop = binder.make();
1270        propertyHolder.addProperty(prop);
1271    }
1272
1273    public static Component fillComponent(
1274            PropertyHolder propertyHolder, PropertyInferredData inferredData,
1275            boolean propertyAccess, boolean isNullable, EntityBinder entityBinder,
1276            boolean isIdentifierMapper, Map JavaDoc<String JavaDoc, Column[]> columnOverride, ExtendedMappings mappings
1277            ) {
1278        Component comp = new Component( propertyHolder.getPersistentClass() );
1279        if ( !isIdentifierMapper ) {
1280            comp.setComponentClassName( inferredData.getReturnedClassName() );
1281        }
1282        else {
1283            comp.setComponentClassName( comp.getOwner().getClassName() );
1284        }
1285        String JavaDoc subpath = StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() );
1286        log.debug("Binding component with path: " + subpath);
1287        Map JavaDoc<String JavaDoc, Column[]> localColumnOverride = propertyHolder.mergeOverridenColumns(columnOverride);
1288        PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(comp, subpath, localColumnOverride);
1289        List JavaDoc<PropertyAnnotatedElement> classElements = new ArrayList JavaDoc<PropertyAnnotatedElement>();
1290        addElementsOfAClass(classElements,
1291                subHolder,
1292                propertyAccess,
1293                inferredData.getReturnedClass() );
1294        for (PropertyAnnotatedElement propertyAnnotatedElement : classElements) {
1295            processElementAnnotations(subHolder, isNullable == true ? Nullability.NO_CONSTRAINT : Nullability.FORCED_NOT_NULL,
1296                    propertyAnnotatedElement.element, propertyAnnotatedElement.inferredData, new HashMap JavaDoc<String JavaDoc, IdGenerator>(),
1297                    new HashMap JavaDoc<String JavaDoc, Properties JavaDoc>(), entityBinder, isIdentifierMapper, mappings);
1298        }
1299        return comp;
1300    }
1301
1302    private static void bindId(
1303            String JavaDoc generatorType, String JavaDoc generatorName,
1304            PropertyInferredData inferredData, Ejb3Column column, PropertyHolder propertyHolder,
1305            Map JavaDoc<String JavaDoc, IdGenerator> localGenerators, HashMap JavaDoc<String JavaDoc, Properties JavaDoc> localGeneratorTables, boolean isComposite, Map JavaDoc<String JavaDoc, Column[]> columnOverride, boolean isPropertyAccess,
1306            EntityBinder entityBinder, Type typeAnn, boolean isEmbedded, ExtendedMappings mappings
1307            ) {
1308        /*
1309         * Fill simple value and property since and Id is a property
1310         */

1311        PersistentClass persistentClass = propertyHolder.getPersistentClass();
1312        if ( ! ( persistentClass instanceof RootClass ) ) {
1313            throw new AnnotationException(
1314                    "Unable to define/override @Id(s) on a subclass: "
1315                    + propertyHolder.getEntityName() );
1316        }
1317        RootClass rootClass = (RootClass) persistentClass;
1318        String JavaDoc persistentClassName = rootClass == null ? null : rootClass.getClassName();
1319        SimpleValue id;
1320        if (isComposite) {
1321            id = fillComponent(propertyHolder, inferredData, isPropertyAccess, false, entityBinder, false, columnOverride, mappings);
1322            ( (Component) id).setKey(true);
1323        }
1324        else {
1325            column.forceNotNull(); //this is an id
1326
SimpleValueBinder value = new SimpleValueBinder();
1327            value.setPropertyName( inferredData.getPropertyName() );
1328            value.setReturnedClassName( inferredData.getReturnedClassName() );
1329            value.setColumns( new Ejb3Column[] { column } );
1330            value.setPersistentClassName( persistentClassName );
1331            value.setMappings( mappings );
1332            value.setExplicitType(typeAnn);
1333            id = value.make();
1334        }
1335        rootClass.setIdentifier(id);
1336        Table table = id.getTable();
1337        table.setIdentifierValue(id);
1338        //generator settings
1339
id.setIdentifierGeneratorStrategy(generatorType);
1340        Properties JavaDoc params = new Properties JavaDoc();
1341        //always settable
1342
params.setProperty(
1343            PersistentIdentifierGenerator.TABLE, table.getName()
1344        );
1345        params.setProperty(
1346            PersistentIdentifierGenerator.PK,
1347            ( (org.hibernate.mapping.Column) id.getColumnIterator().next() ).getName()
1348        );
1349        if (! isDefault(generatorName) ) {
1350            //we have a named generator
1351
IdGenerator gen = mappings.getGenerator(generatorName, localGenerators);
1352            if (gen == null) {
1353                throw new AnnotationException("Unknown Id.generator: " + generatorName);
1354            }
1355            //This is quite vague in the spec but a generator could override the generate choice
1356
final boolean overrideType = ! generatorType(GeneratorType.AUTO).equals( generatorType );
1357            if ( overrideType ) {
1358                id.setIdentifierGeneratorStrategy( gen.getIdentifierGeneratorStrategy() );
1359            }
1360            //checkIfMatchingGenerator(gen, generatorType, generatorName);
1361
Iterator JavaDoc genParams = gen.getParams().entrySet().iterator();
1362            while ( genParams.hasNext() ) {
1363                Map.Entry JavaDoc elt = (Map.Entry JavaDoc) genParams.next();
1364                params.setProperty( (String JavaDoc) elt.getKey(), (String JavaDoc) elt.getValue() );
1365            }
1366            if ( MultipleHiLoPerTableGenerator.class.getName().equals(generatorType) ) {
1367                //try and find the associated Generator Table
1368
fillGeneratorWithGeneratorTableParams(params, localGeneratorTables, generatorName, mappings);
1369            }
1370        }
1371        if (generatorType == "assigned") id.setNullValue("undefined");
1372        id.setIdentifierGeneratorProperties(params);
1373        if (isEmbedded) {
1374            rootClass.setEmbeddedIdentifier( inferredData.getReturnedClass() == null );
1375        }
1376        else {
1377            PropertyBinder binder = new PropertyBinder();
1378            binder.setName( inferredData.getPropertyName() );
1379            binder.setValue(id);
1380            binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
1381            Property prop = binder.make();
1382            rootClass.setIdentifierProperty(prop);
1383        }
1384    }
1385
1386    /**
1387     * @param gen
1388     * @param generatorType
1389     * @param generatorName
1390     */

1391    private static void checkIfMatchingGenerator(IdGenerator gen, String JavaDoc generatorType, String JavaDoc generatorName) {
1392        //this means: the generators are of the same type or
1393
//Id annotation's generatorType == AUTO
1394
//the default generator can be either Sequence, Table or Id, so we should let it go wo restriction
1395
boolean matchingGenerator = gen.getIdentifierGeneratorStrategy().equals(generatorType);
1396        boolean defaultGenerator = generatorType(GeneratorType.AUTO).equals( generatorType );
1397
1398        if ( ! ( matchingGenerator || defaultGenerator ) ) {
1399            //named generator and id one should be compatible
1400
throw new AnnotationException(
1401                "Incompatible generator between Id.generate and its named generator: "
1402                + generatorType + "!=" + generatorName);
1403        }
1404    }
1405
1406    private static void fillGeneratorWithGeneratorTableParams(Properties JavaDoc params, HashMap JavaDoc<String JavaDoc, Properties JavaDoc> localGeneratorTables, String JavaDoc generatorName, ExtendedMappings mappings) {
1407        final String JavaDoc generatorTableName = params.getProperty(GENERATOR_TABLE_NAME_PARAM);
1408        Properties JavaDoc props = mappings.getGeneratorTableProperties(generatorTableName, localGeneratorTables);
1409        if (props == null) {
1410            if ( MultipleHiLoPerTableGenerator.DEFAULT_TABLE.equals(generatorTableName) ) {
1411                //default value
1412
return;
1413            }
1414            else {
1415                throw new AnnotationException("Unable to find a @GeneratedIdTable for table name in " + generatorName + ": " + generatorTableName );
1416            }
1417        }
1418        else {
1419            Iterator JavaDoc properties = props.entrySet().iterator();
1420            java.util.Map.Entry<String JavaDoc, String JavaDoc> property;
1421            while ( properties.hasNext() ) {
1422                property = (java.util.Map.Entry<String JavaDoc, String JavaDoc>) properties.next();
1423                params.setProperty( property.getKey(), property.getValue() );
1424            }
1425        }
1426    }
1427
1428    private static void bindManyToOne(
1429            String JavaDoc cascadeStrategy, Ejb3JoinColumn[] columns, boolean optional, FetchMode fetchMode, String JavaDoc propertyName,
1430            String JavaDoc returnedClassName, Class JavaDoc targetEntity, String JavaDoc propertyAccessorName, PropertyHolder propertyHolder,
1431            boolean unique, boolean isIdentifierMapper, ExtendedMappings mappings
1432            ) {
1433        //All FK columns should be in the same table
1434
org.hibernate.mapping.ManyToOne value = new org.hibernate.mapping.ManyToOne( columns[0].getTable() );
1435        if ( isDefault(targetEntity) ) {
1436            value.setReferencedEntityName(returnedClassName);
1437        }
1438        else {
1439            value.setReferencedEntityName( targetEntity.getName() );
1440        }
1441        value.setFetchMode(fetchMode);
1442        value.setLazy(fetchMode!=FetchMode.JOIN);
1443        if (!optional) {
1444            for(Ejb3JoinColumn column:columns) {
1445                column.setNullable(false);
1446            }
1447        }
1448        value.setTypeName(returnedClassName);
1449        value.setTypeUsingReflection(propertyHolder.getClassName() , propertyName);
1450        String JavaDoc propertyRef = value.getReferencedPropertyName();
1451
1452        //value.createForeignKey();
1453
mappings.addSecondPass( new FkSecondPass(value, columns, unique, mappings) );
1454
1455        Ejb3Column.checkPropertyConsistency(columns, propertyHolder.getEntityName() + propertyName);
1456        PropertyBinder binder = new PropertyBinder();
1457        binder.setName(propertyName);
1458        binder.setValue(value);
1459        binder.setCascade(cascadeStrategy);
1460        if (isIdentifierMapper) {
1461            binder.setInsertable(false);
1462            binder.setInsertable(false);
1463        }
1464        else {
1465            binder.setInsertable( columns[0].isInsertable() );
1466            binder.setUpdatable( columns[0].isUpdatable() );
1467        }
1468        binder.setPropertyAccessorName( propertyAccessorName );
1469        binder.setCascade(cascadeStrategy);
1470        Property prop = binder.make();
1471        //composite FK columns are in the same table so its OK
1472
columns[0].addPropertyToMappingContainer(prop);
1473    }
1474
1475    public static void bindFkSecondPass(
1476            org.hibernate.mapping.ManyToOne manyToOne, Ejb3JoinColumn[] columns, Map JavaDoc persistentClasses, boolean unique,
1477            ExtendedMappings mappings
1478            ) {
1479        PersistentClass ref = (PersistentClass) persistentClasses.get( manyToOne.getReferencedEntityName() );
1480        if (ref == null) throw new AnnotationException("Unable to find entity " + manyToOne.getReferencedEntityName() );
1481        BinderHelper.createSyntheticPropertyReference( columns, ref, manyToOne, mappings );
1482        TableBinder.bindFk(ref, null, columns, manyToOne, unique );
1483    }
1484
1485    private static void bindOneToOne(
1486            String JavaDoc cascadeStrategy,
1487            Ejb3JoinColumn[] columns,
1488            boolean optional,
1489            FetchMode fetchMode,
1490            String JavaDoc propertyName,
1491            String JavaDoc returnedClassName,
1492            Class JavaDoc targetEntity,
1493            String JavaDoc propertyAccessorName,
1494            PropertyHolder propertyHolder,
1495            String JavaDoc mappedBy,
1496            boolean trueOneToOne,
1497            boolean isIdentifierMapper, ExtendedMappings mappings
1498            ) {
1499        //column.getTable() => persistentClass.getTable()
1500
log.debug("Fetching " + propertyName + " with " + fetchMode);
1501        boolean mapToPK = true;
1502        if ( ! trueOneToOne ) {
1503            //try to find a hidden true one to one (FK == PK columns)
1504
Iterator JavaDoc idColumns = propertyHolder.getIdentifier().getColumnIterator();
1505            List JavaDoc<String JavaDoc> idColumnNames = new ArrayList JavaDoc<String JavaDoc>();
1506            org.hibernate.mapping.Column currentColumn;
1507            while( idColumns.hasNext() ) {
1508                currentColumn = (org.hibernate.mapping.Column) idColumns.next();
1509                idColumnNames.add( currentColumn.getName() );
1510            }
1511            for (Ejb3JoinColumn col : columns) {
1512                if ( ! idColumnNames.contains( col.getMappingColumn().getName() ) ) {
1513                    mapToPK = false;
1514                    break;
1515                }
1516            }
1517        }
1518        if ( trueOneToOne || mapToPK || ! isDefault(mappedBy) ) {
1519            //is a true one-to-one
1520
//FIXME referencedColumnName ignored => ordering may fail.
1521
org.hibernate.mapping.OneToOne value = new org.hibernate.mapping.OneToOne( propertyHolder.getTable(), propertyHolder.getPersistentClass() );
1522            value.setPropertyName( propertyName );
1523            if ( isDefault(targetEntity) ) {
1524                value.setReferencedEntityName(returnedClassName);
1525            }
1526            else {
1527                value.setReferencedEntityName( targetEntity.getName() );
1528            }
1529            value.setFetchMode(fetchMode);
1530            value.setLazy(fetchMode!=FetchMode.JOIN);
1531
1532            if (!optional) value.setConstrained(true);
1533            //FIXME Handle bidir OneToOne
1534
value.setForeignKeyType(
1535                value.isConstrained() ?
1536                ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
1537                ForeignKeyDirection.FOREIGN_KEY_TO_PARENT
1538            );
1539            if ( ! isDefault(mappedBy) ) value.setReferencedPropertyName(mappedBy);
1540
1541            String JavaDoc propertyRef = value.getReferencedPropertyName();
1542            if (propertyRef!=null) mappings.addUniquePropertyReference(
1543                value.getReferencedEntityName(),
1544                propertyRef
1545            );
1546            //value.createForeignKey();
1547
PropertyBinder binder = new PropertyBinder();
1548            binder.setName(propertyName);
1549            binder.setValue(value);
1550            binder.setCascade(cascadeStrategy);
1551            binder.setPropertyAccessorName(propertyAccessorName);
1552            Property prop = binder.make();
1553            prop.setCascade(cascadeStrategy);
1554            //no column associated since its a one to one
1555
propertyHolder.addProperty(prop);
1556        }
1557        else {
1558            //has a FK on the table
1559
bindManyToOne(cascadeStrategy, columns, optional, fetchMode, propertyName, returnedClassName, targetEntity,
1560                propertyAccessorName, propertyHolder, true, isIdentifierMapper, mappings);
1561        }
1562    }
1563
1564    private static String JavaDoc generatorType(GeneratorType generatorEnum) {
1565        switch ( generatorEnum ) {
1566            case NONE : return "assigned";
1567            case IDENTITY:return "identity";
1568            case AUTO:return "native";
1569            case TABLE:return MultipleHiLoPerTableGenerator.class.getName();
1570            case SEQUENCE:return "sequence";
1571        }
1572        throw new AssertionFailure("Unknown GeneratorType: " + generatorEnum);
1573    }
1574    
1575    private static EnumSet JavaDoc<CascadeType> convertToHibernateCascadeType(javax.persistence.CascadeType[] ejbCascades) {
1576        EnumSet JavaDoc<CascadeType> hibernateCascadeSet = EnumSet.noneOf(CascadeType.class);
1577        if (ejbCascades != null && ejbCascades.length > 0) {
1578            for(javax.persistence.CascadeType cascade : ejbCascades) {
1579                switch (cascade) {
1580                    case ALL:
1581                        hibernateCascadeSet.add(CascadeType.ALL);
1582                        break;
1583                    case PERSIST:
1584                        hibernateCascadeSet.add(CascadeType.PERSIST);
1585                        break;
1586                    case MERGE:
1587                        hibernateCascadeSet.add(CascadeType.MERGE);
1588                        break;
1589                    case REMOVE:
1590                        hibernateCascadeSet.add(CascadeType.REMOVE);
1591                        break;
1592                    case REFRESH:
1593                        hibernateCascadeSet.add(CascadeType.REFRESH);
1594                        break;
1595                }
1596            }
1597        }
1598        
1599        return hibernateCascadeSet;
1600    }
1601    
1602    private static String JavaDoc getCascadeStrategy(javax.persistence.CascadeType[] ejbCascades, Cascade hibernateCascadeAnnotation) {
1603        EnumSet JavaDoc<CascadeType> hibernateCascadeSet = convertToHibernateCascadeType(ejbCascades);
1604        CascadeType[] hibernateCascades = hibernateCascadeAnnotation == null ? null : hibernateCascadeAnnotation.value();
1605        
1606        if (hibernateCascades != null && hibernateCascades.length > 0) {
1607            for (CascadeType cascadeType : hibernateCascades) {
1608                hibernateCascadeSet.add(cascadeType);
1609            }
1610        }
1611        
1612        StringBuffer JavaDoc cascade = new StringBuffer JavaDoc();
1613        Iterator JavaDoc<CascadeType> cascadeType = hibernateCascadeSet.iterator();
1614        while ( cascadeType.hasNext() ) {
1615            switch( cascadeType.next() ) {
1616                case ALL:
1617                    cascade.append(",").append("all");
1618                    break;
1619                case SAVE_UPDATE:
1620                    cascade.append(",").append("save-update");
1621                    break;
1622                case PERSIST:
1623                    cascade.append(",").append("persist");
1624                    break;
1625                case MERGE:
1626                    cascade.append(",").append("merge");
1627                    break;
1628                case LOCK:
1629                    cascade.append(",").append("lock");
1630                    break;
1631                case REFRESH:
1632                    cascade.append(",").append("refresh");
1633                    break;
1634                case REPLICATE:
1635                    cascade.append(",").append("replicate");
1636                    break;
1637                case EVICT:
1638                    cascade.append(",").append("evict");
1639                    break;
1640                case DELETE:
1641                    cascade.append(",").append("delete");
1642                    break;
1643                case DELETE_ORPHAN:
1644                    cascade.append(",").append("delete-orphan");
1645                    break;
1646                case REMOVE:
1647                    cascade.append(",").append("delete");
1648                    break;
1649            }
1650        }
1651        return cascade.length() > 0 ? cascade.substring(1) : "none";
1652    }
1653    
1654    private static FetchMode getFetchMode(FetchType fetch) {
1655        if (fetch == FetchType.EAGER) {
1656            return FetchMode.JOIN;
1657        }
1658        else {
1659            return FetchMode.SELECT;
1660        }
1661    }
1662    
1663    private static HashMap JavaDoc<String JavaDoc,IdGenerator> buildLocalGenerators(AnnotatedElement JavaDoc annElt) {
1664        HashMap JavaDoc<String JavaDoc,IdGenerator> generators = new HashMap JavaDoc<String JavaDoc, IdGenerator>();
1665        TableGenerator tabGen = annElt.getAnnotation( TableGenerator.class );
1666        SequenceGenerator seqGen = annElt.getAnnotation( SequenceGenerator.class );
1667        GenericGenerator genGen = annElt.getAnnotation( GenericGenerator.class );
1668        if (tabGen != null) {
1669            IdGenerator idGen = buildIdGenerator(tabGen);
1670            generators.put(idGen.getName(), idGen);
1671        }
1672        if (seqGen != null) {
1673            IdGenerator idGen = buildIdGenerator(seqGen);
1674            generators.put(idGen.getName(), idGen);
1675        }
1676        if (genGen != null) {
1677            IdGenerator idGen = buildIdGenerator( genGen);
1678            generators.put( idGen.getName(), idGen );
1679        }
1680        return generators;
1681    }
1682
1683    public static boolean isDefault(String JavaDoc annotationString) {
1684        return ANNOTATION_STRING_DEFAULT.equals(annotationString);
1685    }
1686
1687    public static boolean isDefault(Class JavaDoc clazz) {
1688        return void.class.equals(clazz);
1689    }
1690
1691    public static Map JavaDoc<Class JavaDoc, InheritanceState> buildInheritanceStates(List JavaDoc<Class JavaDoc> orderedClasses) {
1692        Map JavaDoc<Class JavaDoc, InheritanceState> inheritanceStatePerClass = new HashMap JavaDoc<Class JavaDoc, InheritanceState>( orderedClasses.size() );
1693        for (Class JavaDoc clazz : orderedClasses) {
1694            Class JavaDoc superClazz = clazz.getSuperclass();
1695            InheritanceState state = new InheritanceState();
1696            state.setInheritanceType(clazz);
1697            if ( orderedClasses.contains(superClazz) ) {
1698                //the classes are ordered thus preventing an NPE
1699
InheritanceState superState = inheritanceStatePerClass.get(superClazz);
1700                superState.hasSons = true;
1701                if (superState.isEmbeddableSuperclass == true) {
1702                    state.hasParents = false;
1703                    state.hasEmbeddedSuperclass = true;
1704                }
1705                else {
1706                    state.hasParents = true;
1707                    state.hasEmbeddedSuperclass = false;
1708                }
1709                final boolean nonDefault = ! InheritanceType.SINGLE_TABLE.equals( state.type );
1710                if (superState.type != null) {
1711                    final boolean mixingStrategy = ! state.type.equals( superState.type );
1712                    if ( nonDefault && mixingStrategy ) {
1713                        log.warn("Mixing inheritance strategy in a entity hierarchy is not allowed, ignoring sub strategy in: " + clazz.getName() );
1714                    }
1715                    state.type = superState.type;
1716                }
1717            }
1718            inheritanceStatePerClass.put(clazz, state);
1719        }
1720        return inheritanceStatePerClass;
1721    }
1722
1723    private static class PropertyAnnotatedElement {
1724        public PropertyAnnotatedElement(AnnotatedElement JavaDoc elt) {
1725            element = elt;
1726            inferredData = new PropertyInferredData(element);
1727        }
1728        public AnnotatedElement JavaDoc element;
1729        public PropertyInferredData inferredData;
1730    }
1731}
1732
Popular Tags