KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > DbLoader


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20
21 package org.apache.cayenne.access;
22
23 import java.sql.Connection JavaDoc;
24 import java.sql.DatabaseMetaData JavaDoc;
25 import java.sql.ResultSet JavaDoc;
26 import java.sql.SQLException JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Arrays JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.Set JavaDoc;
36
37 import org.apache.cayenne.CayenneException;
38 import org.apache.cayenne.dba.DbAdapter;
39 import org.apache.cayenne.dba.TypesMapping;
40 import org.apache.cayenne.map.DataMap;
41 import org.apache.cayenne.map.DbAttribute;
42 import org.apache.cayenne.map.DbEntity;
43 import org.apache.cayenne.map.DbJoin;
44 import org.apache.cayenne.map.DbRelationship;
45 import org.apache.cayenne.map.Entity;
46 import org.apache.cayenne.map.ObjEntity;
47 import org.apache.cayenne.map.Procedure;
48 import org.apache.cayenne.map.ProcedureParameter;
49 import org.apache.cayenne.util.EntityMergeSupport;
50 import org.apache.cayenne.util.NameConverter;
51 import org.apache.cayenne.util.Util;
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54 import org.objectstyle.ashwood.dbutil.Table;
55
56 /**
57  * Utility class that does reverse engineering of the database. It can create DataMaps
58  * using database meta data obtained via JDBC driver.
59  *
60  * @author Michael Shengaout
61  * @author Andrus Adamchik
62  */

63 public class DbLoader {
64
65     private static Log logObj = LogFactory.getLog(DbLoader.class);
66
67     // TODO: remove this hardcoded stuff once delegate starts to support procedure
68
// loading...
69
private static final Collection JavaDoc EXCLUDED_PROCEDURES = Arrays.asList(new Object JavaDoc[] {
70             "auto_pk_for_table", "auto_pk_for_table;1" /*
71                                                          * the last name is some Mac OS X
72                                                          * Sybase artifact
73                                                          */

74     });
75
76     public static final String JavaDoc WILDCARD = "%";
77
78     /** List of db entities to process. */
79     private List JavaDoc dbEntityList = new ArrayList JavaDoc();
80     
81     /**
82      * CAY-479 - need to track which entities are skipped in
83      * the loader so that relationships to non-skipped entities can be loaded
84      */

85     private Set JavaDoc skippedEntities = new HashSet JavaDoc();
86
87     /** Creates default name for loaded relationship */
88     private static String JavaDoc defaultRelName(String JavaDoc dstName, boolean toMany) {
89         String JavaDoc uglyName = (toMany) ? dstName + "_ARRAY" : "to_" + dstName;
90         return NameConverter.underscoredToJava(uglyName, false);
91     }
92
93     /** Creates a unique name for loaded relationship on the given entity. */
94     private static String JavaDoc uniqueRelName(Entity entity, String JavaDoc dstName, boolean toMany) {
95         int currentSuffix = 1;
96         String JavaDoc baseRelName = defaultRelName(dstName, toMany);
97         String JavaDoc relName = baseRelName;
98
99         while (entity.getRelationship(relName) != null) {
100             relName = baseRelName + currentSuffix;
101             currentSuffix++;
102         }
103         return relName;
104     }
105
106     protected Connection JavaDoc con;
107     protected DbAdapter adapter;
108     protected DatabaseMetaData JavaDoc metaData;
109     protected DbLoaderDelegate delegate;
110     protected String JavaDoc genericClassName;
111
112     /** Creates new DbLoader. */
113     public DbLoader(Connection JavaDoc con, DbAdapter adapter, DbLoaderDelegate delegate) {
114         this.adapter = adapter;
115         this.con = con;
116         this.delegate = delegate;
117     }
118
119     /**
120      * Returns DatabaseMetaData object associated with this DbLoader.
121      */

122     public DatabaseMetaData JavaDoc getMetaData() throws SQLException JavaDoc {
123         if (null == metaData)
124             metaData = con.getMetaData();
125         return metaData;
126     }
127
128     /**
129      * Returns database connection used by this DbLoader.
130      */

131     public Connection JavaDoc getCon() {
132         return con;
133     }
134
135     /**
136      * Returns a name of a generic class that should be used for all ObjEntities. The most
137      * common generic class is {@link org.apache.cayenne.CayenneDataObject}. If
138      * generic class name is null (which is the default), DbLoader will assign each entity
139      * a unique class name derived from the table name.
140      *
141      * @since 1.2
142      */

143     public String JavaDoc getGenericClassName() {
144         return genericClassName;
145     }
146
147     /**
148      * Sets a name of a generic class that should be used for all ObjEntities. The most
149      * common generic class is {@link org.apache.cayenne.CayenneDataObject}. If
150      * generic class name is set to null (which is the default), DbLoader will assign each
151      * entity a unique class name derived from the table name.
152      *
153      * @since 1.2
154      */

155     public void setGenericClassName(String JavaDoc genericClassName) {
156         this.genericClassName = genericClassName;
157     }
158
159     /**
160      * Returns DbAdapter associated with this DbLoader.
161      *
162      * @since 1.1
163      */

164     public DbAdapter getAdapter() {
165         return adapter;
166     }
167
168     /**
169      * Retrieves catalogues for the database associated with this DbLoader.
170      *
171      * @return List with the catalog names, empty Array if none found.
172      */

173     public List JavaDoc getCatalogs() throws SQLException JavaDoc {
174         List JavaDoc catalogs = new ArrayList JavaDoc();
175         ResultSet JavaDoc rs = getMetaData().getCatalogs();
176
177         try {
178             while (rs.next()) {
179                 String JavaDoc catalog_name = rs.getString(1);
180                 catalogs.add(catalog_name);
181             }
182         }
183         finally {
184             rs.close();
185         }
186         return catalogs;
187     }
188
189     /**
190      * Retrieves the schemas for the database.
191      *
192      * @return List with the schema names, empty Array if none found.
193      */

194     public List JavaDoc getSchemas() throws SQLException JavaDoc {
195         List JavaDoc schemas = new ArrayList JavaDoc();
196         ResultSet JavaDoc rs = getMetaData().getSchemas();
197
198         try {
199             while (rs.next()) {
200                 String JavaDoc schema_name = rs.getString(1);
201                 schemas.add(schema_name);
202             }
203         }
204         finally {
205             rs.close();
206         }
207         return schemas;
208     }
209
210     /**
211      * Returns all the table types for the given database. Types may be such as "TABLE",
212      * "VIEW", "SYSTEM TABLE", etc.
213      *
214      * @return List of Strings, empty array if nothing found.
215      */

216     public List JavaDoc getTableTypes() throws SQLException JavaDoc {
217         List JavaDoc types = new ArrayList JavaDoc();
218         ResultSet JavaDoc rs = getMetaData().getTableTypes();
219
220         try {
221             while (rs.next()) {
222                 types.add(rs.getString("TABLE_TYPE").trim());
223             }
224         }
225         finally {
226             rs.close();
227         }
228         return types;
229     }
230
231     /**
232      * Returns all table names for given combination of the criteria.
233      *
234      * @param catalog The name of the catalog, may be null.
235      * @param schemaPattern The pattern for schema name, use "%" for wildcard.
236      * @param tableNamePattern The pattern for table names, % for wildcard, if null or ""
237      * defaults to "%".
238      * @param types The types of table names to retrieve, null returns all types.
239      * @return List of TableInfo objects, empty array if nothing found.
240      */

241     public List JavaDoc getTables(
242             String JavaDoc catalog,
243             String JavaDoc schemaPattern,
244             String JavaDoc tableNamePattern,
245             String JavaDoc[] types) throws SQLException JavaDoc {
246
247         List JavaDoc tables = new ArrayList JavaDoc();
248
249         if (logObj.isDebugEnabled()) {
250             logObj.debug("Read tables: catalog="
251                     + catalog
252                     + ", schema="
253                     + schemaPattern
254                     + ", tableNames="
255                     + tableNamePattern);
256
257             if (types != null && types.length > 0) {
258                 for (int i = 0; i < types.length; i++) {
259                     logObj.debug("Read tables: table type=" + types[i]);
260                 }
261             }
262         }
263
264         ResultSet JavaDoc rs = getMetaData().getTables(
265                 catalog,
266                 schemaPattern,
267                 tableNamePattern,
268                 types);
269
270         try {
271             while (rs.next()) {
272                 String JavaDoc cat = rs.getString("TABLE_CAT");
273                 String JavaDoc schema = rs.getString("TABLE_SCHEM");
274                 String JavaDoc name = rs.getString("TABLE_NAME");
275
276                 // Oracle 9i and newer has a nifty recycle bin feature... but we don't
277
// want dropped tables to be included here; in fact they may even result
278
// in errors on reverse engineering as their names have special chars like
279
// "/", etc. So skip them all together
280

281                 // TODO: Andrus, 10/29/2005 - this type of filtering should be delegated
282
// to adapter
283
if (name == null || name.startsWith("BIN$")) {
284                     continue;
285                 }
286
287                 Table info = new Table(cat, schema, name);
288                 tables.add(info);
289             }
290         }
291         finally {
292             rs.close();
293         }
294         return tables;
295     }
296
297     /**
298      * Loads dbEntities for the specified tables.
299      *
300      * @param map DataMap to be populated with DbEntities.
301      * @param tables The list of org.objectstyle.ashwood.dbutil.Table objects for which
302      * DbEntities must be created.
303      * @return false if loading must be immediately aborted.
304      */

305     public boolean loadDbEntities(DataMap map, List JavaDoc tables) throws SQLException JavaDoc {
306         this.dbEntityList = new ArrayList JavaDoc();
307
308         Iterator JavaDoc iter = tables.iterator();
309         while (iter.hasNext()) {
310             Table table = (Table) iter.next();
311
312             // Check if there already is a DbEntity under such name
313
// if so, consult the delegate what to do
314
DbEntity oldEnt = map.getDbEntity(table.getName());
315             if (oldEnt != null) {
316                 if (delegate == null) {
317                     // no delegate, don't know what to do, cancel import
318
return false;
319                 }
320
321                 try {
322                     if (delegate.overwriteDbEntity(oldEnt)) {
323                         logObj.debug("Overwrite: " + oldEnt.getName());
324                         map.removeDbEntity(oldEnt.getName(), true);
325                         delegate.dbEntityRemoved(oldEnt);
326                     }
327                     else {
328                         logObj.debug("Keep old: " + oldEnt.getName());
329                         
330                         // cay-479 - need to track entities that were not loaded for
331
// relationships exported to entities that were
332
skippedEntities.add(oldEnt);
333                         continue;
334                     }
335                 }
336                 catch (CayenneException ex) {
337                     logObj.debug("Load canceled.");
338
339                     // cancel immediately
340
return false;
341                 }
342             }
343
344             DbEntity dbEntity = new DbEntity();
345             dbEntity.setName(table.getName());
346             dbEntity.setSchema(table.getSchema());
347             dbEntity.setCatalog(table.getCatalog());
348
349             // Create DbAttributes from column information --
350
ResultSet JavaDoc rs = getMetaData().getColumns(
351                     table.getCatalog(),
352                     table.getSchema(),
353                     table.getName(),
354                     "%");
355
356             try {
357                 while (rs.next()) {
358                     // for a reason not quiet apparent to me, Oracle sometimes
359
// returns duplicate record sets for the same table, messing up table
360
// names. E.g. for the system table "WK$_ATTR_MAPPING" columns are
361
// returned twice - as "WK$_ATTR_MAPPING" and "WK$$_ATTR_MAPPING"...
362
// Go figure
363

364                     String JavaDoc tableName = rs.getString("TABLE_NAME");
365                     if (!dbEntity.getName().equals(tableName)) {
366                         logObj.info("Incorrectly returned columns for '"
367                                 + tableName
368                                 + ", skipping.");
369                         continue;
370                     }
371
372                     // gets attribute's (column's) information
373
String JavaDoc columnName = rs.getString("COLUMN_NAME");
374
375                     boolean allowNulls = rs.getBoolean("NULLABLE");
376                     int columnType = rs.getInt("DATA_TYPE");
377                     int columnSize = rs.getInt("COLUMN_SIZE");
378                     String JavaDoc typeName = rs.getString("TYPE_NAME");
379
380                     // ignore precision of non-decimal columns
381
int decimalDigits = -1;
382                     if (TypesMapping.isDecimal(columnType)) {
383                         decimalDigits = rs.getInt("DECIMAL_DIGITS");
384                         if (rs.wasNull()) {
385                             decimalDigits = -1;
386                         }
387                     }
388
389                     // create attribute delegating this task to adapter
390
DbAttribute attr = adapter.buildAttribute(
391                             columnName,
392                             typeName,
393                             columnType,
394                             columnSize,
395                             decimalDigits,
396                             allowNulls);
397                     attr.setEntity(dbEntity);
398                     dbEntity.addAttribute(attr);
399                 }
400             }
401             finally {
402                 rs.close();
403             }
404
405             map.addDbEntity(dbEntity);
406
407             // notify delegate
408
if (delegate != null) {
409                 delegate.dbEntityAdded(dbEntity);
410             }
411
412             // delegate might have thrown this entity out... so check if it is still
413
// around
414
// before continuing processing
415
if (map.getDbEntity(table.getName()) == dbEntity) {
416                 this.dbEntityList.add(dbEntity);
417             }
418         }
419
420         // get primary keys for each table and store it in dbEntity
421
Iterator JavaDoc i = map.getDbEntities().iterator();
422         while (i.hasNext()) {
423             DbEntity dbEntity = (DbEntity) i.next();
424             String JavaDoc tableName = dbEntity.getName();
425             ResultSet JavaDoc rs = metaData.getPrimaryKeys(null, dbEntity.getSchema(), tableName);
426
427             try {
428                 while (rs.next()) {
429                     String JavaDoc keyName = rs.getString(4);
430                     DbAttribute attribute = (DbAttribute) dbEntity.getAttribute(keyName);
431
432                     if (attribute != null) {
433                         attribute.setPrimaryKey(true);
434                     }
435                     else {
436                         // why an attribute might be null is not quiet clear
437
// but there is a bug report 731406 indicating that it is possible
438
// so just print the warning, and ignore
439
logObj.warn("Can't locate attribute for primary key: " + keyName);
440                     }
441                 }
442             }
443             finally {
444                 rs.close();
445             }
446         }
447         
448         // cay-479 - iterate skipped DbEntities to populate exported keys
449
Iterator JavaDoc skippedEntityIter = skippedEntities.iterator();
450         while (skippedEntityIter.hasNext()) {
451
452             DbEntity skippedEntity = (DbEntity) skippedEntityIter.next();
453             loadDbRelationships(skippedEntity, map);
454         }
455             
456         return true;
457         
458     }
459
460     /**
461      * Creates an ObjEntity for each DbEntity in the map. ObjEntities are created empty
462      * without
463      */

464     public void loadObjEntities(DataMap map) {
465
466         Iterator JavaDoc dbEntities = dbEntityList.iterator();
467         if (!dbEntities.hasNext()) {
468             return;
469         }
470
471         List JavaDoc loadedEntities = new ArrayList JavaDoc(dbEntityList.size());
472
473         String JavaDoc packageName = map.getDefaultPackage();
474         if (Util.isEmptyString(packageName)) {
475             packageName = "";
476         }
477         else if (!packageName.endsWith(".")) {
478             packageName = packageName + ".";
479         }
480
481         // load empty ObjEntities for all the tables
482
while (dbEntities.hasNext()) {
483             DbEntity dbEntity = (DbEntity) dbEntities.next();
484
485             // check if there are existing entities
486
Collection JavaDoc existing = map.getMappedEntities(dbEntity);
487             if (existing.size() > 0) {
488                 loadedEntities.addAll(existing);
489                 continue;
490             }
491
492             String JavaDoc objEntityName = NameConverter.underscoredToJava(
493                     dbEntity.getName(),
494                     true);
495             // this loop will terminate even if no valid name is found
496
// to prevent loader from looping forever (though such case is very unlikely)
497
String JavaDoc baseName = objEntityName;
498             for (int i = 1; i < 1000 && map.getObjEntity(objEntityName) != null; i++) {
499                 objEntityName = baseName + i;
500             }
501
502             ObjEntity objEntity = new ObjEntity(objEntityName);
503             objEntity.setDbEntity(dbEntity);
504
505             objEntity.setClassName(getGenericClassName() != null
506                     ? getGenericClassName()
507                     : packageName + objEntity.getName());
508             map.addObjEntity(objEntity);
509             loadedEntities.add(objEntity);
510
511             // added entity without attributes or relationships...
512
if (delegate != null) {
513                 delegate.objEntityAdded(objEntity);
514             }
515         }
516
517         // update ObjEntity attributes and relationships
518
new EntityMergeSupport(map).synchronizeWithDbEntities(loadedEntities);
519     }
520
521     /** Loads database relationships into a DataMap. */
522     public void loadDbRelationships(DataMap map) throws SQLException JavaDoc {
523         Iterator JavaDoc it = dbEntityList.iterator();
524         while (it.hasNext()) {
525             DbEntity pkEntity = (DbEntity) it.next();
526             loadDbRelationships(pkEntity, map);
527         }
528     }
529
530     private void loadDbRelationships(DbEntity pkEntity, DataMap map) throws SQLException JavaDoc {
531         DatabaseMetaData JavaDoc md = getMetaData();
532         String JavaDoc pkEntName = pkEntity.getName();
533
534         // Get all the foreign keys referencing this table
535
ResultSet JavaDoc rs = null;
536
537         try {
538             rs = md.getExportedKeys(
539                     pkEntity.getCatalog(),
540                     pkEntity.getSchema(),
541                     pkEntity.getName());
542         }
543         catch (SQLException JavaDoc cay182Ex) {
544             // Sybase-specific - the line above blows on VIEWS, see CAY-182.
545
logObj.info("Error getting relationships for '"
546                     + pkEntName
547                     + "', ignoring.");
548             return;
549         }
550
551         try {
552             if (!rs.next())
553                 return;
554
555             // these will be initailzed every time a new target entity
556
// is found in the result set (which should be ordered by table name among
557
// other things)
558
DbRelationship forwardRelationship = null;
559             DbRelationship reverseRelationship = null;
560             DbEntity fkEntity = null;
561
562             do {
563                 short keySeq = rs.getShort("KEY_SEQ");
564                 if (keySeq == 1) {
565
566                     if (forwardRelationship != null) {
567                         postprocessMasterDbRelationship(forwardRelationship);
568                         forwardRelationship = null;
569                     }
570
571                     // start new entity
572
String JavaDoc fkEntityName = rs.getString("FKTABLE_NAME");
573
574                     fkEntity = map.getDbEntity(fkEntityName);
575
576                     if (fkEntity == null) {
577                         logObj.info("FK warning: no entity found for name '"
578                                 + fkEntityName
579                                 + "'");
580                     } else if (skippedEntities.contains(pkEntity) && skippedEntities.contains(fkEntity)) {
581                         // cay-479 - don't load relationships between two
582
// skipped entities.
583
continue;
584                     }
585                     else {
586
587                         // init relationship
588
forwardRelationship = new DbRelationship(DbLoader
589                                 .uniqueRelName(pkEntity, fkEntityName, true));
590
591                         forwardRelationship.setSourceEntity(pkEntity);
592                         forwardRelationship.setTargetEntity(fkEntity);
593                         pkEntity.addRelationship(forwardRelationship);
594
595                         reverseRelationship = new DbRelationship(uniqueRelName(
596                                 fkEntity,
597                                 pkEntName,
598                                 false));
599                         reverseRelationship.setToMany(false);
600                         reverseRelationship.setSourceEntity(fkEntity);
601                         reverseRelationship.setTargetEntity(pkEntity);
602                         fkEntity.addRelationship(reverseRelationship);
603                     }
604                 }
605
606                 if (fkEntity != null) {
607                     // Create and append joins
608
String JavaDoc pkName = rs.getString("PKCOLUMN_NAME");
609                     String JavaDoc fkName = rs.getString("FKCOLUMN_NAME");
610
611                     // skip invalid joins...
612
DbAttribute pkAtt = (DbAttribute) pkEntity.getAttribute(pkName);
613                     if (pkAtt == null) {
614                         logObj.info("no attribute for declared primary key: "
615                                 + pkName);
616                         continue;
617                     }
618
619                     DbAttribute fkAtt = (DbAttribute) fkEntity.getAttribute(fkName);
620                     if (fkAtt == null) {
621                         logObj.info("no attribute for declared foreign key: "
622                                 + fkName);
623                         continue;
624                     }
625
626                     forwardRelationship.addJoin(new DbJoin(
627                             forwardRelationship,
628                             pkName,
629                             fkName));
630                     reverseRelationship.addJoin(new DbJoin(
631                             reverseRelationship,
632                             fkName,
633                             pkName));
634                 }
635             } while (rs.next());
636
637             if (forwardRelationship != null) {
638                 postprocessMasterDbRelationship(forwardRelationship);
639                 forwardRelationship = null;
640             }
641
642         }
643         finally {
644             rs.close();
645         }
646     }
647     /**
648      * Detects correct relationship multiplicity and "to dep pk" flag. Only called on
649      * relationships from PK to FK, not the reverse ones.
650      */

651     protected void postprocessMasterDbRelationship(DbRelationship relationship) {
652         boolean toPK = true;
653         List JavaDoc joins = relationship.getJoins();
654
655         Iterator JavaDoc joinsIt = joins.iterator();
656         while (joinsIt.hasNext()) {
657             DbJoin join = (DbJoin) joinsIt.next();
658             if (!join.getTarget().isPrimaryKey()) {
659                 toPK = false;
660                 break;
661             }
662
663         }
664
665         boolean toDependentPK = false;
666         boolean toMany = true;
667
668         if (toPK) {
669             toDependentPK = true;
670             if (((DbEntity) relationship.getTargetEntity()).getPrimaryKey().size() == joins
671                     .size()) {
672                 toMany = false;
673             }
674         }
675
676         // if this is really to-one we need to rename the relationship
677
if (!toMany) {
678             Entity source = relationship.getSourceEntity();
679             source.removeRelationship(relationship.getName());
680             relationship.setName(DbLoader.uniqueRelName(source, relationship
681                     .getTargetEntityName(), false));
682             source.addRelationship(relationship);
683         }
684
685         relationship.setToDependentPK(toDependentPK);
686         relationship.setToMany(toMany);
687     }
688
689     private String JavaDoc[] getDefaultTableTypes() {
690         String JavaDoc viewType = adapter.tableTypeForView();
691         String JavaDoc tableType = adapter.tableTypeForTable();
692
693         // use types that are not null
694
List JavaDoc list = new ArrayList JavaDoc();
695         if (viewType != null) {
696             list.add(viewType);
697         }
698         if (tableType != null) {
699             list.add(tableType);
700         }
701
702         String JavaDoc[] types = new String JavaDoc[list.size()];
703         list.toArray(types);
704         return types;
705     }
706
707     /**
708      * Performs database reverse engineering and generates DataMap that contains default
709      * mapping of the tables and views. By default will include regular tables and views.
710      *
711      * @since 1.0.7
712      */

713     public DataMap loadDataMapFromDB(
714             String JavaDoc schemaName,
715             String JavaDoc tablePattern,
716             DataMap dataMap) throws SQLException JavaDoc {
717
718         String JavaDoc[] types = getDefaultTableTypes();
719         if (types.length == 0) {
720             throw new SQLException JavaDoc("No supported table types found.");
721         }
722
723         return loadDataMapFromDB(schemaName, tablePattern, types, dataMap);
724     }
725
726     /**
727      * Performs database reverse engineering and generates DataMap object that contains
728      * default mapping of the tables and views. Allows to limit types of tables to read.
729      */

730     public DataMap loadDataMapFromDB(
731             String JavaDoc schemaName,
732             String JavaDoc tablePattern,
733             String JavaDoc[] tableTypes,
734             DataMap dataMap) throws SQLException JavaDoc {
735
736         if (tablePattern == null) {
737             tablePattern = WILDCARD;
738         }
739
740         if (!loadDbEntities(
741                 dataMap,
742                 getTables(null, schemaName, tablePattern, tableTypes))) {
743             return dataMap;
744         }
745
746         loadDbRelationships(dataMap);
747         loadObjEntities(dataMap);
748         return dataMap;
749     }
750
751     /**
752      * Loads database stored procedures into the DataMap.
753      * <p>
754      * <i>As of 1.1 there is no boolean property or delegate method to make procedure
755      * loading optional or to implement custom merging logic, so currently this method is
756      * NOT CALLED from "loadDataMapFromDB" and should be invoked explicitly by the user.
757      * </i>
758      * </p>
759      *
760      * @since 1.1
761      */

762     public void loadProceduresFromDB(
763             String JavaDoc schemaPattern,
764             String JavaDoc namePattern,
765             DataMap dataMap) throws SQLException JavaDoc {
766
767         Map JavaDoc procedures = null;
768
769         // get procedures
770
ResultSet JavaDoc rs = getMetaData().getProcedures(null, schemaPattern, namePattern);
771         try {
772             while (rs.next()) {
773                 String JavaDoc name = rs.getString("PROCEDURE_NAME");
774
775                 // TODO: this will be moved to Delegate...
776
if (EXCLUDED_PROCEDURES.contains(name)) {
777                     logObj.info("skipping Cayenne PK procedure: " + name);
778                     continue;
779                 }
780
781                 String JavaDoc catalog = rs.getString("PROCEDURE_CAT");
782                 String JavaDoc schema = rs.getString("PROCEDURE_SCHEM");
783
784                 short type = rs.getShort("PROCEDURE_TYPE");
785
786                 Procedure procedure = new Procedure(name);
787                 procedure.setCatalog(catalog);
788                 procedure.setSchema(schema);
789
790                 switch (type) {
791                     case DatabaseMetaData.procedureNoResult:
792                     case DatabaseMetaData.procedureResultUnknown:
793                         procedure.setReturningValue(false);
794                         break;
795                     case DatabaseMetaData.procedureReturnsResult:
796                         procedure.setReturningValue(true);
797                         break;
798                 }
799
800                 if (procedures == null) {
801                     procedures = new HashMap JavaDoc();
802                 }
803
804                 procedures.put(procedure.getFullyQualifiedName(), procedure);
805             }
806         }
807         finally {
808             rs.close();
809         }
810
811         // if nothing found, return
812
if (procedures == null) {
813             return;
814         }
815
816         // get columns
817
ResultSet JavaDoc columnsRS = getMetaData().getProcedureColumns(
818                 null,
819                 schemaPattern,
820                 namePattern,
821                 null);
822         try {
823             while (columnsRS.next()) {
824
825                 String JavaDoc schema = columnsRS.getString("PROCEDURE_SCHEM");
826                 String JavaDoc name = columnsRS.getString("PROCEDURE_NAME");
827
828                 // TODO: this will be moved to Delegate...
829
if (EXCLUDED_PROCEDURES.contains(name)) {
830                     continue;
831                 }
832
833                 String JavaDoc columnName = columnsRS.getString("COLUMN_NAME");
834                 short type = columnsRS.getShort("COLUMN_TYPE");
835
836                 String JavaDoc key = (schema != null) ? schema + '.' + name : name;
837
838                 // skip ResultSet columns, as they are not described in Cayenne procedures
839
// yet...
840
if (type == DatabaseMetaData.procedureColumnResult) {
841                     logObj.debug("skipping ResultSet column: " + key + "." + columnName);
842                 }
843
844                 Procedure procedure = (Procedure) procedures.get(key);
845
846                 if (procedure == null) {
847                     logObj.info("invalid procedure column, no procedure found: "
848                             + key
849                             + "."
850                             + columnName);
851                     continue;
852                 }
853
854                 ProcedureParameter column = new ProcedureParameter(columnName);
855
856                 if (columnName == null) {
857                     if (type == DatabaseMetaData.procedureColumnReturn) {
858                         logObj.debug("null column name, assuming result column: " + key);
859                         column.setName("_return_value");
860                     }
861                     else {
862                         logObj.info("invalid null column name, skipping column : " + key);
863                         continue;
864                     }
865                 }
866
867                 int columnType = columnsRS.getInt("DATA_TYPE");
868                 int columnSize = columnsRS.getInt("LENGTH");
869
870                 // ignore precision of non-decimal columns
871
int decimalDigits = -1;
872                 if (TypesMapping.isDecimal(columnType)) {
873                     decimalDigits = columnsRS.getShort("SCALE");
874                     if (columnsRS.wasNull()) {
875                         decimalDigits = -1;
876                     }
877                 }
878
879                 switch (type) {
880                     case DatabaseMetaData.procedureColumnIn:
881                         column.setDirection(ProcedureParameter.IN_PARAMETER);
882                         break;
883                     case DatabaseMetaData.procedureColumnInOut:
884                         column.setDirection(ProcedureParameter.IN_OUT_PARAMETER);
885                         break;
886                     case DatabaseMetaData.procedureColumnOut:
887                         column.setDirection(ProcedureParameter.OUT_PARAMETER);
888                         break;
889                     case DatabaseMetaData.procedureColumnReturn:
890                         procedure.setReturningValue(true);
891                         break;
892                 }
893
894                 column.setMaxLength(columnSize);
895                 column.setPrecision(decimalDigits);
896                 column.setProcedure(procedure);
897                 column.setType(columnType);
898                 procedure.addCallParameter(column);
899             }
900         }
901         finally {
902             columnsRS.close();
903         }
904
905         Iterator JavaDoc it = procedures.values().iterator();
906         while (it.hasNext()) {
907             // overwrite existing procedures...
908

909             Procedure procedure = (Procedure) it.next();
910             dataMap.addProcedure(procedure);
911         }
912     }
913 }
914
Popular Tags