1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.cfg;
26
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.Properties;
33 import java.util.StringTokenizer;
34
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.dom4j.Attribute;
38 import org.dom4j.Document;
39 import org.dom4j.Element;
40 import org.hibernate.CacheMode;
41 import org.hibernate.EntityMode;
42 import org.hibernate.FetchMode;
43 import org.hibernate.FlushMode;
44 import org.hibernate.MappingException;
45 import org.hibernate.engine.FilterDefinition;
46 import org.hibernate.engine.NamedQueryDefinition;
47 import org.hibernate.engine.Versioning;
48 import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
49 import org.hibernate.id.PersistentIdentifierGenerator;
50 import org.hibernate.mapping.Any;
51 import org.hibernate.mapping.Array;
52 import org.hibernate.mapping.AuxiliaryDatabaseObject;
53 import org.hibernate.mapping.Backref;
54 import org.hibernate.mapping.Bag;
55 import org.hibernate.mapping.Collection;
56 import org.hibernate.mapping.Column;
57 import org.hibernate.mapping.Component;
58 import org.hibernate.mapping.DependantValue;
59 import org.hibernate.mapping.Fetchable;
60 import org.hibernate.mapping.Filterable;
61 import org.hibernate.mapping.Formula;
62 import org.hibernate.mapping.IdentifierBag;
63 import org.hibernate.mapping.IdentifierCollection;
64 import org.hibernate.mapping.IndexBackref;
65 import org.hibernate.mapping.IndexedCollection;
66 import org.hibernate.mapping.Join;
67 import org.hibernate.mapping.JoinedSubclass;
68 import org.hibernate.mapping.KeyValue;
69 import org.hibernate.mapping.List;
70 import org.hibernate.mapping.ManyToOne;
71 import org.hibernate.mapping.Map;
72 import org.hibernate.mapping.MetaAttribute;
73 import org.hibernate.mapping.OneToMany;
74 import org.hibernate.mapping.OneToOne;
75 import org.hibernate.mapping.PersistentClass;
76 import org.hibernate.mapping.PrimitiveArray;
77 import org.hibernate.mapping.Property;
78 import org.hibernate.mapping.PropertyGeneration;
79 import org.hibernate.mapping.RootClass;
80 import org.hibernate.mapping.Selectable;
81 import org.hibernate.mapping.Set;
82 import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject;
83 import org.hibernate.mapping.SimpleValue;
84 import org.hibernate.mapping.SingleTableSubclass;
85 import org.hibernate.mapping.Subclass;
86 import org.hibernate.mapping.Table;
87 import org.hibernate.mapping.ToOne;
88 import org.hibernate.mapping.TypeDef;
89 import org.hibernate.mapping.UnionSubclass;
90 import org.hibernate.mapping.UniqueKey;
91 import org.hibernate.mapping.Value;
92 import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
93 import org.hibernate.persister.entity.SingleTableEntityPersister;
94 import org.hibernate.persister.entity.UnionSubclassEntityPersister;
95 import org.hibernate.type.DiscriminatorType;
96 import org.hibernate.type.ForeignKeyDirection;
97 import org.hibernate.type.Type;
98 import org.hibernate.type.TypeFactory;
99 import org.hibernate.util.JoinedIterator;
100 import org.hibernate.util.ReflectHelper;
101 import org.hibernate.util.StringHelper;
102
103 /**
104 * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
105 * classes in the <tt>mapping</tt> package)
106 *
107 * @author Gavin King
108 */
109 public final class HbmBinder {
110
111 private static final Logger log = LoggerFactory.getLogger( HbmBinder.class );
112
113 /**
114 * Private constructor to disallow instantiation.
115 */
116 private HbmBinder() {
117 }
118
119 /**
120 * The main contract into the hbm.xml-based binder. Performs necessary binding operations
121 * represented by the given DOM.
122 *
123 * @param doc The DOM to be parsed and bound.
124 * @param mappings Current bind state.
125 * @param inheritedMetas Any inherited meta-tag information.
126 * @throws MappingException
127 */
128 public static void bindRoot(Document doc, Mappings mappings, java.util.Map inheritedMetas)
129 throws MappingException {
130
131 java.util.List names = HbmBinder.getExtendsNeeded( doc, mappings );
132 if ( !names.isEmpty() ) {
133 // classes mentioned in extends not available - so put it in queue
134 Element hmNode = doc.getRootElement();
135 Attribute packNode = hmNode.attribute( "package" );
136 String packageName = null;
137 if ( packNode != null ) {
138 packageName = packNode.getValue();
139 }
140 Iterator itr = names.iterator();
141 while ( itr.hasNext() ) {
142 String extendsName = (String) itr.next();
143 mappings.addToExtendsQueue( new ExtendsQueueEntry( extendsName, packageName, doc ) );
144 }
145 return;
146 }
147
148 Element hmNode = doc.getRootElement();
149 // get meta's from <hibernate-mapping>
150 inheritedMetas = getMetas( hmNode, inheritedMetas, true );
151 extractRootAttributes( hmNode, mappings );
152
153 Iterator rootChildren = hmNode.elementIterator();
154 while ( rootChildren.hasNext() ) {
155 final Element element = (Element) rootChildren.next();
156 final String elementName = element.getName();
157
158 if ( "filter-def".equals( elementName ) ) {
159 parseFilterDef( element, mappings );
160 }
161 else if ( "typedef".equals( elementName ) ) {
162 bindTypeDef( element, mappings );
163 }
164 else if ( "class".equals( elementName ) ) {
165 RootClass rootclass = new RootClass();
166 bindRootClass( element, rootclass, mappings, inheritedMetas );
167 mappings.addClass( rootclass );
168 }
169 else if ( "subclass".equals( elementName ) ) {
170 PersistentClass superModel = getSuperclass( mappings, element );
171 handleSubclass( superModel, mappings, element, inheritedMetas );
172 }
173 else if ( "joined-subclass".equals( elementName ) ) {
174 PersistentClass superModel = getSuperclass( mappings, element );
175 handleJoinedSubclass( superModel, mappings, element, inheritedMetas );
176 }
177 else if ( "union-subclass".equals( elementName ) ) {
178 PersistentClass superModel = getSuperclass( mappings, element );
179 handleUnionSubclass( superModel, mappings, element, inheritedMetas );
180 }
181 else if ( "query".equals( elementName ) ) {
182 bindNamedQuery( element, null, mappings );
183 }
184 else if ( "sql-query".equals( elementName ) ) {
185 bindNamedSQLQuery( element, null, mappings );
186 }
187 else if ( "resultset".equals( elementName ) ) {
188 bindResultSetMappingDefinition( element, null, mappings );
189 }
190 else if ( "import".equals( elementName ) ) {
191 bindImport( element, mappings );
192 }
193 else if ( "database-object".equals( elementName ) ) {
194 bindAuxiliaryDatabaseObject( element, mappings );
195 }
196 }
197 }
198
199 private static void bindImport(Element importNode, Mappings mappings) {
200 String className = getClassName( importNode.attribute( "class" ), mappings );
201 Attribute renameNode = importNode.attribute( "rename" );
202 String rename = ( renameNode == null ) ?
203 StringHelper.unqualify( className ) :
204 renameNode.getValue();
205 log.debug( "Import: " + rename + " -> " + className );
206 mappings.addImport( className, rename );
207 }
208
209 private static void bindTypeDef(Element typedefNode, Mappings mappings) {
210 String typeClass = typedefNode.attributeValue( "class" );
211 String typeName = typedefNode.attributeValue( "name" );
212 Iterator paramIter = typedefNode.elementIterator( "param" );
213 Properties parameters = new Properties();
214 while ( paramIter.hasNext() ) {
215 Element param = (Element) paramIter.next();
216 parameters.setProperty( param.attributeValue( "name" ), param.getTextTrim() );
217 }
218 mappings.addTypeDef( typeName, typeClass, parameters );
219 }
220
221 private static void bindAuxiliaryDatabaseObject(Element auxDbObjectNode, Mappings mappings) {
222 AuxiliaryDatabaseObject auxDbObject = null;
223 Element definitionNode = auxDbObjectNode.element( "definition" );
224 if ( definitionNode != null ) {
225 try {
226 auxDbObject = ( AuxiliaryDatabaseObject ) ReflectHelper
227 .classForName( definitionNode.attributeValue( "class" ) )
228 .newInstance();
229 }
230 catch( ClassNotFoundException e ) {
231 throw new MappingException(
232 "could not locate custom database object class [" +
233 definitionNode.attributeValue( "class" ) + "]"
234 );
235 }
236 catch( Throwable t ) {
237 throw new MappingException(
238 "could not instantiate custom database object class [" +
239 definitionNode.attributeValue( "class" ) + "]"
240 );
241 }
242 }
243 else {
244 auxDbObject = new SimpleAuxiliaryDatabaseObject(
245 auxDbObjectNode.elementTextTrim( "create" ),
246 auxDbObjectNode.elementTextTrim( "drop" )
247 );
248 }
249
250 Iterator dialectScopings = auxDbObjectNode.elementIterator( "dialect-scope" );
251 while ( dialectScopings.hasNext() ) {
252 Element dialectScoping = ( Element ) dialectScopings.next();
253 auxDbObject.addDialectScope( dialectScoping.attributeValue( "name" ) );
254 }
255
256 mappings.addAuxiliaryDatabaseObject( auxDbObject );
257 }
258
259 private static void extractRootAttributes(Element hmNode, Mappings mappings) {
260 Attribute schemaNode = hmNode.attribute( "schema" );
261 mappings.setSchemaName( ( schemaNode == null ) ? null : schemaNode.getValue() );
262
263 Attribute catalogNode = hmNode.attribute( "catalog" );
264 mappings.setCatalogName( ( catalogNode == null ) ? null : catalogNode.getValue() );
265
266 Attribute dcNode = hmNode.attribute( "default-cascade" );
267 mappings.setDefaultCascade( ( dcNode == null ) ? "none" : dcNode.getValue() );
268
269 Attribute daNode = hmNode.attribute( "default-access" );
270 mappings.setDefaultAccess( ( daNode == null ) ? "property" : daNode.getValue() );
271
272 Attribute dlNode = hmNode.attribute( "default-lazy" );
273 mappings.setDefaultLazy( dlNode == null || dlNode.getValue().equals( "true" ) );
274
275 Attribute aiNode = hmNode.attribute( "auto-import" );
276 mappings.setAutoImport( ( aiNode == null ) || "true".equals( aiNode.getValue() ) );
277
278 Attribute packNode = hmNode.attribute( "package" );
279 if ( packNode != null ) mappings.setDefaultPackage( packNode.getValue() );
280 }
281
282 /**
283 * Responsible for perfoming the bind operation related to an <class/> mapping element.
284 *
285 * @param node The DOM Element for the <class/> element.
286 * @param rootClass The mapping instance to which to bind the information.
287 * @param mappings The current bind state.
288 * @param inheritedMetas Any inherited meta-tag information.
289 * @throws MappingException
290 */
291 public static void bindRootClass(Element node, RootClass rootClass, Mappings mappings,
292 java.util.Map inheritedMetas) throws MappingException {
293 bindClass( node, rootClass, mappings, inheritedMetas );
294 inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <class>
295 bindRootPersistentClassCommonValues( node, inheritedMetas, mappings, rootClass );
296 }
297
298 private static void bindRootPersistentClassCommonValues(Element node,
299 java.util.Map inheritedMetas, Mappings mappings, RootClass entity)
300 throws MappingException {
301
302 // DB-OBJECTNAME
303
304 Attribute schemaNode = node.attribute( "schema" );
305 String schema = schemaNode == null ?
306 mappings.getSchemaName() : schemaNode.getValue();
307
308 Attribute catalogNode = node.attribute( "catalog" );
309 String catalog = catalogNode == null ?
310 mappings.getCatalogName() : catalogNode.getValue();
311
312 Table table = mappings.addTable(
313 schema,
314 catalog,
315 getClassTableName( entity, node, schema, catalog, null, mappings ),
316 getSubselect( node ),
317 entity.isAbstract() != null && entity.isAbstract().booleanValue()
318 );
319 entity.setTable( table );
320 bindComment(table, node);
321
322 log.info(
323 "Mapping class: " + entity.getEntityName() +
324 " -> " + entity.getTable().getName()
325 );
326
327 // MUTABLE
328 Attribute mutableNode = node.attribute( "mutable" );
329 entity.setMutable( ( mutableNode == null ) || mutableNode.getValue().equals( "true" ) );
330
331 // WHERE
332 Attribute whereNode = node.attribute( "where" );
333 if ( whereNode != null ) entity.setWhere( whereNode.getValue() );
334
335 // CHECK
336 Attribute chNode = node.attribute( "check" );
337 if ( chNode != null ) table.addCheckConstraint( chNode.getValue() );
338
339 // POLYMORPHISM
340 Attribute polyNode = node.attribute( "polymorphism" );
341 entity.setExplicitPolymorphism( ( polyNode != null )
342 && polyNode.getValue().equals( "explicit" ) );
343
344 // ROW ID
345 Attribute rowidNode = node.attribute( "rowid" );
346 if ( rowidNode != null ) table.setRowId( rowidNode.getValue() );
347
348 Iterator subnodes = node.elementIterator();
349 while ( subnodes.hasNext() ) {
350
351 Element subnode = (Element) subnodes.next();
352 String name = subnode.getName();
353
354 if ( "id".equals( name ) ) {
355 // ID
356 bindSimpleId( subnode, entity, mappings, inheritedMetas );
357 }
358 else if ( "composite-id".equals( name ) ) {
359 // COMPOSITE-ID
360 bindCompositeId( subnode, entity, mappings, inheritedMetas );
361 }
362 else if ( "version".equals( name ) || "timestamp".equals( name ) ) {
363 // VERSION / TIMESTAMP
364 bindVersioningProperty( table, subnode, mappings, name, entity, inheritedMetas );
365 }
366 else if ( "discriminator".equals( name ) ) {
367 // DISCRIMINATOR
368 bindDiscriminatorProperty( table, entity, subnode, mappings );
369 }
370 else if ( "cache".equals( name ) ) {
371 entity.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
372 entity.setCacheRegionName( subnode.attributeValue( "region" ) );
373 entity.setLazyPropertiesCacheable( !"non-lazy".equals( subnode.attributeValue( "include" ) ) );
374 }
375
376 }
377
378 // Primary key constraint
379 entity.createPrimaryKey();
380
381 createClassProperties( node, entity, mappings, inheritedMetas );
382 }
383
384 private static void bindSimpleId(Element idNode, RootClass entity, Mappings mappings,
385 java.util.Map inheritedMetas) throws MappingException {
386 String propertyName = idNode.attributeValue( "name" );
387
388 SimpleValue id = new SimpleValue( entity.getTable() );
389 entity.setIdentifier( id );
390
391 // if ( propertyName == null || entity.getPojoRepresentation() == null ) {
392 // bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
393 // if ( !id.isTypeSpecified() ) {
394 // throw new MappingException( "must specify an identifier type: " + entity.getEntityName()
395 // );
396 // }
397 // }
398 // else {
399 // bindSimpleValue( idNode, id, false, propertyName, mappings );
400 // PojoRepresentation pojo = entity.getPojoRepresentation();
401 // id.setTypeUsingReflection( pojo.getClassName(), propertyName );
402 //
403 // Property prop = new Property();
404 // prop.setValue( id );
405 // bindProperty( idNode, prop, mappings, inheritedMetas );
406 // entity.setIdentifierProperty( prop );
407 // }
408
409 if ( propertyName == null ) {
410 bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
411 }
412 else {
413 bindSimpleValue( idNode, id, false, propertyName, mappings );
414 }
415
416 if ( propertyName == null || !entity.hasPojoRepresentation() ) {
417 if ( !id.isTypeSpecified() ) {
418 throw new MappingException( "must specify an identifier type: "
419 + entity.getEntityName() );
420 }
421 }
422 else {
423 id.setTypeUsingReflection( entity.getClassName(), propertyName );
424 }
425
426 if ( propertyName != null ) {
427 Property prop = new Property();
428 prop.setValue( id );
429 bindProperty( idNode, prop, mappings, inheritedMetas );
430 entity.setIdentifierProperty( prop );
431 }
432
433 // TODO:
434 /*
435 * if ( id.getHibernateType().getReturnedClass().isArray() ) throw new MappingException(
436 * "illegal use of an array as an identifier (arrays don't reimplement equals)" );
437 */
438 makeIdentifier( idNode, id, mappings );
439 }
440
441 private static void bindCompositeId(Element idNode, RootClass entity, Mappings mappings,
442 java.util.Map inheritedMetas) throws MappingException {
443 String propertyName = idNode.attributeValue( "name" );
444 Component id = new Component( entity );
445 entity.setIdentifier( id );
446 bindCompositeId( idNode, id, entity, propertyName, mappings, inheritedMetas );
447 if ( propertyName == null ) {
448 entity.setEmbeddedIdentifier( id.isEmbedded() );
449 if ( id.isEmbedded() ) {
450 // todo : what is the implication of this?
451 id.setDynamic( !entity.hasPojoRepresentation() );
452 /*
453 * Property prop = new Property(); prop.setName("id");
454 * prop.setPropertyAccessorName("embedded"); prop.setValue(id);
455 * entity.setIdentifierProperty(prop);
456 */
457 }
458 }
459 else {
460 Property prop = new Property();
461 prop.setValue( id );
462 bindProperty( idNode, prop, mappings, inheritedMetas );
463 entity.setIdentifierProperty( prop );
464 }
465
466 makeIdentifier( idNode, id, mappings );
467
468 }
469
470 private static void bindVersioningProperty(Table table, Element subnode, Mappings mappings,
471 String name, RootClass entity, java.util.Map inheritedMetas) {
472
473 String propertyName = subnode.attributeValue( "name" );
474 SimpleValue val = new SimpleValue( table );
475 bindSimpleValue( subnode, val, false, propertyName, mappings );
476 if ( !val.isTypeSpecified() ) {
477 // this is either a <version/> tag with no type attribute,
478 // or a <timestamp/> tag
479 if ( "version".equals( name ) ) {
480 val.setTypeName( "integer" );
481 }
482 else {
483 if ( "db".equals( subnode.attributeValue( "source" ) ) ) {
484 val.setTypeName( "dbtimestamp" );
485 }
486 else {
487 val.setTypeName( "timestamp" );
488 }
489 }
490 }
491 Property prop = new Property();
492 prop.setValue( val );
493 bindProperty( subnode, prop, mappings, inheritedMetas );
494 // for version properties marked as being generated, make sure they are "always"
495 // generated; aka, "insert" is invalid; this is dis-allowed by the DTD,
496 // but just to make sure...
497 if ( prop.getGeneration() == PropertyGeneration.INSERT ) {
498 throw new MappingException( "'generated' attribute cannot be 'insert' for versioning property" );
499 }
500 makeVersion( subnode, val );
501 entity.setVersion( prop );
502 entity.addProperty( prop );
503 }
504
505 private static void bindDiscriminatorProperty(Table table, RootClass entity, Element subnode,
506 Mappings mappings) {
507 SimpleValue discrim = new SimpleValue( table );
508 entity.setDiscriminator( discrim );
509 bindSimpleValue(
510 subnode,
511 discrim,
512 false,
513 RootClass.DEFAULT_DISCRIMINATOR_COLUMN_NAME,
514 mappings
515 );
516 if ( !discrim.isTypeSpecified() ) {
517 discrim.setTypeName( "string" );
518 // ( (Column) discrim.getColumnIterator().next() ).setType(type);
519 }
520 entity.setPolymorphic( true );
521 if ( "true".equals( subnode.attributeValue( "force" ) ) )
522 entity.setForceDiscriminator( true );
523 if ( "false".equals( subnode.attributeValue( "insert" ) ) )
524 entity.setDiscriminatorInsertable( false );
525 }
526
527 public static void bindClass(Element node, PersistentClass persistentClass, Mappings mappings,
528 java.util.Map inheritedMetas) throws MappingException {
529 // transfer an explicitly defined entity name
530 // handle the lazy attribute
531 Attribute lazyNode = node.attribute( "lazy" );
532 boolean lazy = lazyNode == null ?
533 mappings.isDefaultLazy() :
534 "true".equals( lazyNode.getValue() );
535 // go ahead and set the lazy here, since pojo.proxy can override it.
536 persistentClass.setLazy( lazy );
537
538 String entityName = node.attributeValue( "entity-name" );
539 if ( entityName == null ) entityName = getClassName( node.attribute("name"), mappings );
540 if ( entityName==null ) {
541 throw new MappingException( "Unable to determine entity name" );
542 }
543 persistentClass.setEntityName( entityName );
544
545 bindPojoRepresentation( node, persistentClass, mappings, inheritedMetas );
546 bindDom4jRepresentation( node, persistentClass, mappings, inheritedMetas );
547 bindMapRepresentation( node, persistentClass, mappings, inheritedMetas );
548
549 bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
550
551 }
552
553 private static void bindPojoRepresentation(Element node, PersistentClass entity,
554 Mappings mappings, java.util.Map metaTags) {
555
556 String className = getClassName( node.attribute( "name" ), mappings );
557 String proxyName = getClassName( node.attribute( "proxy" ), mappings );
558
559 entity.setClassName( className );
560
561 if ( proxyName != null ) {
562 entity.setProxyInterfaceName( proxyName );
563 entity.setLazy( true );
564 }
565 else if ( entity.isLazy() ) {
566 entity.setProxyInterfaceName( className );
567 }
568
569 Element tuplizer = locateTuplizerDefinition( node, EntityMode.POJO );
570 if ( tuplizer != null ) {
571 entity.addTuplizer( EntityMode.POJO, tuplizer.attributeValue( "class" ) );
572 }
573 }
574
575 private static void bindDom4jRepresentation(Element node, PersistentClass entity,
576 Mappings mappings, java.util.Map inheritedMetas) {
577 String nodeName = node.attributeValue( "node" );
578 if (nodeName==null) nodeName = StringHelper.unqualify( entity.getEntityName() );
579 entity.setNodeName(nodeName);
580
581 Element tuplizer = locateTuplizerDefinition( node, EntityMode.DOM4J );
582 if ( tuplizer != null ) {
583 entity.addTuplizer( EntityMode.DOM4J, tuplizer.attributeValue( "class" ) );
584 }
585 }
586
587 private static void bindMapRepresentation(Element node, PersistentClass entity,
588 Mappings mappings, java.util.Map inheritedMetas) {
589 Element tuplizer = locateTuplizerDefinition( node, EntityMode.MAP );
590 if ( tuplizer != null ) {
591 entity.addTuplizer( EntityMode.MAP, tuplizer.attributeValue( "class" ) );
592 }
593 }
594
595 /**
596 * Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
597 *
598 * @param container The containing element (representing the entity/component)
599 * @param entityMode The entity-mode for which to locate the tuplizer element
600 * @return The tuplizer element, or null.
601 */
602 private static Element locateTuplizerDefinition(Element container, EntityMode entityMode) {
603 Iterator itr = container.elements( "tuplizer" ).iterator();
604 while( itr.hasNext() ) {
605 final Element tuplizerElem = ( Element ) itr.next();
606 if ( entityMode.toString().equals( tuplizerElem.attributeValue( "entity-mode") ) ) {
607 return tuplizerElem;
608 }
609 }
610 return null;
611 }
612
613 private static void bindPersistentClassCommonValues(Element node, PersistentClass entity,
614 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
615 // DISCRIMINATOR
616 Attribute discriminatorNode = node.attribute( "discriminator-value" );
617 entity.setDiscriminatorValue( ( discriminatorNode == null )
618 ? entity.getEntityName()
619 : discriminatorNode.getValue() );
620
621 // DYNAMIC UPDATE
622 Attribute dynamicNode = node.attribute( "dynamic-update" );
623 entity.setDynamicUpdate(
624 dynamicNode != null && "true".equals( dynamicNode.getValue() )
625 );
626
627 // DYNAMIC INSERT
628 Attribute insertNode = node.attribute( "dynamic-insert" );
629 entity.setDynamicInsert(
630 insertNode != null && "true".equals( insertNode.getValue() )
631 );
632
633 // IMPORT
634 mappings.addImport( entity.getEntityName(), entity.getEntityName() );
635 if ( mappings.isAutoImport() && entity.getEntityName().indexOf( '.' ) > 0 ) {
636 mappings.addImport(
637 entity.getEntityName(),
638 StringHelper.unqualify( entity.getEntityName() )
639 );
640 }
641
642 // BATCH SIZE
643 Attribute batchNode = node.attribute( "batch-size" );
644 if ( batchNode != null ) entity.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
645
646 // SELECT BEFORE UPDATE
647 Attribute sbuNode = node.attribute( "select-before-update" );
648 if ( sbuNode != null ) entity.setSelectBeforeUpdate( "true".equals( sbuNode.getValue() ) );
649
650 // OPTIMISTIC LOCK MODE
651 Attribute olNode = node.attribute( "optimistic-lock" );
652 entity.setOptimisticLockMode( getOptimisticLockMode( olNode ) );
653
654 entity.setMetaAttributes( getMetas( node, inheritedMetas ) );
655
656 // PERSISTER
657 Attribute persisterNode = node.attribute( "persister" );
658 if ( persisterNode != null ) {
659 try {
660 entity.setEntityPersisterClass( ReflectHelper.classForName( persisterNode
661 .getValue() ) );
662 }
663 catch (ClassNotFoundException cnfe) {
664 throw new MappingException( "Could not find persister class: "
665 + persisterNode.getValue() );
666 }
667 }
668
669 // CUSTOM SQL
670 handleCustomSQL( node, entity );
671
672 Iterator tables = node.elementIterator( "synchronize" );
673 while ( tables.hasNext() ) {
674 entity.addSynchronizedTable( ( (Element) tables.next() ).attributeValue( "table" ) );
675 }
676
677 Attribute abstractNode = node.attribute( "abstract" );
678 Boolean isAbstract = abstractNode == null
679 ? null
680 : "true".equals( abstractNode.getValue() )
681 ? Boolean.TRUE
682 : "false".equals( abstractNode.getValue() )
683 ? Boolean.FALSE
684 : null;
685 entity.setAbstract( isAbstract );
686 }
687
688 private static void handleCustomSQL(Element node, PersistentClass model)
689 throws MappingException {
690 Element element = node.element( "sql-insert" );
691 if ( element != null ) {
692 boolean callable = isCallable( element );
693 model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
694 }
695
696 element = node.element( "sql-delete" );
697 if ( element != null ) {
698 boolean callable = isCallable( element );
699 model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
700 }
701
702 element = node.element( "sql-update" );
703 if ( element != null ) {
704 boolean callable = isCallable( element );
705 model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
706 }
707
708 element = node.element( "loader" );
709 if ( element != null ) {
710 model.setLoaderName( element.attributeValue( "query-ref" ) );
711 }
712 }
713
714 private static void handleCustomSQL(Element node, Join model) throws MappingException {
715 Element element = node.element( "sql-insert" );
716 if ( element != null ) {
717 boolean callable = isCallable( element );
718 model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
719 }
720
721 element = node.element( "sql-delete" );
722 if ( element != null ) {
723 boolean callable = isCallable( element );
724 model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
725 }
726
727 element = node.element( "sql-update" );
728 if ( element != null ) {
729 boolean callable = isCallable( element );
730 model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
731 }
732 }
733
734 private static void handleCustomSQL(Element node, Collection model) throws MappingException {
735 Element element = node.element( "sql-insert" );
736 if ( element != null ) {
737 boolean callable = isCallable( element, true );
738 model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
739 }
740
741 element = node.element( "sql-delete" );
742 if ( element != null ) {
743 boolean callable = isCallable( element, true );
744 model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
745 }
746
747 element = node.element( "sql-update" );
748 if ( element != null ) {
749 boolean callable = isCallable( element, true );
750 model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
751 }
752
753 element = node.element( "sql-delete-all" );
754 if ( element != null ) {
755 boolean callable = isCallable( element, true );
756 model.setCustomSQLDeleteAll( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
757 }
758 }
759
760 private static boolean isCallable(Element e) throws MappingException {
761 return isCallable( e, true );
762 }
763
764 private static boolean isCallable(Element element, boolean supportsCallable)
765 throws MappingException {
766 Attribute attrib = element.attribute( "callable" );
767 if ( attrib != null && "true".equals( attrib.getValue() ) ) {
768 if ( !supportsCallable ) {
769 throw new MappingException( "callable attribute not supported yet!" );
770 }
771 return true;
772 }
773 return false;
774 }
775
776 private static ExecuteUpdateResultCheckStyle getResultCheckStyle(Element element, boolean callable) throws MappingException {
777 Attribute attr = element.attribute( "check" );
778 if ( attr == null ) {
779 // use COUNT as the default. This mimics the old behavior, although
780 // NONE might be a better option moving forward in the case of callable
781 return ExecuteUpdateResultCheckStyle.COUNT;
782 }
783 return ExecuteUpdateResultCheckStyle.parse( attr.getValue() );
784 }
785
786 public static void bindUnionSubclass(Element node, UnionSubclass unionSubclass,
787 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
788
789 bindClass( node, unionSubclass, mappings, inheritedMetas );
790 inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
791
792 if ( unionSubclass.getEntityPersisterClass() == null ) {
793 unionSubclass.getRootClass().setEntityPersisterClass(
794 UnionSubclassEntityPersister.class );
795 }
796
797 Attribute schemaNode = node.attribute( "schema" );
798 String schema = schemaNode == null ?
799 mappings.getSchemaName() : schemaNode.getValue();
800
801 Attribute catalogNode = node.attribute( "catalog" );
802 String catalog = catalogNode == null ?
803 mappings.getCatalogName() : catalogNode.getValue();
804
805 Table denormalizedSuperTable = unionSubclass.getSuperclass().getTable();
806 Table mytable = mappings.addDenormalizedTable(
807 schema,
808 catalog,
809 getClassTableName(unionSubclass, node, schema, catalog, denormalizedSuperTable, mappings ),
810 unionSubclass.isAbstract() != null && unionSubclass.isAbstract().booleanValue(),
811 getSubselect( node ),
812 denormalizedSuperTable
813 );
814 unionSubclass.setTable( mytable );
815
816 log.info(
817 "Mapping union-subclass: " + unionSubclass.getEntityName() +
818 " -> " + unionSubclass.getTable().getName()
819 );
820
821 createClassProperties( node, unionSubclass, mappings, inheritedMetas );
822
823 }
824
825 public static void bindSubclass(Element node, Subclass subclass, Mappings mappings,
826 java.util.Map inheritedMetas) throws MappingException {
827
828 bindClass( node, subclass, mappings, inheritedMetas );
829 inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
830
831 if ( subclass.getEntityPersisterClass() == null ) {
832 subclass.getRootClass()
833 .setEntityPersisterClass( SingleTableEntityPersister.class );
834 }
835
836 log.info(
837 "Mapping subclass: " + subclass.getEntityName() +
838 " -> " + subclass.getTable().getName()
839 );
840
841 // properties
842 createClassProperties( node, subclass, mappings, inheritedMetas );
843 }
844
845 private static String getClassTableName(
846 PersistentClass model, Element node, String schema, String catalog, Table denormalizedSuperTable,
847 Mappings mappings
848 ) {
849 Attribute tableNameNode = node.attribute( "table" );
850 String logicalTableName;
851 String physicalTableName;
852 if ( tableNameNode == null ) {
853 logicalTableName = StringHelper.unqualify( model.getEntityName() );
854 physicalTableName = mappings.getNamingStrategy().classToTableName( model.getEntityName() );
855 }
856 else {
857 logicalTableName = tableNameNode.getValue();
858 physicalTableName = mappings.getNamingStrategy().tableName( logicalTableName );
859 }
860 mappings.addTableBinding( schema, catalog, logicalTableName, physicalTableName, denormalizedSuperTable );
861 return physicalTableName;
862 }
863
864 public static void bindJoinedSubclass(Element node, JoinedSubclass joinedSubclass,
865 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
866
867 bindClass( node, joinedSubclass, mappings, inheritedMetas );
868 inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from
869 // <joined-subclass>
870
871 // joined subclasses
872 if ( joinedSubclass.getEntityPersisterClass() == null ) {
873 joinedSubclass.getRootClass()
874 .setEntityPersisterClass( JoinedSubclassEntityPersister.class );
875 }
876
877 Attribute schemaNode = node.attribute( "schema" );
878 String schema = schemaNode == null ?
879 mappings.getSchemaName() : schemaNode.getValue();
880
881 Attribute catalogNode = node.attribute( "catalog" );
882 String catalog = catalogNode == null ?
883 mappings.getCatalogName() : catalogNode.getValue();
884
885 Table mytable = mappings.addTable(
886 schema,
887 catalog,
888 getClassTableName( joinedSubclass, node, schema, catalog, null, mappings ),
889 getSubselect( node ),
890 false
891 );
892 joinedSubclass.setTable( mytable );
893 bindComment(mytable, node);
894
895 log.info(
896 "Mapping joined-subclass: " + joinedSubclass.getEntityName() +
897 " -> " + joinedSubclass.getTable().getName()
898 );
899
900 // KEY
901 Element keyNode = node.element( "key" );
902 SimpleValue key = new DependantValue( mytable, joinedSubclass.getIdentifier() );
903 joinedSubclass.setKey( key );
904 key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
905 bindSimpleValue( keyNode, key, false, joinedSubclass.getEntityName(), mappings );
906
907 // model.getKey().setType( new Type( model.getIdentifier() ) );
908 joinedSubclass.createPrimaryKey();
909 joinedSubclass.createForeignKey();
910
911 // CHECK
912 Attribute chNode = node.attribute( "check" );
913 if ( chNode != null ) mytable.addCheckConstraint( chNode.getValue() );
914
915 // properties
916 createClassProperties( node, joinedSubclass, mappings, inheritedMetas );
917
918 }
919
920 private static void bindJoin(Element node, Join join, Mappings mappings,
921 java.util.Map inheritedMetas) throws MappingException {
922
923 PersistentClass persistentClass = join.getPersistentClass();
924 String path = persistentClass.getEntityName();
925
926 // TABLENAME
927
928 Attribute schemaNode = node.attribute( "schema" );
929 String schema = schemaNode == null ?
930 mappings.getSchemaName() : schemaNode.getValue();
931 Attribute catalogNode = node.attribute( "catalog" );
932 String catalog = catalogNode == null ?
933 mappings.getCatalogName() : catalogNode.getValue();
934 Table primaryTable = persistentClass.getTable();
935 Table table = mappings.addTable(
936 schema,
937 catalog,
938 getClassTableName( persistentClass, node, schema, catalog, primaryTable, mappings ),
939 getSubselect( node ),
940 false
941 );
942 join.setTable( table );
943 bindComment(table, node);
944
945 Attribute fetchNode = node.attribute( "fetch" );
946 if ( fetchNode != null ) {
947 join.setSequentialSelect( "select".equals( fetchNode.getValue() ) );
948 }
949
950 Attribute invNode = node.attribute( "inverse" );
951 if ( invNode != null ) {
952 join.setInverse( "true".equals( invNode.getValue() ) );
953 }
954
955 Attribute nullNode = node.attribute( "optional" );
956 if ( nullNode != null ) {
957 join.setOptional( "true".equals( nullNode.getValue() ) );
958 }
959
960 log.info(
961 "Mapping class join: " + persistentClass.getEntityName() +
962 " -> " + join.getTable().getName()
963 );
964
965 // KEY
966 Element keyNode = node.element( "key" );
967 SimpleValue key = new DependantValue( table, persistentClass.getIdentifier() );
968 join.setKey( key );
969 key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
970 bindSimpleValue( keyNode, key, false, persistentClass.getEntityName(), mappings );
971
972 // join.getKey().setType( new Type( lazz.getIdentifier() ) );
973 join.createPrimaryKey();
974 join.createForeignKey();
975
976 // PROPERTIES
977 Iterator iter = node.elementIterator();
978 while ( iter.hasNext() ) {
979 Element subnode = (Element) iter.next();
980 String name = subnode.getName();
981 String propertyName = subnode.attributeValue( "name" );
982
983 Value value = null;
984 if ( "many-to-one".equals( name ) ) {
985 value = new ManyToOne( table );
986 bindManyToOne( subnode, (ManyToOne) value, propertyName, true, mappings );
987 }
988 else if ( "any".equals( name ) ) {
989 value = new Any( table );
990 bindAny( subnode, (Any) value, true, mappings );
991 }
992 else if ( "property".equals( name ) ) {
993 value = new SimpleValue( table );
994 bindSimpleValue( subnode, (SimpleValue) value, true, propertyName, mappings );
995 }
996 else if ( "component".equals( name ) || "dynamic-component".equals( name ) ) {
997 String subpath = StringHelper.qualify( path, propertyName );
998 value = new Component( join );
999 bindComponent(
1000 subnode,
1001 (Component) value,
1002 join.getPersistentClass().getClassName(),
1003 propertyName,
1004 subpath,
1005 true,
1006 false,
1007 mappings,
1008 inheritedMetas,
1009 false
1010 );
1011 }
1012
1013 if ( value != null ) {
1014 Property prop = createProperty( value, propertyName, persistentClass
1015 .getEntityName(), subnode, mappings, inheritedMetas );
1016 prop.setOptional( join.isOptional() );
1017 join.addProperty( prop );
1018 }
1019
1020 }
1021
1022 // CUSTOM SQL
1023 handleCustomSQL( node, join );
1024
1025 }
1026
1027 public static void bindColumns(final Element node, final SimpleValue simpleValue,
1028 final boolean isNullable, final boolean autoColumn, final String propertyPath,
1029 final Mappings mappings) throws MappingException {
1030
1031 Table table = simpleValue.getTable();
1032
1033 // COLUMN(S)
1034 Attribute columnAttribute = node.attribute( "column" );
1035 if ( columnAttribute == null ) {
1036 Iterator iter = node.elementIterator();
1037 int count = 0;
1038 while ( iter.hasNext() ) {
1039 Element columnElement = (Element) iter.next();
1040 if ( columnElement.getName().equals( "column" ) ) {
1041 Column column = new Column();
1042 column.setValue( simpleValue );
1043 column.setTypeIndex( count++ );
1044 bindColumn( columnElement, column, isNullable );
1045 String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
1046 columnElement.attributeValue( "name" ), propertyPath
1047 );
1048 column.setName( mappings.getNamingStrategy().columnName(
1049 logicalColumnName ) );
1050 if ( table != null ) {
1051 table.addColumn( column ); // table=null -> an association
1052 // - fill it in later
1053 //TODO fill in the mappings for table == null
1054 mappings.addColumnBinding( logicalColumnName, column, table );
1055 }
1056
1057
1058 simpleValue.addColumn( column );
1059 // column index
1060 bindIndex( columnElement.attribute( "index" ), table, column, mappings );
1061 bindIndex( node.attribute( "index" ), table, column, mappings );
1062 //column unique-key
1063 bindUniqueKey( columnElement.attribute( "unique-key" ), table, column, mappings );
1064 bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
1065 }
1066 else if ( columnElement.getName().equals( "formula" ) ) {
1067 Formula formula = new Formula();
1068 formula.setFormula( columnElement.getText() );
1069 simpleValue.addFormula( formula );
1070 }
1071 }
1072 }
1073 else {
1074 if ( node.elementIterator( "column" ).hasNext() ) {
1075 throw new MappingException(
1076 "column attribute may not be used together with <column> subelement" );
1077 }
1078 if ( node.elementIterator( "formula" ).hasNext() ) {
1079 throw new MappingException(
1080 "column attribute may not be used together with <formula> subelement" );
1081 }
1082
1083 Column column = new Column();
1084 column.setValue( simpleValue );
1085 bindColumn( node, column, isNullable );
1086 String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
1087 columnAttribute.getValue(), propertyPath
1088 );
1089 column.setName( mappings.getNamingStrategy().columnName( logicalColumnName ) );
1090 if ( table != null ) {
1091 table.addColumn( column ); // table=null -> an association - fill
1092 // it in later
1093 //TODO fill in the mappings for table == null
1094 mappings.addColumnBinding( logicalColumnName, column, table );
1095 }
1096 simpleValue.addColumn( column );
1097 bindIndex( node.attribute( "index" ), table, column, mappings );
1098 bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
1099 }
1100
1101 if ( autoColumn && simpleValue.getColumnSpan() == 0 ) {
1102 Column column = new Column();
1103 column.setValue( simpleValue );
1104 bindColumn( node, column, isNullable );
1105 column.setName( mappings.getNamingStrategy().propertyToColumnName( propertyPath ) );
1106 String logicalName = mappings.getNamingStrategy().logicalColumnName( null, propertyPath );
1107 mappings.addColumnBinding( logicalName, column, table );
1108 /* TODO: joinKeyColumnName & foreignKeyColumnName should be called either here or at a
1109 * slightly higer level in the stack (to get all the information we need)
1110 * Right now HbmBinder does not support the
1111 */
1112 simpleValue.getTable().addColumn( column );
1113 simpleValue.addColumn( column );
1114 bindIndex( node.attribute( "index" ), table, column, mappings );
1115 bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
1116 }
1117
1118 }
1119
1120 private static void bindIndex(Attribute indexAttribute, Table table, Column column, Mappings mappings) {
1121 if ( indexAttribute != null && table != null ) {
1122 StringTokenizer tokens = new StringTokenizer( indexAttribute.getValue(), ", " );
1123 while ( tokens.hasMoreTokens() ) {
1124 table.getOrCreateIndex( tokens.nextToken() ).addColumn( column );
1125 }
1126 }
1127 }
1128
1129 private static void bindUniqueKey(Attribute uniqueKeyAttribute, Table table, Column column, Mappings mappings) {
1130 if ( uniqueKeyAttribute != null && table != null ) {
1131 StringTokenizer tokens = new StringTokenizer( uniqueKeyAttribute.getValue(), ", " );
1132 while ( tokens.hasMoreTokens() ) {
1133 table.getOrCreateUniqueKey( tokens.nextToken() ).addColumn( column );
1134 }
1135 }
1136 }
1137
1138 // automatically makes a column with the default name if none is specifed by XML
1139 public static void bindSimpleValue(Element node, SimpleValue simpleValue, boolean isNullable,
1140 String path, Mappings mappings) throws MappingException {
1141 bindSimpleValueType( node, simpleValue, mappings );
1142
1143 bindColumnsOrFormula( node, simpleValue, path, isNullable, mappings );
1144
1145 Attribute fkNode = node.attribute( "foreign-key" );
1146 if ( fkNode != null ) simpleValue.setForeignKeyName( fkNode.getValue() );
1147 }
1148
1149 private static void bindSimpleValueType(Element node, SimpleValue simpleValue, Mappings mappings)
1150 throws MappingException {
1151 String typeName = null;
1152
1153 Properties parameters = new Properties();
1154
1155 Attribute typeNode = node.attribute( "type" );
1156 if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
1157 if ( typeNode != null ) typeName = typeNode.getValue();
1158
1159 Element typeChild = node.element( "type" );
1160 if ( typeName == null && typeChild != null ) {
1161 typeName = typeChild.attribute( "name" ).getValue();
1162 Iterator typeParameters = typeChild.elementIterator( "param" );
1163
1164 while ( typeParameters.hasNext() ) {
1165 Element paramElement = (Element) typeParameters.next();
1166 parameters.setProperty(
1167 paramElement.attributeValue( "name" ),
1168 paramElement.getTextTrim()
1169 );
1170 }
1171 }
1172
1173 TypeDef typeDef = mappings.getTypeDef( typeName );
1174 if ( typeDef != null ) {
1175 typeName = typeDef.getTypeClass();
1176 // parameters on the property mapping should
1177 // override parameters in the typedef
1178 Properties allParameters = new Properties();
1179 allParameters.putAll( typeDef.getParameters() );
1180 allParameters.putAll( parameters );
1181 parameters = allParameters;
1182 }
1183
1184 if ( !parameters.isEmpty() ) simpleValue.setTypeParameters( parameters );
1185
1186 if ( typeName != null ) simpleValue.setTypeName( typeName );
1187 }
1188
1189 public static void bindProperty(
1190 Element node,
1191 Property property,
1192 Mappings mappings,
1193 java.util.Map inheritedMetas) throws MappingException {
1194
1195 String propName = node.attributeValue( "name" );
1196 property.setName( propName );
1197 String nodeName = node.attributeValue( "node" );
1198 if (nodeName==null) nodeName = propName;
1199 property.setNodeName( nodeName );
1200
1201 // TODO:
1202 //Type type = model.getValue().getType();
1203 //if (type==null) throw new MappingException(
1204 //"Could not determine a property type for: " + model.getName() );
1205
1206 Attribute accessNode = node.attribute( "access" );
1207 if ( accessNode != null ) {
1208 property.setPropertyAccessorName( accessNode.getValue() );
1209 }
1210 else if ( node.getName().equals( "properties" ) ) {
1211 property.setPropertyAccessorName( "embedded" );
1212 }
1213 else {
1214 property.setPropertyAccessorName( mappings.getDefaultAccess() );
1215 }
1216
1217 Attribute cascadeNode = node.attribute( "cascade" );
1218 property.setCascade( cascadeNode == null ? mappings.getDefaultCascade() : cascadeNode
1219 .getValue() );
1220
1221 Attribute updateNode = node.attribute( "update" );
1222 property.setUpdateable( updateNode == null || "true".equals( updateNode.getValue() ) );
1223
1224 Attribute insertNode = node.attribute( "insert" );
1225 property.setInsertable( insertNode == null || "true".equals( insertNode.getValue() ) );
1226
1227 Attribute lockNode = node.attribute( "optimistic-lock" );
1228 property.setOptimisticLocked( lockNode == null || "true".equals( lockNode.getValue() ) );
1229
1230 Attribute generatedNode = node.attribute( "generated" );
1231 String generationName = generatedNode == null ? null : generatedNode.getValue();
1232 PropertyGeneration generation = PropertyGeneration.parse( generationName );
1233 property.setGeneration( generation );
1234
1235 if ( generation == PropertyGeneration.ALWAYS || generation == PropertyGeneration.INSERT ) {
1236 // generated properties can *never* be insertable...
1237 if ( property.isInsertable() ) {
1238 if ( insertNode == null ) {
1239 // insertable simply because that is the user did not specify
1240 // anything; just override it
1241 property.setInsertable( false );
1242 }
1243 else {
1244 // the user specifically supplied insert="true",
1245 // which constitutes an illegal combo
1246 throw new MappingException(
1247 "cannot specify both insert=\"true\" and generated=\"" + generation.getName() +
1248 "\" for property: " +
1249 propName
1250 );
1251 }
1252 }
1253
1254 // properties generated on update can never be updateable...
1255 if ( property.isUpdateable() && generation == PropertyGeneration.ALWAYS ) {
1256 if ( updateNode == null ) {
1257 // updateable only because the user did not specify
1258 // anything; just override it
1259 property.setUpdateable( false );
1260 }
1261 else {
1262 // the user specifically supplied update="true",
1263 // which constitutes an illegal combo
1264 throw new MappingException(
1265 "cannot specify both update=\"true\" and generated=\"" + generation.getName() +
1266 "\" for property: " +
1267 propName
1268 );
1269 }
1270 }
1271 }
1272
1273 boolean isLazyable = "property".equals( node.getName() ) ||
1274 "component".equals( node.getName() ) ||
1275 "many-to-one".equals( node.getName() ) ||
1276 "one-to-one".equals( node.getName() ) ||
1277 "any".equals( node.getName() );
1278 if ( isLazyable ) {
1279 Attribute lazyNode = node.attribute( "lazy" );
1280 property.setLazy( lazyNode != null && "true".equals( lazyNode.getValue() ) );
1281 }
1282
1283 if ( log.isDebugEnabled() ) {
1284 String msg = "Mapped property: " + property.getName();
1285 String columns = columns( property.getValue() );
1286 if ( columns.length() > 0 ) msg += " -> " + columns;
1287 // TODO: this fails if we run with debug on!
1288 // if ( model.getType()!=null ) msg += ", type: " + model.getType().getName();
1289 log.debug( msg );
1290 }
1291
1292 property.setMetaAttributes( getMetas( node, inheritedMetas ) );
1293
1294 }
1295
1296 private static String columns(Value val) {
1297 StringBuffer columns = new StringBuffer();
1298 Iterator iter = val.getColumnIterator();
1299 while ( iter.hasNext() ) {
1300 columns.append( ( (Selectable) iter.next() ).getText() );
1301 if ( iter.hasNext() ) columns.append( ", " );
1302 }
1303 return columns.toString();
1304 }
1305
1306 /**
1307 * Called for all collections
1308 */
1309 public static void bindCollection(Element node, Collection collection, String className,
1310 String path, Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
1311
1312 // ROLENAME
1313 collection.setRole(path);
1314
1315 Attribute inverseNode = node.attribute( "inverse" );
1316 if ( inverseNode != null ) {
1317 collection.setInverse( "true".equals( inverseNode.getValue() ) );
1318 }
1319
1320 Attribute mutableNode = node.attribute( "mutable" );
1321 if ( mutableNode != null ) {
1322 collection.setMutable( !"false".equals( mutableNode.getValue() ) );
1323 }
1324
1325 Attribute olNode = node.attribute( "optimistic-lock" );
1326 collection.setOptimisticLocked( olNode == null || "true".equals( olNode.getValue() ) );
1327
1328 Attribute orderNode = node.attribute( "order-by" );
1329 if ( orderNode != null ) {
1330 if ( Environment.jvmSupportsLinkedHashCollections() || ( collection instanceof Bag ) ) {
1331 collection.setOrderBy( orderNode.getValue() );
1332 }
1333 else {
1334 log.warn( "Attribute \"order-by\" ignored in JDK1.3 or less" );
1335 }
1336 }
1337 Attribute whereNode = node.attribute( "where" );
1338 if ( whereNode != null ) {
1339 collection.setWhere( whereNode.getValue() );
1340 }
1341 Attribute batchNode = node.attribute( "batch-size" );
1342 if ( batchNode != null ) {
1343 collection.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
1344 }
1345
1346 String nodeName = node.attributeValue( "node" );
1347 if ( nodeName == null ) nodeName = node.attributeValue( "name" );
1348 collection.setNodeName( nodeName );
1349 String embed = node.attributeValue( "embed-xml" );
1350 collection.setEmbedded( embed==null || "true".equals(embed) );
1351
1352
1353 // PERSISTER
1354 Attribute persisterNode = node.attribute( "persister" );
1355 if ( persisterNode != null ) {
1356 try {
1357 collection.setCollectionPersisterClass( ReflectHelper.classForName( persisterNode
1358 .getValue() ) );
1359 }
1360 catch (ClassNotFoundException cnfe) {
1361 throw new MappingException( "Could not find collection persister class: "
1362 + persisterNode.getValue() );
1363 }
1364 }
1365
1366 Attribute typeNode = node.attribute( "collection-type" );
1367 if ( typeNode != null ) {
1368 String typeName = typeNode.getValue();
1369 TypeDef typeDef = mappings.getTypeDef( typeName );
1370 if ( typeDef != null ) {
1371 collection.setTypeName( typeDef.getTypeClass() );
1372 collection.setTypeParameters( typeDef.getParameters() );
1373 }
1374 else {
1375 collection.setTypeName( typeName );
1376 }
1377 }
1378
1379 // FETCH STRATEGY
1380
1381 initOuterJoinFetchSetting( node, collection );
1382
1383 if ( "subselect".equals( node.attributeValue("fetch") ) ) {
1384 collection.setSubselectLoadable(true);
1385 collection.getOwner().setSubselectLoadableCollections(true);
1386 }
1387
1388 initLaziness( node, collection, mappings, "true", mappings.isDefaultLazy() );
1389 //TODO: suck this into initLaziness!
1390 if ( "extra".equals( node.attributeValue("lazy") ) ) {
1391 collection.setLazy(true);
1392 collection.setExtraLazy(true);
1393 }
1394
1395 Element oneToManyNode = node.element( "one-to-many" );
1396 if ( oneToManyNode != null ) {
1397 OneToMany oneToMany = new OneToMany( collection.getOwner() );
1398 collection.setElement( oneToMany );
1399 bindOneToMany( oneToManyNode, oneToMany, mappings );
1400 // we have to set up the table later!! yuck
1401 }
1402 else {
1403 // TABLE
1404 Attribute tableNode = node.attribute( "table" );
1405 String tableName;
1406 if ( tableNode != null ) {
1407 tableName = mappings.getNamingStrategy().tableName( tableNode.getValue() );
1408 }
1409 else {
1410 //tableName = mappings.getNamingStrategy().propertyToTableName( className, path );
1411 Table ownerTable = collection.getOwner().getTable();
1412 //TODO mappings.getLogicalTableName(ownerTable)
1413 String logicalOwnerTableName = ownerTable.getName();
1414 //FIXME we don't have the associated entity table name here, has to be done in a second pass
1415 tableName = mappings.getNamingStrategy().collectionTableName(
1416 collection.getOwner().getEntityName(),
1417 logicalOwnerTableName ,
1418 null,
1419 null,
1420 path
1421 );
1422 }
1423 Attribute schemaNode = node.attribute( "schema" );
1424 String schema = schemaNode == null ?
1425 mappings.getSchemaName() : schemaNode.getValue();
1426
1427 Attribute catalogNode = node.attribute( "catalog" );
1428 String catalog = catalogNode == null ?
1429 mappings.getCatalogName() : catalogNode.getValue();
1430
1431 Table table = mappings.addTable(
1432 schema,
1433 catalog,
1434 tableName,
1435 getSubselect( node ),
1436 false
1437 );
1438 collection.setCollectionTable( table );
1439 bindComment(table, node);
1440
1441 log.info(
1442 "Mapping collection: " + collection.getRole() +
1443 " -> " + collection.getCollectionTable().getName()
1444 );
1445 }
1446
1447 // SORT
1448 Attribute sortedAtt = node.attribute( "sort" );
1449 // unsorted, natural, comparator.class.name
1450 if ( sortedAtt == null || sortedAtt.getValue().equals( "unsorted" ) ) {
1451 collection.setSorted( false );
1452 }
1453 else {
1454 collection.setSorted( true );
1455 String comparatorClassName = sortedAtt.getValue();
1456 if ( !comparatorClassName.equals( "natural" ) ) {
1457 collection.setComparatorClassName(comparatorClassName);
1458 }
1459 }
1460
1461 // ORPHAN DELETE (used for programmer error detection)
1462 Attribute cascadeAtt = node.attribute( "cascade" );
1463 if ( cascadeAtt != null && cascadeAtt.getValue().indexOf( "delete-orphan" ) >= 0 ) {
1464 collection.setOrphanDelete( true );
1465 }
1466
1467 // CUSTOM SQL
1468 handleCustomSQL( node, collection );
1469 // set up second pass
1470 if ( collection instanceof List ) {
1471 mappings.addSecondPass( new ListSecondPass( node, mappings, (List) collection, inheritedMetas ) );
1472 }
1473 else if ( collection instanceof Map ) {
1474 mappings.addSecondPass( new MapSecondPass( node, mappings, (Map) collection, inheritedMetas ) );
1475 }
1476 else if ( collection instanceof IdentifierCollection ) {
1477 mappings.addSecondPass( new IdentifierCollectionSecondPass(
1478 node,
1479 mappings,
1480 collection,
1481 inheritedMetas
1482 ) );
1483 }
1484 else {
1485 mappings.addSecondPass( new CollectionSecondPass( node, mappings, collection, inheritedMetas ) );
1486 }
1487
1488 Iterator iter = node.elementIterator( "filter" );
1489 while ( iter.hasNext() ) {
1490 final Element filter = (Element) iter.next();
1491 parseFilter( filter, collection, mappings );
1492 }
1493
1494 Iterator tables = node.elementIterator( "synchronize" );
1495 while ( tables.hasNext() ) {
1496 collection.getSynchronizedTables().add(
1497 ( (Element) tables.next() ).attributeValue( "table" ) );
1498 }
1499
1500 Element element = node.element( "loader" );
1501 if ( element != null ) {
1502 collection.setLoaderName( element.attributeValue( "query-ref" ) );
1503 }
1504
1505 collection.setReferencedPropertyName( node.element( "key" ).attributeValue( "property-ref" ) );
1506 }
1507
1508 private static void initLaziness(
1509 Element node,
1510 Fetchable fetchable,
1511 Mappings mappings,
1512 String proxyVal,
1513 boolean defaultLazy
1514 ) {
1515 Attribute lazyNode = node.attribute( "lazy" );
1516 boolean isLazyTrue = lazyNode == null ?
1517 defaultLazy && fetchable.isLazy() : //fetch="join" overrides default laziness
1518 lazyNode.getValue().equals(proxyVal); //fetch="join" overrides default laziness
1519 fetchable.setLazy( isLazyTrue );
1520 }
1521
1522 private static void initLaziness(
1523 Element node,
1524 ToOne fetchable,
1525 Mappings mappings,
1526 boolean defaultLazy
1527 ) {
1528 if ( "no-proxy".equals( node.attributeValue( "lazy" ) ) ) {
1529 fetchable.setUnwrapProxy(true);
1530 fetchable.setLazy(true);
1531 //TODO: better to degrade to lazy="false" if uninstrumented
1532 }
1533 else {
1534 initLaziness(node, fetchable, mappings, "proxy", defaultLazy);
1535 }
1536 }
1537
1538 private static void bindColumnsOrFormula(Element node, SimpleValue simpleValue, String path,
1539 boolean isNullable, Mappings mappings) {
1540 Attribute formulaNode = node.attribute( "formula" );
1541 if ( formulaNode != null ) {
1542 Formula f = new Formula();
1543 f.setFormula( formulaNode.getText() );
1544 simpleValue.addFormula( f );
1545 }
1546 else {
1547 bindColumns( node, simpleValue, isNullable, true, path, mappings );
1548 }
1549 }
1550
1551 private static void bindComment(Table table, Element node) {
1552 Element comment = node.element("comment");
1553 if (comment!=null) table.setComment( comment.getTextTrim() );
1554 }
1555
1556 public static void bindManyToOne(Element node, ManyToOne manyToOne, String path,
1557 boolean isNullable, Mappings mappings) throws MappingException {
1558
1559 bindColumnsOrFormula( node, manyToOne, path, isNullable, mappings );
1560 initOuterJoinFetchSetting( node, manyToOne );
1561 initLaziness( node, manyToOne, mappings, true );
1562
1563 Attribute ukName = node.attribute( "property-ref" );
1564 if ( ukName != null ) {
1565 manyToOne.setReferencedPropertyName( ukName.getValue() );
1566 }
1567
1568 manyToOne.setReferencedEntityName( getEntityName( node, mappings ) );
1569
1570 String embed = node.attributeValue( "embed-xml" );
1571 manyToOne.setEmbedded( embed == null || "true".equals( embed ) );
1572
1573 String notFound = node.attributeValue( "not-found" );
1574 manyToOne.setIgnoreNotFound( "ignore".equals( notFound ) );
1575
1576 if( ukName != null && !manyToOne.isIgnoreNotFound() ) {
1577 if ( !node.getName().equals("many-to-many") ) { //TODO: really bad, evil hack to fix!!!
1578 mappings.addSecondPass( new ManyToOneSecondPass(manyToOne) );
1579 }
1580 }
1581
1582 Attribute fkNode = node.attribute( "foreign-key" );
1583 if ( fkNode != null ) manyToOne.setForeignKeyName( fkNode.getValue() );
1584
1585 validateCascade( node, path );
1586 }
1587
1588 private static void validateCascade(Element node, String path) {
1589 String cascade = node.attributeValue( "cascade" );
1590 if ( cascade != null && cascade.indexOf( "delete-orphan" ) >= 0 ) {
1591 throw new MappingException( "single-valued associations do not support orphan delete: " + path );
1592 }
1593 }
1594
1595 public static void bindAny(Element node, Any any, boolean isNullable, Mappings mappings)
1596 throws MappingException {
1597 any.setIdentifierType( getTypeFromXML( node ) );
1598 Attribute metaAttribute = node.attribute( "meta-type" );
1599 if ( metaAttribute != null ) {
1600 any.setMetaType( metaAttribute.getValue() );
1601
1602 Iterator iter = node.elementIterator( "meta-value" );
1603 if ( iter.hasNext() ) {
1604 HashMap values = new HashMap();
1605 org.hibernate.type.Type metaType = TypeFactory.heuristicType( any.getMetaType() );
1606 while ( iter.hasNext() ) {
1607 Element metaValue = (Element) iter.next();
1608 try {
1609 Object value = ( (DiscriminatorType) metaType ).stringToObject( metaValue
1610 .attributeValue( "value" ) );
1611 String entityName = getClassName( metaValue.attribute( "class" ), mappings );
1612 values.put( value, entityName );
1613 }
1614 catch (ClassCastException cce) {
1615 throw new MappingException( "meta-type was not a DiscriminatorType: "
1616 + metaType.getName() );
1617 }
1618 catch (Exception e) {
1619 throw new MappingException( "could not interpret meta-value", e );
1620 }
1621 }
1622 any.setMetaValues( values );
1623 }
1624
1625 }
1626
1627 bindColumns( node, any, isNullable, false, null, mappings );
1628 }
1629
1630 public static void bindOneToOne(Element node, OneToOne oneToOne, String path, boolean isNullable,
1631 Mappings mappings) throws MappingException {
1632
1633 bindColumns( node, oneToOne, isNullable, false, null, mappings );
1634
1635 Attribute constrNode = node.attribute( "constrained" );
1636 boolean constrained = constrNode != null && constrNode.getValue().equals( "true" );
1637 oneToOne.setConstrained( constrained );
1638
1639 oneToOne.setForeignKeyType( constrained ?
1640 ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
1641 ForeignKeyDirection.FOREIGN_KEY_TO_PARENT );
1642
1643 initOuterJoinFetchSetting( node, oneToOne );
1644 initLaziness( node, oneToOne, mappings, true );
1645
1646 oneToOne.setEmbedded( "true".equals( node.attributeValue( "embed-xml" ) ) );
1647
1648 Attribute fkNode = node.attribute( "foreign-key" );
1649 if ( fkNode != null ) oneToOne.setForeignKeyName( fkNode.getValue() );
1650
1651 Attribute ukName = node.attribute( "property-ref" );
1652 if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() );
1653
1654 oneToOne.setPropertyName( node.attributeValue( "name" ) );
1655
1656 oneToOne.setReferencedEntityName( getEntityName( node, mappings ) );
1657
1658 validateCascade( node, path );
1659 }
1660
1661 public static void bindOneToMany(Element node, OneToMany oneToMany, Mappings mappings)
1662 throws MappingException {
1663
1664 oneToMany.setReferencedEntityName( getEntityName( node, mappings ) );
1665
1666 String embed = node.attributeValue( "embed-xml" );
1667 oneToMany.setEmbedded( embed == null || "true".equals( embed ) );
1668
1669 String notFound = node.attributeValue( "not-found" );
1670 oneToMany.setIgnoreNotFound( "ignore".equals( notFound ) );
1671
1672 }
1673
1674 public static void bindColumn(Element node, Column column, boolean isNullable) {
1675 Attribute lengthNode = node.attribute( "length" );
1676 if ( lengthNode != null ) column.setLength( Integer.parseInt( lengthNode.getValue() ) );
1677 Attribute scalNode = node.attribute( "scale" );
1678 if ( scalNode != null ) column.setScale( Integer.parseInt( scalNode.getValue() ) );
1679 Attribute precNode = node.attribute( "precision" );
1680 if ( precNode != null ) column.setPrecision( Integer.parseInt( precNode.getValue() ) );
1681
1682 Attribute nullNode = node.attribute( "not-null" );
1683 column.setNullable( nullNode == null ? isNullable : nullNode.getValue().equals( "false" ) );
1684
1685 Attribute unqNode = node.attribute( "unique" );
1686 if ( unqNode != null ) column.setUnique( unqNode.getValue().equals( "true" ) );
1687
1688 column.setCheckConstraint( node.attributeValue( "check" ) );
1689 column.setDefaultValue( node.attributeValue( "default" ) );
1690
1691 Attribute typeNode = node.attribute( "sql-type" );
1692 if ( typeNode != null ) column.setSqlType( typeNode.getValue() );
1693
1694 Element comment = node.element("comment");
1695 if (comment!=null) column.setComment( comment.getTextTrim() );
1696
1697 }
1698
1699 /**
1700 * Called for arrays and primitive arrays
1701 */
1702 public static void bindArray(Element node, Array array, String prefix, String path,
1703 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
1704
1705 bindCollection( node, array, prefix, path, mappings, inheritedMetas );
1706
1707 Attribute att = node.attribute( "element-class" );
1708 if ( att != null ) array.setElementClassName( getClassName( att, mappings ) );
1709
1710 }
1711
1712 private static Class reflectedPropertyClass(String className, String propertyName)
1713 throws MappingException {
1714 if ( className == null ) return null;
1715 return ReflectHelper.reflectedPropertyClass( className, propertyName );
1716 }
1717
1718 public static void bindComposite(Element node, Component component, String path,
1719 boolean isNullable, Mappings mappings, java.util.Map inheritedMetas)
1720 throws MappingException {
1721 bindComponent(
1722 node,
1723 component,
1724 null,
1725 null,
1726 path,
1727 isNullable,
1728 false,
1729 mappings,
1730 inheritedMetas,
1731 false
1732 );
1733 }
1734
1735 public static void bindCompositeId(Element node, Component component,
1736 PersistentClass persistentClass, String propertyName, Mappings mappings,
1737 java.util.Map inheritedMetas) throws MappingException {
1738
1739 component.setKey( true );
1740
1741 String path = StringHelper.qualify(
1742 persistentClass.getEntityName(),
1743 propertyName == null ? "id" : propertyName );
1744
1745 bindComponent(
1746 node,
1747 component,
1748 persistentClass.getClassName(),
1749 propertyName,
1750 path,
1751 false,
1752 node.attribute( "class" ) == null
1753 && propertyName == null,
1754 mappings,
1755 inheritedMetas,
1756 false
1757 );
1758
1759 if ( "true".equals( node.attributeValue("mapped") ) ) {
1760 if ( propertyName!=null ) {
1761 throw new MappingException("cannot combine mapped=\"true\" with specified name");
1762 }
1763 Component mapper = new Component(persistentClass);
1764 bindComponent(
1765 node,
1766 mapper,
1767 persistentClass.getClassName(),
1768 null,
1769 path,
1770 false,
1771 true,
1772 mappings,
1773 inheritedMetas,
1774 true
1775 );
1776 persistentClass.setIdentifierMapper(mapper);
1777 Property property = new Property();
1778 property.setName("_identifierMapper");
1779 property.setNodeName("id");
1780 property.setUpdateable(false);
1781 property.setInsertable(false);
1782 property.setValue(mapper);
1783 property.setPropertyAccessorName( "embedded" );
1784 persistentClass.addProperty(property);
1785 }
1786
1787 }
1788
1789 public static void bindComponent(
1790 Element node,
1791 Component component,
1792 String ownerClassName,
1793 String parentProperty,
1794 String path,
1795 boolean isNullable,
1796 boolean isEmbedded,
1797 Mappings mappings,
1798 java.util.Map inheritedMetas,
1799 boolean isIdentifierMapper) throws MappingException {
1800
1801 component.setEmbedded( isEmbedded );
1802 component.setRoleName( path );
1803
1804 inheritedMetas = getMetas( node, inheritedMetas );
1805 component.setMetaAttributes( inheritedMetas );
1806
1807 Attribute classNode = isIdentifierMapper ? null : node.attribute( "class" );
1808 if ( classNode != null ) {
1809 component.setComponentClassName( getClassName( classNode, mappings ) );
1810 }
1811 else if ( "dynamic-component".equals( node.getName() ) ) {
1812 component.setDynamic( true );
1813 }
1814 else if ( isEmbedded ) {
1815 // an "embedded" component (composite ids and unique)
1816 // note that this does not handle nested components
1817 if ( component.getOwner().hasPojoRepresentation() ) {
1818 component.setComponentClassName( component.getOwner().getClassName() );
1819 }
1820 else {
1821 component.setDynamic(true);
1822 }
1823 }
1824 else {
1825 // todo : again, how *should* this work for non-pojo entities?
1826 if ( component.getOwner().hasPojoRepresentation() ) {
1827 Class reflectedClass = reflectedPropertyClass( ownerClassName, parentProperty );
1828 if ( reflectedClass != null ) {
1829 component.setComponentClassName( reflectedClass.getName() );
1830 }
1831 }
1832 else {
1833 component.setDynamic(true);
1834 }
1835 }
1836
1837 String nodeName = node.attributeValue( "node" );
1838 if ( nodeName == null ) nodeName = node.attributeValue( "name" );
1839 if ( nodeName == null ) nodeName = component.getOwner().getNodeName();
1840 component.setNodeName( nodeName );
1841
1842 Iterator iter = node.elementIterator();
1843 while ( iter.hasNext() ) {
1844
1845 Element subnode = (Element) iter.next();
1846 String name = subnode.getName();
1847 String propertyName = getPropertyName( subnode );
1848 String subpath = propertyName == null ? null : StringHelper
1849 .qualify( path, propertyName );
1850
1851 CollectionType collectType = CollectionType.collectionTypeFromString( name );
1852 Value value = null;
1853 if ( collectType != null ) {
1854 Collection collection = collectType.create(
1855 subnode,
1856 subpath,
1857 component.getOwner(),
1858 mappings, inheritedMetas
1859 );
1860 mappings.addCollection( collection );
1861 value = collection;
1862 }
1863 else if ( "many-to-one".equals( name ) || "key-many-to-one".equals( name ) ) {
1864 value = new ManyToOne( component.getTable() );
1865 String relativePath;
1866 if (isEmbedded) {
1867 relativePath = propertyName;
1868 }
1869 else {
1870 relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
1871 }
1872 bindManyToOne( subnode, (ManyToOne) value, relativePath, isNullable, mappings );
1873 }
1874 else if ( "one-to-one".equals( name ) ) {
1875 value = new OneToOne( component.getTable(), component.getOwner() );
1876 String relativePath;
1877 if (isEmbedded) {
1878 relativePath = propertyName;
1879 }
1880 else {
1881 relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
1882 }
1883 bindOneToOne( subnode, (OneToOne) value, relativePath, isNullable, mappings );
1884 }
1885 else if ( "any".equals( name ) ) {
1886 value = new Any( component.getTable() );
1887 bindAny( subnode, (Any) value, isNullable, mappings );
1888 }
1889 else if ( "property".equals( name ) || "key-property".equals( name ) ) {
1890 value = new SimpleValue( component.getTable() );
1891 String relativePath;
1892 if (isEmbedded) {
1893 relativePath = propertyName;
1894 }
1895 else {
1896 relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
1897 }
1898 bindSimpleValue( subnode, (SimpleValue) value, isNullable, relativePath, mappings );
1899 }
1900 else if ( "component".equals( name )
1901 || "dynamic-component".equals( name )
1902 || "nested-composite-element".equals( name ) ) {
1903 value = new Component( component ); // a nested composite element
1904 bindComponent(
1905 subnode,
1906 (Component) value,
1907 component.getComponentClassName(),
1908 propertyName,
1909 subpath,
1910 isNullable,
1911 isEmbedded,
1912 mappings,
1913 inheritedMetas,
1914 isIdentifierMapper
1915 );
1916 }
1917 else if ( "parent".equals( name ) ) {
1918 component.setParentProperty( propertyName );
1919 }
1920
1921 if ( value != null ) {
1922 Property property = createProperty( value, propertyName, component
1923 .getComponentClassName(), subnode, mappings, inheritedMetas );
1924 if (isIdentifierMapper) {
1925 property.setInsertable(false);
1926 property.setUpdateable(false);
1927 }
1928 component.addProperty( property );
1929 }
1930 }
1931
1932 if ( "true".equals( node.attributeValue( "unique" ) ) ) {
1933 iter = component.getColumnIterator();
1934 ArrayList cols = new ArrayList();
1935 while ( iter.hasNext() ) {
1936 cols.add( iter.next() );
1937 }
1938 component.getOwner().getTable().createUniqueKey( cols );
1939 }
1940
1941 iter = node.elementIterator( "tuplizer" );
1942 while ( iter.hasNext() ) {
1943 final Element tuplizerElem = ( Element ) iter.next();
1944 EntityMode mode = EntityMode.parse( tuplizerElem.attributeValue( "entity-mode" ) );
1945 component.addTuplizer( mode, tuplizerElem.attributeValue( "class" ) );
1946 }
1947 }
1948
1949 public static String getTypeFromXML(Element node) throws MappingException {
1950 // TODO: handle TypeDefs
1951 Attribute typeNode = node.attribute( "type" );
1952 if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
1953 if ( typeNode == null ) return null; // we will have to use reflection
1954 return typeNode.getValue();
1955 }
1956
1957 private static void initOuterJoinFetchSetting(Element node, Fetchable model) {
1958 Attribute fetchNode = node.attribute( "fetch" );
1959 final FetchMode fetchStyle;
1960 boolean lazy = true;
1961 if ( fetchNode == null ) {
1962 Attribute jfNode = node.attribute( "outer-join" );
1963 if ( jfNode == null ) {
1964 if ( "many-to-many".equals( node.getName() ) ) {
1965 //NOTE SPECIAL CASE:
1966 // default to join and non-lazy for the "second join"
1967 // of the many-to-many
1968 lazy = false;
1969 fetchStyle = FetchMode.JOIN;
1970 }
1971 else if ( "one-to-one".equals( node.getName() ) ) {
1972 //NOTE SPECIAL CASE:
1973 // one-to-one constrained=false cannot be proxied,
1974 // so default to join and non-lazy
1975 lazy = ( (OneToOne) model ).isConstrained();
1976 fetchStyle = lazy ? FetchMode.DEFAULT : FetchMode.JOIN;
1977 }
1978 else {
1979 fetchStyle = FetchMode.DEFAULT;
1980 }
1981 }
1982 else {
1983 // use old (HB 2.1) defaults if outer-join is specified
1984 String eoj = jfNode.getValue();
1985 if ( "auto".equals( eoj ) ) {
1986 fetchStyle = FetchMode.DEFAULT;
1987 }
1988 else {
1989 boolean join = "true".equals( eoj );
1990 fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
1991 }
1992 }
1993 }
1994 else {
1995 boolean join = "join".equals( fetchNode.getValue() );
1996 //lazy = !join;
1997 fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
1998 }
1999 model.setFetchMode( fetchStyle );
2000 model.setLazy(lazy);
2001 }
2002
2003 private static void makeIdentifier(Element node, SimpleValue model, Mappings mappings) {
2004
2005 // GENERATOR
2006 Element subnode = node.element( "generator" );
2007 if ( subnode != null ) {
2008 model.setIdentifierGeneratorStrategy( subnode.attributeValue( "class" ) );
2009
2010 Properties params = new Properties();
2011
2012 if ( mappings.getSchemaName() != null ) {
2013 params.setProperty( PersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
2014 }
2015 if ( mappings.getCatalogName() != null ) {
2016 params.setProperty( PersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
2017 }
2018
2019 Iterator iter = subnode.elementIterator( "param" );
2020 while ( iter.hasNext() ) {
2021 Element childNode = (Element) iter.next();
2022 params.setProperty( childNode.attributeValue( "name" ), childNode.getTextTrim() );
2023 }
2024
2025 model.setIdentifierGeneratorProperties( params );
2026 }
2027
2028 model.getTable().setIdentifierValue( model );
2029
2030 // ID UNSAVED-VALUE
2031 Attribute nullValueNode = node.attribute( "unsaved-value" );
2032 if ( nullValueNode != null ) {
2033 model.setNullValue( nullValueNode.getValue() );
2034 }
2035 else {
2036 if ( "assigned".equals( model.getIdentifierGeneratorStrategy() ) ) {
2037 model.setNullValue( "undefined" );
2038 }
2039 else {
2040 model.setNullValue( null );
2041 }
2042 }
2043 }
2044
2045 private static final void makeVersion(Element node, SimpleValue model) {
2046
2047 // VERSION UNSAVED-VALUE
2048 Attribute nullValueNode = node.attribute( "unsaved-value" );
2049 if ( nullValueNode != null ) {
2050 model.setNullValue( nullValueNode.getValue() );
2051 }
2052 else {
2053 model.setNullValue( "undefined" );
2054 }
2055
2056 }
2057
2058 protected static void createClassProperties(Element node, PersistentClass persistentClass,
2059 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2060 createClassProperties(node, persistentClass, mappings, inheritedMetas, null, true, true, false);
2061 }
2062
2063 protected static void createClassProperties(Element node, PersistentClass persistentClass,
2064 Mappings mappings, java.util.Map inheritedMetas, UniqueKey uniqueKey,
2065 boolean mutable, boolean nullable, boolean naturalId) throws MappingException {
2066
2067 String entityName = persistentClass.getEntityName();
2068 Table table = persistentClass.getTable();
2069
2070 Iterator iter = node.elementIterator();
2071 while ( iter.hasNext() ) {
2072 Element subnode = (Element) iter.next();
2073 String name = subnode.getName();
2074 String propertyName = subnode.attributeValue( "name" );
2075
2076 CollectionType collectType = CollectionType.collectionTypeFromString( name );
2077 Value value = null;
2078 if ( collectType != null ) {
2079 Collection collection = collectType.create(
2080 subnode,
2081 StringHelper.qualify( entityName, propertyName ),
2082 persistentClass,
2083 mappings, inheritedMetas
2084 );
2085 mappings.addCollection( collection );
2086 value = collection;
2087 }
2088 else if ( "many-to-one".equals( name ) ) {
2089 value = new ManyToOne( table );
2090 bindManyToOne( subnode, (ManyToOne) value, propertyName, nullable, mappings );
2091 }
2092 else if ( "any".equals( name ) ) {
2093 value = new Any( table );
2094 bindAny( subnode, (Any) value, nullable, mappings );
2095 }
2096 else if ( "one-to-one".equals( name ) ) {
2097 value = new OneToOne( table, persistentClass );
2098 bindOneToOne( subnode, (OneToOne) value, propertyName, true, mappings );
2099 }
2100 else if ( "property".equals( name ) ) {
2101 value = new SimpleValue( table );
2102 bindSimpleValue( subnode, (SimpleValue) value, nullable, propertyName, mappings );
2103 }
2104 else if ( "component".equals( name )
2105 || "dynamic-component".equals( name )
2106 || "properties".equals( name ) ) {
2107 String subpath = StringHelper.qualify( entityName, propertyName );
2108 value = new Component( persistentClass );
2109
2110 bindComponent(
2111 subnode,
2112 (Component) value,
2113 persistentClass.getClassName(),
2114 propertyName,
2115 subpath,
2116 true,
2117 "properties".equals( name ),
2118 mappings,
2119 inheritedMetas,
2120 false
2121 );
2122 }
2123 else if ( "join".equals( name ) ) {
2124 Join join = new Join();
2125 join.setPersistentClass( persistentClass );
2126 bindJoin( subnode, join, mappings, inheritedMetas );
2127 persistentClass.addJoin( join );
2128 }
2129 else if ( "subclass".equals( name ) ) {
2130 handleSubclass( persistentClass, mappings, subnode, inheritedMetas );
2131 }
2132 else if ( "joined-subclass".equals( name ) ) {
2133 handleJoinedSubclass( persistentClass, mappings, subnode, inheritedMetas );
2134 }
2135 else if ( "union-subclass".equals( name ) ) {
2136 handleUnionSubclass( persistentClass, mappings, subnode, inheritedMetas );
2137 }
2138 else if ( "filter".equals( name ) ) {
2139 parseFilter( subnode, persistentClass, mappings );
2140 }
2141 else if ( "natural-id".equals( name ) ) {
2142 UniqueKey uk = new UniqueKey();
2143 uk.setName("_UniqueKey");
2144 uk.setTable(table);
2145 //by default, natural-ids are "immutable" (constant)
2146 boolean mutableId = "true".equals( subnode.attributeValue("mutable") );
2147 createClassProperties(
2148 subnode,
2149 persistentClass,
2150 mappings,
2151 inheritedMetas,
2152 uk,
2153 mutableId,
2154 false,
2155 true
2156 );
2157 table.addUniqueKey(uk);
2158 }
2159 else if ( "query".equals(name) ) {
2160 bindNamedQuery(subnode, persistentClass.getEntityName(), mappings);
2161 }
2162 else if ( "sql-query".equals(name) ) {
2163 bindNamedSQLQuery(subnode, persistentClass.getEntityName(), mappings);
2164 }
2165 else if ( "resultset".equals(name) ) {
2166 bindResultSetMappingDefinition( subnode, persistentClass.getEntityName(), mappings );
2167 }
2168
2169 if ( value != null ) {
2170 Property property = createProperty( value, propertyName, persistentClass
2171 .getClassName(), subnode, mappings, inheritedMetas );
2172 if ( !mutable ) property.setUpdateable(false);
2173 if ( naturalId ) property.setNaturalIdentifier(true);
2174 persistentClass.addProperty( property );
2175 if ( uniqueKey!=null ) uniqueKey.addColumns( property.getColumnIterator() );
2176 }
2177
2178 }
2179 }
2180
2181 private static Property createProperty(
2182 final Value value,
2183 final String propertyName,
2184 final String className,
2185 final Element subnode,
2186 final Mappings mappings,
2187 java.util.Map inheritedMetas) throws MappingException {
2188
2189 if ( StringHelper.isEmpty( propertyName ) ) {
2190 throw new MappingException( subnode.getName() + " mapping must defined a name attribute [" + className + "]" );
2191 }
2192
2193 value.setTypeUsingReflection( className, propertyName );
2194
2195 // this is done here 'cos we might only know the type here (ugly!)
2196 // TODO: improve this a lot:
2197 if ( value instanceof ToOne ) {
2198 ToOne toOne = (ToOne) value;
2199 String propertyRef = toOne.getReferencedPropertyName();
2200 if ( propertyRef != null ) {
2201 mappings.addUniquePropertyReference( toOne.getReferencedEntityName(), propertyRef );
2202 }
2203 }
2204 else if ( value instanceof Collection ) {
2205 Collection coll = (Collection) value;
2206 String propertyRef = coll.getReferencedPropertyName();
2207 // not necessarily a *unique* property reference
2208 if ( propertyRef != null ) {
2209 mappings.addPropertyReference( coll.getOwnerEntityName(), propertyRef );
2210 }
2211 }
2212
2213 value.createForeignKey();
2214 Property prop = new Property();
2215 prop.setValue( value );
2216 bindProperty( subnode, prop, mappings, inheritedMetas );
2217 return prop;
2218 }
2219
2220 private static void handleUnionSubclass(PersistentClass model, Mappings mappings,
2221 Element subnode, java.util.Map inheritedMetas) throws MappingException {
2222 UnionSubclass subclass = new UnionSubclass( model );
2223 bindUnionSubclass( subnode, subclass, mappings, inheritedMetas );
2224 model.addSubclass( subclass );
2225 mappings.addClass( subclass );
2226 }
2227
2228 private static void handleJoinedSubclass(PersistentClass model, Mappings mappings,
2229 Element subnode, java.util.Map inheritedMetas) throws MappingException {
2230 JoinedSubclass subclass = new JoinedSubclass( model );
2231 bindJoinedSubclass( subnode, subclass, mappings, inheritedMetas );
2232 model.addSubclass( subclass );
2233 mappings.addClass( subclass );
2234 }
2235
2236 private static void handleSubclass(PersistentClass model, Mappings mappings, Element subnode,
2237 java.util.Map inheritedMetas) throws MappingException {
2238 Subclass subclass = new SingleTableSubclass( model );
2239 bindSubclass( subnode, subclass, mappings, inheritedMetas );
2240 model.addSubclass( subclass );
2241 mappings.addClass( subclass );
2242 }
2243
2244 /**
2245 * Called for Lists, arrays, primitive arrays
2246 */
2247 public static void bindListSecondPass(Element node, List list, java.util.Map classes,
2248 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2249
2250 bindCollectionSecondPass( node, list, classes, mappings, inheritedMetas );
2251
2252 Element subnode = node.element( "list-index" );
2253 if ( subnode == null ) subnode = node.element( "index" );
2254 SimpleValue iv = new SimpleValue( list.getCollectionTable() );
2255 bindSimpleValue(
2256 subnode,
2257 iv,
2258 list.isOneToMany(),
2259 IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2260 mappings
2261 );
2262 iv.setTypeName( "integer" );
2263 list.setIndex( iv );
2264 String baseIndex = subnode.attributeValue( "base" );
2265 if ( baseIndex != null ) list.setBaseIndex( Integer.parseInt( baseIndex ) );
2266 list.setIndexNodeName( subnode.attributeValue("node") );
2267
2268 if ( list.isOneToMany() && !list.getKey().isNullable() && !list.isInverse() ) {
2269 String entityName = ( (OneToMany) list.getElement() ).getReferencedEntityName();
2270 PersistentClass referenced = mappings.getClass( entityName );
2271 IndexBackref ib = new IndexBackref();
2272 ib.setName( '_' + list.getOwnerEntityName() + "." + node.attributeValue( "name" ) + "IndexBackref" );
2273 ib.setUpdateable( false );
2274 ib.setSelectable( false );
2275 ib.setCollectionRole( list.getRole() );
2276 ib.setEntityName( list.getOwner().getEntityName() );
2277 ib.setValue( list.getIndex() );
2278 // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
2279 // ).setNullable(false);
2280 referenced.addProperty( ib );
2281 }
2282 }
2283
2284 public static void bindIdentifierCollectionSecondPass(Element node,
2285 IdentifierCollection collection, java.util.Map persistentClasses, Mappings mappings,
2286 java.util.Map inheritedMetas) throws MappingException {
2287
2288 bindCollectionSecondPass( node, collection, persistentClasses, mappings, inheritedMetas );
2289
2290 Element subnode = node.element( "collection-id" );
2291 SimpleValue id = new SimpleValue( collection.getCollectionTable() );
2292 bindSimpleValue(
2293 subnode,
2294 id,
2295 false,
2296 IdentifierCollection.DEFAULT_IDENTIFIER_COLUMN_NAME,
2297 mappings
2298 );
2299 collection.setIdentifier( id );
2300 makeIdentifier( subnode, id, mappings );
2301
2302 }
2303
2304 /**
2305 * Called for Maps
2306 */
2307 public static void bindMapSecondPass(Element node, Map map, java.util.Map classes,
2308 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2309
2310 bindCollectionSecondPass( node, map, classes, mappings, inheritedMetas );
2311
2312 Iterator iter = node.elementIterator();
2313 while ( iter.hasNext() ) {
2314 Element subnode = (Element) iter.next();
2315 String name = subnode.getName();
2316
2317 if ( "index".equals( name ) || "map-key".equals( name ) ) {
2318 SimpleValue value = new SimpleValue( map.getCollectionTable() );
2319 bindSimpleValue(
2320 subnode,
2321 value,
2322 map.isOneToMany(),
2323 IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2324 mappings
2325 );
2326 if ( !value.isTypeSpecified() ) {
2327 throw new MappingException( "map index element must specify a type: "
2328 + map.getRole() );
2329 }
2330 map.setIndex( value );
2331 map.setIndexNodeName( subnode.attributeValue("node") );
2332 }
2333 else if ( "index-many-to-many".equals( name ) || "map-key-many-to-many".equals( name ) ) {
2334 ManyToOne mto = new ManyToOne( map.getCollectionTable() );
2335 bindManyToOne(
2336 subnode,
2337 mto,
2338 IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2339 map.isOneToMany(),
2340 mappings
2341 );
2342 map.setIndex( mto );
2343
2344 }
2345 else if ( "composite-index".equals( name ) || "composite-map-key".equals( name ) ) {
2346 Component component = new Component( map );
2347 bindComposite(
2348 subnode,
2349 component,
2350 map.getRole() + ".index",
2351 map.isOneToMany(),
2352 mappings,
2353 inheritedMetas
2354 );
2355 map.setIndex( component );
2356 }
2357 else if ( "index-many-to-any".equals( name ) ) {
2358 Any any = new Any( map.getCollectionTable() );
2359 bindAny( subnode, any, map.isOneToMany(), mappings );
2360 map.setIndex( any );
2361 }
2362 }
2363
2364 // TODO: this is a bit of copy/paste from IndexedCollection.createPrimaryKey()
2365 boolean indexIsFormula = false;
2366 Iterator colIter = map.getIndex().getColumnIterator();
2367 while ( colIter.hasNext() ) {
2368 if ( ( (Selectable) colIter.next() ).isFormula() ) indexIsFormula = true;
2369 }
2370
2371 if ( map.isOneToMany() && !map.getKey().isNullable() && !map.isInverse() && !indexIsFormula ) {
2372 String entityName = ( (OneToMany) map.getElement() ).getReferencedEntityName();
2373 PersistentClass referenced = mappings.getClass( entityName );
2374 IndexBackref ib = new IndexBackref();
2375 ib.setName( '_' + map.getOwnerEntityName() + "." + node.attributeValue( "name" ) + "IndexBackref" );
2376 ib.setUpdateable( false );
2377 ib.setSelectable( false );
2378 ib.setCollectionRole( map.getRole() );
2379 ib.setEntityName( map.getOwner().getEntityName() );
2380 ib.setValue( map.getIndex() );
2381 // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
2382 // ).setNullable(false);
2383 referenced.addProperty( ib );
2384 }
2385 }
2386
2387 /**
2388 * Called for all collections
2389 */
2390 public static void bindCollectionSecondPass(Element node, Collection collection,
2391 java.util.Map persistentClasses, Mappings mappings, java.util.Map inheritedMetas)
2392 throws MappingException {
2393
2394 if ( collection.isOneToMany() ) {
2395 OneToMany oneToMany = (OneToMany) collection.getElement();
2396 String assocClass = oneToMany.getReferencedEntityName();
2397 PersistentClass persistentClass = (PersistentClass) persistentClasses.get( assocClass );
2398 if ( persistentClass == null ) {
2399 throw new MappingException( "Association references unmapped class: " + assocClass );
2400 }
2401 oneToMany.setAssociatedClass( persistentClass );
2402 collection.setCollectionTable( persistentClass.getTable() );
2403
2404 log.info(
2405 "Mapping collection: " + collection.getRole() +
2406 " -> " + collection.getCollectionTable().getName()
2407 );
2408 }
2409
2410 // CHECK
2411 Attribute chNode = node.attribute( "check" );
2412 if ( chNode != null ) {
2413 collection.getCollectionTable().addCheckConstraint( chNode.getValue() );
2414 }
2415
2416 // contained elements:
2417 Iterator iter = node.elementIterator();
2418 while ( iter.hasNext() ) {
2419 Element subnode = (Element) iter.next();
2420 String name = subnode.getName();
2421
2422 if ( "key".equals( name ) ) {
2423 KeyValue keyVal;
2424 String propRef = collection.getReferencedPropertyName();
2425 if ( propRef == null ) {
2426 keyVal = collection.getOwner().getIdentifier();
2427 }
2428 else {
2429 keyVal = (KeyValue) collection.getOwner().getRecursiveProperty( propRef ).getValue();
2430 }
2431 SimpleValue key = new DependantValue( collection.getCollectionTable(), keyVal );
2432 key.setCascadeDeleteEnabled( "cascade"
2433 .equals( subnode.attributeValue( "on-delete" ) ) );
2434 bindSimpleValue(
2435 subnode,
2436 key,
2437 collection.isOneToMany(),
2438 Collection.DEFAULT_KEY_COLUMN_NAME,
2439 mappings
2440 );
2441 collection.setKey( key );
2442
2443 Attribute notNull = subnode.attribute( "not-null" );
2444 ( (DependantValue) key ).setNullable( notNull == null
2445 || notNull.getValue().equals( "false" ) );
2446 Attribute updateable = subnode.attribute( "update" );
2447 ( (DependantValue) key ).setUpdateable( updateable == null
2448 || updateable.getValue().equals( "true" ) );
2449
2450 }
2451 else if ( "element".equals( name ) ) {
2452 SimpleValue elt = new SimpleValue( collection.getCollectionTable() );
2453 collection.setElement( elt );
2454 bindSimpleValue(
2455 subnode,
2456 elt,
2457 true,
2458 Collection.DEFAULT_ELEMENT_COLUMN_NAME,
2459 mappings
2460 );
2461 }
2462 else if ( "many-to-many".equals( name ) ) {
2463 ManyToOne element = new ManyToOne( collection.getCollectionTable() );
2464 collection.setElement( element );
2465 bindManyToOne(
2466 subnode,
2467 element,
2468 Collection.DEFAULT_ELEMENT_COLUMN_NAME,
2469 false,
2470 mappings
2471 );
2472 bindManyToManySubelements( collection, subnode, mappings );
2473 }
2474 else if ( "composite-element".equals( name ) ) {
2475 Component element = new Component( collection );
2476 collection.setElement( element );
2477 bindComposite(
2478 subnode,
2479 element,
2480 collection.getRole() + ".element",
2481 true,
2482 mappings,
2483 inheritedMetas
2484 );
2485 }
2486 else if ( "many-to-any".equals( name ) ) {
2487 Any element = new Any( collection.getCollectionTable() );
2488 collection.setElement( element );
2489 bindAny( subnode, element, true, mappings );
2490 }
2491 else if ( "cache".equals( name ) ) {
2492 collection.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
2493 collection.setCacheRegionName( subnode.attributeValue( "region" ) );
2494 }
2495
2496 String nodeName = subnode.attributeValue( "node" );
2497 if ( nodeName != null ) collection.setElementNodeName( nodeName );
2498
2499 }
2500
2501 if ( collection.isOneToMany()
2502 && !collection.isInverse()
2503 && !collection.getKey().isNullable() ) {
2504 // for non-inverse one-to-many, with a not-null fk, add a backref!
2505 String entityName = ( (OneToMany) collection.getElement() ).getReferencedEntityName();
2506 PersistentClass referenced = mappings.getClass( entityName );
2507 Backref prop = new Backref();
2508 prop.setName( '_' + collection.getOwnerEntityName() + "." + node.attributeValue( "name" ) + "Backref" );
2509 prop.setUpdateable( false );
2510 prop.setSelectable( false );
2511 prop.setCollectionRole( collection.getRole() );
2512 prop.setEntityName( collection.getOwner().getEntityName() );
2513 prop.setValue( collection.getKey() );
2514 referenced.addProperty( prop );
2515 }
2516 }
2517
2518 private static void bindManyToManySubelements(
2519 Collection collection,
2520 Element manyToManyNode,
2521 Mappings model) throws MappingException {
2522 // Bind the where
2523 Attribute where = manyToManyNode.attribute( "where" );
2524 String whereCondition = where == null ? null : where.getValue();
2525 collection.setManyToManyWhere( whereCondition );
2526
2527 // Bind the order-by
2528 Attribute order = manyToManyNode.attribute( "order-by" );
2529 String orderFragment = order == null ? null : order.getValue();
2530 collection.setManyToManyOrdering( orderFragment );
2531
2532 // Bind the filters
2533 Iterator filters = manyToManyNode.elementIterator( "filter" );
2534 if ( ( filters.hasNext() || whereCondition != null ) &&
2535 collection.getFetchMode() == FetchMode.JOIN &&
2536 collection.getElement().getFetchMode() != FetchMode.JOIN ) {
2537 throw new MappingException(
2538 "many-to-many defining filter or where without join fetching " +
2539 "not valid within collection using join fetching [" + collection.getRole() + "]"
2540 );
2541 }
2542 while ( filters.hasNext() ) {
2543 final Element filterElement = ( Element ) filters.next();
2544 final String name = filterElement.attributeValue( "name" );
2545 String condition = filterElement.getTextTrim();
2546 if ( StringHelper.isEmpty(condition) ) condition = filterElement.attributeValue( "condition" );
2547 if ( StringHelper.isEmpty(condition) ) {
2548 condition = model.getFilterDefinition(name).getDefaultFilterCondition();
2549 }
2550 if ( condition==null) {
2551 throw new MappingException("no filter condition found for filter: " + name);
2552 }
2553 log.debug(
2554 "Applying many-to-many filter [" + name +
2555 "] as [" + condition +
2556 "] to role [" + collection.getRole() + "]"
2557 );
2558 collection.addManyToManyFilter( name, condition );
2559 }
2560 }
2561
2562 public static final FlushMode getFlushMode(String flushMode) {
2563 if ( flushMode == null ) {
2564 return null;
2565 }
2566 else if ( "auto".equals( flushMode ) ) {
2567 return FlushMode.AUTO;
2568 }
2569 else if ( "commit".equals( flushMode ) ) {
2570 return FlushMode.COMMIT;
2571 }
2572 else if ( "never".equals( flushMode ) ) {
2573 return FlushMode.NEVER;
2574 }
2575 else if ( "manual".equals( flushMode ) ) {
2576 return FlushMode.MANUAL;
2577 }
2578 else if ( "always".equals( flushMode ) ) {
2579 return FlushMode.ALWAYS;
2580 }
2581 else {
2582 throw new MappingException( "unknown flushmode" );
2583 }
2584 }
2585
2586 private static void bindNamedQuery(Element queryElem, String path, Mappings mappings) {
2587 String queryName = queryElem.attributeValue( "name" );
2588 if (path!=null) queryName = path + '.' + queryName;
2589 String query = queryElem.getText();
2590 log.debug( "Named query: " + queryName + " -> " + query );
2591
2592 boolean cacheable = "true".equals( queryElem.attributeValue( "cacheable" ) );
2593 String region = queryElem.attributeValue( "cache-region" );
2594 Attribute tAtt = queryElem.attribute( "timeout" );
2595 Integer timeout = tAtt == null ? null : new Integer( tAtt.getValue() );
2596 Attribute fsAtt = queryElem.attribute( "fetch-size" );
2597 Integer fetchSize = fsAtt == null ? null : new Integer( fsAtt.getValue() );
2598 Attribute roAttr = queryElem.attribute( "read-only" );
2599 boolean readOnly = roAttr != null && "true".equals( roAttr.getValue() );
2600 Attribute cacheModeAtt = queryElem.attribute( "cache-mode" );
2601 String cacheMode = cacheModeAtt == null ? null : cacheModeAtt.getValue();
2602 Attribute cmAtt = queryElem.attribute( "comment" );
2603 String comment = cmAtt == null ? null : cmAtt.getValue();
2604
2605 NamedQueryDefinition namedQuery = new NamedQueryDefinition(
2606 query,
2607 cacheable,
2608 region,
2609 timeout,
2610 fetchSize,
2611 getFlushMode( queryElem.attributeValue( "flush-mode" ) ) ,
2612 getCacheMode( cacheMode ),
2613 readOnly,
2614 comment,
2615 getParameterTypes(queryElem)
2616 );
2617
2618 mappings.addQuery( queryName, namedQuery );
2619 }
2620
2621 public static CacheMode getCacheMode(String cacheMode) {
2622 if (cacheMode == null) return null;
2623 if ( "get".equals( cacheMode ) ) return CacheMode.GET;
2624 if ( "ignore".equals( cacheMode ) ) return CacheMode.IGNORE;
2625 if ( "normal".equals( cacheMode ) ) return CacheMode.NORMAL;
2626 if ( "put".equals( cacheMode ) ) return CacheMode.PUT;
2627 if ( "refresh".equals( cacheMode ) ) return CacheMode.REFRESH;
2628 throw new MappingException("Unknown Cache Mode: " + cacheMode);
2629 }
2630
2631 public static java.util.Map getParameterTypes(Element queryElem) {
2632 java.util.Map result = new java.util.LinkedHashMap();
2633 Iterator iter = queryElem.elementIterator("query-param");
2634 while ( iter.hasNext() ) {
2635 Element element = (Element) iter.next();
2636 result.put( element.attributeValue("name"), element.attributeValue("type") );
2637 }
2638 return result;
2639 }
2640
2641 private static void bindResultSetMappingDefinition(Element resultSetElem, String path, Mappings mappings) {
2642 mappings.addSecondPass( new ResultSetMappingSecondPass( resultSetElem, path, mappings ) );
2643 }
2644
2645 private static void bindNamedSQLQuery(Element queryElem, String path, Mappings mappings) {
2646 mappings.addSecondPass( new NamedSQLQuerySecondPass( queryElem, path, mappings ) );
2647 }
2648
2649 private static String getPropertyName(Element node) {
2650 return node.attributeValue( "name" );
2651 }
2652
2653 private static PersistentClass getSuperclass(Mappings mappings, Element subnode)
2654 throws MappingException {
2655 String extendsName = subnode.attributeValue( "extends" );
2656 PersistentClass superModel = mappings.getClass( extendsName );
2657 if ( superModel == null ) {
2658 String qualifiedExtendsName = getClassName( extendsName, mappings );
2659 superModel = mappings.getClass( qualifiedExtendsName );
2660 }
2661
2662 if ( superModel == null ) {
2663 throw new MappingException( "Cannot extend unmapped class " + extendsName );
2664 }
2665 return superModel;
2666 }
2667
2668 static class CollectionSecondPass extends org.hibernate.cfg.CollectionSecondPass {
2669 Element node;
2670
2671 CollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
2672 super(mappings, collection, inheritedMetas);
2673 this.node = node;
2674 }
2675
2676 public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
2677 throws MappingException {
2678 HbmBinder.bindCollectionSecondPass(
2679 node,
2680 collection,
2681 persistentClasses,
2682 mappings,
2683 inheritedMetas
2684 );
2685 }
2686 }
2687
2688 static class IdentifierCollectionSecondPass extends CollectionSecondPass {
2689 IdentifierCollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
2690 super( node, mappings, collection, inheritedMetas );
2691 }
2692
2693 public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
2694 throws MappingException {
2695 HbmBinder.bindIdentifierCollectionSecondPass(
2696 node,
2697 (IdentifierCollection) collection,
2698 persistentClasses,
2699 mappings,
2700 inheritedMetas
2701 );
2702 }
2703
2704 }
2705
2706 static class MapSecondPass extends CollectionSecondPass {
2707 MapSecondPass(Element node, Mappings mappings, Map collection, java.util.Map inheritedMetas) {
2708 super( node, mappings, collection, inheritedMetas );
2709 }
2710
2711 public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
2712 throws MappingException {
2713 HbmBinder.bindMapSecondPass(
2714 node,
2715 (Map) collection,
2716 persistentClasses,
2717 mappings,
2718 inheritedMetas
2719 );
2720 }
2721
2722 }
2723
2724
2725 static class ManyToOneSecondPass implements SecondPass {
2726 private final ManyToOne manyToOne;
2727
2728 ManyToOneSecondPass(ManyToOne manyToOne) {
2729 this.manyToOne = manyToOne;
2730 }
2731
2732 public void doSecondPass(java.util.Map persistentClasses) throws MappingException {
2733 manyToOne.createPropertyRefConstraints(persistentClasses);
2734 }
2735
2736 }
2737
2738 static class ListSecondPass extends CollectionSecondPass {
2739 ListSecondPass(Element node, Mappings mappings, List collection, java.util.Map inheritedMetas) {
2740 super( node, mappings, collection, inheritedMetas );
2741 }
2742
2743 public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
2744 throws MappingException {
2745 HbmBinder.bindListSecondPass(
2746 node,
2747 (List) collection,
2748 persistentClasses,
2749 mappings,
2750 inheritedMetas
2751 );
2752 }
2753
2754 }
2755
2756 // This inner class implements a case statement....perhaps im being a bit over-clever here
2757 abstract static class CollectionType {
2758 private String xmlTag;
2759
2760 public abstract Collection create(Element node, String path, PersistentClass owner,
2761 Mappings mappings, java.util.Map inheritedMetas) throws MappingException;
2762
2763 CollectionType(String xmlTag) {
2764 this.xmlTag = xmlTag;
2765 }
2766
2767 public String toString() {
2768 return xmlTag;
2769 }
2770
2771 private static final CollectionType MAP = new CollectionType( "map" ) {
2772 public Collection create(Element node, String path, PersistentClass owner,
2773 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2774 Map map = new Map( owner );
2775 bindCollection( node, map, owner.getEntityName(), path, mappings, inheritedMetas );
2776 return map;
2777 }
2778 };
2779 private static final CollectionType SET = new CollectionType( "set" ) {
2780 public Collection create(Element node, String path, PersistentClass owner,
2781 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2782 Set set = new Set( owner );
2783 bindCollection( node, set, owner.getEntityName(), path, mappings, inheritedMetas );
2784 return set;
2785 }
2786 };
2787 private static final CollectionType LIST = new CollectionType( "list" ) {
2788 public Collection create(Element node, String path, PersistentClass owner,
2789 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2790 List list = new List( owner );
2791 bindCollection( node, list, owner.getEntityName(), path, mappings, inheritedMetas );
2792 return list;
2793 }
2794 };
2795 private static final CollectionType BAG = new CollectionType( "bag" ) {
2796 public Collection create(Element node, String path, PersistentClass owner,
2797 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2798 Bag bag = new Bag( owner );
2799 bindCollection( node, bag, owner.getEntityName(), path, mappings, inheritedMetas );
2800 return bag;
2801 }
2802 };
2803 private static final CollectionType IDBAG = new CollectionType( "idbag" ) {
2804 public Collection create(Element node, String path, PersistentClass owner,
2805 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2806 IdentifierBag bag = new IdentifierBag( owner );
2807 bindCollection( node, bag, owner.getEntityName(), path, mappings, inheritedMetas );
2808 return bag;
2809 }
2810 };
2811 private static final CollectionType ARRAY = new CollectionType( "array" ) {
2812 public Collection create(Element node, String path, PersistentClass owner,
2813 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2814 Array array = new Array( owner );
2815 bindArray( node, array, owner.getEntityName(), path, mappings, inheritedMetas );
2816 return array;
2817 }
2818 };
2819 private static final CollectionType PRIMITIVE_ARRAY = new CollectionType( "primitive-array" ) {
2820 public Collection create(Element node, String path, PersistentClass owner,
2821 Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
2822 PrimitiveArray array = new PrimitiveArray( owner );
2823 bindArray( node, array, owner.getEntityName(), path, mappings, inheritedMetas );
2824 return array;
2825 }
2826 };
2827 private static final HashMap INSTANCES = new HashMap();
2828
2829 static {
2830 INSTANCES.put( MAP.toString(), MAP );
2831 INSTANCES.put( BAG.toString(), BAG );
2832 INSTANCES.put( IDBAG.toString(), IDBAG );
2833 INSTANCES.put( SET.toString(), SET );
2834 INSTANCES.put( LIST.toString(), LIST );
2835 INSTANCES.put( ARRAY.toString(), ARRAY );
2836 INSTANCES.put( PRIMITIVE_ARRAY.toString(), PRIMITIVE_ARRAY );
2837 }
2838
2839 public static CollectionType collectionTypeFromString(String xmlTagName) {
2840 return (CollectionType) INSTANCES.get( xmlTagName );
2841 }
2842 }
2843
2844 private static int getOptimisticLockMode(Attribute olAtt) throws MappingException {
2845
2846 if ( olAtt == null ) return Versioning.OPTIMISTIC_LOCK_VERSION;
2847 String olMode = olAtt.getValue();
2848 if ( olMode == null || "version".equals( olMode ) ) {
2849 return Versioning.OPTIMISTIC_LOCK_VERSION;
2850 }
2851 else if ( "dirty".equals( olMode ) ) {
2852 return Versioning.OPTIMISTIC_LOCK_DIRTY;
2853 }
2854 else if ( "all".equals( olMode ) ) {
2855 return Versioning.OPTIMISTIC_LOCK_ALL;
2856 }
2857 else if ( "none".equals( olMode ) ) {
2858 return Versioning.OPTIMISTIC_LOCK_NONE;
2859 }
2860 else {
2861 throw new MappingException( "Unsupported optimistic-lock style: " + olMode );
2862 }
2863 }
2864
2865 private static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta) {
2866 return getMetas( node, inheritedMeta, false );
2867 }
2868
2869 public static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta,
2870 boolean onlyInheritable) {
2871 java.util.Map map = new HashMap();
2872 map.putAll( inheritedMeta );
2873
2874 Iterator iter = node.elementIterator( "meta" );
2875 while ( iter.hasNext() ) {
2876 Element metaNode = (Element) iter.next();
2877 boolean inheritable = Boolean
2878 .valueOf( metaNode.attributeValue( "inherit" ) )
2879 .booleanValue();
2880 if ( onlyInheritable & !inheritable ) {
2881 continue;
2882 }
2883 String name = metaNode.attributeValue( "attribute" );
2884
2885 MetaAttribute meta = (MetaAttribute) map.get( name );
2886 MetaAttribute inheritedAttribute = (MetaAttribute) inheritedMeta.get( name );
2887 if ( meta == null ) {
2888 meta = new MetaAttribute( name );
2889 map.put( name, meta );
2890 } else if (meta == inheritedAttribute) { // overriding inherited meta attribute. HBX-621 & HBX-793
2891 meta = new MetaAttribute( name );
2892 map.put( name, meta );
2893 }
2894 meta.addValue( metaNode.getText() );
2895 }
2896 return map;
2897 }
2898
2899 public static String getEntityName(Element elem, Mappings model) {
2900 String entityName = elem.attributeValue( "entity-name" );
2901 return entityName == null ? getClassName( elem.attribute( "class" ), model ) : entityName;
2902 }
2903
2904 private static String getClassName(Attribute att, Mappings model) {
2905 if ( att == null ) return null;
2906 return getClassName( att.getValue(), model );
2907 }
2908
2909 public static String getClassName(String unqualifiedName, Mappings model) {
2910 return getClassName( unqualifiedName, model.getDefaultPackage() );
2911 }
2912
2913 public static String getClassName(String unqualifiedName, String defaultPackage) {
2914 if ( unqualifiedName == null ) return null;
2915 if ( unqualifiedName.indexOf( '.' ) < 0 && defaultPackage != null ) {
2916 return defaultPackage + '.' + unqualifiedName;
2917 }
2918 return unqualifiedName;
2919 }
2920
2921 private static void parseFilterDef(Element element, Mappings mappings) {
2922 String name = element.attributeValue( "name" );
2923 log.debug( "Parsing filter-def [" + name + "]" );
2924 String defaultCondition = element.getTextTrim();
2925 if ( StringHelper.isEmpty( defaultCondition ) ) {
2926 defaultCondition = element.attributeValue( "condition" );
2927 }
2928 HashMap paramMappings = new HashMap();
2929 Iterator params = element.elementIterator( "filter-param" );
2930 while ( params.hasNext() ) {
2931 final Element param = (Element) params.next();
2932 final String paramName = param.attributeValue( "name" );
2933 final String paramType = param.attributeValue( "type" );
2934 log.debug( "adding filter parameter : " + paramName + " -> " + paramType );
2935 final Type heuristicType = TypeFactory.heuristicType( paramType );
2936 log.debug( "parameter heuristic type : " + heuristicType );
2937 paramMappings.put( paramName, heuristicType );
2938 }
2939 log.debug( "Parsed filter-def [" + name + "]" );
2940 FilterDefinition def = new FilterDefinition( name, defaultCondition, paramMappings );
2941 mappings.addFilterDefinition( def );
2942 }
2943
2944 private static void parseFilter(Element filterElement, Filterable filterable, Mappings model) {
2945 final String name = filterElement.attributeValue( "name" );
2946 String condition = filterElement.getTextTrim();
2947 if ( StringHelper.isEmpty(condition) ) {
2948 condition = filterElement.attributeValue( "condition" );
2949 }
2950 //TODO: bad implementation, cos it depends upon ordering of mapping doc
2951 // fixing this requires that Collection/PersistentClass gain access
2952 // to the Mappings reference from Configuration (or the filterDefinitions
2953 // map directly) sometime during Configuration.buildSessionFactory
2954 // (after all the types/filter-defs are known and before building
2955 // persisters).
2956 if ( StringHelper.isEmpty(condition) ) {
2957 condition = model.getFilterDefinition(name).getDefaultFilterCondition();
2958 }
2959 if ( condition==null) {
2960 throw new MappingException("no filter condition found for filter: " + name);
2961 }
2962 log.debug( "Applying filter [" + name + "] as [" + condition + "]" );
2963 filterable.addFilter( name, condition );
2964 }
2965
2966 private static String getSubselect(Element element) {
2967 String subselect = element.attributeValue( "subselect" );
2968 if ( subselect != null ) {
2969 return subselect;
2970 }
2971 else {
2972 Element subselectElement = element.element( "subselect" );
2973 return subselectElement == null ? null : subselectElement.getText();
2974 }
2975 }
2976
2977 /**
2978 * For the given document, locate all extends attributes which refer to
2979 * entities (entity-name or class-name) not defined within said document.
2980 *
2981 * @param doc The document to check
2982 * @param mappings The already processed mappings.
2983 * @return The list of unresolved extends names.
2984 */
2985 public static java.util.List getExtendsNeeded(Document doc, Mappings mappings) {
2986 java.util.List extendz = new ArrayList();
2987 Iterator[] subclasses = new Iterator[3];
2988 final Element hmNode = doc.getRootElement();
2989
2990 Attribute packNode = hmNode.attribute( "package" );
2991 final String packageName = packNode == null ? null : packNode.getValue();
2992 if ( packageName != null ) {
2993 mappings.setDefaultPackage( packageName );
2994 }
2995
2996 // first, iterate over all elements capable of defining an extends attribute
2997 // collecting all found extends references if they cannot be resolved
2998 // against the already processed mappings.
2999 subclasses[0] = hmNode.elementIterator( "subclass" );
3000 subclasses[1] = hmNode.elementIterator( "joined-subclass" );
3001 subclasses[2] = hmNode.elementIterator( "union-subclass" );
3002
3003 Iterator iterator = new JoinedIterator( subclasses );
3004 while ( iterator.hasNext() ) {
3005 final Element element = (Element) iterator.next();
3006 final String extendsName = element.attributeValue( "extends" );
3007 // mappings might contain either the "raw" extends name (in the case of
3008 // an entity-name mapping) or a FQN (in the case of a POJO mapping).
3009 if ( mappings.getClass( extendsName ) == null && mappings.getClass( getClassName( extendsName, mappings ) ) == null ) {
3010 extendz.add( extendsName );
3011 }
3012 }
3013
3014 if ( !extendz.isEmpty() ) {
3015 // we found some extends attributes referencing entities which were
3016 // not already processed. here we need to locate all entity-names
3017 // and class-names contained in this document itself, making sure
3018 // that these get removed from the extendz list such that only
3019 // extends names which require us to delay processing (i.e.
3020 // external to this document and not yet processed) are contained
3021 // in the returned result
3022 final java.util.Set set = new HashSet( extendz );
3023 EntityElementHandler handler = new EntityElementHandler() {
3024 public void handleEntity(String entityName, String className, Mappings mappings) {
3025 if ( entityName != null ) {
3026 set.remove( entityName );
3027 }
3028 else {
3029 String fqn = getClassName( className, packageName );
3030 set.remove( fqn );
3031 if ( packageName != null ) {
3032 set.remove( StringHelper.unqualify( fqn ) );
3033 }
3034 }
3035 }
3036 };
3037 recognizeEntities( mappings, hmNode, handler );
3038 extendz.clear();
3039 extendz.addAll( set );
3040 }
3041
3042 return extendz;
3043 }
3044
3045 /**
3046 * Given an entity-containing-element (startNode) recursively locate all
3047 * entity names defined within that element.
3048 *
3049 * @param mappings The already processed mappings
3050 * @param startNode The containing element
3051 * @param handler The thing that knows what to do whenever we recognize an
3052 * entity-name
3053 */
3054 private static void recognizeEntities(
3055 Mappings mappings,
3056 final Element startNode,
3057 EntityElementHandler handler) {
3058 Iterator[] classes = new Iterator[4];
3059 classes[0] = startNode.elementIterator( "class" );
3060 classes[1] = startNode.elementIterator( "subclass" );
3061 classes[2] = startNode.elementIterator( "joined-subclass" );
3062 classes[3] = startNode.elementIterator( "union-subclass" );
3063
3064 Iterator classIterator = new JoinedIterator( classes );
3065 while ( classIterator.hasNext() ) {
3066 Element element = (Element) classIterator.next();
3067 handler.handleEntity(
3068 element.attributeValue( "entity-name" ),
3069 element.attributeValue( "name" ),
3070 mappings
3071 );
3072 recognizeEntities( mappings, element, handler );
3073 }
3074 }
3075
3076 private static interface EntityElementHandler {
3077 public void handleEntity(String entityName, String className, Mappings mappings);
3078 }
3079 }