/* * Copyright 2006 Le Duc Bao, Ralf Joachim * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.castor.ddlgen; import java.io.OutputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.castor.ddlgen.schemaobject.Field; import org.castor.ddlgen.schemaobject.ForeignKey; import org.castor.ddlgen.schemaobject.KeyGenerator; import org.castor.ddlgen.schemaobject.PrimaryKey; import org.castor.ddlgen.schemaobject.Schema; import org.castor.ddlgen.schemaobject.Table; import org.castor.ddlgen.typeinfo.TypeInfo; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.xml.ClassChoice; import org.exolab.castor.mapping.xml.ClassMapping; import org.exolab.castor.mapping.xml.FieldMapping; import org.exolab.castor.mapping.xml.KeyGeneratorDef; import org.exolab.castor.mapping.xml.MapTo; import org.exolab.castor.mapping.xml.MappingRoot; import org.exolab.castor.mapping.xml.Sql; /** * AbstractGenerator is the base class for various DDL generator of specific DB and * handles following tasks: * <li/> Extract information from Mapping to Schema * <li/> Loop through the schema and provide a skeleton for DDL creation * * <p/>AbstractGenerator will automatically extract necessary information for DDL * creation. That information is handled by Schema. * <p/>To create new generator for a DBMS, you should: * <li/> Overwrite this class to create new generator for a DBMS. * <li/> If the syntax of DBMS is different to standard DDL syntax, you should * overwrite SchemaObject (Table, Field, KeyGenerator, Index, ForeignKey,...) classes. * The class SchemaObjectFactory who handles the SchemaObject creation must * be overwritten. * <li/> You must overwrite the TypeMapper if mapping between JDBC types and * specific DBMS�s types is different among various DBMS. * <p/>The example bellow shows how to create a generator for DB2: * <li/> <b>Generator for DB2</b> * <pre> *public class Db2Generator extends AbstractGenerator { * * public Db2Generator(final String globConf, final String dbConf) * throws GeneratorException { * super(globConf, dbConf); * setTypeMapper(new Db2TypeMapper(getConf())); * } *} * </pre> * <li/><b>TypeMapper for DB2</b> * <pre> *public final class Db2TypeMapper extends AbstractTypeMapper { * public Db2TypeMapper(final Configuration conf) { * super(conf); * } * * protected void initialize(final Configuration conf) { * // numeric types * this.add(new NotSupportedType("bit")); * LOG.warn("Db2 does not support 'TINY' type, use SMALLINT instead."); * this.add(new NoParamType("tinyint", "SMALLINT")); * this.add(new NoParamType("smallint", "SMALLINT")); * this.add(new NoParamType("integer", "INTEGER")); * this.add(new NoParamType("bigint", "BIGINT")); * } *} *</pre> * <li/><b>Field for DB2</b> *<pre> *public class Db2Field extends Field { * public Db2Field() { * super(); * } * * public String toDDL() throws GeneratorException { * StringBuffer buff = new StringBuffer(); * buff.append(getName()).append(" "); * buff.append(getType().toDDL(this)); * * if (isIdentity()) { * buff.append(" NOT NULL"); * } * * KeyGenerator keyGen = getKeyGenerator(); * if (keyGen != null && isIdentity()) { * * if (KeyGenerator.IDENTITY_KEY.equalsIgnoreCase(keyGen.getName())) { * buff.append(" GENERATED BY DEFAULT AS IDENTITY "). * append("START WITH 1 INCREMENT BY 1"); * } * } * * return buff.toString(); * } *} *</pre> * <li/><b>Field for DB2</b> *<pre> *public class Db2SchemaFactory extends SchemaFactory { * public Db2SchemaFactory() { * super(); * } * public Field createField() { * return new Db2Field(); * } * *} *</pre> * The GeneratorFactory class handles the specific database generator creation. * For example: * <pre> * Generator generator = GeneratorFactory. * createDDLGenerator(�mysql�, �ddl.properties�, �mysql.properties�); * </pre> * * And to generate DDL, it should specify the printer and call generateDDL method. * <pre> * generator.setPrinter(System.out); * Mapping mapping = new Mapping(); * mapping.loadMapping("mapping.xml"); * generator.generateDDL(mapping); * </pre> * * @author <a href="mailto:leducbao AT gmail DOT com">Le Duc Bao</a> * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a> * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $ * @since 1.1 */ public abstract class AbstractGenerator implements Generator { //-------------------------------------------------------------------------- /** handle all configurations (key, value). */ private final DDLGenConfiguration _configuration; /** handle the key gen registry. */ private KeyGeneratorRegistry _keyGenRegistry; /** handle the MappingHelper. */ private MappingHelper _mappingHelper; /** handle the typemapper. */ private TypeMapper _typeMapper; /** handle schema factory. */ private SchemaFactory _schemaFactory; /** handle the _mapping document. */ private Mapping _mapping; /** schema. */ private Schema _schema; /** handle all resolving tables. */ private final Map<String, ClassMapping> _resolveTable = new HashMap<String, ClassMapping>(); //-------------------------------------------------------------------------- /** * Constructor for AbstractGenerator. * * @param configuration Configuration to use by the generator. */ protected AbstractGenerator(final DDLGenConfiguration configuration) { _configuration = configuration; } //-------------------------------------------------------------------------- /** * Get configuration of generator. * * @return Configuration of generator. */ public final DDLGenConfiguration getConfiguration() { return _configuration; } /** * Set key generator registry. * * @param keyGenRegistry Key generator registry. */ public final void setKeyGenRegistry(final KeyGeneratorRegistry keyGenRegistry) { _keyGenRegistry = keyGenRegistry; } /** * Set mapping helper. * * @param mappingHelper Mapping helper. */ protected final void setMappingHelper(final MappingHelper mappingHelper) { _mappingHelper = mappingHelper; _mappingHelper.setTypeMapper(_typeMapper); } /** * Get mapping helper. * * @return Mapping helper. */ public final MappingHelper getMappingHelper() { return _mappingHelper; } /** * Set type mapper. * * @param typeMapper Type mapper. */ public final void setTypeMapper(final TypeMapper typeMapper) { _typeMapper = typeMapper; _mappingHelper.setTypeMapper(_typeMapper); } /** * Get type mapper. * * @return Type mapper. */ public final TypeMapper getTypeMapper() { return _typeMapper; } /** * Set schema factory. * * @param schemaFactory Schema factory. */ protected final void setSchemaFactory(final SchemaFactory schemaFactory) { _schemaFactory = schemaFactory; } /** * Get schema factory. * * @return Schema factory. */ public final SchemaFactory getSchemaFactory() { return _schemaFactory; } /** * Set mapping document. * * @param mapping Mapping document. */ public final void setMapping(final Mapping mapping) { _mapping = mapping; _mappingHelper.setMapping(_mapping); } /** * Get mapping document. * * @return Mapping document. */ public final Mapping getMapping() { return _mapping; } /** * Get schema. * * @return Schema */ public final Schema getSchema() { return _schema; } //-------------------------------------------------------------------------- /** * {@inheritDoc} */ public final void generateDDL(final OutputStream output) throws GeneratorException { DDLWriter writer = new DDLWriter(output, _configuration); // Create schema. createSchema(); // Generate DDL. String groupBy = _configuration.getStringValue( DDLGenConfiguration.GROUP_DDL_KEY, DDLGenConfiguration.GROUP_DDL_BY_TABLE); if (DDLGenConfiguration.GROUP_DDL_BY_TABLE.equalsIgnoreCase(groupBy)) { generateDDLGroupByTable(writer); } else if (DDLGenConfiguration.GROUP_DDL_BY_DDLTYPE.equalsIgnoreCase(groupBy)) { generateDDLGroupByDDLType(writer); } else { throw new GeneratorException("group ddl by do not support: " + groupBy); } writer.close(); } /** * Generating ddl grouped by ddl type of DDL (e.g DROP, CREATE TABLE, create * Primary key, create foreign key). * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ private void generateDDLGroupByDDLType(final DDLWriter writer) throws GeneratorException { boolean genSchema = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_SCHEMA_KEY, true); boolean genDrop = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_DROP_KEY, true); boolean genCreate = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_CREATE_KEY, true); boolean genPrimaryKey = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_PRIMARYKEY_KEY, true); boolean genForeignKey = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_FOREIRNKEY_KEY, true); boolean genIndex = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_INDEX_KEY, true); boolean genKeyGen = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_KEYGENERATOR_KEY, true); generateHeader(writer); //generate ddl for schema if (genSchema) { _schema.toCreateDDL(writer); } //generate drop statemetn if (genDrop) { generateDrop(writer); } //generate create statement if (genCreate) { generateCreate(writer); } //generate primary key creation statement if (genPrimaryKey) { generatePrimaryKey(writer); } //generate foreign key creation statement if (genForeignKey) { generateForeignKey(writer); } //generate index creation statement if (genIndex) { generateIndex(writer); } if (genKeyGen) { generateKeyGenerator(writer); } } /** * Generate DDL for drop statement of table. * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void generateDrop(final DDLWriter writer) throws GeneratorException { for (int i = 0; i < _schema.getTableCount(); i++) { _schema.getTable(i).toDropDDL(writer); } } /** * Generate DDL for create statementof table. * <pre> * CREATE TABLE prod ( * id INTEGER NOT NULL, * name CHAR(16) * ); * * CREATE TABLE prod_detail ( * id INTEGER NOT NULL, * prod_id CHAR(16) * ); * </pre> * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void generateCreate(final DDLWriter writer) throws GeneratorException { for (int i = 0; i < _schema.getTableCount(); i++) { _schema.getTable(i).toCreateDDL(writer); } } /** * Generate DDL for primany keys. * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void generatePrimaryKey(final DDLWriter writer) throws GeneratorException { for (int i = 0; i < _schema.getTableCount(); i++) { _schema.getTable(i).getPrimaryKey().toCreateDDL(writer); } } /** * Generate DDL for foreign keys. * <pre> * ALTER TABLE `prod_group` ADD CONSTRAINT `FK_prod_group_1` * FOREIGN KEY `FK_prod_group_1` (`id`, `name`) * REFERENCES `category` (`id`, `name`) * ON DELETE SET NULL * ON UPDATE CASCADE; * </pre> * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void generateForeignKey(final DDLWriter writer) throws GeneratorException { for (int i = 0; i < _schema.getTableCount(); i++) { createForeignKeyDDL(_schema.getTable(i), writer); } } /** * Generate DDL for indices. * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void generateIndex(final DDLWriter writer) throws GeneratorException { for (int i = 0; i < _schema.getTableCount(); i++) { createIndex(_schema.getTable(i), writer); } } /** * Generate DDL for key generators (sequence/trigger). * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void generateKeyGenerator(final DDLWriter writer) throws GeneratorException { for (int i = 0; i < _schema.getTableCount(); i++) { Table table = _schema.getTable(i); if (table.getKeyGenerator() != null) { table.getKeyGenerator().setTable(table); table.getKeyGenerator().toCreateDDL(writer); } } } /** * Generating ddl group by table. * * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ private void generateDDLGroupByTable(final DDLWriter writer) throws GeneratorException { boolean genSchema = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_SCHEMA_KEY, true); boolean genDrop = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_DROP_KEY, true); boolean genCreate = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_CREATE_KEY, true); boolean genPrimaryKey = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_PRIMARYKEY_KEY, true); boolean genForeignKey = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_FOREIRNKEY_KEY, true); boolean genIndex = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_INDEX_KEY, true); boolean genKeyGen = _configuration.getBoolValue( DDLGenConfiguration.GENERATE_DDL_FOR_KEYGENERATOR_KEY, true); generateHeader(writer); if (genSchema) { _schema.toCreateDDL(writer); } for (int i = 0; i < _schema.getTableCount(); i++) { Table table = _schema.getTable(i); if (genDrop) { table.toDropDDL(writer); } if (genCreate) { table.toCreateDDL(writer); } if (genPrimaryKey) { table.getPrimaryKey().toCreateDDL(writer); } if (genForeignKey) { createForeignKeyDDL(table, writer); } if (genIndex) { createIndex(table, writer); } if (genKeyGen && (table.getKeyGenerator() != null)) { table.getKeyGenerator().setTable(table); table.getKeyGenerator().toCreateDDL(writer); } } } /** * Generate DDL for foreign key. * * @param table Table to generate DDL of foreign key for. * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ protected final void createForeignKeyDDL(final Table table, final DDLWriter writer) throws GeneratorException { for (int i = 0; i < table.getForeignKeyCount(); i++) { table.getForeignKey(i).toCreateDDL(writer); } } /** * Generate DDL for indices of given table. * * @param table Table to generate DDL of indices for. * @param writer DDLWriter to write schema objects to. * @throws GeneratorException If failed to generate DDL. */ public final void createIndex(final Table table, final DDLWriter writer) throws GeneratorException { for (int i = 0; i < table.getIndexCount(); i++) { table.getIndex(i).toCreateDDL(writer); } } //-------------------------------------------------------------------------- /** * Generate header comment. * * @param writer DDLWriter to write schema objects to. */ public abstract void generateHeader(final DDLWriter writer); //-------------------------------------------------------------------------- /** * Extracting informations from mapping to schema, this is done by 3 steps. * <ul> * <li>Create key generators</li> * <li>Create tables</li> * <li>Create additional tables for many-many relations</li> * </ul> * * @throws GeneratorException If failed to create schema objects. */ public final void createSchema() throws GeneratorException { // Create schema. MappingRoot root = _mapping.getRoot(); _schema = _schemaFactory.createSchema(); _schema.setConfiguration(_configuration); // Create key generators. Enumeration<? extends KeyGeneratorDef> ekg = root.enumerateKeyGeneratorDef(); while (ekg.hasMoreElements()) { KeyGeneratorDef definition = ekg.nextElement(); _keyGenRegistry.createKeyGenerator(definition); } // Create tables. Enumeration<? extends ClassMapping> ec = root.enumerateClassMapping(); while (ec.hasMoreElements()) { ClassMapping cm = ec.nextElement(); Table table = createTable(cm); if (table != null) { _schema.addTable(table); } } // Create N:M relation tables. Iterator<String> i = _resolveTable.keySet().iterator(); while (i.hasNext()) { ClassMapping cm = _resolveTable.get(i.next()); Table table = createTable(cm); if (table != null) { _schema.addTable(table); } } } /** * Create table from a ClassMapping. * * @param cm ClassMapping. * @return Table schema object. * @throws GeneratorException If failed to create schema objects. */ private Table createTable(final ClassMapping cm) throws GeneratorException { String tableName = cm.getMapTo().getTable(); if (tableName == null) { return null; } Table table = _schemaFactory.createTable(); table.setName(tableName); table.setConfiguration(_configuration); table.setSchema(_schema); PrimaryKey primarykey = _schemaFactory.createPrimaryKey(); primarykey.setConfiguration(_configuration); primarykey.setTable(table); primarykey.setName("pk_" + tableName); table.setPrimaryKey(primarykey); // Return if there are no field in the table. if (cm.getClassChoice() == null) { return table; } boolean isUseFieldIdentity = _mappingHelper.isUseFieldIdentity(cm); Enumeration<? extends FieldMapping> ef = cm.getClassChoice().enumerateFieldMapping(); // Process key generator. String keygenerator = cm.getKeyGenerator(); KeyGenerator keyGen = null; if (keygenerator != null) { keyGen = _keyGenRegistry.getKeyGenerator(keygenerator.toUpperCase()); } table.setKeyGenerator(keyGen); while (ef.hasMoreElements()) { FieldMapping fm = ef.nextElement(); // Skip if <sql> tag is not defined and we have no mapping to DB. if (fm.getSql() == null) { continue; } boolean isFieldIdentity = fm.getIdentity(); if (!isUseFieldIdentity) { isFieldIdentity = _mappingHelper.isIdentity(cm, fm); } // Checke for many-key, many-table definition. if (fm.getSql().getManyTable() != null) { // Generate resolving table for many-many relationship addResolveField(fm, cm); } // Process column creation if sql name is defined. String[] sqlnames = fm.getSql().getName(); if ((sqlnames != null) && (sqlnames.length > 0) && (fm.getSql().getManyTable() == null)) { // Normal case, using sql name as column name. String sqltype = fm.getSql().getType(); TypeInfo typeInfo = null; ClassMapping cmRef = null; String[] refIdTypes = null; boolean isUseReferenceType = false; // Get type info. if (sqltype != null) { typeInfo = _typeMapper.getType(sqltype); } // If typeInfo is null, this table has a reference to another one. if (typeInfo == null) { cmRef = _mappingHelper.getClassMappingByName(fm.getType()); // Use field type if reference class could not be found. if (cmRef == null) { typeInfo = _typeMapper.getType(fm.getType()); if (typeInfo == null) { throw new TypeNotFoundException("can not resolve type " + fm.getType() + " in class '" + cm.getName() + "'"); } } else { isUseReferenceType = true; refIdTypes = _mappingHelper.resolveTypeReferenceForIds( fm.getType()); // If number of reference table's Id's differ from number of // field elements. if (refIdTypes.length != sqlnames.length) { throw new TypeNotFoundException( "number of reference table's Id differs" + " to number of field elements '" + fm.getName() + "' of class '" + cm.getName() + "'" + refIdTypes.length + "," + sqlnames.length); } } } // Create fields. for (int i = 0; i < sqlnames.length; i++) { Field field = _schemaFactory.createField(); field.setConfiguration(_configuration); if (isUseReferenceType) { // Each sqlname correspond to a identity of the reference table. // Should be able to get the original type of the reference // field. typeInfo = _typeMapper.getType(refIdTypes[i]); if (typeInfo == null) { throw new TypeNotFoundException( "can not find reference type " + refIdTypes[i] + " of class " + cm.getName()); } } // process attributes of field field.setName(sqlnames[i]); field.setTable(table); field.setType(typeInfo); field.setIdentity(isFieldIdentity); field.setRequired(fm.getRequired()); field.setKeyGenerator(keyGen); table.addField(field); if (isFieldIdentity) { primarykey.addField(field); } } // Create foreign keys. if (isUseReferenceType) { addOneOneForeignKey(table, fm); } } } // Process extends, if extends is defined. processExtendedClass(table, cm); return table; } /** * Extract identities from extended ClassMapping and add them to table. * * @param table Table to add identities of extended mapping to. * @param cm ClassMapping of table to extract get mappings from. * @throws GeneratorException throw exception if key-gen is not found. */ private void processExtendedClass(final Table table, final ClassMapping cm) throws GeneratorException { Object extendClass = cm.getExtends(); if (extendClass == null) { return; } ClassMapping extendCm = (ClassMapping) extendClass; String[] childIds = _mappingHelper.getClassMappingSqlIdentity(cm, false); if (childIds.length != 0) { // Check consistency. String[] childTypes = _mappingHelper.resolveTypeReferenceForIds(cm); String[] parentTypes = _mappingHelper.resolveTypeReferenceForIds(extendCm); if (childTypes.length != parentTypes.length) { throw new GeneratorException("Cannot resolve type for class '" + cm.getName() + "' from extend class '" + extendCm.getName() + "'"); } for (int i = 0; i < childTypes.length; i++) { if (!childTypes[i].equalsIgnoreCase(parentTypes[i])) { throw new GeneratorException("Cannot resolve type for class '" + cm.getName() + "' from extend class '" + extendCm.getName() + "'"); } } return; } boolean isUseFieldIdentity = _mappingHelper.isUseFieldIdentity(extendCm); Enumeration<? extends FieldMapping> extendEf = extendCm.getClassChoice().enumerateFieldMapping(); // Process key generator. String keygenerator = extendCm.getKeyGenerator(); KeyGenerator keyGen = null; if (keygenerator != null) { keyGen = _keyGenRegistry.getKeyGenerator(keygenerator.toUpperCase()); } table.setKeyGenerator(keyGen); while (extendEf.hasMoreElements()) { FieldMapping extendFm = extendEf.nextElement(); // Skip if <sql> tag is not defined. if (extendFm.getSql() == null) { continue; } boolean isFieldIdentity = extendFm.getIdentity(); if (!isUseFieldIdentity) { isFieldIdentity = _mappingHelper.isIdentity(extendCm, extendFm); } // Checke for many-key, many-table definition. if (isFieldIdentity && extendFm.getSql().getManyKeyCount() <= 0) { // Column is defiend as normal column in child, but it is id which is // inherited from parent. if (mergeIfDefInBothClasses(table, cm, extendFm)) { continue; } String[] sqlnames = extendFm.getSql().getName(); String sqltype = extendFm.getSql().getType(); TypeInfo typeInfo = null; ClassMapping cmRef = null; String[] refIdTypes = null; boolean isUseReferenceType = false; if (sqltype != null) { typeInfo = _typeMapper.getType(sqltype); } // If typeInfo is null, this table has a reference to another one. if (typeInfo == null) { cmRef = _mappingHelper.getClassMappingByName(extendFm.getType()); // If cmRef is null, the reference class could not be found. if (cmRef == null) { typeInfo = _typeMapper.getType(extendFm.getType()); if (typeInfo == null) { throw new TypeNotFoundException("can not resolve type " + extendFm.getType()); } } else { isUseReferenceType = true; refIdTypes = _mappingHelper.resolveTypeReferenceForIds(extendFm .getType()); // If number of reference table's Ids differ from number of // field elements. if (refIdTypes.length != sqlnames.length) { throw new TypeNotFoundException( "number of reference table's Id differs" + " to number of field elements '" + extendFm.getName() + "' of class '" + extendCm.getName() + "'" + refIdTypes.length + "," + sqlnames.length); } } } // Create fields. for (int i = 0; i < sqlnames.length; i++) { Field field = _schemaFactory.createField(); field.setConfiguration(_configuration); if (isUseReferenceType) { // Each sqlname is correspond to an identity of the reference // table so, it should be possible to get the original type of // the reference field. typeInfo = _typeMapper.getType(refIdTypes[i]); if (typeInfo == null) { throw new TypeNotFoundException( "can not find reference type " + refIdTypes[i] + " of class " + extendCm.getName()); } } field.setName(sqlnames[i]); field.setTable(table); field.setType(typeInfo); field.setIdentity(isFieldIdentity); field.setKeyGenerator(keyGen); if (isFieldIdentity) { table.getPrimaryKey().addField(field); } table.addField(field); } } } // Process extends. if (extendCm.getExtends() != null) { processExtendedClass(table, extendCm); } } /** * This function is used to merge a table if it is mapped to many classes. * * @param table Table to merge. * @param cm ClassMapping of table. * @param extendFm FieldMapping of extended class to be merged into table. * @return <code>true</code> if column is defiend as normal column in child, but is * identity which is inherited from parent. */ private boolean mergeIfDefInBothClasses(final Table table, final ClassMapping cm, final FieldMapping extendFm) { Enumeration<? extends FieldMapping> ef = cm.getClassChoice().enumerateFieldMapping(); while (ef.hasMoreElements()) { FieldMapping fm = ef.nextElement(); String fname = fm.getName(); // If extend field has the same name with one of parent's fields. if (fname != null && fname.equalsIgnoreCase(extendFm.getName())) { if (fm.getSql() == null) { continue; } String[] sqlnames = fm.getSql().getName(); for (int i = 0; i < sqlnames.length; i++) { table.getField(sqlnames[i]).setIdentity(true); } return true; } } return false; } /** * Add foreign key for 1:1 relations to table schema object. * * @param table Table to add foreign key to. * @param fm FieldMapping of relation. * @throws GeneratorException If failed to create foreign key schema object. */ private void addOneOneForeignKey(final Table table, final FieldMapping fm) throws GeneratorException { ForeignKey fk = _schemaFactory.createForeignKey(); fk.setConfiguration(_configuration); fk.setTable(table); fk.setName(table.getName() + "_" + fm.getName()); String[] fieldNames = fm.getSql().getName(); for (int i = 0; i < fieldNames.length; i++) { for (int j = 0; j < table.getFieldCount(); j++) { Field field = table.getField(j); if (fieldNames[i].equals(field.getName())) { fk.addField(field); } } } ClassMapping cm = _mappingHelper.getClassMappingByName(fm.getType()); if (cm == null) { throw new GeneratorException("can not find class " + fm.getType()); } String referenceTableName = cm.getMapTo().getTable(); Table referenceTable = null; referenceTable = table.getSchema().getTable(referenceTableName); fk.setReferenceTable(referenceTable); String[] manykeys = fm.getSql().getManyKey(); if (manykeys == null || manykeys.length == 0) { manykeys = _mappingHelper.getClassMappingSqlIdentity(cm, true); } for (int i = 0; i < manykeys.length; i++) { for (int j = 0; j < referenceTable.getFieldCount(); j++) { Field field = referenceTable.getField(j); if (manykeys[i].equals(field.getName())) { fk.addReferenceField(field); } } } fk.setRelationType(ForeignKey.ONE_ONE); table.addForeignKey(fk); } /** * Add column for a resolving table which is required by M:N relationship. * * @param fm FieldMapping. * @param cm ClassMapping. */ private void addResolveField(final FieldMapping fm, final ClassMapping cm) { String keyGen = cm.getKeyGenerator(); ClassMapping resolveCm = null; // Get table, if not existe, create one. if (_resolveTable.containsKey(fm.getSql().getManyTable())) { resolveCm = _resolveTable.get(fm.getSql().getManyTable()); } else { resolveCm = new ClassMapping(); resolveCm.setName(fm.getSql().getManyTable()); resolveCm.setKeyGenerator(keyGen); MapTo mapto = new MapTo(); mapto.setTable(fm.getSql().getManyTable()); resolveCm.setMapTo(mapto); _resolveTable.put(fm.getSql().getManyTable(), resolveCm); } FieldMapping resolveFm = new FieldMapping(); resolveFm.setIdentity(true); resolveFm.setName(cm.getMapTo().getTable()); resolveFm.setType(cm.getName()); ClassChoice cc = resolveCm.getClassChoice(); if (cc == null) { cc = new ClassChoice(); resolveCm.setClassChoice(cc); } cc.addFieldMapping(resolveFm); Sql sql = new Sql(); String[] sqlname = fm.getSql().getManyKey(); if (sqlname == null || sqlname.length == 0) { _mappingHelper.getClassMappingSqlIdentity(cm, true); } sql.setName(sqlname); resolveFm.setSql(sql); } //-------------------------------------------------------------------------- }