1 23 24 29 30 package com.sun.jdo.spi.persistence.generator.database; 31 32 import java.io.IOException ; 33 import java.io.OutputStream ; 34 import java.util.ArrayList ; 35 import java.util.Iterator ; 36 import java.util.HashMap ; 37 import java.util.HashSet ; 38 import java.util.List ; 39 import java.util.Map ; 40 import java.util.Map.Entry; 41 import java.util.Set ; 42 import java.util.ResourceBundle ; 43 44 import org.netbeans.modules.dbschema.*; 45 import org.netbeans.modules.dbschema.util.NameUtil; 46 import com.sun.jdo.api.persistence.model.mapping.*; 47 import com.sun.jdo.api.persistence.model.mapping.impl.*; 48 import com.sun.jdo.api.persistence.model.jdo.*; 49 import com.sun.jdo.api.persistence.model.jdo.impl.*; 50 import com.sun.jdo.api.persistence.model.Model; 51 import com.sun.jdo.api.persistence.model.ModelException; 52 import com.sun.jdo.spi.persistence.utility.JavaTypeHelper; 53 54 import com.sun.jdo.spi.persistence.utility.I18NHelper; 55 import com.sun.jdo.spi.persistence.utility.logging.Logger; 56 57 58 62 public class DatabaseGenerator { 63 64 private static final char DOT = DatabaseGenerationConstants.DOT; 65 66 67 private final Model model; 68 69 70 private final MappingPolicy mappingPolicy; 71 72 74 private final List pcClasses; 75 76 77 private final Map mappingClasses = new HashMap (); 79 80 81 private final SchemaElement schema; 82 83 87 private final String classSuffix; 88 89 90 private static final Logger logger = 91 LogHelperDatabaseGenerator.getLogger(); 92 93 94 private static final ResourceBundle messages = 95 I18NHelper.loadBundle(DatabaseGenerator.class); 96 97 98 101 public class Results { 102 103 private final SchemaElement schema; 104 105 106 private final Set mappingClasses; 111 112 Results(SchemaElement schema, Map mappingClasses) { 113 this.schema = schema; 114 this.mappingClasses = new HashSet (mappingClasses.values()); 115 } 116 117 118 public SchemaElement getSchema() { 119 return schema; 120 } 121 122 123 public Set getMappingClasses() { 124 return mappingClasses; 125 } 126 } 127 128 129 137 public static class NameTuple { 138 139 140 private final String persistenceClassName; 141 142 143 private final String desiredTableName; 144 145 146 private final String hashClassName; 147 148 155 public NameTuple( 156 String persistenceClassName, String desiredTableName, 157 String hashClassName) { 158 159 this.persistenceClassName = persistenceClassName; 160 this.desiredTableName = desiredTableName; 161 this.hashClassName = (hashClassName != null) ? 162 hashClassName : persistenceClassName; 163 } 164 165 170 public NameTuple(String persistenceClassName, String desiredTableName) { 171 this(persistenceClassName, desiredTableName, null); 172 } 173 174 175 public String getPersistenceClassName() { 176 return persistenceClassName; 177 } 178 179 180 public String getHashClassName() { 181 return hashClassName; 182 } 183 184 185 public String getDesiredTableName() { 186 return desiredTableName; 187 } 188 } 189 190 202 private DatabaseGenerator( 203 Model model, List pcClasses, MappingPolicy mappingPolicy, 204 String schemaName, String classSuffix) 205 throws DBException { 206 207 this.model = model; 208 this.pcClasses = pcClasses; 209 this.mappingPolicy = mappingPolicy; 210 this.schema = DBElementFactory.createSchema(schemaName); 211 this.classSuffix = classSuffix; 212 } 213 214 237 public static Results generate( 238 Model model, List pcClasses, MappingPolicy mappingPolicy, 239 String schemaName, String classSuffix, 240 boolean generateMappingClasses) 241 throws DBException, IOException , ModelException { 242 243 DatabaseGenerator generator = new DatabaseGenerator( 244 model, pcClasses, mappingPolicy, 245 schemaName, classSuffix); 246 247 Results rc = generator.generate(); 248 249 mappingPolicy.resetCounter(); 250 251 return rc; 252 } 253 254 263 private Results generate() throws DBException, ModelException { 264 for (Iterator i = pcClasses.iterator(); i.hasNext();) { 265 NameTuple nameTuple = (NameTuple) i.next(); 266 String pcClassName = nameTuple.getPersistenceClassName(); 267 String desiredTableName = nameTuple.getDesiredTableName(); 268 269 PersistenceClassElement pcClass = 270 model.getPersistenceClass(pcClassName); 271 272 String tableName = mappingPolicy.getTableName( 273 desiredTableName, getShortClassName(nameTuple.getHashClassName())); 274 TableElement table = DBElementFactory.createAndAttachTable( 275 schema, tableName); 276 UniqueKeyElement pKey = DBElementFactory.createAndAttachPrimaryKey( 277 table, 278 mappingPolicy.getPrimaryKeyConstraintName( 279 table.getName().getName())); 280 MappingClassElement mappingClass = createMappingClass( 281 pcClass, table); 282 283 PersistenceFieldElement[] fields = pcClass.getFields(); 284 if (fields != null) { 285 for (int j = 0; j < fields.length; j++) { 286 PersistenceFieldElement field = fields[j]; 287 String fieldName = field.getName(); 288 if (!(field instanceof RelationshipElement)) { 289 String columnName = mappingPolicy.getColumnName( 290 desiredTableName, fieldName, tableName); 291 String fieldType = model.getFieldType( 292 pcClassName, fieldName); 293 String fullFieldName = 294 new StringBuffer (desiredTableName) 295 .append(DOT).append(fieldName).toString(); 296 JDBCInfo columnType = 297 DBElementFactory.getColumnType( 298 fullFieldName, 299 fieldType, 300 mappingPolicy); 301 if (logger.isLoggable(Logger.FINEST)) { 302 logger.fine( 303 "DBGenerator.generate: " + tableName + "." + columnName + ": " + columnType.toString()); 306 } 307 ColumnElement column = 308 DBElementFactory.createAndAttachColumn( 309 columnName, table, columnType); 310 MappingFieldElement mappingField = 311 createAndAttachMappingField( 312 fieldName, mappingClass, column); 313 314 if (field.isKey()) { 315 column.setNullable(false); 316 pKey.addColumn(column); 317 pKey.getAssociatedIndex().addColumn(column); 318 mappingClass.getTable(tableName).addKeyColumn( 319 column); 320 } 321 } 322 } 323 } 324 mappingClasses.put(pcClassName, mappingClass); 325 } 326 addRelationships(); 327 328 return new Results(schema, mappingClasses); 329 } 330 331 337 private TableElement getPrimaryTable(MappingClassElement mappingClass) 338 throws DBException { 339 340 List tables = mappingClass.getTables(); 341 342 MappingTableElement tbl = (MappingTableElement) tables.get(0); 343 if (tbl != null) { 344 DBIdentifier tblName = DBIdentifier.create(tbl.getTable()); 345 return schema.getTable(tblName); 346 } else { 347 return null; 348 } 349 } 350 351 358 private MappingClassElement createMappingClass( 359 PersistenceClassElement pcClass, TableElement table) 360 throws ModelException { 361 362 MappingClassElement mappingClass = 363 new MappingClassElementImpl(pcClass); 364 365 mappingClass.setDatabaseRoot(schema); 366 mappingClass.addTable(table); 367 return mappingClass; 368 } 369 370 377 private MappingFieldElement createAndAttachMappingField( 378 String fieldName, MappingClassElement mappingClass, 379 ColumnElement column) throws ModelException { 380 381 MappingFieldElement mappingField = 382 new MappingFieldElementImpl(fieldName, mappingClass); 383 384 mappingClass.addField(mappingField); 385 mappingField.addColumn(column); 386 if (column.isBlobType()) { 387 mappingField.setFetchGroup(MappingFieldElement.GROUP_NONE); 388 } else { 389 mappingField.setFetchGroup(MappingFieldElement.GROUP_DEFAULT); 390 } 391 return mappingField; 392 } 393 394 395 405 private void addMappingRelationship(String relationName, 406 MappingClassElement declaringClass, ForeignKeyElement fkey) 407 throws ModelException { 408 409 MappingRelationshipElement impl = new MappingRelationshipElementImpl( 410 relationName, declaringClass); 411 ColumnPairElement [] pairs = fkey.getColumnPairs(); 412 413 for (int i = 0; i < pairs.length; i++) { 414 ColumnPairElement pair = pairs[i]; 415 impl.addColumn(pair); 416 } 417 declaringClass.addField(impl); 418 } 419 420 430 private void addAssocMappingRelationship(String relationName, 431 MappingClassElement declaringClass, ForeignKeyElement fkeyForeign) 432 throws ModelException { 433 434 MappingRelationshipElement impl = 435 (MappingRelationshipElement) declaringClass.getField( 436 relationName); 437 438 if (null == impl) { 439 impl = new MappingRelationshipElementImpl( 440 relationName, declaringClass); 441 declaringClass.addField(impl); 442 } 443 444 ColumnPairElement [] pairs = fkeyForeign.getColumnPairs(); 446 for (int i = 0; i < pairs.length; i++) { 447 ColumnPairElement pair = pairs[i]; 448 impl.addAssociatedColumn(pair); 449 } 450 } 451 452 468 private void addInverseMappingRelationship(String relationName, 469 MappingClassElement declaringClass, ForeignKeyElement fkey, 470 boolean isJoin) 471 throws ModelException, DBException { 472 473 MappingRelationshipElement impl = 474 (MappingRelationshipElement) declaringClass.getField( 475 relationName); 476 477 if (null == impl) { 479 impl = new MappingRelationshipElementImpl(relationName, 480 declaringClass); 481 declaringClass.addField(impl); 482 } 483 484 TableElement declaringTbl = getPrimaryTable(declaringClass); 485 ColumnPairElement [] pairs = fkey.getColumnPairs(); 486 487 for (int i = 0; i < pairs.length; i++) { 489 ColumnPairElement pair = pairs[i]; 490 ColumnPairElement inversePair = DBElementFactory.createColumnPair( 491 pair.getReferencedColumn(), pair.getLocalColumn(), 492 declaringTbl); 493 494 if (isJoin) { 495 impl.addLocalColumn(inversePair); 496 } else { 497 impl.addColumn(inversePair); 498 } 499 } 500 } 501 502 515 private ForeignKeyElement createRelationship(TableElement srcTable, 516 TableElement relTable, String relName, String inverseRelName, 517 MappingClassElement mappingClass, 518 MappingClassElement relMappingClass, 519 String uniqueId, boolean srcIsJoin) 520 throws DBException, ModelException { 521 522 ForeignKeyElement fKey = DBElementFactory.createAndAttachForeignKey( 523 srcTable, relTable, relName, mappingPolicy, uniqueId); 524 525 if (srcIsJoin) { 526 addInverseMappingRelationship(relName, mappingClass, 527 fKey, true); 528 addAssocMappingRelationship(inverseRelName, relMappingClass, fKey); 529 } else { 530 addMappingRelationship(relName, mappingClass, fKey); 531 addInverseMappingRelationship(inverseRelName, relMappingClass, 532 fKey, false); 533 } 534 return fKey; 535 } 536 537 543 private void addRelationships() 544 throws DBException, ModelException { 545 if (logger.isLoggable(Logger.FINE)) { 546 logger.fine("add relationship"); } 548 549 Map relationFKey = new HashMap (); 550 551 List deferredRelationships = new ArrayList (); 556 557 for (Iterator i = mappingClasses.values().iterator(); i.hasNext();) { 558 MappingClassElement mappingClass = (MappingClassElement) i.next(); 559 String pcClassName = mappingClass.getName(); 560 PersistenceClassElement pcClass = 561 model.getPersistenceClass(pcClassName); 562 validateModel(pcClass, "pcClass", pcClassName); TableElement sourceTable = getPrimaryTable(mappingClass); 564 validateModel(sourceTable, "sourceTable", pcClassName); 566 String uniqueId = getShortClassName(pcClassName); 568 int want = 8; int end = uniqueId.length(); 570 int start = want > end ? 0 : end - want; 571 uniqueId = uniqueId.substring(start, end); 572 573 RelationshipElement [] rels = pcClass.getRelationships(); 574 if (rels != null) { 575 for (int j = 0; j < rels.length; j++) { 576 577 RelationshipElement relation = rels[j]; 579 String relationName = relation.getName(); 580 int upperBound = relation.getUpperBound(); 581 582 String inverseRelName = 584 relation.getInverseRelationshipName(); 585 validateModel(inverseRelName, 586 "inverseRelName", relationName); String relClassName = model.getRelatedClass(relation); 588 validateModel(relClassName, 589 "relClassName", relationName); 591 MappingClassElement relMappingClass = 593 (MappingClassElement) mappingClasses.get(relClassName); 594 validateModel(relMappingClass, 595 "relMappingClass", relClassName); PersistenceClassElement relClass = 597 model.getPersistenceClass(relClassName); 598 validateModel(relClass, 599 "relClass", relClassName); RelationshipElement inverseRelation = 601 relClass.getRelationship(inverseRelName); 602 validateModel(inverseRelation, 603 "inverseRelation", inverseRelName); TableElement relTable = getPrimaryTable(relMappingClass); 605 validateModel(relTable, 606 "relTable", relClassName); int relUpperBound = inverseRelation.getUpperBound(); 608 609 if (logger.isLoggable(Logger.FINE)) { 610 logger.fine( 611 "Before adding relationship:" + getTblInfo("sourceTable", sourceTable, relationName) + getTblInfo("relTable", relTable, inverseRelName)); } 615 616 618 if ((upperBound > 1) && (relUpperBound > 1)) { 619 if (logger.isLoggable(Logger.FINE)) { 621 logger.fine("M-N relationship"); } 623 624 ForeignKeyElement fKey = getMappedForeignKey( 625 relation, inverseRelation, relationFKey); 626 if (fKey == null) { 627 TableElement joinTable = 628 DBElementFactory.createAndAttachTable( 629 schema, 630 mappingPolicy.getJoinTableName( 631 sourceTable.getName().getName(), 632 relTable.getName().getName())); 633 fKey = createRelationship( 634 joinTable, sourceTable, relationName, 635 inverseRelName, mappingClass, 636 relMappingClass, uniqueId, true); 637 relationFKey.put(relation, fKey); 638 ForeignKeyElement fKey2 = createRelationship( 639 joinTable, relTable, inverseRelName, 640 relationName, relMappingClass, 641 mappingClass, uniqueId, true); 642 relationFKey.put(inverseRelation, fKey2); 643 } 644 645 } else if ((upperBound > 1) && (relUpperBound == 1)) { 646 651 if (logger.isLoggable(Logger.FINE)) { 652 logger.fine("M-1 relationship: skip"); } 654 655 } else if ((upperBound == 1) && (relUpperBound >1)) { 656 659 if (logger.isLoggable(Logger.FINE)) { 660 logger.fine("1-M relationship"); } 662 663 ForeignKeyElement fKey = getMappedForeignKey(relation, 664 inverseRelation, relationFKey); 665 if (fKey == null) { 666 fKey = createRelationship(sourceTable, relTable, 667 relationName, inverseRelName, mappingClass, 668 relMappingClass, uniqueId, false); 669 relationFKey.put(relation, fKey); 670 } 671 672 } else if ((upperBound == 1) && (relUpperBound == 1)) { 673 679 ForeignKeyElement fKey = getMappedForeignKey(relation, 680 inverseRelation, relationFKey); 681 if (fKey == null) { 682 if (relation.getDeleteAction() == 683 RelationshipElement.CASCADE_ACTION) { 684 if (logger.isLoggable(Logger.FINE)) { 685 logger.fine("1-1 relationship: cascade(this)"); } 687 fKey = createRelationship( 688 sourceTable, relTable, relationName, 689 inverseRelName, mappingClass, 690 relMappingClass, uniqueId, false); 691 relationFKey.put(relation, fKey); 692 } else if (inverseRelation.getDeleteAction() == 693 RelationshipElement.CASCADE_ACTION) { 694 if (logger.isLoggable(Logger.FINE)) { 695 logger.fine("1-1 relationship: cascade(inverse)"); } 697 fKey = createRelationship( 698 relTable, sourceTable, 699 inverseRelName, relationName, 700 relMappingClass, mappingClass, 701 uniqueId, false); 702 relationFKey.put(inverseRelation, fKey); 703 } else { 704 if (logger.isLoggable(Logger.FINE)) { 705 logger.fine("1-1 relationship: defer"); } 707 deferredRelationships.add( 708 new DeferredRelationship( 709 relation, inverseRelation, 710 sourceTable, relTable, 711 relationName, inverseRelName, 712 mappingClass, relMappingClass, 713 uniqueId)); 714 } 715 } 716 } 717 if (logger.isLoggable(Logger.FINE)) { 718 logger.fine( 719 "After adding relationship:" + getTblInfo("sourceTable", sourceTable, relationName) + getTblInfo("relTable", relTable, inverseRelName)); } 723 } 724 } 725 } 726 727 if (deferredRelationships.size() > 0) { 728 addDeferredRelationships(deferredRelationships, relationFKey); 729 } 730 } 731 732 740 private void addDeferredRelationships( 741 List deferredRelationships, Map relationFKey) 742 throws DBException, ModelException { 743 744 for (Iterator i = deferredRelationships.iterator(); i.hasNext();) { 745 DeferredRelationship dr = (DeferredRelationship)i.next(); 746 747 RelationshipElement relation = dr.getRelation(); 748 RelationshipElement inverseRelation = dr.getInverseRelation(); 749 750 ForeignKeyElement fKey = 751 getMappedForeignKey(relation, inverseRelation, relationFKey); 752 753 if (fKey == null) { 755 756 TableElement sourceTable = dr.getSourceTable(); 757 TableElement relTable = dr.getRelTable(); 758 759 String relationName = dr.getRelationName(); 760 String inverseRelName = dr.getInverseRelName(); 761 762 MappingClassElement mappingClass = dr.getMappingClass(); 763 MappingClassElement relMappingClass = dr.getRelMappingClass(); 764 765 String uniqueId = dr.getUniqueId(); 766 767 ForeignKeyElement keys[] = sourceTable.getForeignKeys(); 770 if (null != keys && keys.length > 0) { 771 fKey = createRelationship( 772 sourceTable, relTable, 773 relationName, inverseRelName, 774 mappingClass, relMappingClass, 775 uniqueId, false); 776 if (logger.isLoggable(Logger.FINE)) { 777 logger.fine( 778 "1-1 deferred relationship (this)" + getTblInfo("sourceTable", sourceTable, relationName) + getTblInfo("relTable", relTable, inverseRelName)); } 782 } else { 783 fKey = createRelationship( 784 relTable, sourceTable, 785 inverseRelName, relationName, 786 relMappingClass, mappingClass, 787 uniqueId, false); 788 if (logger.isLoggable(Logger.FINE)) { 789 logger.fine( 790 "1-1 deferred relationship (inverse)" + getTblInfo("sourceTable", sourceTable, relationName) + getTblInfo("relTable", relTable, inverseRelName)); } 794 } 795 relationFKey.put(relation, fKey); 796 } 797 } 798 } 799 800 801 805 static class DeferredRelationship { 806 private final RelationshipElement relation; 807 private final RelationshipElement inverseRelation; 808 private final TableElement sourceTable; 809 private final TableElement relTable; 810 private final String relationName; 811 private final String inverseRelName; 812 private final MappingClassElement mappingClass; 813 private final MappingClassElement relMappingClass; 814 private final String uniqueId; 815 816 817 DeferredRelationship(RelationshipElement relation, 818 RelationshipElement inverseRelation, 819 TableElement sourceTable, 820 TableElement relTable, 821 String relationName, 822 String inverseRelName, 823 MappingClassElement mappingClass, 824 MappingClassElement relMappingClass, 825 String uniqueId) { 826 827 this.relation = relation; 828 this.inverseRelation = inverseRelation; 829 this.sourceTable = sourceTable; 830 this.relTable = relTable; 831 this.relationName = relationName; 832 this.inverseRelName = inverseRelName; 833 this.mappingClass = mappingClass; 834 this.relMappingClass = relMappingClass; 835 this.uniqueId = uniqueId; 836 } 837 838 RelationshipElement getRelation() { return relation; } 839 RelationshipElement getInverseRelation() { return inverseRelation; } 840 841 TableElement getSourceTable() { return sourceTable; } 842 TableElement getRelTable() { return relTable; } 843 844 String getRelationName() { return relationName; } 845 String getInverseRelName() { return inverseRelName; } 846 847 MappingClassElement getMappingClass() { return mappingClass; } 848 MappingClassElement getRelMappingClass() { return relMappingClass; } 849 850 String getUniqueId() { return uniqueId; } 851 } 852 853 854 855 862 private ForeignKeyElement getMappedForeignKey( 863 RelationshipElement relation, RelationshipElement inverseRelation, 864 Map relationFKey) { 865 866 ForeignKeyElement fkey = 867 (ForeignKeyElement) relationFKey.get(relation); 868 869 if (fkey == null) { 870 return (ForeignKeyElement) relationFKey.get(inverseRelation); 871 } else { 872 return fkey; 873 } 874 } 875 876 883 private String getShortClassName(String className) { 884 String shortName = JavaTypeHelper.getShortClassName(className); 885 886 if ((classSuffix != null) && (!shortName.equals(classSuffix))) { 887 int index = shortName.lastIndexOf(classSuffix); 888 if (index != -1) { 889 shortName = shortName.substring(0, index); 890 } 891 } 892 return shortName; 893 } 894 895 903 private void validateModel(Object o, 904 String failedItem, 905 String accessor) throws ModelException { 906 if (null == o) { 907 String msg = I18NHelper.getMessage( 908 messages, 909 "EXC_InvalidRelationshipMapping", failedItem, 911 accessor); 912 logger.log(Logger.SEVERE, msg); 913 throw new ModelException(msg); 914 } 915 } 916 917 924 private static String getTblInfo(String tblName, TableElement tbl, String relName) { 925 int numFK = tbl.getForeignKeys().length; 926 ForeignKeyElement fk = null; 927 if (numFK > 0) { 928 fk = tbl.getForeignKeys()[0]; 929 } 930 return " " + tblName + "=" + tbl.toString() 931 + ", # keys=" + numFK + ", 1st key=" + fk + "; relationship Name=" + relName; } 935 } 936 | Popular Tags |