DefaultTableGeneratorpublic class DefaultTableGenerator extends Object DefaultTableGenerator is a utility class used to generate a default table schema for a TopLink project object.
The utility can be used in TopLink CMP for OC4J to perform the table auto creation process, which can be triggered
at deployment time when TopLink project descriptor is absent (default mapping) or present.
The utility can also be used to any TopLink application to perform the table drop/creation at runtime.
The utility handles all direct/relational mappings, inheritance, multiple tables, interface with/without tables,
optimistic version/timestamp lockings, nested relationships, BLOB/CLOB generation.
The utility is platform-agnostic.
Usage:
- CMP
1. set "autocreate-tables=true|false, autodelete-tables=true|false" in oc4j application deployment
descriptor files (config/system-application.xml, config/application.xml, or orion-application.xml in an .ear)
2. Default Mapping: the same as CMP, plus system properties setting -Dtoplink.defaultmapping.autocreate-tables='true|false'
and -Dtoplink.defaultmapping.autodelete-tables='true|false'
- Non-CMP:
TODO: sessions.xml support (CR 4355200)
1. Configuration: through sessions.xml
2. Directly runtime call through schema framework:
SchemaManager mgr = new SchemaManager(session);
mgr.replaceDefaultTables(); //drop and create
mgr.createDefaultTables(); //create only
The utility currently only supports relational project. |
Fields Summary |
---|
Project | project | private DatabasePlatform | databasePlatform | private Map | tableMap | private Map | fieldMap | private Map | databaseFields |
Constructors Summary |
---|
public DefaultTableGenerator(Project project)Default construcotr
this.project = project;
databasePlatform = project.getLogin().getPlatform();
tableMap = new HashMap();
fieldMap = new HashMap();
databaseFields = new HashMap();
|
Methods Summary |
---|
private void | addForeignKeyConstraint(oracle.toplink.essentials.tools.schemaframework.TableDefinition sourceTableDef, oracle.toplink.essentials.tools.schemaframework.TableDefinition targetTableDef, java.util.Vector fkFields, java.util.Vector targetFields)Add a foreign key constraint to the source table.
assert fkFields.size() > 0 && fkFields.size() == targetFields.size();
// target keys could be primary keys or candidate(unique) keys of the target table
Vector<String> fkFieldNames = fkFields;
Vector<String> targetFieldNames = targetFields;
if (fkFields.size() > 1) {
// if composite key, we should consider the order of keys.
// Foreign Key constraint should follow the primary/unique key order of the target table.
// e.g. if the primary key constraint of the target table is (p2, p1),
// foreign key constraint should be "(f2, f1) REFERENCES TARGET (p2, p1)".
// we try to reorder keys using primary keys or unique keys order of the target table,
// but if we might not resolve it due to incorrect field name, then let it as it is.
// This will trigger underlying database exception so users can recognize errors.
boolean resolved = false;
boolean error = false;
Map<String, String> targetToFkField = new HashMap<String, String>();
for (int index = 0; index < fkFields.size(); index++) {
String targetField = targetFields.get(index);
if (targetToFkField.containsKey(targetField)) {
//target key column appears more than once
error = true;
break;
}
targetToFkField.put(targetField, fkFields.get(index));
}
Vector<String> orderedFkFields = new Vector<String>(fkFields.size());
Vector<String> orderedTargetFields = new Vector<String>(targetFields.size());
if (!error) {
// if target fields are primary keys
resolved = true;
for (String pkField : (Vector<String>)targetTableDef.getPrimaryKeyFieldNames()) {
String fkField = targetToFkField.get(pkField);
if (fkField == null) {
//primary key column not found
resolved = false;
break;
}
orderedFkFields.add(fkField);
orderedTargetFields.add(pkField);
}
}
if (!error && !resolved) {
// if target fields are unique keys
for (UniqueKeyConstraint uniqueConstraint : targetTableDef.getUniqueKeys()) {
orderedFkFields.setSize(0);
orderedTargetFields.setSize(0);
resolved = true;
for (String ukField : uniqueConstraint.getSourceFields()) {
String fkField = targetToFkField.get(ukField);
if (fkField == null) {
//unique key column not found
resolved = false;
break;
}
orderedFkFields.add(fkField);
orderedTargetFields.add(ukField);
}
if (resolved) {
break;
}
}
}
if (resolved) {
fkFieldNames = orderedFkFields;
targetFieldNames = orderedTargetFields;
}
}
// For bidirectional relationships both side of mapping will make the same FK constraint twice.
// TableDefinition.addForeignKeyConstraint() will ignore the same FK constraint.
ForeignKeyConstraint fkc = sourceTableDef.buildForeignKeyConstraint(fkFieldNames, targetFieldNames,
targetTableDef, databasePlatform);
sourceTableDef.addForeignKeyConstraint(fkc);
| private void | addForeignKeyFieldToSourceTargetTable(oracle.toplink.essentials.mappings.OneToOneMapping mapping)
if (!mapping.isForeignKeyRelationship()) {
return;
}
addForeignMappingFkConstraint(mapping.getSourceToTargetKeyFields());
| private void | addForeignKeyFieldToSourceTargetTable(oracle.toplink.essentials.mappings.OneToManyMapping mapping)
addForeignMappingFkConstraint(mapping.getTargetForeignKeysToSourceKeys());
| private void | addForeignMappingFkConstraint(java.util.Map srcFields)
// srcFields map from the foreign key field to the target key field
if(srcFields.size() == 0) {
return;
}
List<DatabaseField> fkFields = new Vector<DatabaseField>();
List<DatabaseField> targetFields = new Vector<DatabaseField>();
for (DatabaseField fkField : srcFields.keySet()) {
fkFields.add(fkField);
targetFields.add(srcFields.get(fkField));
}
addJoinColumnsFkConstraint(fkFields, targetFields);
| private void | addForeignkeyFieldToAggregateTargetTable(oracle.toplink.essentials.mappings.AggregateCollectionMapping mapping)Add the foreign key to the aggregate collection mapping target table
//unlike normal one-to-many mapping, aggregate collection mapping does not have 1:1 back reference
//mapping, so the target foreign key fields are not stored in the target descriptor.
Iterator targFKIter = mapping.getTargetForeignKeyFields().iterator();
while (targFKIter.hasNext()) {
DatabaseField dbField = (DatabaseField) targFKIter.next();
//retrive the target table denifition
TableDefinition targTblDef = getTableDefFromDBTable(dbField.getTable());
//add the target foreign key field definition to the table definition
targTblDef.addField(getFieldDefFromDBField(dbField, false));
}
| private void | addJoinColumnsFkConstraint(java.util.List fkFields, java.util.List targetFields)
assert fkFields.size() == targetFields.size();
if (fkFields.size() == 0) {
return;
}
DatabaseField fkField = null;
DatabaseField targetField = null;
Vector<String> fkFieldNames = new Vector();
Vector<String> targetFieldNames = new Vector();
for (int i=0; i < fkFields.size(); i++) {
fkField = fkFields.get(i);
targetField = targetFields.get(i);
fkFieldNames.add(fkField.getName());
targetFieldNames.add(targetField.getName());
FieldDefinition fkFieldDef = fieldMap.get(fkField);
FieldDefinition targetFieldDef = fieldMap.get(targetField);
if (fkFieldDef != null && targetFieldDef != null) {
// Also ensure that the type, size and subsize of the foreign key field is
// same as that of the original field.
fkFieldDef.setType(targetFieldDef.getType());
fkFieldDef.setSize(targetFieldDef.getSize());
fkFieldDef.setSubSize(targetFieldDef.getSubSize());
}
}
// add a foreign key constraint
DatabaseTable sourceTable = fkField.getTable();
DatabaseTable targetTable = targetField.getTable();
TableDefinition sourceTableDef = getTableDefFromDBTable(sourceTable);
TableDefinition targetTableDef = getTableDefFromDBTable(targetTable);
addForeignKeyConstraint(sourceTableDef, targetTableDef, fkFieldNames, targetFieldNames);
| private void | addUniqueKeyConstraints(oracle.toplink.essentials.tools.schemaframework.TableDefinition sourceTableDef, java.util.Vector uniqueConstraints)
UniqueKeyConstraint uniqueKeyConstraint;
int serialNumber = 0;
for (String[] uniqueConstraint : uniqueConstraints) {
if(uniqueConstraint == null || uniqueConstraint.length == 0) continue;
uniqueKeyConstraint = sourceTableDef.buildUniqueKeyConstraint(uniqueConstraint, serialNumber++, databasePlatform);
sourceTableDef.addUniqueKeyConstraint(uniqueKeyConstraint);
}
| private void | buildDirectCollectionTableDefinition(oracle.toplink.essentials.mappings.DirectCollectionMapping mapping, oracle.toplink.essentials.descriptors.ClassDescriptor desc)Build direct collection table definitions in a TopLink desciptor
//first create direct collection table
TableDefinition tblDef = getTableDefFromDBTable(mapping.getReferenceTable());
DatabaseField dbField = null;
//add the table reference key(s)
Vector refPkFields = mapping.getReferenceKeyFields();
for (int index = 0; index < refPkFields.size(); index++) {
dbField = resolveDatabaseField((DatabaseField) refPkFields.get(index), (DatabaseField) mapping.getSourceKeyFields().get(index));
tblDef.addField(getDirectCollectionReferenceKeyFieldDefFromDBField(dbField));
}
//add the direct collection field to the table.
tblDef.addField(getFieldDefFromDBField(mapping.getDirectField(), false));
//if the mapping is direct-map field, add the direct key field to the table as well.
if (mapping.isDirectMapMapping()) {
dbField = ((DirectMapMapping) mapping).getDirectKeyField();
tblDef.addField(getFieldDefFromDBField(dbField, false));
}
| private void | buildRelationTableDefinition(oracle.toplink.essentials.mappings.ManyToManyMapping mapping)Build relation table definitions for all many-to-many relationships in a TopLink desciptor.
//first create relation table
TableDefinition tblDef = getTableDefFromDBTable(mapping.getRelationTable());
//add source foreign key fields into the relation table
Vector srcFkFields = mapping.getSourceRelationKeyFields();
Vector srcKeyFields = mapping.getSourceKeyFields();
buildRelationTableFields(tblDef, srcFkFields, srcKeyFields);
//add target foreign key fields into the relation table
Vector targFkFields = mapping.getTargetRelationKeyFields();
Vector targKeyFields = mapping.getTargetKeyFields();
buildRelationTableFields(tblDef, targFkFields, targKeyFields);
| private void | buildRelationTableFields(oracle.toplink.essentials.tools.schemaframework.TableDefinition tblDef, java.util.Vector fkFields, java.util.Vector targetFields)Build field definitions and foreign key constraints for all many-to-many relation table.
assert fkFields.size() > 0 && fkFields.size() == targetFields.size();
DatabaseField fkField = null;
DatabaseField targetField = null;
Vector<String> fkFieldNames = new Vector();
Vector<String> targetFieldNames = new Vector();
for (int index = 0; index < fkFields.size(); index++) {
fkField = (DatabaseField) fkFields.get(index);
targetField = (DatabaseField) targetFields.get(index);
fkFieldNames.add(fkField.getName());
targetFieldNames.add(targetField.getName());
fkField = resolveDatabaseField(fkField, targetField);
setFieldToRelationTable(fkField, tblDef);
}
// add a foreign key constraint from fk field to target field
DatabaseTable targetTable = targetField.getTable();
TableDefinition targetTblDef = getTableDefFromDBTable(targetTable);
addForeignKeyConstraint(tblDef, targetTblDef, fkFieldNames, targetFieldNames);
| public oracle.toplink.essentials.tools.schemaframework.TableCreator | generateDefaultTableCreator()Generate a default TableCreator object from the TopLink project object.
TableCreator tblCreator = new TableCreator();
//go through each descriptor and build the table/field definitions out of mappings
Iterator descIter = project.getDescriptors().values().iterator();
while (descIter.hasNext()) {
ClassDescriptor desc = (ClassDescriptor)descIter.next();
//aggregate RelationalDescriptor does not contains table/field data
if (!desc.isAggregateDescriptor()) {
initTableSchema(desc);
}
}
//Post init the schema for relation table and direct collection/map tables, and several special mapping handlings.
descIter = project.getOrderedDescriptors().iterator();
while (descIter.hasNext()) {
ClassDescriptor desc = (ClassDescriptor)descIter.next();
if (!desc.isAggregateDescriptor()) {
postInitTableSchema(desc);
}
}
tblCreator.addTableDefinitions(tableMap.values());
return tblCreator;
| public oracle.toplink.essentials.tools.schemaframework.TableCreator | generateFilteredDefaultTableCreator(oracle.toplink.essentials.internal.sessions.AbstractSession session)Generate a default TableCreator object from the TopLink project object,
and porform the table existence check through jdbc table metadata, and filter out
tables which are already in the database.
TableCreator tblCreator = generateDefaultTableCreator();
try {
//table exisitence check.
java.sql.Connection conn = null;
if (session.isServerSession()) {
//acquire a connection from the pool
conn = ((ServerSession)session).getDefaultConnectionPool().acquireConnection().getConnection();
} else if (session.isDatabaseSession()) {
conn = ((DatabaseSessionImpl)session).getAccessor().getConnection();
}
if (conn == null) {
//TODO: this is not pretty, connection is not obtained for some reason.
return tblCreator;
}
DatabaseMetaData dbMetaData = conn.getMetaData();
ResultSet resultSet = dbMetaData.getTables(null, dbMetaData.getUserName(), null, new String[] { "TABLE" });
java.util.List tablesInDatabase = new java.util.ArrayList();
while (resultSet.next()) {
//save all tables from the database
tablesInDatabase.add(resultSet.getString("TABLE_NAME"));
}
resultSet.close();
java.util.List existedTables = new java.util.ArrayList();
java.util.List existedTableNames = new java.util.ArrayList();
Iterator tblDefIter = tblCreator.getTableDefinitions().iterator();
while (tblDefIter.hasNext()) {
TableDefinition tblDef = (TableDefinition) tblDefIter.next();
//check if the to-be-created table is already in the database
if (tablesInDatabase.contains(tblDef.getFullName())) {
existedTables.add(tblDef);
existedTableNames.add(tblDef.getFullName());
}
}
if (!existedTableNames.isEmpty()) {
session.getSessionLog().log(SessionLog.FINEST, "skip_create_existing_tables", existedTableNames);
//remove the existed tables, won't create them.
tblCreator.getTableDefinitions().removeAll(existedTables);
}
} catch (SQLException sqlEx) {
throw DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection();
}
return tblCreator;
| private oracle.toplink.essentials.tools.schemaframework.FieldDefinition | getDirectCollectionReferenceKeyFieldDefFromDBField(oracle.toplink.essentials.internal.helper.DatabaseField dbField)Build a field definition object from a database field.
FieldDefinition fieldDef = (FieldDefinition)getFieldDefFromDBField(dbField, true).clone();
//direct collection/map table reference kye filed is not unique, need to set it as non-pk.
fieldDef.setIsPrimaryKey(false);
return fieldDef;
| private oracle.toplink.essentials.tools.schemaframework.FieldDefinition | getFieldDefFromDBField(oracle.toplink.essentials.internal.helper.DatabaseField dbField, boolean isPrimaryKey)Build a field definition object from a database field.
FieldDefinition fieldDef = this.fieldMap.get(dbField);
if (fieldDef == null) {
//not built yet, build one
fieldDef = new FieldDefinition();
fieldDef.setName(dbField.getName());
if (dbField.getColumnDefinition().length() > 0) {
// This column definition would include the complete definition of the
// column like type, size, "NULL/NOT NULL" clause, unique key clause
fieldDef.setTypeDefinition(dbField.getColumnDefinition());
} else {
Class fieldType = dbField.getType();
// Check if the user field is a String and only then allow the length specified
// in the @Column annotation to be set on the field.
if ((fieldType != null)) {
if (fieldType.equals(ClassConstants.STRING) ||
fieldType.equals(ClassConstants.APCHAR) ||
fieldType.equals(ClassConstants.ACHAR)) {
// The field size is defaulted to "255" or use the user supplied length
fieldDef.setSize(dbField.getLength());
} else {
if (dbField.getPrecision() > 0) {
fieldDef.setSize(dbField.getPrecision());
fieldDef.setSubSize(dbField.getScale());
}
}
}
if ((fieldType == null) || (!fieldType.isPrimitive() &&
(databasePlatform.getFieldTypeDefinition(fieldType) == null))) {
//TODO: log a warning for inaccessiable type or not convertable type.
AbstractSessionLog.getLog().log(SessionLog.FINEST, "field_type_set_to_java_lang_string", dbField.getQualifiedName(), fieldType);
//set the default type (lang.String) to all un-resolved java type, like null, Number, util.Date, NChar/NType, Calendar
//sql.Blob/Clob, Object, or unknown type). Please refer to bug 4352820.
fieldDef.setType(ClassConstants.STRING);
} else {
//need to convert the primitive type if applied.
fieldDef.setType(ConversionManager.getObjectClass(fieldType));
}
fieldDef.setShouldAllowNull(dbField.isNullable());
fieldDef.setUnique(dbField.isUnique());
}
fieldDef.setIsPrimaryKey(isPrimaryKey);
fieldMap.put(dbField, fieldDef);
databaseFields.put(dbField, dbField);
}
return fieldDef;
| private oracle.toplink.essentials.tools.schemaframework.TableDefinition | getTableDefFromDBTable(oracle.toplink.essentials.internal.helper.DatabaseTable dbTbl)Build a table definition object from a database table object
TableDefinition tblDef = this.tableMap.get(dbTbl.getName());
if (tblDef == null) {
//table not built yet, simply built it
tblDef = new TableDefinition();
tblDef.setName(dbTbl.getName());
tblDef.setQualifier(dbTbl.getTableQualifier());
addUniqueKeyConstraints(tblDef, dbTbl.getUniqueConstraints());
tableMap.put(dbTbl.getName(), tblDef);
}
return tblDef;
| protected void | initTableSchema(oracle.toplink.essentials.descriptors.ClassDescriptor desc)Build tables/fields infomation into the table creator object from a TopLink descriptor.
This should handle most of the direct/relational mappings except many-to-many and direct
collection/map mappings, witch must be down in postInit method.
TableDefinition tblDef = null;
DatabaseTable dbTbl = null;
Iterator dbTblIter = desc.getTables().iterator();
//create a table definition for each mapped database table
while (dbTblIter.hasNext()) {
dbTbl = (DatabaseTable) dbTblIter.next();
tblDef = getTableDefFromDBTable(dbTbl);
}
//build each field definition and figure out which table it goes
Iterator fieldIter = desc.getFields().iterator();
DatabaseField dbField = null;
while (fieldIter.hasNext()) {
dbField = (DatabaseField) fieldIter.next();
boolean isPKField = false;
//first check if the filed is a pk field in the default table.
isPKField = desc.getPrimaryKeyFields().contains(dbField);
//then check if the field is a pk field in the secondary table(s), this is only applied to the multiple tables case.
Map secondaryKeyMap = (Map) desc.getAdditionalTablePrimaryKeyFields().get(dbField.getTable());
if (secondaryKeyMap != null) {
isPKField = isPKField || secondaryKeyMap.containsValue(dbField);
}
//build or retrieve the field definition.
FieldDefinition fieldDef = getFieldDefFromDBField(dbField, isPKField);
if (isPKField) {
// Check if the generation strategy is IDENTITY
String sequenceName = desc.getSequenceNumberName();
DatabaseLogin login = project.getLogin();
Sequence seq = login.getSequence(sequenceName);
if(seq instanceof DefaultSequence) {
seq = login.getDefaultSequence();
}
//The native sequence whose value should be aquired after insert is identity sequence
boolean isIdentity = seq instanceof NativeSequence && seq.shouldAcquireValueAfterInsert();
fieldDef.setIsIdentity(isIdentity);
}
//find the table the field belongs to, and add it to the table, ony if not already added.
tblDef = tableMap.get(dbField.getTableName());
if (!tblDef.getFields().contains(fieldDef)) {
tblDef.addField(fieldDef);
}
}
| private void | postInitTableSchema(oracle.toplink.essentials.descriptors.ClassDescriptor desc)Build additional table/field definitions for the dscriptor, like relation table
and direct-collection, direct-map table, as well as reset LOB type for serialized
object mapping and type conversion maping for LOB usage
Iterator mappingIter = desc.getMappings().iterator();
while (mappingIter.hasNext()) {
DatabaseMapping mapping = (DatabaseMapping) mappingIter.next();
if (mapping.isManyToManyMapping()) {
buildRelationTableDefinition((ManyToManyMapping) mapping);
} else if (mapping.isDirectCollectionMapping()) {
buildDirectCollectionTableDefinition((DirectCollectionMapping) mapping, desc);
} else if (mapping.isAggregateCollectionMapping()) {
//need to figure out the target foreign key field and add it into the aggregate target table
addForeignkeyFieldToAggregateTargetTable((AggregateCollectionMapping) mapping);
} else if (mapping.isForeignReferenceMapping()) {
if (mapping.isOneToOneMapping())
addForeignKeyFieldToSourceTargetTable((OneToOneMapping) mapping);
else if (mapping.isOneToManyMapping())
addForeignKeyFieldToSourceTargetTable((OneToManyMapping) mapping);
}
}
processAdditionalTablePkFields(desc);
| private void | processAdditionalTablePkFields(oracle.toplink.essentials.descriptors.ClassDescriptor desc)
// only if there are additional tables
if (!desc.hasMultipleTables()) {
return;
}
DatabaseTable dbTbl = null;
Iterator dbTblIter = desc.getTables().iterator();
while (dbTblIter.hasNext()) {
dbTbl = (DatabaseTable) dbTblIter.next();
Map<DatabaseField, DatabaseField> srcFields = desc.getAdditionalTablePrimaryKeyFields().get(dbTbl);
if ((null != srcFields) && srcFields.size() > 0) {
// srcFields is from the secondary field to the primary key field
// Let's make fk constraint from the secondary field to the primary key field
List<DatabaseField> fkFields = new Vector<DatabaseField>();
List<DatabaseField> pkFields = new Vector<DatabaseField>();
for (DatabaseField pkField : srcFields.keySet()) {
pkFields.add(pkField);
fkFields.add(srcFields.get(pkField));
}
addJoinColumnsFkConstraint(fkFields, pkFields);
}
}
| private oracle.toplink.essentials.internal.helper.DatabaseField | resolveDatabaseField(oracle.toplink.essentials.internal.helper.DatabaseField childField, oracle.toplink.essentials.internal.helper.DatabaseField parentField)Resolve the foreign key database field metadata in relation table or direct collection/map table.
Those metadata includes type, and maybe dbtype/size/subsize if DatabaseField carries those info.
//set through the type from the source table key field to the relation or direct collection table key field.
DatabaseField resolvedDatabaseField = new DatabaseField();
// find original field in the parent table, which contains actual type definitions
// if 'resolvedParentField' is null, there is no corresponding field definition (typo?)
DatabaseField resolvedParentField = databaseFields.get(parentField);
resolvedDatabaseField.setName(childField.getName());
//Table should be set, otherwise other same name field will be used wrongly because equals() is true.
//Fix for GF#1392 the same name column for the entity and many-to-many table cause wrong pk constraint.
resolvedDatabaseField.setTable(childField.getTable());
// type definitions from parent field definition
if(resolvedParentField != null) {
resolvedDatabaseField.setType(resolvedParentField.getType());
resolvedDatabaseField.setScale(resolvedParentField.getScale());
resolvedDatabaseField.setLength(resolvedParentField.getLength());
resolvedDatabaseField.setPrecision(resolvedParentField.getPrecision());
}
// these are defined in childField definition(see @JoinColumn)
resolvedDatabaseField.setUnique(childField.isUnique());
resolvedDatabaseField.setNullable(childField.isNullable());
resolvedDatabaseField.setUpdatable(childField.isUpdatable());
resolvedDatabaseField.setInsertable(childField.isInsertable());
String columnDef = childField.getColumnDefinition();
if(columnDef == null || columnDef.trim().equals("")) {
// if childField has no column definition, follow the definition of the parent field
if(resolvedParentField != null) {
resolvedDatabaseField.setColumnDefinition(resolvedParentField.getColumnDefinition());
}
} else {
resolvedDatabaseField.setColumnDefinition(columnDef);
}
return resolvedDatabaseField;
| private void | setFieldToRelationTable(oracle.toplink.essentials.internal.helper.DatabaseField dbField, oracle.toplink.essentials.tools.schemaframework.TableDefinition tblDef)Build and add a field definition object to relation table
FieldDefinition fieldDef = getFieldDefFromDBField(dbField, false);
if (!tblDef.getFields().contains(fieldDef)) {
//only add the field once, to avoid add twice if m:m is bi-directional.
tblDef.addField(getFieldDefFromDBField(dbField, false));
fieldDef.setIsPrimaryKey(true);
}
|
|