1 56 57 package org.objectstyle.cayenne.access; 58 59 import java.sql.Connection ; 60 import java.sql.DatabaseMetaData ; 61 import java.sql.ResultSet ; 62 import java.sql.SQLException ; 63 import java.util.ArrayList ; 64 import java.util.Arrays ; 65 import java.util.Collection ; 66 import java.util.HashMap ; 67 import java.util.Iterator ; 68 import java.util.List ; 69 import java.util.Map ; 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 95 public class DbLoader { 96 97 private static Logger logObj = Logger.getLogger(DbLoader.class); 98 99 private static final Collection EXCLUDED_PROCEDURES = Arrays.asList(new Object [] { 102 "auto_pk_for_table", "auto_pk_for_table;1" 106 }); 107 108 public static final String WILDCARD = "%"; 109 110 111 private List dbEntityList = new ArrayList (); 112 113 114 private static String defaultRelName(String dstName, boolean toMany) { 115 String uglyName = (toMany) ? dstName + "_ARRAY" : "to_" + dstName; 116 return NameConverter.undescoredToJava(uglyName, false); 117 } 118 119 120 private static String uniqueRelName(Entity entity, String dstName, boolean toMany) { 121 int currentSuffix = 1; 122 String baseRelName = defaultRelName(dstName, toMany); 123 String relName = baseRelName; 124 125 while (entity.getRelationship(relName) != null) { 126 relName = baseRelName + currentSuffix; 127 currentSuffix++; 128 } 129 return relName; 130 } 131 132 protected Connection con; 133 protected DbAdapter adapter; 134 protected DatabaseMetaData metaData; 135 protected DbLoaderDelegate delegate; 136 137 138 public DbLoader(Connection con, DbAdapter adapter, DbLoaderDelegate delegate) { 139 this.adapter = adapter; 140 this.con = con; 141 this.delegate = delegate; 142 } 143 144 145 public DatabaseMetaData getMetaData() throws SQLException { 146 if (null == metaData) 147 metaData = con.getMetaData(); 148 return metaData; 149 } 150 151 154 public Connection getCon() { 155 return con; 156 } 157 158 163 public DbAdapter getAdapter() { 164 return adapter; 165 } 166 167 172 public List getCatalogs() throws SQLException { 173 List catalogs = new ArrayList (); 174 ResultSet rs = getMetaData().getCatalogs(); 175 176 try { 177 while (rs.next()) { 178 String catalog_name = rs.getString(1); 179 catalogs.add(catalog_name); 180 } 181 } 182 finally { 183 rs.close(); 184 } 185 return catalogs; 186 } 187 188 193 public List getSchemas() throws SQLException { 194 List schemas = new ArrayList (); 195 ResultSet rs = getMetaData().getSchemas(); 196 197 try { 198 while (rs.next()) { 199 String schema_name = rs.getString(1); 200 schemas.add(schema_name); 201 } 202 } 203 finally { 204 rs.close(); 205 } 206 return schemas; 207 } 208 209 215 public List getTableTypes() throws SQLException { 216 List types = new ArrayList (); 217 ResultSet 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 240 public List getTables( 241 String catalog, 242 String schemaPattern, 243 String tableNamePattern, 244 String [] types) throws SQLException { 245 246 List tables = new ArrayList (); 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 rs = getMetaData().getTables( 264 catalog, 265 schemaPattern, 266 tableNamePattern, 267 types); 268 269 try { 270 while (rs.next()) { 271 String cat = rs.getString("TABLE_CAT"); 272 String schema = rs.getString("TABLE_SCHEM"); 273 String 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 292 public boolean loadDbEntities(DataMap map, List tables) throws SQLException { 293 this.dbEntityList = new ArrayList (); 294 295 Iterator iter = tables.iterator(); 296 while (iter.hasNext()) { 297 Table table = (Table) iter.next(); 298 299 DbEntity oldEnt = map.getDbEntity(table.getName()); 302 if (oldEnt != null) { 303 if (delegate == null) { 304 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 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 ResultSet rs = getMetaData().getColumns( 334 table.getCatalog(), 335 table.getSchema(), 336 table.getName(), 337 "%"); 338 339 try { 340 while (rs.next()) { 341 347 String 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 String 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 typeName = rs.getString("TYPE_NAME"); 362 363 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 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 if (delegate != null) { 392 delegate.dbEntityAdded(dbEntity); 393 } 394 395 if (map.getDbEntity(table.getName()) == dbEntity) { 399 this.dbEntityList.add(dbEntity); 400 } 401 } 402 403 Iterator i = map.getDbEntities().iterator(); 405 while (i.hasNext()) { 406 DbEntity dbEntity = (DbEntity) i.next(); 407 String tableName = dbEntity.getName(); 408 ResultSet rs = metaData.getPrimaryKeys(null, dbEntity.getSchema(), tableName); 409 410 try { 411 while (rs.next()) { 412 String keyName = rs.getString(4); 413 DbAttribute attribute = (DbAttribute) dbEntity.getAttribute(keyName); 414 415 if (attribute != null) { 416 attribute.setPrimaryKey(true); 417 } 418 else { 419 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 437 public void loadObjEntities(DataMap map) { 438 439 Iterator dbEntities = dbEntityList.iterator(); 440 if (!dbEntities.hasNext()) { 441 return; 442 } 443 444 List loadedEntities = new ArrayList (dbEntityList.size()); 445 446 while (dbEntities.hasNext()) { 448 DbEntity dbEntity = (DbEntity) dbEntities.next(); 449 450 Collection existing = map.getMappedEntities(dbEntity); 452 if (existing.size() > 0) { 453 loadedEntities.addAll(existing); 454 continue; 455 } 456 457 String objEntityName = NameConverter.undescoredToJava( 458 dbEntity.getName(), 459 true); 460 String 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 if (delegate != null) { 475 delegate.objEntityAdded(objEntity); 476 } 477 } 478 479 new EntityMergeSupport(map).synchronizeWithDbEntities(loadedEntities); 481 } 482 483 484 public void loadDbRelationships(DataMap map) throws SQLException { 485 DatabaseMetaData md = getMetaData(); 486 Iterator it = dbEntityList.iterator(); 487 while (it.hasNext()) { 488 DbEntity pkEntity = (DbEntity) it.next(); 489 String pkEntName = pkEntity.getName(); 490 491 ResultSet rs = null; 493 494 try { 495 rs = md.getExportedKeys( 496 pkEntity.getCatalog(), 497 pkEntity.getSchema(), 498 pkEntity.getName()); 499 } 500 catch (SQLException cay182Ex) { 501 logObj.info("Error getting relationships for '" 503 + pkEntName 504 + "', ignoring."); 505 continue; 506 } 507 508 try { 509 if (!rs.next()) 510 continue; 511 512 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 String 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 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 String pkName = rs.getString("PKCOLUMN_NAME"); 562 String fkName = rs.getString("FKCOLUMN_NAME"); 563 564 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 606 protected void postprocessMasterDbRelationship(DbRelationship relationship) { 607 boolean toPK = true; 608 List joins = relationship.getJoins(); 609 610 Iterator 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 (!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 [] getDefaultTableTypes() { 645 String viewType = adapter.tableTypeForView(); 646 String tableType = adapter.tableTypeForTable(); 647 648 List list = new ArrayList (); 650 if (viewType != null) { 651 list.add(viewType); 652 } 653 if (tableType != null) { 654 list.add(tableType); 655 } 656 657 String [] types = new String [list.size()]; 658 list.toArray(types); 659 return types; 660 } 661 662 668 public DataMap loadDataMapFromDB( 669 String schemaName, 670 String tablePattern, 671 DataMap dataMap) throws SQLException { 672 673 String [] types = getDefaultTableTypes(); 674 if (types.length == 0) { 675 throw new SQLException ("No supported table types found."); 676 } 677 678 return loadDataMapFromDB(schemaName, tablePattern, types, dataMap); 679 } 680 681 685 public DataMap loadDataMapFromDB( 686 String schemaName, 687 String tablePattern, 688 String [] tableTypes, 689 DataMap dataMap) throws SQLException { 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 717 public void loadProceduresFromDB( 718 String schemaPattern, 719 String namePattern, 720 DataMap dataMap) throws SQLException { 721 722 Map procedures = null; 723 724 ResultSet rs = getMetaData().getProcedures(null, schemaPattern, namePattern); 726 try { 727 while (rs.next()) { 728 String name = rs.getString("PROCEDURE_NAME"); 729 730 if (EXCLUDED_PROCEDURES.contains(name)) { 732 logObj.info("skipping Cayenne PK procedure: " + name); 733 continue; 734 } 735 736 String catalog = rs.getString("PROCEDURE_CAT"); 737 String 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 (); 757 } 758 759 procedures.put(procedure.getFullyQualifiedName(), procedure); 760 } 761 } 762 finally { 763 rs.close(); 764 } 765 766 if (procedures == null) { 768 return; 769 } 770 771 ResultSet columnsRS = getMetaData().getProcedureColumns( 773 null, 774 schemaPattern, 775 namePattern, 776 null); 777 try { 778 while (columnsRS.next()) { 779 780 String schema = columnsRS.getString("PROCEDURE_SCHEM"); 781 String name = columnsRS.getString("PROCEDURE_NAME"); 782 783 if (EXCLUDED_PROCEDURES.contains(name)) { 785 continue; 786 } 787 788 String columnName = columnsRS.getString("COLUMN_NAME"); 789 short type = columnsRS.getShort("COLUMN_TYPE"); 790 791 String key = (schema != null) ? schema + '.' + name : name; 792 793 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 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 it = procedures.values().iterator(); 861 while (it.hasNext()) { 862 864 Procedure procedure = (Procedure) it.next(); 865 dataMap.addProcedure(procedure); 866 } 867 } 868 } | Popular Tags |