alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (DatabaseGenerator.java)

This example Glassfish source code file (DatabaseGenerator.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

columnpairelement, dbexception, foreignkeyelement, io, mappingclasselement, mappingclasselement, modelexception, noi18n, noi18n, relationshipelement, results, string, string, tableelement, tableelement, util

The Glassfish DatabaseGenerator.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * DatabaseGenerator.java
 *
 * Created on Jan 14, 2003
 */

package com.sun.jdo.spi.persistence.generator.database;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.ResourceBundle;

import org.netbeans.modules.dbschema.*;
import org.netbeans.modules.dbschema.util.NameUtil;
import com.sun.jdo.api.persistence.model.mapping.*;
import com.sun.jdo.api.persistence.model.mapping.impl.*;
import com.sun.jdo.api.persistence.model.jdo.*;
import com.sun.jdo.api.persistence.model.jdo.impl.*;
import com.sun.jdo.api.persistence.model.Model;
import com.sun.jdo.api.persistence.model.ModelException;
import com.sun.jdo.spi.persistence.utility.JavaTypeHelper;

import org.glassfish.persistence.common.I18NHelper;
import com.sun.jdo.spi.persistence.utility.logging.Logger;


/**
 * This class generates a database schema and a Map of mapping classes from a
 * set of JDO classes.
 */
public class DatabaseGenerator {
    /** @see DatabaseGenerationConstants#DOT */
    private static final char DOT = DatabaseGenerationConstants.DOT;

    /** Holds Java type information */
    private final Model model;

    /** Provides information on how database info should be generated */
    private final MappingPolicy mappingPolicy;

    /** List of NameTuple objects which holds persistence class name,
     * desired table name and hash class name for database generation. */
    private final List pcClasses;

    /** Map from persistence-capable class names to MappingClassElement's. */
    // See also DatabaseGenerator.Results.mappingClasses.
    private final Map mappingClasses = new HashMap();
    
    /** Generated database schema. */
    private final SchemaElement schema;

    /**
     * Used to recognize and remove classname suffixes.  See {@link
     * #getShortClassName}.
     */
    private final String classSuffix;

    /** The logger */
    private static final Logger logger =
        LogHelperDatabaseGenerator.getLogger();
    
    /** I18N message handler */
    private static final ResourceBundle messages =
        I18NHelper.loadBundle(DatabaseGenerator.class);


    /**
     * Contains the results of invoking DatabaseGenerator.generate()
     */
    public class Results {
        /** Generated database schema. */
        private final SchemaElement schema;

        /** Set of generated MappingClassElement's. */
        // Notice that this is a Set, while the outer class has a Map.  This
        // is intentional: DatabaseGenerator.addRelationships needs to get a
        // MappingClassElement for a pc class name, but the clients of the
        // DatabaseGenerator should only need the mapping classes.
        private final Set mappingClasses;

        Results(SchemaElement schema, Map mappingClasses) {
            this.schema = schema;
            this.mappingClasses = new HashSet(mappingClasses.values());
        }

        /** @return Generated SchemaElement. */
        public SchemaElement getSchema() {
            return schema;
        }

        /** @return Generated mapping classes. */
        public Set getMappingClasses() {
            return mappingClasses;
        }
    }
    

    /**
     * This class holds three strings which contain three type of information.
     * For database generation, for each persistence class, we need to have 
     * persistence class name to look up persistence class, desired table name
     * for table name and hash class name for unique table name. Depending on 
     * the caller's option, hash class name can be same as persistence class 
     * name or it can be different from persistence class name.
     */
    public static class NameTuple {

        /** persistence class name */
        private final String persistenceClassName;

        /** desired table name */
        private final String desiredTableName;

        /** hash class name */
        private final String hashClassName;

        /** 
         * An object holds three string objects.
         * @param persistenceClassName persistence class name for 
         * persistence class look up 
         * @param desiredTableName it can be used for table name
         * @param hashClassName it can be used for unique table name
         */
        public NameTuple(
            String persistenceClassName, String desiredTableName, 
                    String hashClassName) {

            this.persistenceClassName = persistenceClassName;
            this.desiredTableName = desiredTableName;
            this.hashClassName = (hashClassName != null) ? 
                hashClassName : persistenceClassName;
        }

        /** 
         * An object holds three string objects.
         * @param persistenceClassName persistence class name
         * @param desiredTableName name for creating table name
         */
        public NameTuple(String persistenceClassName, String desiredTableName) {
            this(persistenceClassName, desiredTableName, null);
        }

        /** @return persistence class name. */
        public String getPersistenceClassName() {
            return persistenceClassName;
        }

        /** @return hash class name. */
        public String getHashClassName() {
            return hashClassName;
        }

        /** @return desired table name. */
        public String getDesiredTableName() {
            return desiredTableName;
        }
    }

    /**
     * Generate database schema and mapping model from given map of
     * persistence class name.
     * @param model Holds java type information.
     * @param pcClasses A list of NameTuple objects containing persistence 
     * class name and table names.
     * @param mappingPolicy Determines how dbvendor and user influence
     * generated schema.
     * @param schemaName Identifies the generated schema.
     * @param classSuffix Class name suffix that should be removed when
     * creating table names from class names.
     */
    private DatabaseGenerator(
            Model model, List pcClasses, MappingPolicy mappingPolicy,
            String schemaName, String classSuffix)
            throws DBException {

        this.model = model;
        this.pcClasses = pcClasses;
        this.mappingPolicy = mappingPolicy;
        this.schema = DBElementFactory.createSchema(schemaName);
        this.classSuffix = classSuffix;
    }

    /**
     * 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 <i>outputDir.schemaName.
     * @param model Holds java type information.
     * @param pcClasses A List of NameTuple objects containing persistence
     * class name and table names.
     * @param mappingPolicy Determines how dbvendor and user influence
     * generated schema.
     * @param schemaName Identifies the generated schema.
     * @param classSuffix Class name suffix that should be removed when
     * creating table names from class names.
     * @param generateMappingClasses if it is true, generate mapping classes
     * also.  <em>Currently MappingClassElement's are always
     * generated, and the parameter's value is ignored.</em>
     * @return A DatabaseGenerator.Results instance which holds the results
     * of generation.
     */
    public static Results generate(
            Model model, List pcClasses, MappingPolicy mappingPolicy,
            String schemaName, String classSuffix,
            boolean generateMappingClasses) 
            throws DBException, IOException, ModelException {

        DatabaseGenerator generator = new DatabaseGenerator(
                model, pcClasses, mappingPolicy,
                schemaName, classSuffix);

        Results rc = generator.generate();

        mappingPolicy.resetCounter();

        return rc;
    }

    /**
     * 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}.
     * @return A DatabaseGenerator.Results instance which holds the results
     * of generation.
     */
    private Results generate() throws DBException, ModelException {
        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);
    }

    /**
     * Get the primary table element for the mapping class.
     * @param mappingClass That which is associated with the table.
     * @return Table that is associated with mapping class.
     * @throws DBException
     */
    private TableElement getPrimaryTable(MappingClassElement mappingClass)
            throws DBException {

        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;
        }
    }

    /**
     * Create mapping class and associated table and PC class
     * @param pcClass PC class that mapping class associated
     * @param table table element that mapping class associated
     * @return MappingClassElement associated with table and PC class
     * @throws ModelException
     */
    private MappingClassElement createMappingClass(
            PersistenceClassElement pcClass, TableElement table)
            throws ModelException {

         MappingClassElement mappingClass =
             new MappingClassElementImpl(pcClass);

         mappingClass.setDatabaseRoot(schema);
         mappingClass.addTable(table);
         return mappingClass;
    }

    /**
     * Create mapping field and add to mapping class
     * @param fieldName a String for field name
     * @param mappingClass mapping class object that field belong to
     * @return mapping field object
     * @throws ModelException
     */
    private MappingFieldElement createAndAttachMappingField(
            String fieldName, MappingClassElement mappingClass,
            ColumnElement column) throws ModelException {

        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;
    }


    /**
     * 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
     * @param relationName relationship name for the declaring mapping class
     * @param mappingClass mapping class that holds the relationship
     * @param foreign key which hold column pair for the relationship
     * @throws ModelException
     */
    private void addMappingRelationship(String relationName,
            MappingClassElement declaringClass, ForeignKeyElement fkey)
            throws ModelException {

        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);
    }

    /**
     * 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
     * @param relationName a String for relation name
     * @param mappingClass mapping class that holds the relationship
     * @param fkeyForeign holding column pair information for the relationship
     * @throws ModelException
     */
    private void addAssocMappingRelationship(String relationName,
            MappingClassElement declaringClass, ForeignKeyElement fkeyForeign)
            throws ModelException {

        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);
        }
    }

    /**
     * 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
     * @param relationName a String for relation name
     * @param mappingClass mapping class that holds the relationship
     * @param fkeyForeign holding column pair information for the relationship
     * @throws ModelException
     * @throws DBException
     */
    private void addInverseMappingRelationship(String relationName,
            MappingClassElement declaringClass, ForeignKeyElement fkey,
            boolean isJoin)
            throws ModelException, DBException {

        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);
            }
        }
    }

    /**
     * Create and add a relationship.
     * @param srcTable Source table of the relationship.
     * @param relTable Related table.
     * @param relName Name of the relationship.
     * @param inverseRelName Name of the inverse relationship.
     * @param mappingClass Mapping information for the source table.
     * @param relMappingClass Mapping information for the related table.
     * @param uniqueId Id that can be appened to relName to distinguish it
     * from other relNames in the database.
     * @param srcIsJoin True if srcTable is a join table
     * @return ForeignKeyElement representing the relationship.
     */
    private ForeignKeyElement createRelationship(TableElement srcTable,
            TableElement relTable, String relName, String inverseRelName,
            MappingClassElement mappingClass,
            MappingClassElement relMappingClass,
            String uniqueId, boolean srcIsJoin) 
            throws DBException, ModelException {

        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;
    }

    /**
     * Generate relationships for schema and mapping model from 
     * mappingClasses which already have all mapping fields populated. 
     * @throws DBException
     * @throws ModelExpception
     */
    private void addRelationships() 
        throws DBException, ModelException {
        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);
        }
    }

    /**
     * Generate foreign keys for relationships that were deferred; see {@link
     * addRelationships}.
     * @param deferredRelationships List of 1-1 relationships which are not
     * yet mapped by foreign keys
     * @param relationFKey Map from RelationshipElement to ForeignKeyElement,
     * indicating which relationships have already been mapped.
     */
    private void addDeferredRelationships(
            List deferredRelationships, Map relationFKey) 
            throws DBException, ModelException {

        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);
            }
        }
    }


    /**
     * A DeferredRelationship instance represents all the information required
     * to create a foreign key from the relationship.
     */
    static class DeferredRelationship {
        private final RelationshipElement relation;
        private final RelationshipElement inverseRelation;
        private final TableElement sourceTable;
        private final TableElement relTable;
        private final String relationName;
        private final String inverseRelName;
        private final MappingClassElement mappingClass;
        private final MappingClassElement relMappingClass;
        private final String uniqueId;
        
        
        DeferredRelationship(RelationshipElement relation,
                             RelationshipElement inverseRelation,
                             TableElement sourceTable,
                             TableElement relTable,
                             String relationName,
                             String inverseRelName,
                             MappingClassElement mappingClass,
                             MappingClassElement relMappingClass,
                             String uniqueId) {

            this.relation = relation;
            this.inverseRelation = inverseRelation;
            this.sourceTable = sourceTable;
            this.relTable = relTable;
            this.relationName = relationName;
            this.inverseRelName = inverseRelName;
            this.mappingClass = mappingClass;
            this.relMappingClass = relMappingClass;
            this.uniqueId = uniqueId;
        }

        RelationshipElement getRelation() { return relation; }
        RelationshipElement getInverseRelation() { return inverseRelation; }

        TableElement getSourceTable() { return sourceTable; }
        TableElement getRelTable() { return relTable; }

        String getRelationName() { return relationName; }
        String getInverseRelName() { return inverseRelName; }

        MappingClassElement getMappingClass() { return mappingClass; }
        MappingClassElement getRelMappingClass() { return relMappingClass; }

        String getUniqueId() { return uniqueId; }
    }


    
    /**
     * Check if the relationship has been visited
     * @param relation current visiting relationship
     * @param inverseRelation inverse relationship 
     * @param relationFKey a map to hold relation and foreign key
     * @return the foreign key element or null 
     */
    private ForeignKeyElement getMappedForeignKey(
            RelationshipElement relation, RelationshipElement inverseRelation,
            Map relationFKey) {

        ForeignKeyElement fkey =
                (ForeignKeyElement) relationFKey.get(relation);

        if (fkey == null) {
            return (ForeignKeyElement) relationFKey.get(inverseRelation);
        } else {
            return fkey;
        }
    }

    /**
     * Computes the class name (without package) for the supplied
     * class name and trims off the class suffix.
     * @param className the fully qualified name of the class
     * @return the class name (without package and class suffix) 
     * for the supplied class name
     */
    private String getShortClassName(String className) {
        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;
    }

    /**
     * Assert that the given object reference is <em>not null.
     * @param o Object reference that is checked for null.
     * @param failedItem String that names the item that is being checked.
     * @param accessor String which names an object that was use to try and
      * get object <code>o.
     * @throws ModelException If o is null.
     */
    private void validateModel(Object o,
                               String failedItem,
                               String accessor) throws ModelException {
        if (null == o) {
            String msg = I18NHelper.getMessage(
                    messages,
                    "EXC_InvalidRelationshipMapping",  // NOI18N
                    failedItem,
                    accessor);
            logger.log(Logger.SEVERE, msg);
            throw new ModelException(msg);
        }
    }

    /**
     * Debug support. Returns a string describing the table and it's keys
     * (only the first key is listed).
     * @param tblName name of the table described
     * @param tbl table being described
     * @param relName name of a relationship 
     */
    private static String getTblInfo(String tblName, TableElement tbl, String relName) {
        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
    }
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish DatabaseGenerator.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.