KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56
57 package org.objectstyle.cayenne.access;
58
59 import java.sql.Connection JavaDoc;
60 import java.sql.DatabaseMetaData JavaDoc;
61 import java.sql.ResultSet JavaDoc;
62 import java.sql.SQLException JavaDoc;
63 import java.util.ArrayList JavaDoc;
64 import java.util.Arrays JavaDoc;
65 import java.util.Collection JavaDoc;
66 import java.util.HashMap JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.List JavaDoc;
69 import java.util.Map JavaDoc;
70
71 import org.apache.log4j.Logger;
72 import org.objectstyle.ashwood.dbutil.Table;
73 import org.objectstyle.cayenne.CayenneException;
74 import org.objectstyle.cayenne.dba.DbAdapter;
75 import org.objectstyle.cayenne.dba.TypesMapping;
76 import org.objectstyle.cayenne.map.DataMap;
77 import org.objectstyle.cayenne.map.DbAttribute;
78 import org.objectstyle.cayenne.map.DbEntity;
79 import org.objectstyle.cayenne.map.DbJoin;
80 import org.objectstyle.cayenne.map.DbRelationship;
81 import org.objectstyle.cayenne.map.Entity;
82 import org.objectstyle.cayenne.map.ObjEntity;
83 import org.objectstyle.cayenne.map.Procedure;
84 import org.objectstyle.cayenne.map.ProcedureParameter;
85 import org.objectstyle.cayenne.util.EntityMergeSupport;
86 import org.objectstyle.cayenne.util.NameConverter;
87
88 /**
89  * Utility class that does reverse engineering of the database. It can create DataMaps
90  * using database meta data obtained via JDBC driver.
91  *
92  * @author Michael Shengaout
93  * @author Andrei Adamchik
94  */

95 public class DbLoader {
96
97     private static Logger logObj = Logger.getLogger(DbLoader.class);
98
99     // TODO: remove this hardcoded stuff once delegate starts to support procedure
100
// loading...
101
private static final Collection JavaDoc EXCLUDED_PROCEDURES = Arrays.asList(new Object JavaDoc[] {
102             "auto_pk_for_table", "auto_pk_for_table;1" /*
103                                                         * the last name is some Mac OS X
104                                                         * Sybase artifact
105                                                         */

106     });
107
108     public static final String JavaDoc WILDCARD = "%";
109
110     /** List of db entities to process. */
111     private List JavaDoc dbEntityList = new ArrayList JavaDoc();
112
113     /** Creates default name for loaded relationship */
114     private static String JavaDoc defaultRelName(String JavaDoc dstName, boolean toMany) {
115         String JavaDoc uglyName = (toMany) ? dstName + "_ARRAY" : "to_" + dstName;
116         return NameConverter.undescoredToJava(uglyName, false);
117     }
118
119     /** Creates a unique name for loaded relationship on the given entity. */
120     private static String JavaDoc uniqueRelName(Entity entity, String JavaDoc dstName, boolean toMany) {
121         int currentSuffix = 1;
122         String JavaDoc baseRelName = defaultRelName(dstName, toMany);
123         String JavaDoc relName = baseRelName;
124
125         while (entity.getRelationship(relName) != null) {
126             relName = baseRelName + currentSuffix;
127             currentSuffix++;
128         }
129         return relName;
130     }
131
132     protected Connection JavaDoc con;
133     protected DbAdapter adapter;
134     protected DatabaseMetaData JavaDoc metaData;
135     protected DbLoaderDelegate delegate;
136
137     /** Creates new DbLoader. */
138     public DbLoader(Connection JavaDoc con, DbAdapter adapter, DbLoaderDelegate delegate) {
139         this.adapter = adapter;
140         this.con = con;
141         this.delegate = delegate;
142     }
143
144     /** Returns DatabaseMetaData object associated with this DbLoader. */
145     public DatabaseMetaData JavaDoc getMetaData() throws SQLException JavaDoc {
146         if (null == metaData)
147             metaData = con.getMetaData();
148         return metaData;
149     }
150
151     /**
152      * Returns database connection used by this DbLoader.
153      */

154     public Connection JavaDoc getCon() {
155         return con;
156     }
157
158     /**
159      * Returns DbAdapter associated with this DbLoader.
160      *
161      * @since 1.1
162      */

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

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

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

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

240     public List JavaDoc getTables(
241             String JavaDoc catalog,
242             String JavaDoc schemaPattern,
243             String JavaDoc tableNamePattern,
244             String JavaDoc[] types) throws SQLException JavaDoc {
245
246         List JavaDoc tables = new ArrayList JavaDoc();
247
248         if (logObj.isDebugEnabled()) {
249             logObj.debug("Read tables: catalog="
250                     + catalog
251                     + ", schema="
252                     + schemaPattern
253                     + ", tableNames="
254                     + tableNamePattern);
255
256             if (types != null && types.length > 0) {
257                 for (int i = 0; i < types.length; i++) {
258                     logObj.debug("Read tables: table type=" + types[i]);
259                 }
260             }
261         }
262
263         ResultSet JavaDoc rs = getMetaData().getTables(
264                 catalog,
265                 schemaPattern,
266                 tableNamePattern,
267                 types);
268
269         try {
270             while (rs.next()) {
271                 String JavaDoc cat = rs.getString("TABLE_CAT");
272                 String JavaDoc schema = rs.getString("TABLE_SCHEM");
273                 String JavaDoc name = rs.getString("TABLE_NAME");
274                 Table info = new Table(cat, schema, name);
275                 tables.add(info);
276             }
277         }
278         finally {
279             rs.close();
280         }
281         return tables;
282     }
283
284     /**
285      * Loads dbEntities for the specified tables.
286      *
287      * @param map DataMap to be populated with DbEntities.
288      * @param tables The list of org.objectstyle.ashwood.dbutil.Table objects for which
289      * DbEntities must be created.
290      * @return false if loading must be immediately aborted.
291      */

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

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

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

606     protected void postprocessMasterDbRelationship(DbRelationship relationship) {
607         boolean toPK = true;
608         List JavaDoc joins = relationship.getJoins();
609
610         Iterator JavaDoc joinsIt = joins.iterator();
611         while (joinsIt.hasNext()) {
612             DbJoin join = (DbJoin) joinsIt.next();
613             if (!join.getTarget().isPrimaryKey()) {
614                 toPK = false;
615                 break;
616             }
617
618         }
619
620         boolean toDependentPK = false;
621         boolean toMany = true;
622
623         if (toPK) {
624             toDependentPK = true;
625             if (((DbEntity) relationship.getTargetEntity()).getPrimaryKey().size() == joins
626                     .size()) {
627                 toMany = false;
628             }
629         }
630
631         // if this is really to-one we need to rename the relationship
632
if (!toMany) {
633             Entity source = relationship.getSourceEntity();
634             source.removeRelationship(relationship.getName());
635             relationship.setName(DbLoader.uniqueRelName(source, relationship
636                     .getTargetEntityName(), false));
637             source.addRelationship(relationship);
638         }
639
640         relationship.setToDependentPK(toDependentPK);
641         relationship.setToMany(toMany);
642     }
643
644     private String JavaDoc[] getDefaultTableTypes() {
645         String JavaDoc viewType = adapter.tableTypeForView();
646         String JavaDoc tableType = adapter.tableTypeForTable();
647
648         // use types that are not null
649
List JavaDoc list = new ArrayList JavaDoc();
650         if (viewType != null) {
651             list.add(viewType);
652         }
653         if (tableType != null) {
654             list.add(tableType);
655         }
656
657         String JavaDoc[] types = new String JavaDoc[list.size()];
658         list.toArray(types);
659         return types;
660     }
661
662     /**
663      * Performs database reverse engineering and generates DataMap that contains default
664      * mapping of the tables and views. By default will include regular tables and views.
665      *
666      * @since 1.0.7
667      */

668     public DataMap loadDataMapFromDB(
669             String JavaDoc schemaName,
670             String JavaDoc tablePattern,
671             DataMap dataMap) throws SQLException JavaDoc {
672
673         String JavaDoc[] types = getDefaultTableTypes();
674         if (types.length == 0) {
675             throw new SQLException JavaDoc("No supported table types found.");
676         }
677
678         return loadDataMapFromDB(schemaName, tablePattern, types, dataMap);
679     }
680
681     /**
682      * Performs database reverse engineering and generates DataMap object that contains
683      * default mapping of the tables and views. Allows to limit types of tables to read.
684      */

685     public DataMap loadDataMapFromDB(
686             String JavaDoc schemaName,
687             String JavaDoc tablePattern,
688             String JavaDoc[] tableTypes,
689             DataMap dataMap) throws SQLException JavaDoc {
690
691         if (tablePattern == null) {
692             tablePattern = WILDCARD;
693         }
694
695         if (!loadDbEntities(
696                 dataMap,
697                 getTables(null, schemaName, tablePattern, tableTypes))) {
698             return dataMap;
699         }
700
701         loadDbRelationships(dataMap);
702         loadObjEntities(dataMap);
703         return dataMap;
704     }
705
706     /**
707      * Loads database stored procedures into the DataMap.
708      * <p>
709      * <i>As of 1.1 there is no boolean property or delegate method to make procedure
710      * loading optional or to implement custom merging logic, so currently this method is
711      * NOT CALLED from "loadDataMapFromDB" and should be invoked explicitly by the user.
712      * </i>
713      * </p>
714      *
715      * @since 1.1
716      */

717     public void loadProceduresFromDB(
718             String JavaDoc schemaPattern,
719             String JavaDoc namePattern,
720             DataMap dataMap) throws SQLException JavaDoc {
721
722         Map JavaDoc procedures = null;
723
724         // get procedures
725
ResultSet JavaDoc rs = getMetaData().getProcedures(null, schemaPattern, namePattern);
726         try {
727             while (rs.next()) {
728                 String JavaDoc name = rs.getString("PROCEDURE_NAME");
729
730                 // TODO: this will be moved to Delegate...
731
if (EXCLUDED_PROCEDURES.contains(name)) {
732                     logObj.info("skipping Cayenne PK procedure: " + name);
733                     continue;
734                 }
735
736                 String JavaDoc catalog = rs.getString("PROCEDURE_CAT");
737                 String JavaDoc schema = rs.getString("PROCEDURE_SCHEM");
738
739                 short type = rs.getShort("PROCEDURE_TYPE");
740
741                 Procedure procedure = new Procedure(name);
742                 procedure.setCatalog(catalog);
743                 procedure.setSchema(schema);
744
745                 switch (type) {
746                     case DatabaseMetaData.procedureNoResult:
747                     case DatabaseMetaData.procedureResultUnknown:
748                         procedure.setReturningValue(false);
749                         break;
750                     case DatabaseMetaData.procedureReturnsResult:
751                         procedure.setReturningValue(true);
752                         break;
753                 }
754
755                 if (procedures == null) {
756                     procedures = new HashMap JavaDoc();
757                 }
758
759                 procedures.put(procedure.getFullyQualifiedName(), procedure);
760             }
761         }
762         finally {
763             rs.close();
764         }
765
766         // if nothing found, return
767
if (procedures == null) {
768             return;
769         }
770
771         // get columns
772
ResultSet JavaDoc columnsRS = getMetaData().getProcedureColumns(
773                 null,
774                 schemaPattern,
775                 namePattern,
776                 null);
777         try {
778             while (columnsRS.next()) {
779
780                 String JavaDoc schema = columnsRS.getString("PROCEDURE_SCHEM");
781                 String JavaDoc name = columnsRS.getString("PROCEDURE_NAME");
782
783                 // TODO: this will be moved to Delegate...
784
if (EXCLUDED_PROCEDURES.contains(name)) {
785                     continue;
786                 }
787
788                 String JavaDoc columnName = columnsRS.getString("COLUMN_NAME");
789                 short type = columnsRS.getShort("COLUMN_TYPE");
790
791                 String JavaDoc key = (schema != null) ? schema + '.' + name : name;
792
793                 // skip ResultSet columns, as they are not described in Cayenne procedures
794
// yet...
795
if (type == DatabaseMetaData.procedureColumnResult) {
796                     logObj.debug("skipping ResultSet column: " + key + "." + columnName);
797                 }
798
799                 Procedure procedure = (Procedure) procedures.get(key);
800
801                 if (procedure == null) {
802                     logObj.info("invalid procedure column, no procedure found: "
803                             + key
804                             + "."
805                             + columnName);
806                     continue;
807                 }
808
809                 ProcedureParameter column = new ProcedureParameter(columnName);
810
811                 if (columnName == null) {
812                     if (type == DatabaseMetaData.procedureColumnReturn) {
813                         logObj.debug("null column name, assuming result column: " + key);
814                         column.setName("_return_value");
815                     }
816                     else {
817                         logObj.info("invalid null column name, skipping column : " + key);
818                         continue;
819                     }
820                 }
821
822                 int columnType = columnsRS.getInt("DATA_TYPE");
823                 int columnSize = columnsRS.getInt("LENGTH");
824
825                 // ignore precision of non-decimal columns
826
int decimalDigits = -1;
827                 if (TypesMapping.isDecimal(columnType)) {
828                     decimalDigits = columnsRS.getShort("SCALE");
829                     if (columnsRS.wasNull()) {
830                         decimalDigits = -1;
831                     }
832                 }
833
834                 switch (type) {
835                     case DatabaseMetaData.procedureColumnIn:
836                         column.setDirection(ProcedureParameter.IN_PARAMETER);
837                         break;
838                     case DatabaseMetaData.procedureColumnInOut:
839                         column.setDirection(ProcedureParameter.IN_OUT_PARAMETER);
840                         break;
841                     case DatabaseMetaData.procedureColumnOut:
842                         column.setDirection(ProcedureParameter.OUT_PARAMETER);
843                         break;
844                     case DatabaseMetaData.procedureColumnReturn:
845                         procedure.setReturningValue(true);
846                         break;
847                 }
848
849                 column.setMaxLength(columnSize);
850                 column.setPrecision(decimalDigits);
851                 column.setProcedure(procedure);
852                 column.setType(columnType);
853                 procedure.addCallParameter(column);
854             }
855         }
856         finally {
857             columnsRS.close();
858         }
859
860         Iterator JavaDoc it = procedures.values().iterator();
861         while (it.hasNext()) {
862             // overwrite existing procedures...
863

864             Procedure procedure = (Procedure) it.next();
865             dataMap.addProcedure(procedure);
866         }
867     }
868 }
Popular Tags