DatabaseGeneratorpublic class DatabaseGenerator extends Object This class generates a database schema and a Map of mapping classes from a
set of JDO classes. |
Fields Summary |
---|
private static final char | DOT | private final com.sun.jdo.api.persistence.model.Model | modelHolds Java type information | private final MappingPolicy | mappingPolicyProvides information on how database info should be generated | private final List | pcClassesList of NameTuple objects which holds persistence class name,
desired table name and hash class name for database generation. | private final Map | mappingClassesMap from persistence-capable class names to MappingClassElement's. | private final SchemaElement | schemaGenerated database schema. | private final String | classSuffixUsed to recognize and remove classname suffixes. See {@link
#getShortClassName}. | private static final com.sun.jdo.spi.persistence.utility.logging.Logger | loggerThe logger | private static final ResourceBundle | messagesI18N message handler |
Constructors Summary |
---|
private DatabaseGenerator(com.sun.jdo.api.persistence.model.Model model, List pcClasses, MappingPolicy mappingPolicy, String schemaName, String classSuffix)Generate database schema and mapping model from given map of
persistence class name.
this.model = model;
this.pcClasses = pcClasses;
this.mappingPolicy = mappingPolicy;
this.schema = DBElementFactory.createSchema(schemaName);
this.classSuffix = classSuffix;
|
Methods Summary |
---|
private void | addAssocMappingRelationship(java.lang.String relationName, MappingClassElement declaringClass, ForeignKeyElement fkeyForeign)Create and add MappingRelationship with associated column pairs
for join table. The column pair for mappingRelationship is same
order as the column pair from foreign key.
It is for column pairs between the join table and the foreign table
MappingRelationshipElement impl =
(MappingRelationshipElement) declaringClass.getField(
relationName);
if (null == impl) {
impl = new MappingRelationshipElementImpl(
relationName, declaringClass);
declaringClass.addField(impl);
}
// Add column pair for join table and foreign table
ColumnPairElement [] pairs = fkeyForeign.getColumnPairs();
for (int i = 0; i < pairs.length; i++) {
ColumnPairElement pair = pairs[i];
impl.addAssociatedColumn(pair);
}
| private void | addDeferredRelationships(java.util.List deferredRelationships, java.util.Map relationFKey)Generate foreign keys for relationships that were deferred; see {@link
addRelationships}.
for (Iterator i = deferredRelationships.iterator(); i.hasNext();) {
DeferredRelationship dr = (DeferredRelationship)i.next();
RelationshipElement relation = dr.getRelation();
RelationshipElement inverseRelation = dr.getInverseRelation();
ForeignKeyElement fKey =
getMappedForeignKey(relation, inverseRelation, relationFKey);
// Only map if not already mapped
if (fKey == null) {
TableElement sourceTable = dr.getSourceTable();
TableElement relTable = dr.getRelTable();
String relationName = dr.getRelationName();
String inverseRelName = dr.getInverseRelName();
MappingClassElement mappingClass = dr.getMappingClass();
MappingClassElement relMappingClass = dr.getRelMappingClass();
String uniqueId = dr.getUniqueId();
// If this side already has any foreign keys, map it on this
// side, else on the other side.
ForeignKeyElement keys[] = sourceTable.getForeignKeys();
if (null != keys && keys.length > 0) {
fKey = createRelationship(
sourceTable, relTable,
relationName, inverseRelName,
mappingClass, relMappingClass,
uniqueId, false);
if (logger.isLoggable(Logger.FINE)) {
logger.fine(
"1-1 deferred relationship (this)" // NOI18N
+ getTblInfo("sourceTable", sourceTable, relationName) // NOI18N
+ getTblInfo("relTable", relTable, inverseRelName)); // NOI18N
}
} else {
fKey = createRelationship(
relTable, sourceTable,
inverseRelName, relationName,
relMappingClass, mappingClass,
uniqueId, false);
if (logger.isLoggable(Logger.FINE)) {
logger.fine(
"1-1 deferred relationship (inverse)" // NOI18N
+ getTblInfo("sourceTable", sourceTable, relationName) // NOI18N
+ getTblInfo("relTable", relTable, inverseRelName)); // NOI18N
}
}
relationFKey.put(relation, fKey);
}
}
| private void | addInverseMappingRelationship(java.lang.String relationName, MappingClassElement declaringClass, ForeignKeyElement fkey, boolean isJoin)Create and add MappingRelationship with local inverse column pair
(for join table) or inverse column pair
(for non join table) for referenced table.
It is for column pairs between local table and join table
or for column pairs between local table and foreign table (contains
foreign key)
The column pair for mappingRelationship is inverse order
as the column pair from foreign key. Foreign key is not in the passing
mapping class but in the inverse mapping class
MappingRelationshipElement impl =
(MappingRelationshipElement) declaringClass.getField(
relationName);
// for join table, need to add two MappingRelationshipElement
if (null == impl) {
impl = new MappingRelationshipElementImpl(relationName,
declaringClass);
declaringClass.addField(impl);
}
TableElement declaringTbl = getPrimaryTable(declaringClass);
ColumnPairElement [] pairs = fkey.getColumnPairs();
// Column pair get inverted since adding to referenced table
for (int i = 0; i < pairs.length; i++) {
ColumnPairElement pair = pairs[i];
ColumnPairElement inversePair = DBElementFactory.createColumnPair(
pair.getReferencedColumn(), pair.getLocalColumn(),
declaringTbl);
if (isJoin) {
impl.addLocalColumn(inversePair);
} else {
impl.addColumn(inversePair);
}
}
| private void | addMappingRelationship(java.lang.String relationName, MappingClassElement declaringClass, ForeignKeyElement fkey)Create and add mapping relationship with column pairs to mapping class.
The column pair for mappingRelationship is same order as the column
pair from foreign key. It is used for 1-1 or 1-M relationship
It is column pair between local table and foreign table
MappingRelationshipElement impl = new MappingRelationshipElementImpl(
relationName, declaringClass);
ColumnPairElement [] pairs = fkey.getColumnPairs();
for (int i = 0; i < pairs.length; i++) {
ColumnPairElement pair = pairs[i];
impl.addColumn(pair);
}
declaringClass.addField(impl);
| private void | addRelationships()Generate relationships for schema and mapping model from
mappingClasses which already have all mapping fields populated.
if (logger.isLoggable(Logger.FINE)) {
logger.fine("add relationship"); // NOI18N
}
Map relationFKey = new HashMap();
// This is a list of 1-1 relationships that are deferred for
// processing until all other relationships are processed. Deferral
// allows us to concentrate foreign keys on one side of the
// relationship.
List deferredRelationships = new ArrayList();
for (Iterator i = mappingClasses.values().iterator(); i.hasNext();) {
MappingClassElement mappingClass = (MappingClassElement) i.next();
String pcClassName = mappingClass.getName();
PersistenceClassElement pcClass =
model.getPersistenceClass(pcClassName);
validateModel(pcClass, "pcClass", pcClassName); // NOI18N
TableElement sourceTable = getPrimaryTable(mappingClass);
validateModel(sourceTable, "sourceTable", pcClassName); // NOI18N
// Create a string that can keep names unique
String uniqueId = getShortClassName(pcClassName);
int want = 8; // Ideally, take this many chars from end
int end = uniqueId.length();
int start = want > end ? 0 : end - want;
uniqueId = uniqueId.substring(start, end);
RelationshipElement [] rels = pcClass.getRelationships();
if (rels != null) {
for (int j = 0; j < rels.length; j++) {
// relationship
RelationshipElement relation = rels[j];
String relationName = relation.getName();
int upperBound = relation.getUpperBound();
// inverseRelationship
String inverseRelName =
relation.getInverseRelationshipName();
validateModel(inverseRelName,
"inverseRelName", relationName); // NOI18N
String relClassName = model.getRelatedClass(relation);
validateModel(relClassName,
"relClassName", relationName); // NOI18N
// get related MappingClass and PersistenceClass
MappingClassElement relMappingClass =
(MappingClassElement) mappingClasses.get(relClassName);
validateModel(relMappingClass,
"relMappingClass", relClassName); // NOI18N
PersistenceClassElement relClass =
model.getPersistenceClass(relClassName);
validateModel(relClass,
"relClass", relClassName); // NOI18N
RelationshipElement inverseRelation =
relClass.getRelationship(inverseRelName);
validateModel(inverseRelation,
"inverseRelation", inverseRelName); // NOI18N
TableElement relTable = getPrimaryTable(relMappingClass);
validateModel(relTable,
"relTable", relClassName); // NOI18N
int relUpperBound = inverseRelation.getUpperBound();
if (logger.isLoggable(Logger.FINE)) {
logger.fine(
"Before adding relationship:" // NOI18N
+ getTblInfo("sourceTable", sourceTable, relationName) // NOI18N
+ getTblInfo("relTable", relTable, inverseRelName)); // NOI18N
}
// XXX Suggest making each block below a separate method.
if ((upperBound > 1) && (relUpperBound > 1)) {
// M-N relationship, create new table
if (logger.isLoggable(Logger.FINE)) {
logger.fine("M-N relationship"); // NOI18N
}
ForeignKeyElement fKey = getMappedForeignKey(
relation, inverseRelation, relationFKey);
if (fKey == null) {
TableElement joinTable =
DBElementFactory.createAndAttachTable(
schema,
mappingPolicy.getJoinTableName(
sourceTable.getName().getName(),
relTable.getName().getName()));
fKey = createRelationship(
joinTable, sourceTable, relationName,
inverseRelName, mappingClass,
relMappingClass, uniqueId, true);
relationFKey.put(relation, fKey);
ForeignKeyElement fKey2 = createRelationship(
joinTable, relTable, inverseRelName,
relationName, relMappingClass,
mappingClass, uniqueId, true);
relationFKey.put(inverseRelation, fKey2);
}
} else if ((upperBound > 1) && (relUpperBound == 1)) {
// M-1 relationship, add foreign key at upper bound
// equal 1 side. So here, we do nothing: We add
// relationships at the 1 side for 1-M relationships,
// and the current mapping class is the many side.
if (logger.isLoggable(Logger.FINE)) {
logger.fine("M-1 relationship: skip"); // NOI18N
}
} else if ((upperBound == 1) && (relUpperBound >1)) {
// 1-M relationship, add foreign key at upperBound =
// 1 side
if (logger.isLoggable(Logger.FINE)) {
logger.fine("1-M relationship"); // NOI18N
}
ForeignKeyElement fKey = getMappedForeignKey(relation,
inverseRelation, relationFKey);
if (fKey == null) {
fKey = createRelationship(sourceTable, relTable,
relationName, inverseRelName, mappingClass,
relMappingClass, uniqueId, false);
relationFKey.put(relation, fKey);
}
} else if ((upperBound == 1) && (relUpperBound == 1)) {
// 1-1 relationship, add foreign key at either side.
// Check existence of foreign key at the other side
// before adding one to here. If there is cascade
// delete in this side, add FK. Otherwise, defer
// adding it until all other relationships are added.
ForeignKeyElement fKey = getMappedForeignKey(relation,
inverseRelation, relationFKey);
if (fKey == null) {
if (relation.getDeleteAction() ==
RelationshipElement.CASCADE_ACTION) {
if (logger.isLoggable(Logger.FINE)) {
logger.fine("1-1 relationship: cascade(this)"); // NOI18N
}
fKey = createRelationship(
sourceTable, relTable, relationName,
inverseRelName, mappingClass,
relMappingClass, uniqueId, false);
relationFKey.put(relation, fKey);
} else if (inverseRelation.getDeleteAction() ==
RelationshipElement.CASCADE_ACTION) {
if (logger.isLoggable(Logger.FINE)) {
logger.fine("1-1 relationship: cascade(inverse)"); // NOI18N
}
fKey = createRelationship(
relTable, sourceTable,
inverseRelName, relationName,
relMappingClass, mappingClass,
uniqueId, false);
relationFKey.put(inverseRelation, fKey);
} else {
if (logger.isLoggable(Logger.FINE)) {
logger.fine("1-1 relationship: defer"); // NOI18N
}
deferredRelationships.add(
new DeferredRelationship(
relation, inverseRelation,
sourceTable, relTable,
relationName, inverseRelName,
mappingClass, relMappingClass,
uniqueId));
}
}
}
if (logger.isLoggable(Logger.FINE)) {
logger.fine(
"After adding relationship:" // NOI18N
+ getTblInfo("sourceTable", sourceTable, relationName) // NOI18N
+ getTblInfo("relTable", relTable, inverseRelName)); // NOI18N
}
}
}
}
if (deferredRelationships.size() > 0) {
addDeferredRelationships(deferredRelationships, relationFKey);
}
| private MappingFieldElement | createAndAttachMappingField(java.lang.String fieldName, MappingClassElement mappingClass, ColumnElement column)Create mapping field and add to mapping class
MappingFieldElement mappingField =
new MappingFieldElementImpl(fieldName, mappingClass);
mappingClass.addField(mappingField);
mappingField.addColumn(column);
if (column.isBlobType()) {
mappingField.setFetchGroup(MappingFieldElement.GROUP_NONE);
} else {
mappingField.setFetchGroup(MappingFieldElement.GROUP_DEFAULT);
}
return mappingField;
| private MappingClassElement | createMappingClass(PersistenceClassElement pcClass, TableElement table)Create mapping class and associated table and PC class
MappingClassElement mappingClass =
new MappingClassElementImpl(pcClass);
mappingClass.setDatabaseRoot(schema);
mappingClass.addTable(table);
return mappingClass;
| private ForeignKeyElement | createRelationship(TableElement srcTable, TableElement relTable, java.lang.String relName, java.lang.String inverseRelName, MappingClassElement mappingClass, MappingClassElement relMappingClass, java.lang.String uniqueId, boolean srcIsJoin)Create and add a relationship.
ForeignKeyElement fKey = DBElementFactory.createAndAttachForeignKey(
srcTable, relTable, relName, mappingPolicy, uniqueId);
if (srcIsJoin) {
addInverseMappingRelationship(relName, mappingClass,
fKey, true);
addAssocMappingRelationship(inverseRelName, relMappingClass, fKey);
} else {
addMappingRelationship(relName, mappingClass, fKey);
addInverseMappingRelationship(inverseRelName, relMappingClass,
fKey, false);
}
return fKey;
| public static com.sun.jdo.spi.persistence.generator.database.DatabaseGenerator$Results | generate(com.sun.jdo.api.persistence.model.Model model, java.util.List pcClasses, MappingPolicy mappingPolicy, java.lang.String schemaName, java.lang.String classSuffix, boolean generateMappingClasses)Generate database schema and mapping model from given map of
persistence class names. The schema is not put into the SchemaElement
cache as a result of this generation. The next call to
SchemaElement.forName will result in the schema being placed in the
cache if it is not already there. If it is already there, it is the
caller's responsibility to remove the old version if desired using
SchemaElement's removeFromCache method before a SchemaElement.forName
call. The generated schema is saved in outputDir.schemaName.
DatabaseGenerator generator = new DatabaseGenerator(
model, pcClasses, mappingPolicy,
schemaName, classSuffix);
Results rc = generator.generate();
mappingPolicy.resetCounter();
return rc;
| private com.sun.jdo.spi.persistence.generator.database.DatabaseGenerator$Results | generate()Generate database schema and mapping classes. Iterate over all
persistence-capable classes, generating a table for each. Within each
persistence-capable class, iterate over all fields and make columns
for each. Then handle relationships separately, see {@link
#addRelationships}.
for (Iterator i = pcClasses.iterator(); i.hasNext();) {
NameTuple nameTuple = (NameTuple) i.next();
String pcClassName = nameTuple.getPersistenceClassName();
String desiredTableName = nameTuple.getDesiredTableName();
PersistenceClassElement pcClass =
model.getPersistenceClass(pcClassName);
String tableName = mappingPolicy.getTableName(
desiredTableName, getShortClassName(nameTuple.getHashClassName()));
TableElement table = DBElementFactory.createAndAttachTable(
schema, tableName);
UniqueKeyElement pKey = DBElementFactory.createAndAttachPrimaryKey(
table,
mappingPolicy.getPrimaryKeyConstraintName(
table.getName().getName()));
MappingClassElement mappingClass = createMappingClass(
pcClass, table);
PersistenceFieldElement[] fields = pcClass.getFields();
if (fields != null) {
for (int j = 0; j < fields.length; j++) {
PersistenceFieldElement field = fields[j];
String fieldName = field.getName();
if (!(field instanceof RelationshipElement)) {
String columnName = mappingPolicy.getColumnName(
desiredTableName, fieldName, tableName);
String fieldType = model.getFieldType(
pcClassName, fieldName);
String fullFieldName =
new StringBuffer(desiredTableName)
.append(DOT).append(fieldName).toString();
JDBCInfo columnType =
DBElementFactory.getColumnType(
fullFieldName,
fieldType,
mappingPolicy);
if (logger.isLoggable(Logger.FINEST)) {
logger.fine(
"DBGenerator.generate: " // NOI18N
+ tableName + "." + columnName + ": " // NOI18N
+ columnType.toString());
}
ColumnElement column =
DBElementFactory.createAndAttachColumn(
columnName, table, columnType);
MappingFieldElement mappingField =
createAndAttachMappingField(
fieldName, mappingClass, column);
if (field.isKey()) {
column.setNullable(false);
pKey.addColumn(column);
pKey.getAssociatedIndex().addColumn(column);
mappingClass.getTable(tableName).addKeyColumn(
column);
}
}
}
}
mappingClasses.put(pcClassName, mappingClass);
}
addRelationships();
return new Results(schema, mappingClasses);
| private ForeignKeyElement | getMappedForeignKey(RelationshipElement relation, RelationshipElement inverseRelation, java.util.Map relationFKey)Check if the relationship has been visited
ForeignKeyElement fkey =
(ForeignKeyElement) relationFKey.get(relation);
if (fkey == null) {
return (ForeignKeyElement) relationFKey.get(inverseRelation);
} else {
return fkey;
}
| private TableElement | getPrimaryTable(MappingClassElement mappingClass)Get the primary table element for the mapping class.
List tables = mappingClass.getTables();
MappingTableElement tbl = (MappingTableElement) tables.get(0);
if (tbl != null) {
DBIdentifier tblName = DBIdentifier.create(tbl.getTable());
return schema.getTable(tblName);
} else {
return null;
}
| private java.lang.String | getShortClassName(java.lang.String className)Computes the class name (without package) for the supplied
class name and trims off the class suffix.
String shortName = JavaTypeHelper.getShortClassName(className);
if ((classSuffix != null) && (!shortName.equals(classSuffix))) {
int index = shortName.lastIndexOf(classSuffix);
if (index != -1) {
shortName = shortName.substring(0, index);
}
}
return shortName;
| private static java.lang.String | getTblInfo(java.lang.String tblName, TableElement tbl, java.lang.String relName)Debug support. Returns a string describing the table and it's keys
(only the first key is listed).
int numFK = tbl.getForeignKeys().length;
ForeignKeyElement fk = null;
if (numFK > 0) {
fk = tbl.getForeignKeys()[0];
}
return " " + tblName + "=" + tbl.toString()
+ ", # keys=" + numFK // NOI18N
+ ", 1st key=" + fk // NOI18N
+ "; relationship Name=" + relName; // NOI18N
| private void | validateModel(java.lang.Object o, java.lang.String failedItem, java.lang.String accessor)Assert that the given object reference is not null.
if (null == o) {
String msg = I18NHelper.getMessage(
messages,
"EXC_InvalidRelationshipMapping", // NOI18N
failedItem,
accessor);
logger.log(Logger.SEVERE, msg);
throw new ModelException(msg);
}
|
|