0001: /**
0002: * Copyright (C) 2001-2004 France Telecom R&D
0003: *
0004: * This library is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License as published by the Free Software Foundation; either
0007: * version 2 of the License, or (at your option) any later version.
0008: *
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Lesser General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Lesser General Public
0015: * License along with this library; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: */package org.objectweb.speedo.generation.jorm.rdb;
0018:
0019: import java.util.Collection;
0020: import java.util.Iterator;
0021: import java.util.Map;
0022:
0023: import org.objectweb.jorm.api.PException;
0024: import org.objectweb.jorm.lib.JormPathHelper;
0025: import org.objectweb.jorm.mapper.rdb.metainfo.RdbClassMapping;
0026: import org.objectweb.jorm.mapper.rdb.metainfo.RdbClassMultiMapping;
0027: import org.objectweb.jorm.mapper.rdb.metainfo.RdbExternalTable;
0028: import org.objectweb.jorm.mapper.rdb.metainfo.RdbGenClassMapping;
0029: import org.objectweb.jorm.mapper.rdb.metainfo.RdbJoin;
0030: import org.objectweb.jorm.mapper.rdb.metainfo.RdbPrimitiveElementMapping;
0031: import org.objectweb.jorm.mapper.rdb.metainfo.RdbTable;
0032: import org.objectweb.jorm.metainfo.api.Class;
0033: import org.objectweb.jorm.metainfo.api.ClassMapping;
0034: import org.objectweb.jorm.metainfo.api.GenClassMapping;
0035: import org.objectweb.jorm.metainfo.api.GenClassRef;
0036: import org.objectweb.jorm.metainfo.api.Manager;
0037: import org.objectweb.jorm.metainfo.api.Mapping;
0038: import org.objectweb.jorm.metainfo.api.MetaObject;
0039: import org.objectweb.jorm.metainfo.api.NameDef;
0040: import org.objectweb.jorm.metainfo.api.PrimitiveElement;
0041: import org.objectweb.jorm.metainfo.api.PrimitiveElementMapping;
0042: import org.objectweb.speedo.api.SpeedoException;
0043: import org.objectweb.speedo.api.SpeedoProperties;
0044: import org.objectweb.speedo.generation.jorm.JormMIMappingBuilder;
0045: import org.objectweb.speedo.metadata.SpeedoClass;
0046: import org.objectweb.speedo.metadata.SpeedoCollection;
0047: import org.objectweb.speedo.metadata.SpeedoColumn;
0048: import org.objectweb.speedo.metadata.SpeedoCommonField;
0049: import org.objectweb.speedo.metadata.SpeedoDiscriminator;
0050: import org.objectweb.speedo.metadata.SpeedoElement;
0051: import org.objectweb.speedo.metadata.SpeedoField;
0052: import org.objectweb.speedo.metadata.SpeedoIdentity;
0053: import org.objectweb.speedo.metadata.SpeedoInheritedField;
0054: import org.objectweb.speedo.metadata.SpeedoJoinColumn;
0055: import org.objectweb.speedo.metadata.SpeedoMap;
0056: import org.objectweb.speedo.metadata.SpeedoNoFieldColumn;
0057: import org.objectweb.speedo.metadata.SpeedoTable;
0058: import org.objectweb.speedo.naming.api.MIBuilderHelper;
0059: import org.objectweb.util.monolog.api.BasicLevel;
0060: import org.objectweb.util.monolog.api.Loggable;
0061: import org.objectweb.util.monolog.api.Logger;
0062: import org.objectweb.util.monolog.api.LoggerFactory;
0063:
0064: /**
0065: * This class is an implementation of the JormMIMappingBuilder for the mapper
0066: * rdb and its sub mappers. It defines the O/R mapping of persistent classes.
0067: * It supports the mapping a of a class into several tables (multi table class
0068: * mapping).
0069: *
0070: * @author S.Chassande-Barrioz
0071: */
0072: public class RdbJORMMapping implements JormMIMappingBuilder,
0073: SpeedoProperties, Loggable {
0074:
0075: private Logger logger;
0076:
0077: private boolean debug = false;
0078:
0079: // IMPLEMENTATION OF THE JormMIMappingBuilder INTERFACE //
0080: //------------------------------------------------------//
0081:
0082: /**
0083: * Defines the mapping the a class. It creates the table(s) where the
0084: * class is stored. When there are several tables (multitable mapping) or
0085: * in case of vertical inheritance, this method bulds the join between
0086: * the main table and the secondary table.
0087: * @param clazz is the JORM meta object representing the persistent class.
0088: * @param sc is the Speedo meta object representing the persistent class.
0089: * @param mapping is the JORM Meta object associated to the persistent class
0090: * @return the JORM meta object of mapping corresponding to the class.
0091: */
0092: public ClassMapping createClassMapping(Class clazz, SpeedoClass sc,
0093: Mapping mapping) throws PException, SpeedoException {
0094: //build ClassMapping instance
0095: RdbClassMultiMapping rcm = new RdbClassMultiMapping(
0096: "multi-table", clazz, mapping);
0097: mapping.setClassMapping(rcm);
0098:
0099: //Build JORM meta objects for the main table
0100: if (sc.mainTable != null) {
0101: if (debug) {
0102: logger.log(BasicLevel.DEBUG, "define main table '"
0103: + sc.mainTable.name + "' of the class '"
0104: + sc.getFQName() + "'.");
0105: }
0106: rcm.createRdbTable(sc.mainTable.name);
0107: }
0108: //Build JORM meta objects for the secondary (external) tables
0109: if (sc.joinToExtTables != null) {
0110: for (int i = 0; i < sc.joinToExtTables.length; i++) {
0111: RdbExternalTable extTable = rcm
0112: .createRdbExternalTable(sc.joinToExtTables[i].extTable.name);
0113: RdbJoin join = extTable.createRdbJoin("_" + i);
0114: if (debug) {
0115: logger
0116: .log(
0117: BasicLevel.DEBUG,
0118: "define external table '"
0119: + sc.joinToExtTables[i].extTable.name
0120: + "' for the class '"
0121: + sc.getFQName() + "'.");
0122: }
0123: for (Iterator it = sc.joinToExtTables[i].columns
0124: .iterator(); it.hasNext();) {
0125: SpeedoJoinColumn jc = (SpeedoJoinColumn) it.next();
0126: join.addJoinColumnNames(jc.targetColumn,
0127: jc.column.name);
0128: if (debug) {
0129: logger
0130: .log(
0131: BasicLevel.DEBUG,
0132: "\tdefine join between columns '"
0133: + jc.column.name
0134: + "' and '"
0135: + jc.column.targetColumn
0136: + "'.");
0137: }
0138: }
0139: }
0140: }
0141:
0142: //manage inheritance
0143: if (sc.inheritance != null
0144: && sc.inheritance.super ClassName != null) {
0145: String ruleName = null;
0146: if (sc.inheritance.isFilteredMapping()) {
0147: ruleName = RdbClassMapping.MAP_NEW_FIELDS_TO_EXTENDED_STRUCTURES;
0148: } else if (sc.inheritance.isHorizontalMapping()) {
0149: ruleName = RdbClassMapping.REMAP_FIELDS_TO_NEW_STRUCTURES;
0150: } else if (sc.inheritance.isVerticalMapping()) {
0151: ruleName = RdbClassMapping.MAP_NEW_FIELDS_TO_ADDED_STRUCTURES;
0152: //TODO: link the main table to the one of the parent
0153: }
0154: SpeedoClass parent = sc.getSpeedoClassFromContext(sc
0155: .getSuperClassName());
0156: rcm.createParentClassMapping(ruleName, sc.jormclass
0157: .getSuperClass(parent.getFQName()));
0158: //Add inter dependencies between the parent and its child
0159: if (debug) {
0160: logger.log(BasicLevel.DEBUG,
0161: "Add dependencies between "
0162: + parent.getFQName() + " and "
0163: + sc.getFQName());
0164: }
0165: rcm.addDependency(parent.getFQName());
0166: getClassMapping(mapping, parent.jormclass).addDependency(
0167: sc.getFQName());
0168: }
0169: return rcm;
0170: }
0171:
0172: /**
0173: * Maps the identifier fields.
0174: * @param cm is the JORM meta object representing the mapping of a
0175: * persistent class.
0176: * @param nd is the JORM meta object representing the name definition of the
0177: * persistent class.
0178: * @param sc is the Speedo meta object representing the persistent class
0179: * @param mibh is a helper for the building of the JORM meta information.
0180: */
0181: public void createClassIdentifierNameDefMapping(ClassMapping cm,
0182: NameDef nd, SpeedoClass sc, MIBuilderHelper mibh)
0183: throws PException, SpeedoException {
0184: Class clazz = (Class) nd.getParent();
0185: RdbClassMapping rcm = (RdbClassMapping) cm;
0186: if (nd.isFieldName()) {
0187: //The identifier of the class is based on a single field
0188: PrimitiveElement pe = (PrimitiveElement) clazz
0189: .getTypedElement(nd.getFieldName());
0190: if (pe == null) {
0191: throw new SpeedoException(
0192: "impossible to map the fields of the namedef of the metaobject '"
0193: + JormPathHelper.getPath(clazz) + "'.");
0194: }
0195: SpeedoField sf = sc.getField(pe.getName());
0196: if (sf == null) {
0197: // The single field used as identifier is not a visible
0198: // persistent field
0199: if (sc.identity.columns != null
0200: && sc.identity.columns.length == 1
0201: && sc.identity.columns[0] != null
0202: && sc.identity.columns[0].column != null
0203: && sc.identity.columns[0].column.name != null) {
0204: // the column name of the hidden identity field has been
0205: // specified
0206: createFieldMapping(pe,
0207: sc.identity.columns[0].column.name,
0208: sc.identity.columns[0].column.sqlType, rcm
0209: .getMainRdbTable(), null);
0210: } else {
0211: // No column name specified
0212: createFieldMapping(pe, (SpeedoCommonField) null, cm);
0213: }
0214: }
0215: } else {
0216: // The identifier of the persistent class is based on several
0217: // persistent field.
0218: Collection c = nd.getNameRef().getProjection().values();
0219: int i = 0;
0220: for (Iterator it = c.iterator(); it.hasNext();) {
0221: PrimitiveElement pe = (PrimitiveElement) clazz
0222: .getTypedElement((String) it.next());
0223: if (pe == null) {
0224: throw new SpeedoException(
0225: "impossible to map the fields of the namedef of the metaobject '"
0226: + JormPathHelper.getPath(clazz)
0227: + "'.");
0228: }
0229: //find the corresponding Speedo meta object.
0230: SpeedoField sf = sc.getField(pe.getName());
0231: if (sf == null) {
0232: SpeedoNoFieldColumn col = getIdentityColumn(
0233: sc.identity, pe.getName());
0234: if (col != null) {
0235: //column name is specified for the field
0236: createFieldMapping(pe, col.column.name,
0237: col.column.sqlType, rcm
0238: .getMainRdbTable(), null);
0239: } else {
0240: // no column found
0241: createFieldMapping(pe,
0242: (SpeedoCommonField) null, cm);
0243: }
0244: }
0245: i++;
0246: }
0247: }
0248: }
0249:
0250: /**
0251: * Finds the column among identity column, corresponding to a field name
0252: * @param identity is the identity meta object of a class
0253: * @param pkField is the name of the expected field.
0254: */
0255: private SpeedoNoFieldColumn getIdentityColumn(
0256: SpeedoIdentity identity, String pkField) {
0257: if (identity.columns == null) {
0258: return null;
0259: }
0260: for (int i = 0; i < identity.columns.length; i++) {
0261: if (identity.columns[i].column != null
0262: && pkField
0263: .equals(identity.columns[i].column.targetField)) {
0264: return identity.columns[i];
0265: }
0266: }
0267: return null;
0268: }
0269:
0270: /**
0271: * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
0272: * is created only if the column does not already exist in the table
0273: * @param pe is the primitive element to map
0274: * @param colName is the column name where the PE has to be mapped
0275: * @param colType is the column type where the PE has to be mapped
0276: * @param t is the table where to map the PE.
0277: * @param join is the JORM meta object representing the join to the
0278: * secondary/external table to specify when the column is in a secondary /
0279: * external table. If the column is in the main table, this parameter must
0280: * be null.
0281: * @return the created or existing PEM corresponding to the specified
0282: * column name.
0283: */
0284: private RdbPrimitiveElementMapping createFieldMapping(
0285: PrimitiveElement pe, String colName, String colType,
0286: RdbTable t, RdbJoin join) throws PException,
0287: SpeedoException {
0288: if (logger.isLoggable(BasicLevel.DEBUG)) {
0289: logger.log(BasicLevel.DEBUG, "The Primitive field "
0290: + pe.getName()
0291: + " is mappeded over the column (name=" + colName
0292: + ", sql type=" + colType + ")");
0293: }
0294: RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) t
0295: .getPrimitiveElementMappingByCol(colName);
0296: if (pem == null) {
0297: pem = t.createPrimitiveElementMapping(pe, colName, colType,
0298: false);
0299: }
0300: if (join != null) {
0301: pem.bindPrimitiveElement(join, pe);
0302: }
0303: return pem;
0304: }
0305:
0306: /**
0307: * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
0308: * is created only if the column does not already exist in the table
0309: * @param pe is the primitive element to map
0310: * @param sf is the persistent field holding column definition
0311: * @param cm is the RdbClassMapping correponding to the persistent class
0312: * owning the field. It permits to find tha table where the field is mapped.
0313: * @return the created or existing PEM corresponding to the specified
0314: * column name.
0315: */
0316: private PrimitiveElementMapping createFieldMapping(
0317: PrimitiveElement pe, SpeedoCommonField sf, ClassMapping cm)
0318: throws PException, SpeedoException {
0319: String colName;
0320: String colType;
0321: RdbClassMultiMapping rcm = (RdbClassMultiMapping) cm;
0322: RdbTable t;
0323: RdbJoin join = null;
0324: if (sf == null) {
0325: //auto compute column name
0326: colName = pe.getName();
0327: colType = null;
0328: //get the main table as default
0329: t = rcm.getMainRdbTable();
0330: } else {
0331: //find the column corresponding to the primitive field
0332: SpeedoColumn[] cols = sf.columns;
0333: if (cols == null || cols.length == 0) {
0334: throw new SpeedoException("No column defined for the "
0335: + sf.getSourceDesc());
0336: }
0337: if (cols.length > 1) {
0338: throw new SpeedoException(
0339: "More than one column for the "
0340: + sf.getSourceDesc());
0341: }
0342: SpeedoColumn col = cols[0];
0343: colName = col.name;
0344: colType = col.sqlType;
0345: //Find the table (JORM Meta object)
0346: if (sf.join == null) {
0347: t = rcm.getMainRdbTable();
0348: } else {
0349: t = rcm.getRdbExternalTable(sf.join.extTable.name);
0350: int idx = sf.moClass.getJoinIndex(sf.join);
0351: if (idx != -1) {
0352: join = ((RdbExternalTable) t).getRdbJoin("_" + idx);
0353: }
0354: }
0355: }
0356: return createFieldMapping(pe, colName, colType, t, join);
0357: }
0358:
0359: /**
0360: * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
0361: * is created only if the column does not already exist in the table
0362: * @param pe is the primitive element to map
0363: * @param sf is the persistent field holding column definition
0364: * @param cm is the RdbClassMapping correponding to the persistent class
0365: * owning the field. It permits to find tha table where the field is mapped.
0366: * @return the created or existing PEM corresponding to the specified
0367: * column name.
0368: */
0369: public PrimitiveElementMapping createFieldMapping(
0370: PrimitiveElement pe, SpeedoField sf, ClassMapping cm)
0371: throws PException, SpeedoException {
0372: return createFieldMapping(pe, (SpeedoCommonField) sf, cm);
0373: }
0374:
0375: /**
0376: * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
0377: * is created only if the column does not already exist in the table
0378: * @param pe is the primitive element to map
0379: * @param sif is the persistent field holding column definition
0380: * @param cm is the RdbClassMapping correponding to the persistent class
0381: * owning the field. It permits to find tha table where the field is mapped.
0382: * @return the created or existing PEM corresponding to the specified
0383: * column name.
0384: */
0385: public PrimitiveElementMapping createFieldMapping(
0386: PrimitiveElement pe, SpeedoInheritedField sif,
0387: ClassMapping cm) throws PException, SpeedoException {
0388: return createFieldMapping(pe, (SpeedoCommonField) sif, cm);
0389: }
0390:
0391: /**
0392: * Creates a PEM (PrimitiveElementMapping) for a PrimitiveElement. The PEM
0393: * is created only if the column does not already exist in the table
0394: * @param pe is the primitive element to map
0395: * @param sif is the persistent field holding column definition
0396: * @param cm is the RdbClassMapping correponding to the persistent class
0397: * owning the field. It permits to find tha table where the field is mapped.
0398: * @return the created or existing PEM corresponding to the specified
0399: * column name.
0400: */
0401: public PrimitiveElementMapping createFieldMapping(
0402: PrimitiveElement pe, SpeedoNoFieldColumn snfc,
0403: ClassMapping cm) throws PException, SpeedoException {
0404:
0405: return createFieldMapping(pe, snfc.column.name,
0406: snfc.column.sqlType, ((RdbClassMultiMapping) cm)
0407: .getMainRdbTable(), null);
0408: }
0409:
0410: /**
0411: * Creates the mapping of the name def (JORM meta object) corresponding to
0412: * a reference to a persistent class from a persistent class.
0413: * According to the mapping information hold by the SpeedoField, the
0414: * Class Reference is mapped in the main table or in an external table.
0415: * The external can be the table of the targeted class in case of One-One
0416: * relationship.
0417: * As the implementation of this method is a bit complex, it has been divided
0418: * in two local methods:
0419: * - createLocalClassRefNameDefMapping
0420: * - createExternalClassRefNameDefMapping
0421: *
0422: * @param cm is the MappingStructure which will host the mapping of the
0423: * reference
0424: * @param nd is the namedef corresponding to the reference
0425: * @param sf is the Speedo meta object representing the persistent field.
0426: * referencing a class.
0427: * @see #createLocalClassRefNameDefMapping(RdbClassMultiMapping, Class, SpeedoCommonField, NameDef, NameDef, SpeedoClass)
0428: * @see #createExternalClassRefNameDefMapping(RdbClassMultiMapping, Class, SpeedoCommonField, NameDef, RdbClassMultiMapping, NameDef, SpeedoClass)
0429: */
0430: public void createClassRefNameDefMapping(ClassMapping cm,
0431: NameDef nd, SpeedoCommonField sf) throws PException,
0432: SpeedoException {
0433: RdbClassMultiMapping rcm = (RdbClassMultiMapping) cm;
0434: SpeedoClass tclass = null;
0435: if (sf instanceof SpeedoField) {
0436: tclass = ((SpeedoField) sf).getReferencedClass();
0437: } else if (sf instanceof SpeedoInheritedField) {
0438: tclass = ((SpeedoInheritedField) sf).inheritedField
0439: .getReferencedClass();
0440: }
0441: RdbClassMultiMapping trcm = (RdbClassMultiMapping) tclass.jormclass
0442: .getClassProject(cm.getProjectName()).getMapping(
0443: cm.getMapperName()).getClassMapping();
0444: NameDef tnd = (NameDef) trcm.getIdentifierMapping()
0445: .getLinkedMO();
0446: Class jclass = sf.moClass.jormclass;
0447: if (sf.join == null) {
0448: createLocalClassRefNameDefMapping(rcm, jclass, sf, nd, tnd,
0449: tclass);
0450: } else {
0451: createExternalClassRefNameDefMapping(rcm, jclass, sf, nd,
0452: trcm, tnd, tclass);
0453: }
0454: //add the dependency
0455: trcm.addDependency(sf.moClass.getFQName());
0456: }
0457:
0458: /**
0459: * Creates the mapping of the name def (JORM meta object) corresponding to
0460: * a reference to a persistent class from a persistent class. The class
0461: * reference is mapped into a local foreign key.
0462: *
0463: * @param rcm is the MappingStructure which will host the mapping of the
0464: * reference
0465: * @param sf is the Speedo meta object representing the persistent field.
0466: * referencing a class.
0467: * @param nd is the namedef corresponding to the reference
0468: * @param tnd is the namedef corresponding to the referenced class
0469: * (identifier)
0470: * @param tclass is the referenced class.
0471: * @see createClassRefNameDefMapping
0472: */
0473: private void createLocalClassRefNameDefMapping(
0474: RdbClassMultiMapping rcm, Class jclass,
0475: SpeedoCommonField sf, NameDef nd, NameDef tnd,
0476: SpeedoClass tclass) throws PException, SpeedoException {
0477: RdbTable table = rcm.getRdbTable();
0478: //Map the name def of the reference over new local columns
0479: if (nd.isFieldName()) {
0480: PrimitiveElement pe = (PrimitiveElement) jclass
0481: .getTypedElement(nd.getFieldName());
0482: RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) table
0483: .getPrimitiveElementMappingByCol(sf.columns[0].name);
0484: if (pem == null) {
0485: // create it
0486: table.createPrimitiveElementMapping(pe,
0487: sf.columns[0].name, sf.columns[0].sqlType,
0488: false);
0489: } else {
0490: //it already exists
0491: //remove the old hidden field
0492: ((Class) pe.getParent()).removeTypedElement(pe
0493: .getName());
0494: //Use the existing one in the name def
0495: pe = (PrimitiveElement) pem.getLinkedMO();
0496: nd.setFieldName(pe.getName());
0497: if (pem.getType() == null) {
0498: pem.setType(sf.columns[0].sqlType);
0499: }
0500: }
0501: } else {
0502: Map tndproj = tnd.getNameRef().getProjection();
0503: Iterator it = nd.getNameRef().getProjection().entrySet()
0504: .iterator();
0505: while (it.hasNext()) {
0506: Map.Entry me = (Map.Entry) it.next();
0507: //find the field in the class used in by this name def
0508: PrimitiveElement pe = (PrimitiveElement) jclass
0509: .getTypedElement((String) me.getValue());
0510: //find the pk column
0511: RdbPrimitiveElementMapping tpem = getPEMOfField(tclass,
0512: (Mapping) rcm.getParent(), (String) tndproj
0513: .get(me.getKey()));
0514: //find the fk column name
0515: SpeedoColumn fkCol = sf.getFKColumn(tpem.getName());
0516: //check if the fk column name is already used
0517: RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) table
0518: .getPrimitiveElementMappingByCol(fkCol.name);
0519: if (pem == null) {
0520: // create it
0521: table.createPrimitiveElementMapping(pe, fkCol.name,
0522: fkCol.sqlType, false);
0523: } else {
0524: //it already exists
0525: //remove the old hidden field
0526: ((Class) pe.getParent()).removeTypedElement(pe
0527: .getName());
0528: //Use the existing one in the name def
0529: pe = (PrimitiveElement) pem.getLinkedMO();
0530: me.setValue(pe.getName());
0531: if (pem.getType() == null) {
0532: pem.setType(sf.columns[0].sqlType);
0533: }
0534: }
0535: }
0536: }
0537: }
0538:
0539: /**
0540: * Creates the mapping of the name def (JORM meta object) corresponding to
0541: * a reference to a persistent class from a persistent class. The class
0542: * reference is mapped into an external table. This external table can be
0543: * the table of the referenced class (backward foreign key) in case of
0544: * One-One relationship.
0545: *
0546: * @param rcm is the MappingStructure which will host the mapping of the
0547: * reference
0548: * @param trcm is the MappingStructure hosting the referenced class.
0549: * @param sf is the Speedo meta object representing the persistent field.
0550: * referencing a class.
0551: * @param nd is the namedef corresponding to the reference
0552: * @param tnd is the namedef corresponding to the referenced class
0553: * (identifier)
0554: * @param tclass is the referenced class.
0555: * @see createClassRefNameDefMapping
0556: */
0557: private void createExternalClassRefNameDefMapping(
0558: RdbClassMultiMapping rcm, Class jclass,
0559: SpeedoCommonField sf, NameDef nd,
0560: RdbClassMultiMapping trcm, NameDef tnd, SpeedoClass tclass)
0561: throws PException, SpeedoException {
0562: RdbExternalTable exttable = rcm
0563: .createRdbExternalTable(sf.join.extTable.name);
0564: if (sf.join.extTable.name.equals(tclass.mainTable.name)) {
0565: // The external table is the table of the targeted class
0566: exttable.setColocated(true);
0567: trcm.getRdbTable().setColocated(true);
0568: trcm.getRdbTable().setColocatedMaster(true);
0569: trcm.addDependency(sf.moClass.getFQName());
0570: rcm.addDependency(tclass.getFQName());
0571: //optimisation: if there is a reverse field then we suppose
0572: // that the user will do the coherence.
0573: if (sf instanceof SpeedoField) {
0574: exttable
0575: .setReadOnly(((SpeedoField) sf).isCoherentReverseField);
0576: } else if (sf instanceof SpeedoInheritedField) {
0577: exttable
0578: .setReadOnly(((SpeedoInheritedField) sf).inheritedField.isCoherentReverseField);
0579: }
0580: exttable.setColocated(true);
0581: exttable.setColocatedMaster(false);
0582: }
0583:
0584: //Define the join to the external table
0585: RdbJoin j = exttable.createRdbJoin(sf.name);
0586: for (Iterator fkColIt = sf.join.columns.iterator(); fkColIt
0587: .hasNext();) {
0588: SpeedoJoinColumn jc = (SpeedoJoinColumn) fkColIt.next();
0589: j.addJoinColumnNames(jc.targetColumn, jc.column.name);
0590: }
0591: // map the name def over pk field of the referenced class
0592: if (tnd.isFieldName()) {
0593: RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) trcm
0594: .getPrimitiveElementMapping(tnd.getFieldName(),
0595: true);
0596: PrimitiveElement pe = (PrimitiveElement) jclass
0597: .getTypedElement(nd.getFieldName());
0598: exttable.createPrimitiveElementMapping(pe, pem.getName(),
0599: null, false, j);
0600: } else {
0601: Map tclassndproj = tnd.getNameRef().getProjection();
0602: Iterator it = nd.getNameRef().getProjection().entrySet()
0603: .iterator();
0604: while (it.hasNext()) {
0605: Map.Entry me = (Map.Entry) it.next();
0606: PrimitiveElement pe = (PrimitiveElement) jclass
0607: .getTypedElement((String) me.getValue());
0608: RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) trcm
0609: .getPrimitiveElementMapping(
0610: (String) tclassndproj.get(me.getKey()),
0611: true);
0612: exttable.createPrimitiveElementMapping(pe, pem
0613: .getName(), null, false, j);
0614: }
0615: }
0616: }
0617:
0618: /**
0619: * Creates the mapping of the name def (JORM meta object) corresponding to
0620: * a reference to a persistent class from a generic persistent class.
0621: * The class reference is always stored in into the table of the genric
0622: * class. But it is important to check if the column used for the class
0623: * reference can be reused for index or generic class identifier.
0624: * @param gcm is the MappingStructure which will host the mapping of the
0625: * reference
0626: * @param nd is the namedef corresponding to the reference
0627: * @param sf is the Speedo meta object representing the persistent field
0628: * referencing a generic class.
0629: */
0630: public void createClassRefNameDefMapping(GenClassMapping gcm,
0631: NameDef nd, SpeedoField sf) throws PException,
0632: SpeedoException {
0633: RdbGenClassMapping rgcm = (RdbGenClassMapping) gcm;
0634: //Find the table (JORM Meta object)
0635: RdbTable table = rgcm.getRdbTable();
0636: GenClassRef gcr = (GenClassRef) gcm.getLinkedMO();
0637: if (nd.isFieldName()) {
0638: createPEMInGC(gcr.getHiddenField(nd.getFieldName()),
0639: sf.columns[0], table, nd, null);
0640: } else {
0641: SpeedoClass tclass = sf.getReferencedClass();
0642: RdbClassMultiMapping trcm = getClassMapping((Mapping) gcm
0643: .getParent(), tclass.jormclass);
0644: NameDef tnd = (NameDef) trcm.getIdentifierMapping()
0645: .getLinkedMO();
0646: Map tndproj = tnd.getNameRef().getProjection();
0647: Iterator it = nd.getNameRef().getProjection().entrySet()
0648: .iterator();
0649: while (it.hasNext()) {
0650: Map.Entry me = (Map.Entry) it.next();
0651: //find the field in the genclass used in by this name def
0652: PrimitiveElement pe = gcr.getHiddenField((String) me
0653: .getValue());
0654: //find the pk column
0655: RdbPrimitiveElementMapping tpem = getPEMOfField(tclass,
0656: (Mapping) gcm.getParent(), (String) tndproj
0657: .get(me.getKey()));
0658: //find the fk column name
0659: SpeedoColumn fkCol = sf.getFKColumn(tpem.getName());
0660: createPEMInGC(pe, fkCol, table, nd, (String) me
0661: .getKey());
0662: }
0663: }
0664: }
0665:
0666: /**
0667: * It creates the mapping of a primitive field (element of the generic
0668: * class).
0669: * @param pe is the Jorm meta object representing a primitive field
0670: * @param gcm is the MappingStructure which will host the mapping of the
0671: * field
0672: * @param sf is the Speedo meta object representing the persistent field
0673: * referencing a generic class.
0674: * @return a PrimitiveElementMapping corresponding to the given primitive
0675: * field.
0676: * @throws PException if it is not possible to build the mapping of the
0677: * primitive field.
0678: */
0679: public PrimitiveElementMapping createGenClassElementMapping(
0680: PrimitiveElement pe, SpeedoField sf, GenClassMapping gcm)
0681: throws PException, SpeedoException {
0682: //find the column corresponding to the generic class element
0683: SpeedoColumn[] cols = sf.columns;
0684: if (cols == null || cols.length == 0) {
0685: throw new SpeedoException(
0686: "No column defined for the element of the "
0687: + sf.getSourceDesc());
0688: }
0689: if (cols.length > 1) {
0690: throw new SpeedoException(
0691: "More than one column for the element of the "
0692: + sf.getSourceDesc());
0693: }
0694: return createFieldMapping(pe, cols[0].name, cols[0].sqlType,
0695: ((RdbGenClassMapping) gcm).getRdbTable(), null);
0696: }
0697:
0698: /**
0699: * It creates the mapping of a primitive field used as index in the generic
0700: * class.
0701: * @param pe is the Jorm meta object representing a primitive field
0702: * @param gcm is the MappingStructure which will host the mapping of the
0703: * field
0704: * @param sf is the Speedo meta object representing the persistent field
0705: * referencing a generic class.
0706: * @return a PrimitiveElementMapping corresponding to the given primitive
0707: * field.
0708: * @throws PException if it is not possible to build the mapping of the
0709: * primitive field.
0710: */
0711: public PrimitiveElementMapping createGenClassIndexMapping(
0712: PrimitiveElement pe, SpeedoField sf, GenClassMapping gcm)
0713: throws PException, SpeedoException {
0714: SpeedoColumn col = null;
0715: if (sf.jdoTuple instanceof SpeedoCollection) {
0716: col = ((SpeedoCollection) sf.jdoTuple).indexColumns;
0717: } else if (sf.jdoTuple instanceof SpeedoMap) {
0718: col = ((SpeedoMap) sf.jdoTuple).keyColumns;
0719: }
0720: return createFieldMapping(pe, col.name, col.sqlType,
0721: ((RdbGenClassMapping) gcm).getRdbTable(), null);
0722: }
0723:
0724: /**
0725: * Creates the mapping of the name def (JORM meta object) corresponding to
0726: * the identifier of a persistent generic class (collection, map, ...).
0727: * @param gcm is the MappingStructure which will host the mapping of the
0728: * generic class
0729: * @param nd is the namedef corresponding to the identifier of the
0730: * generic class
0731: * @param sf is the Speedo meta object representing the persistent field
0732: * referencing a generic class.
0733: */
0734: public void createGenClassIdentifierNameDefMapping(
0735: GenClassMapping gcm, NameDef nd, SpeedoField sf,
0736: MIBuilderHelper mibh) throws PException, SpeedoException {
0737: GenClassRef gcr = (GenClassRef) nd.getParent();
0738: RdbGenClassMapping rgcm = (RdbGenClassMapping) gcm;
0739: RdbTable table = rgcm.getRdbTable();
0740: if (nd.isFieldName()) {
0741: PrimitiveElement pe = gcr.getHiddenField(nd.getFieldName());
0742: SpeedoJoinColumn col = (SpeedoJoinColumn) sf.join.columns
0743: .get(0);
0744: createPEMInGC(pe, col.column, table, nd, null);
0745: } else {
0746: //get the ClassMapping of class owning the generic class
0747: RdbClassMultiMapping rcmOwner = (RdbClassMultiMapping) sf.moClass.jormclass
0748: .getClassProject(rgcm.getProjectName()).getMapping(
0749: rgcm.getMapperName()).getClassMapping();
0750: //get the namedef identifier of the generic class owner
0751: NameDef ndIdOwner = (NameDef) rcmOwner
0752: .getIdentifierMapping().getLinkedMO();
0753: //:compute the prefix for genclass field(s) used as identifier
0754: String prefix = mibh.getNameDefFieldPrefix(gcr, true, true,
0755: sf);
0756: Map classNdProj = ndIdOwner.getNameRef().getProjection();
0757: Iterator it = nd.getNameRef().getProjection().entrySet()
0758: .iterator();
0759: while (it.hasNext()) {
0760: Map.Entry me = (Map.Entry) it.next();
0761: String compositeFieldName = (String) me.getValue();
0762: //get the primitive element corresponding in the Generic class
0763: PrimitiveElement peInGC = gcr
0764: .getHiddenField(compositeFieldName);
0765: String classIdFieldName = (String) classNdProj.get(me
0766: .getKey());
0767: SpeedoField pkfield = (SpeedoField) sf.moClass
0768: .getField(classIdFieldName);
0769: SpeedoColumn col = sf.getJoinColumn(pkfield);
0770: createPEMInGC(peInGC, col, table, nd, (String) me
0771: .getKey());
0772: }
0773: }
0774: }
0775:
0776: /**
0777: * Creates a primitive element mapping in a table if no column with the
0778: * same name alrready exists. The PEM to create corresponds to a field
0779: * used in a name def. It can be the name def of the generic class
0780: * identifier, or the name def of a classref in a generic class.
0781: * If the column already exists in the table, that means two hidden fields
0782: * are mapped over the same column. In this case the specified primitive
0783: * element is removed from the GenClassRef. And its usage is replaced by
0784: * the existing one.
0785: * @param pe is a hidden field of a generic class
0786: * @param col is the column descriptor (Speedo meta object)
0787: * @param table is the table hosting the PEM.
0788: * @param nd is the name def using the PE.
0789: * @param compositeFieldName is the field name in a composite name in case
0790: * of the name def is based on a cmposite name. Otherwise this parameter is
0791: * not used.
0792: * @throws PException
0793: */
0794: private void createPEMInGC(PrimitiveElement pe, SpeedoColumn col,
0795: RdbTable table, NameDef nd, String compositeFieldName)
0796: throws PException {
0797: if (col.name == null) {
0798: // default mapping
0799: col.name = pe.getName();
0800: }
0801: //check if the column name is already used
0802: RdbPrimitiveElementMapping pem = (RdbPrimitiveElementMapping) table
0803: .getPrimitiveElementMappingByCol(col.name);
0804: if (pem == null) {
0805: // create it
0806: table.createPrimitiveElementMapping(pe, col.name,
0807: col.sqlType, !col.allowNull);
0808: } else {
0809: //it exists then assign the real primitive element to the
0810: // the name def
0811: ((GenClassRef) pe.getParent()).removeTypedElement(pe
0812: .getName());
0813: pe = (PrimitiveElement) pem.getLinkedMO();
0814: if (nd.isFieldName()) {
0815: nd.setFieldName(pe.getName());
0816: } else {
0817: nd.getNameRef().getProjection().put(compositeFieldName,
0818: pe.getName());
0819: }
0820: if (pem.getType() == null) {
0821: pem.setType(col.sqlType);
0822: }
0823: }
0824: }
0825:
0826: /**
0827: * It builds a GenClassMapping, assignes it to the mapping and builds
0828: * mapping structure for the class (RdbTable, directory name, ...).
0829: * @param gcr is the Jorm meta object representing the gen class which the
0830: * GenClassMapping must be built.
0831: * @param mapping is the Mapping instance which will host the GenClassMapping.
0832: * @param sf is the SpeedoField corresponding to the generic class.
0833: * @return the GenClassMapping instance built by the method (never null).
0834: * @throws PException if it is not possible to build the GenClassMapping
0835: */
0836: public GenClassMapping createGenClassMapping(GenClassRef gcr,
0837: SpeedoField sf, Mapping mapping) throws PException,
0838: SpeedoException {
0839: RdbGenClassMapping rgcm = new RdbGenClassMapping("to-table",
0840: gcr, mapping);
0841: mapping.addGenClassMapping(gcr.getGenClassId(), rgcm);
0842: RdbTable t = null;
0843: if (gcr.isPrimitive()) {
0844: if (logger.isLoggable(BasicLevel.DEBUG)) {
0845: logger.log(BasicLevel.DEBUG, "The GCR field "
0846: + gcr.getName()
0847: + " is mappeded over a dedicated table: "
0848: + sf.join.extTable.name);
0849: }
0850: //Create the RdbTable for the Generic class
0851: rgcm.createRdbTable(sf.join.extTable.name);
0852: } else if (gcr.isClassRef()) {
0853: SpeedoField reverseField = sf.getReverseField();
0854: SpeedoClass tclass = sf.moClass
0855: .getSpeedoClassFromContext(gcr.getClassRef()
0856: .getMOClass().getFQName());
0857: Class targetClassJMO = tclass.jormclass;
0858: mapping.getClassMapping().addDependency(
0859: targetClassJMO.getFQName());
0860: switch (sf.relationType) {
0861: case SpeedoField.MANY_MANY_BI_RELATION:
0862: //Create the join table for the Generic class
0863: t = rgcm.createRdbTable(sf.join.extTable.name);
0864: if (reverseField.mappedByReversefield
0865: || (reverseField.join != null
0866: && reverseField.join.extTable != null && t
0867: .getName()
0868: .equalsIgnoreCase(
0869: reverseField.join.extTable.name))) {
0870: //The MANY-MANY relation is mapped over the same join table
0871: if (sf.name.compareToIgnoreCase(reverseField.name) < 0) {
0872: // master side is not randomly choosen
0873: t.setColocatedMaster(true);
0874: } else {
0875: //write are done only on master side. The slave side
0876: // reads only the generic class
0877: t.setReadOnly(true);
0878: t.setColocated(true);
0879: }
0880: }
0881: if (logger.isLoggable(BasicLevel.DEBUG)) {
0882: logger.log(BasicLevel.DEBUG, "Many-Many relation,"
0883: + " join table: " + t.getName()
0884: + (t.isColocated() ? ", colocated" : "")
0885: + (t.isColocatedMaster() ? ", master" : "")
0886: + (t.isReadOnly() ? ", readonly" : ""));
0887: }
0888: break;
0889: case SpeedoField.ONE_MANY_BI_RELATION:
0890: reverseField = sf.getReverseField();
0891: RdbClassMultiMapping trcm = (RdbClassMultiMapping) getClassMapping(
0892: mapping, targetClassJMO);
0893: if (sf.mappedByReversefield) {
0894: RdbTable tTable;
0895: SpeedoTable table;
0896: if (reverseField.join != null) {
0897: table = reverseField.join.extTable;
0898: tTable = trcm.getRdbExternalTable(table.name);
0899: } else {
0900: table = reverseField.moClass.mainTable;
0901: tTable = trcm.getRdbTable();
0902: }
0903: t = rgcm.createRdbTable(table.name);
0904: tTable.setColocated(true);
0905: t.setColocated(true);
0906: tTable.setColocatedMaster(true);
0907: t.setColocatedMaster(false);
0908: trcm.addDependency(sf.moClass.getFQName());
0909: if (sf.isCoherentReverseField) {
0910: //optimisation: the reverse field assumes the coherence.
0911: // Then there is no need to write the generic class.
0912: t.setReadOnly(true);
0913: }
0914: } else if (sf.join != null) {
0915: t = rgcm.createRdbTable(sf.join.extTable.name);
0916: }
0917: if (t != null && logger.isLoggable(BasicLevel.DEBUG)) {
0918: logger.log(BasicLevel.DEBUG, "One-Many relation,"
0919: + " join table: " + t.getName()
0920: + (t.isColocated() ? ", colocated" : "")
0921: + (t.isColocatedMaster() ? ", master" : "")
0922: + (t.isReadOnly() ? ", readonly" : ""));
0923: }
0924: break;
0925: default:
0926: //Create the RdbTable for the Generic class
0927: rgcm.createRdbTable(sf.join.extTable.name);
0928: //add the dependency
0929: trcm = (RdbClassMultiMapping) getClassMapping(mapping,
0930: targetClassJMO);
0931: trcm.addDependency(sf.moClass.getFQName());
0932: if (t != null && logger.isLoggable(BasicLevel.DEBUG)) {
0933: logger.log(BasicLevel.DEBUG, "GCR field,"
0934: + " join table: " + sf.join.extTable.name);
0935: }
0936: break;
0937: }
0938: }
0939: return rgcm;
0940: }
0941:
0942: public void createGenClassRefNameDefMapping(ClassMapping cm,
0943: NameDef nd, SpeedoCommonField sf) throws PException,
0944: SpeedoException {
0945: // TODO: support mapping of GCR on field which does not correspond to
0946: // identifier field of the GC owner (polymorphid).
0947: }
0948:
0949: /**
0950: * Find the PEM corresponding to a persistent field.
0951: * @param tsc is
0952: * @param map
0953: * @param fieldName
0954: * @return
0955: * @throws PException
0956: */
0957: private RdbPrimitiveElementMapping getPEMOfField(SpeedoClass sc,
0958: Mapping map, String fieldName) throws PException {
0959: SpeedoClass current = sc;
0960: RdbPrimitiveElementMapping pem = null;
0961: while (pem == null) {
0962: RdbClassMultiMapping trcm = getClassMapping(map,
0963: current.jormclass);
0964: pem = (RdbPrimitiveElementMapping) trcm
0965: .getPrimitiveElementMapping(fieldName, true);
0966: current = sc.getSuper();
0967: }
0968: if (pem == null) {
0969: throw new PException("No mapping found for the field '"
0970: + fieldName + "' for the class '" + sc.getFQName()
0971: + "' or its parent.");
0972: }
0973: return pem;
0974: }
0975:
0976: // IMPLEMENTATION OF THE Loggable INTERFACE //
0977: //------------------------------------------//
0978:
0979: public Logger getLogger() {
0980: return logger;
0981: }
0982:
0983: public LoggerFactory getLoggerFactory() {
0984: return null;
0985: }
0986:
0987: public void setLogger(Logger logger) {
0988: this .logger = logger;
0989: debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
0990: }
0991:
0992: public void setLoggerFactory(LoggerFactory loggerFactory) {
0993: setLogger(loggerFactory.getLogger(SpeedoProperties.LOGGER_NAME
0994: + ".generation.jorm.mimappingbuilder"));
0995: }
0996:
0997: private Manager getManager(MetaObject mo) {
0998: MetaObject m = mo;
0999: while (m != null && !(m instanceof Manager)) {
1000: m = m.getParent();
1001: }
1002: return (Manager) m;
1003: }
1004:
1005: private RdbClassMultiMapping getClassMapping(Mapping map,
1006: Class clazz) {
1007: return (RdbClassMultiMapping) clazz.getClassProject(
1008: map.getClassMapping().getProjectName()).getMapping(
1009: map.getMapperName()).getClassMapping();
1010: }
1011: }
|