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.io.Serializable;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Properties;
32 import java.util.HashMap;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.hibernate.DuplicateMappingException;
37 import org.hibernate.MappingException;
38 import org.hibernate.engine.FilterDefinition;
39 import org.hibernate.engine.NamedQueryDefinition;
40 import org.hibernate.engine.NamedSQLQueryDefinition;
41 import org.hibernate.engine.ResultSetMappingDefinition;
42 import org.hibernate.mapping.Collection;
43 import org.hibernate.mapping.DenormalizedTable;
44 import org.hibernate.mapping.PersistentClass;
45 import org.hibernate.mapping.Table;
46 import org.hibernate.mapping.TypeDef;
47 import org.hibernate.mapping.AuxiliaryDatabaseObject;
48 import org.hibernate.mapping.Column;
49 import org.hibernate.util.StringHelper;
50
51 /**
52 * A collection of mappings from classes and collections to
53 * relational database tables. (Represents a single
54 * <tt><hibernate-mapping></tt> element.)
55 * @author Gavin King
56 */
57 public class Mappings implements Serializable {
58
59 private static final Logger log = LoggerFactory.getLogger(Mappings.class);
60
61 protected final Map classes;
62 protected final Map collections;
63 protected final Map tables;
64 protected final Map queries;
65 protected final Map sqlqueries;
66 protected final Map resultSetMappings;
67 protected final Map typeDefs;
68 protected final List secondPasses;
69 protected final Map imports;
70 protected String schemaName;
71 protected String catalogName;
72 protected String defaultCascade;
73 protected String defaultPackage;
74 protected String defaultAccess;
75 protected boolean autoImport;
76 protected boolean defaultLazy;
77 protected final List propertyReferences;
78 protected final NamingStrategy namingStrategy;
79 protected final Map filterDefinitions;
80 protected final List auxiliaryDatabaseObjects;
81
82 protected final Map extendsQueue;
83 // private final List extendsQueue;
84
85 /**
86 * binding table between the logical column name and the name out of the naming strategy
87 * for each table.
88 * According that when the column name is not set, the property name is considered as such
89 * This means that while theorically possible through the naming strategy contract, it is
90 * forbidden to have 2 real columns having the same logical name
91 * <Table, ColumnNames >
92 */
93 protected final Map columnNameBindingPerTable;
94 /**
95 * binding between logical table name and physical one (ie after the naming strategy has been applied)
96 * <String, TableDescription>
97 */
98 protected final Map tableNameBinding;
99
100
101 Mappings(
102 final Map classes,
103 final Map collections,
104 final Map tables,
105 final Map queries,
106 final Map sqlqueries,
107 final Map sqlResultSetMappings,
108 final Map imports,
109 final List secondPasses,
110 final List propertyReferences,
111 final NamingStrategy namingStrategy,
112 final Map typeDefs,
113 final Map filterDefinitions,
114 // final List extendsQueue,
115 final Map extendsQueue,
116 final List auxiliaryDatabaseObjects,
117 final Map tableNamebinding,
118 final Map columnNameBindingPerTable
119 ) {
120 this.classes = classes;
121 this.collections = collections;
122 this.queries = queries;
123 this.sqlqueries = sqlqueries;
124 this.resultSetMappings = sqlResultSetMappings;
125 this.tables = tables;
126 this.imports = imports;
127 this.secondPasses = secondPasses;
128 this.propertyReferences = propertyReferences;
129 this.namingStrategy = namingStrategy;
130 this.typeDefs = typeDefs;
131 this.filterDefinitions = filterDefinitions;
132 this.extendsQueue = extendsQueue;
133 this.auxiliaryDatabaseObjects = auxiliaryDatabaseObjects;
134 this.tableNameBinding = tableNamebinding;
135 this.columnNameBindingPerTable = columnNameBindingPerTable;
136 }
137
138 public void addClass(PersistentClass persistentClass) throws MappingException {
139 Object old = classes.put( persistentClass.getEntityName(), persistentClass );
140 if ( old!=null ) {
141 throw new DuplicateMappingException( "class/entity", persistentClass.getEntityName() );
142 }
143 }
144 public void addCollection(Collection collection) throws MappingException {
145 Object old = collections.put( collection.getRole(), collection );
146 if ( old!=null ) {
147 throw new DuplicateMappingException( "collection role", collection.getRole() );
148 }
149 }
150 public PersistentClass getClass(String className) {
151 return (PersistentClass) classes.get(className);
152 }
153 public Collection getCollection(String role) {
154 return (Collection) collections.get(role);
155 }
156
157 public void addImport(String className, String rename) throws MappingException {
158 String existing = (String) imports.put(rename, className);
159 if ( existing!=null ) {
160 if ( existing.equals(className) ) {
161 log.info( "duplicate import: " + className + "->" + rename );
162 }
163 else {
164 throw new DuplicateMappingException(
165 "duplicate import: " + rename +
166 " refers to both " + className +
167 " and " + existing +
168 " (try using auto-import=\"false\")",
169 "import",
170 rename
171 );
172 }
173 }
174 }
175
176 public Table addTable(String schema,
177 String catalog,
178 String name,
179 String subselect,
180 boolean isAbstract
181 ) {
182 String key = subselect==null ?
183 Table.qualify(catalog, schema, name) :
184 subselect;
185 Table table = (Table) tables.get(key);
186
187 if (table == null) {
188 table = new Table();
189 table.setAbstract(isAbstract);
190 table.setName(name);
191 table.setSchema(schema);
192 table.setCatalog(catalog);
193 table.setSubselect(subselect);
194 tables.put(key, table);
195 }
196 else {
197 if (!isAbstract) table.setAbstract(false);
198 }
199
200 return table;
201 }
202
203 public Table addDenormalizedTable(
204 String schema,
205 String catalog,
206 String name,
207 boolean isAbstract,
208 String subselect,
209 Table includedTable)
210 throws MappingException {
211 String key = subselect==null ?
212 Table.qualify(catalog, schema, name) :
213 subselect;
214 if ( tables.containsKey(key) ) {
215 throw new DuplicateMappingException("table", name);
216 }
217
218 Table table = new DenormalizedTable(includedTable);
219 table.setAbstract(isAbstract);
220 table.setName(name);
221 table.setSchema(schema);
222 table.setCatalog(catalog);
223 table.setSubselect(subselect);
224 tables.put(key, table);
225 return table;
226 }
227
228 public Table getTable(String schema, String catalog, String name) {
229 String key = Table.qualify(catalog, schema, name);
230 return (Table) tables.get(key);
231 }
232
233 public String getSchemaName() {
234 return schemaName;
235 }
236
237 public String getCatalogName() {
238 return catalogName;
239 }
240
241 public String getDefaultCascade() {
242 return defaultCascade;
243 }
244
245 /**
246 * Sets the schemaName.
247 * @param schemaName The schemaName to set
248 */
249 public void setSchemaName(String schemaName) {
250 this.schemaName = schemaName;
251 }
252
253 /**
254 * Sets the catalogName.
255 * @param catalogName The catalogName to set
256 */
257 public void setCatalogName(String catalogName) {
258 this.catalogName = catalogName;
259 }
260
261 /**
262 * Sets the defaultCascade.
263 * @param defaultCascade The defaultCascade to set
264 */
265 public void setDefaultCascade(String defaultCascade) {
266 this.defaultCascade = defaultCascade;
267 }
268
269 /**
270 * sets the default access strategy
271 * @param defaultAccess the default access strategy.
272 */
273 public void setDefaultAccess(String defaultAccess) {
274 this.defaultAccess = defaultAccess;
275 }
276
277 public String getDefaultAccess() {
278 return defaultAccess;
279 }
280
281 public void addQuery(String name, NamedQueryDefinition query) throws MappingException {
282 checkQueryExist(name);
283 queries.put( name.intern(), query );
284 }
285
286 public void addSQLQuery(String name, NamedSQLQueryDefinition query) throws MappingException {
287 checkQueryExist(name);
288 sqlqueries.put( name.intern(), query );
289 }
290
291 private void checkQueryExist(String name) throws MappingException {
292 if ( sqlqueries.containsKey(name) || queries.containsKey(name) ) {
293 throw new DuplicateMappingException("query", name);
294 }
295 }
296
297 public void addResultSetMapping(ResultSetMappingDefinition sqlResultSetMapping) {
298 final String name = sqlResultSetMapping.getName();
299 if ( resultSetMappings.containsKey(name) ) {
300 throw new DuplicateMappingException("resultSet", name);
301 }
302 resultSetMappings.put(name, sqlResultSetMapping);
303 }
304
305 public ResultSetMappingDefinition getResultSetMapping(String name) {
306 return (ResultSetMappingDefinition) resultSetMappings.get(name);
307 }
308
309
310 public NamedQueryDefinition getQuery(String name) {
311 return (NamedQueryDefinition) queries.get(name);
312 }
313
314 public void addSecondPass(SecondPass sp) {
315 addSecondPass(sp, false);
316 }
317
318 public void addSecondPass(SecondPass sp, boolean onTopOfTheQueue) {
319 if (onTopOfTheQueue) {
320 secondPasses.add(0, sp);
321 }
322 else {
323 secondPasses.add(sp);
324 }
325 }
326
327 /**
328 * Returns the autoImport.
329 * @return boolean
330 */
331 public boolean isAutoImport() {
332 return autoImport;
333 }
334
335 /**
336 * Sets the autoImport.
337 * @param autoImport The autoImport to set
338 */
339 public void setAutoImport(boolean autoImport) {
340 this.autoImport = autoImport;
341 }
342
343 void addUniquePropertyReference(String referencedClass, String propertyName) {
344 PropertyReference upr = new PropertyReference();
345 upr.referencedClass = referencedClass;
346 upr.propertyName = propertyName;
347 upr.unique = true;
348 propertyReferences.add(upr);
349 }
350
351 void addPropertyReference(String referencedClass, String propertyName) {
352 PropertyReference upr = new PropertyReference();
353 upr.referencedClass = referencedClass;
354 upr.propertyName = propertyName;
355 propertyReferences.add(upr);
356 }
357
358 private String buildTableNameKey(String schema, String catalog, String finalName) {
359 StringBuffer keyBuilder = new StringBuffer();
360 if (schema != null) keyBuilder.append( schema );
361 keyBuilder.append( ".");
362 if (catalog != null) keyBuilder.append( catalog );
363 keyBuilder.append( ".");
364 keyBuilder.append( finalName );
365 return keyBuilder.toString();
366 }
367
368 static final class PropertyReference implements Serializable {
369 String referencedClass;
370 String propertyName;
371 boolean unique;
372 }
373
374 /**
375 * @return Returns the defaultPackage.
376 */
377 public String getDefaultPackage() {
378 return defaultPackage;
379 }
380
381 /**
382 * @param defaultPackage The defaultPackage to set.
383 */
384 public void setDefaultPackage(String defaultPackage) {
385 this.defaultPackage = defaultPackage;
386 }
387
388 public NamingStrategy getNamingStrategy() {
389 return namingStrategy;
390 }
391
392 public void addTypeDef(String typeName, String typeClass, Properties paramMap) {
393 TypeDef def = new TypeDef(typeClass, paramMap);
394 typeDefs.put(typeName, def);
395 log.debug("Added " + typeName + " with class " + typeClass);
396 }
397
398 public TypeDef getTypeDef(String typeName) {
399 return (TypeDef) typeDefs.get(typeName);
400 }
401
402 public Iterator iterateCollections() {
403 return collections.values().iterator();
404 }
405
406 public Iterator iterateTables() {
407 return tables.values().iterator();
408 }
409
410 public Map getFilterDefinitions() {
411 return filterDefinitions;
412 }
413
414 public void addFilterDefinition(FilterDefinition definition) {
415 filterDefinitions.put( definition.getFilterName(), definition );
416 }
417
418 public FilterDefinition getFilterDefinition(String name) {
419 return (FilterDefinition) filterDefinitions.get(name);
420 }
421
422 public boolean isDefaultLazy() {
423 return defaultLazy;
424 }
425 public void setDefaultLazy(boolean defaultLazy) {
426 this.defaultLazy = defaultLazy;
427 }
428
429 public void addToExtendsQueue(ExtendsQueueEntry entry) {
430 extendsQueue.put( entry, null );
431 }
432
433 public PersistentClass locatePersistentClassByEntityName(String entityName) {
434 PersistentClass persistentClass = ( PersistentClass ) classes.get( entityName );
435 if ( persistentClass == null ) {
436 String actualEntityName = ( String ) imports.get( entityName );
437 if ( StringHelper.isNotEmpty( actualEntityName ) ) {
438 persistentClass = ( PersistentClass ) classes.get( actualEntityName );
439 }
440 }
441 return persistentClass;
442 }
443
444 public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject) {
445 auxiliaryDatabaseObjects.add( auxiliaryDatabaseObject );
446 }
447
448 public void addTableBinding(
449 String schema, String catalog, String logicalName, String physicalName, Table denormalizedSuperTable
450 ) {
451 String key = buildTableNameKey( schema, catalog, physicalName );
452 TableDescription tableDescription = new TableDescription(
453 logicalName, denormalizedSuperTable
454 );
455 TableDescription oldDescriptor = (TableDescription) tableNameBinding.put( key, tableDescription );
456 if ( oldDescriptor != null && ! oldDescriptor.logicalName.equals( logicalName ) ) {
457 //TODO possibly relax that
458 throw new MappingException("Same physical table name reference several logical table names: "
459 + physicalName + " => " + "'" + oldDescriptor.logicalName + "' and '" + logicalName + "'");
460 }
461 }
462
463 public void addColumnBinding(String logicalName, Column finalColumn, Table table) {
464 ColumnNames binding = (ColumnNames) columnNameBindingPerTable.get(table);
465 if (binding == null) {
466 binding = new ColumnNames();
467 columnNameBindingPerTable.put(table, binding);
468 }
469 String oldFinalName = (String) binding.logicalToPhysical.put(
470 logicalName.toLowerCase(),
471 finalColumn.getQuotedName()
472 );
473 if ( oldFinalName != null &&
474 ! ( finalColumn.isQuoted() ?
475 oldFinalName.equals( finalColumn.getQuotedName() ) :
476 oldFinalName.equalsIgnoreCase( finalColumn.getQuotedName() ) ) ) {
477 //TODO possibly relax that
478 throw new MappingException("Same logical column name referenced by different physical ones: "
479 + table.getName() + "." + logicalName + " => '" + oldFinalName + "' and '" + finalColumn.getQuotedName() + "'" );
480 }
481 String oldLogicalName = (String) binding.physicalToLogical.put(
482 finalColumn.getQuotedName(),
483 logicalName
484 );
485 if ( oldLogicalName != null && ! oldLogicalName.equals( logicalName ) ) {
486 //TODO possibly relax that
487 throw new MappingException("Same physical column represented by different logical column names: "
488 + table.getName() + "." + finalColumn.getQuotedName() + " => '" + oldLogicalName + "' and '" + logicalName + "'");
489 }
490 }
491
492 private String getLogicalTableName(String schema, String catalog, String physicalName) {
493 String key = buildTableNameKey( schema, catalog, physicalName );
494 TableDescription descriptor = (TableDescription) tableNameBinding.get( key );
495 if (descriptor == null) {
496 throw new MappingException( "Unable to find physical table: " + physicalName);
497 }
498 return descriptor.logicalName;
499 }
500
501 public String getPhysicalColumnName(String logicalName, Table table) {
502 logicalName = logicalName.toLowerCase();
503 String finalName = null;
504 Table currentTable = table;
505 do {
506 ColumnNames binding = (ColumnNames) columnNameBindingPerTable.get(currentTable);
507 if (binding != null) {
508 finalName = (String) binding.logicalToPhysical.get( logicalName );
509 }
510 String key = buildTableNameKey( currentTable.getSchema(), currentTable.getCatalog(), currentTable.getName() );
511 TableDescription description = (TableDescription) tableNameBinding.get(key);
512 if (description != null) currentTable = description.denormalizedSupertable;
513 }
514 while (finalName == null && currentTable != null);
515 if (finalName == null) {
516 throw new MappingException( "Unable to find column with logical name "
517 + logicalName + " in table " + table.getName() );
518 }
519 return finalName;
520 }
521
522 public String getLogicalColumnName(String physicalName, Table table) {
523 String logical = null;
524 Table currentTable = table;
525 TableDescription description = null;
526 do {
527 ColumnNames binding = (ColumnNames) columnNameBindingPerTable.get(currentTable);
528 if (binding != null) {
529 logical = (String) binding.physicalToLogical.get( physicalName );
530 }
531 String key = buildTableNameKey( currentTable.getSchema(), currentTable.getCatalog(), currentTable.getName() );
532 description = (TableDescription) tableNameBinding.get(key);
533 if (description != null) currentTable = description.denormalizedSupertable;
534 }
535 while (logical == null && currentTable != null && description != null);
536 if (logical == null) {
537 throw new MappingException( "Unable to find logical column name from physical name "
538 + physicalName + " in table " + table.getName() );
539 }
540 return logical;
541 }
542
543 public String getLogicalTableName(Table table) {
544 return getLogicalTableName( table.getQuotedSchema(), table.getCatalog(), table.getQuotedName() );
545 }
546
547 static public class ColumnNames implements Serializable {
548 //<String, String>
549 public Map logicalToPhysical = new HashMap();
550 //<String, String>
551 public Map physicalToLogical = new HashMap();
552 public ColumnNames() {
553 }
554 }
555
556 static public class TableDescription implements Serializable {
557 public TableDescription(String logicalName, Table denormalizedSupertable) {
558 this.logicalName = logicalName;
559 this.denormalizedSupertable = denormalizedSupertable;
560 }
561
562 public String logicalName;
563 public Table denormalizedSupertable;
564 }
565 }