0001: /**********************************************************************
0002: Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: 2003 Erik Bengtson - Important changes regarding the application identity
0018: support when fetching PreparedStatements. Added an
0019: inner class that should be moved away.
0020: 2004 Erik Bengtson - added commentary and Localisation
0021: 2004 Andy Jefferson - added capability to handle Abstract PC fields for
0022: both SingleFieldIdentity and AID
0023: 2007 Andy Jefferson - implement RelationMappingCallbacks
0024: ...
0025: **********************************************************************/package org.jpox.store.mapping;
0026:
0027: import java.lang.reflect.Field;
0028: import java.math.BigDecimal;
0029: import java.sql.ResultSet;
0030: import java.sql.SQLException;
0031: import java.util.Collection;
0032:
0033: import javax.jdo.JDOObjectNotFoundException;
0034: import javax.jdo.spi.PersistenceCapable;
0035:
0036: import org.jpox.ClassLoaderResolver;
0037: import org.jpox.FetchPlan;
0038: import org.jpox.ObjectManager;
0039: import org.jpox.ObjectManagerHelper;
0040: import org.jpox.StateManager;
0041: import org.jpox.api.ApiAdapter;
0042: import org.jpox.exceptions.JPOXDataStoreException;
0043: import org.jpox.exceptions.JPOXException;
0044: import org.jpox.exceptions.JPOXObjectNotFoundException;
0045: import org.jpox.exceptions.JPOXUserException;
0046: import org.jpox.identity.OID;
0047: import org.jpox.metadata.AbstractClassMetaData;
0048: import org.jpox.metadata.AbstractMemberMetaData;
0049: import org.jpox.metadata.ClassMetaData;
0050: import org.jpox.metadata.ColumnMetaData;
0051: import org.jpox.metadata.IdentityStrategy;
0052: import org.jpox.metadata.IdentityType;
0053: import org.jpox.metadata.InheritanceStrategy;
0054: import org.jpox.metadata.MetaDataManager;
0055: import org.jpox.metadata.Relation;
0056: import org.jpox.sco.SCOCollection;
0057: import org.jpox.store.DatastoreAdapter;
0058: import org.jpox.store.DatastoreClass;
0059: import org.jpox.store.DatastoreContainerObject;
0060: import org.jpox.store.DatastoreElementContainer;
0061: import org.jpox.store.DatastoreField;
0062: import org.jpox.store.DatastoreIdentifier;
0063: import org.jpox.store.FieldValues;
0064: import org.jpox.store.IdentifierFactory;
0065: import org.jpox.store.MappedStoreManager;
0066: import org.jpox.store.StatementExpressionIndex;
0067: import org.jpox.store.StoreManager;
0068: import org.jpox.store.exceptions.NotYetFlushedException;
0069: import org.jpox.store.exceptions.ReachableObjectNotCascadedException;
0070: import org.jpox.store.expression.LogicSetExpression;
0071: import org.jpox.store.expression.ObjectExpression;
0072: import org.jpox.store.expression.ObjectLiteral;
0073: import org.jpox.store.expression.QueryExpression;
0074: import org.jpox.store.expression.ScalarExpression;
0075: import org.jpox.util.ClassUtils;
0076: import org.jpox.util.JPOXLogger;
0077: import org.jpox.util.StringUtils;
0078:
0079: /**
0080: * Maps a java field to a PersistenceCapable class.
0081: * For PersistenceCapable classes using datastore identity most of the necessary behaviour
0082: * is coded in the OIDMapping super class.
0083: * TODO Split this from OIDMapping since a PCMapping may represent an application
0084: * identity object instead of a datastore identity object
0085: *
0086: * @version $Revision: 1.154 $
0087: */
0088: public class PersistenceCapableMapping extends OIDMapping implements
0089: MappingCallbacks {
0090: /** Mappings for all fields necessary to represent the id of the PC object. */
0091: protected JavaTypeMapping[] javaTypeMappings = new JavaTypeMapping[0];
0092:
0093: //----------------------- convenience fields to improve performance ----------------------//
0094: /** ClassMetaData for the represented class. Create a new one on each getObject invoke is expensive **/
0095: private AbstractClassMetaData cmd;
0096:
0097: /** number of DatastoreField - convenience field to improve performance **/
0098: private int numberOfDatastoreFields = 0;
0099:
0100: /**
0101: * Create a new empty PersistenceCapableMapping.
0102: * The caller must call one of the initialize methods to initialize the instance with the
0103: * DatastoreAdapter and its type.
0104: */
0105: public PersistenceCapableMapping() {
0106: }
0107:
0108: /**
0109: * Initialize this JavaTypeMapping with the given DatastoreAdapter for
0110: * the given FieldMetaData.
0111: *
0112: * @param dba The Datastore Adapter that this Mapping should use.
0113: * @param fmd FieldMetaData for the field to be mapped (if any)
0114: * @param container The datastore container storing this mapping (if any)
0115: * @param clr the ClassLoaderResolver
0116: */
0117: public void initialize(DatastoreAdapter dba,
0118: AbstractMemberMetaData fmd,
0119: DatastoreContainerObject container, ClassLoaderResolver clr) {
0120: super .initialize(dba, fmd, container, clr);
0121: prepareDatastoreMapping(clr);
0122: }
0123:
0124: /**
0125: * Add a new JavaTypeMapping
0126: * @param mapping the JavaTypeMapping
0127: */
0128: public void addJavaTypeMapping(JavaTypeMapping mapping) {
0129: if (mapping == null) {
0130: throw new JPOXException(
0131: "mapping argument in PersistenceCapableMapping.addJavaTypeMapping is null")
0132: .setFatal();
0133: }
0134: JavaTypeMapping[] jtm = javaTypeMappings;
0135: javaTypeMappings = new JavaTypeMapping[jtm.length + 1];
0136: System.arraycopy(jtm, 0, javaTypeMappings, 0, jtm.length);
0137: javaTypeMappings[jtm.length] = mapping;
0138: }
0139:
0140: /**
0141: * Method to prepare the PC mapping and add its associated datastore mappings.
0142: */
0143: protected void prepareDatastoreMapping() {
0144: // Does nothing - added to prevent column creation by the method in OIDMapping
0145: // If we change the inheritance so this doesnt extend OIDMapping, then this can be deleted
0146: }
0147:
0148: /**
0149: * Method to prepare the PC mapping and add its associated datastore mappings.
0150: * @param clr The ClassLoaderResolver
0151: */
0152: protected void prepareDatastoreMapping(ClassLoaderResolver clr) {
0153: // Either one end of a 1-1 relation, or the N end of a N-1
0154: StoreManager storeMgr = datastoreContainer.getStoreManager();
0155: AbstractClassMetaData refCmd = storeMgr.getMetaDataManager()
0156: .getMetaDataForClass(fmd.getType(), clr);
0157: JavaTypeMapping referenceMapping = null;
0158: if (refCmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUBCLASS_TABLE) {
0159: // Find the actual tables storing the other end (can be multiple subclasses)
0160: AbstractClassMetaData[] cmds = storeMgr
0161: .getClassesManagingTableForClass(refCmd, clr);
0162: if (cmds != null && cmds.length > 0) {
0163: if (cmds.length > 1) {
0164: JPOXLogger.PERSISTENCE
0165: .warn("Field "
0166: + fmd.getFullFieldName()
0167: + " represents either a 1-1 relation, "
0168: + "or a N-1 relation where the other end uses \"subclass-table\" inheritance strategy and more "
0169: + "than 1 subclasses with a table. This is not fully supported by JPOX");
0170: }
0171: } else {
0172: // No subclasses of the class using "subclasses-table" so no mapping!
0173: // TODO Throw an exception ?
0174: return;
0175: }
0176: // TODO We need a mapping for each of the possible subclass tables
0177: referenceMapping = storeMgr.getDatastoreClass(
0178: cmds[0].getFullClassName(), clr).getIDMapping();
0179: } else {
0180: referenceMapping = storeMgr.getDatastoreClass(
0181: fmd.getType().getName(), clr).getIDMapping();
0182: }
0183:
0184: // Generate a mapping from the columns of the referenced object to this mapping's ColumnMetaData
0185: CorrespondentColumnsMapper correspondentColumnsMapping = new CorrespondentColumnsMapper(
0186: fmd, referenceMapping, true);
0187:
0188: // Find any related field where this is part of a bidirectional relation
0189: int relationType = fmd.getRelationType(clr);
0190: boolean createDatastoreMappings = true;
0191: if (relationType == Relation.MANY_TO_ONE_BI) {
0192: AbstractMemberMetaData[] relatedMmds = fmd
0193: .getRelatedMemberMetaData(clr);
0194: // TODO Cater for more than 1 related field
0195: createDatastoreMappings = (relatedMmds[0].getJoinMetaData() == null);
0196: } else if (relationType == Relation.ONE_TO_ONE_BI) {
0197: // Put the FK at the end without "mapped-by"
0198: createDatastoreMappings = (fmd.getMappedBy() == null);
0199: }
0200:
0201: // Loop through the datastore fields in the referenced class and create a datastore field for each
0202: for (int i = 0; i < referenceMapping
0203: .getNumberOfDatastoreFields(); i++) {
0204: DatastoreMapping refDatastoreMapping = referenceMapping
0205: .getDataStoreMapping(i);
0206: JavaTypeMapping mapping = dba.getMapping(
0207: refDatastoreMapping.getJavaTypeMapping()
0208: .getJavaType(), storeMgr);
0209: this .addJavaTypeMapping(mapping);
0210:
0211: // Create physical datastore columns where we require a FK link to the related table.
0212: if (createDatastoreMappings) {
0213: // Find the Column MetaData that maps to the referenced datastore field
0214: ColumnMetaData colmd = correspondentColumnsMapping
0215: .getColumnMetaDataByIdentifier(refDatastoreMapping
0216: .getDatastoreField().getIdentifier());
0217: if (colmd == null) {
0218: throw new JPOXUserException(LOCALISER.msg("041038",
0219: refDatastoreMapping.getDatastoreField()
0220: .getIdentifier(), toString()))
0221: .setFatal();
0222: }
0223:
0224: // Create a Datastore field to equate to the referenced classes datastore field
0225: MappingManager mmgr = datastoreContainer
0226: .getStoreManager().getMappingManager();
0227: DatastoreField col = mmgr.createDatastoreField(fmd,
0228: datastoreContainer, mapping, colmd,
0229: refDatastoreMapping.getDatastoreField(), clr);
0230:
0231: // Add its datastore mapping
0232: DatastoreMapping datastoreMapping = mmgr
0233: .createDatastoreMapping(
0234: mapping,
0235: storeMgr,
0236: col,
0237: refDatastoreMapping
0238: .getJavaTypeMapping()
0239: .getJavaTypeForDatastoreMapping(
0240: i));
0241: this .addDataStoreMapping(datastoreMapping);
0242: } else {
0243: mapping.setReferenceMapping(referenceMapping);
0244: }
0245: }
0246: }
0247:
0248: /**
0249: * Accessor for the Java type mappings
0250: * @return The Java type mappings
0251: */
0252: public JavaTypeMapping[] getJavaTypeMapping() {
0253: return javaTypeMappings;
0254: }
0255:
0256: /**
0257: * Accessor for the number of datastore fields.
0258: * Zero datastore fields implies that the mapping uses a FK in the associated class
0259: * to reference back.
0260: * @return Number of datastore fields for this PC mapping.
0261: */
0262: public int getNumberOfDatastoreFields() {
0263: if (numberOfDatastoreFields == 0) {
0264: for (int i = 0; i < javaTypeMappings.length; i++) {
0265: numberOfDatastoreFields += javaTypeMappings[i]
0266: .getNumberOfDatastoreFields();
0267: }
0268: }
0269: return numberOfDatastoreFields;
0270: }
0271:
0272: /**
0273: * Accessor for a datastore mapping.
0274: * This method works through the java type mappings, and for each mapping through its datastore mappings
0275: * incrementing the index with each datastore mapping.
0276: * @param index The position of the mapping.
0277: * @return The datastore mapping.
0278: */
0279: public DatastoreMapping getDataStoreMapping(int index) {
0280: int currentIndex = 0;
0281: int numberJavaMappings = javaTypeMappings.length;
0282: for (int i = 0; i < numberJavaMappings; i++) {
0283: int numberDatastoreMappings = javaTypeMappings[i]
0284: .getNumberOfDatastoreFields();
0285: for (int j = 0; j < numberDatastoreMappings; j++) {
0286: if (currentIndex == index) {
0287: return javaTypeMappings[i].getDataStoreMapping(j);
0288: }
0289: currentIndex++;
0290: }
0291: }
0292: // TODO Localise this message
0293: throw new JPOXException("Invalid index " + index
0294: + " for DataStoreMapping.").setFatal();
0295: }
0296:
0297: /**
0298: * Method to set an object in the datastore.
0299: * @param om The ObjectManager
0300: * @param ps The Prepared Statement
0301: * @param param The parameter ids in the statement
0302: * @param value The value to put in the statement at these ids
0303: * @throws NotYetFlushedException
0304: */
0305: public void setObject(ObjectManager om, Object ps, int[] param,
0306: Object value) {
0307: setObject(om, ps, param, value, null, -1);
0308: }
0309:
0310: /**
0311: * Method to set an object reference (FK) in the datastore.
0312: * @param om The Object Manager
0313: * @param ps The Prepared Statement
0314: * @param param The parameter ids in the statement
0315: * @param value The value to put in the statement at these ids
0316: * @param ownerSM StateManager for the owner object
0317: * @param ownerFieldNumber Field number of this PC object in the owner
0318: * @throws NotYetFlushedException
0319: */
0320: public void setObject(ObjectManager om, Object ps, int[] param,
0321: Object value, StateManager ownerSM, int ownerFieldNumber) {
0322: if (value == null) {
0323: setObjectAsNull(om, ps, param);
0324: } else {
0325: setObjectAsValue(om, ps, param, value, ownerSM,
0326: ownerFieldNumber);
0327: }
0328: }
0329:
0330: /**
0331: * Populates the PreparedStatement with a null value for the mappings of this PersistenceCapableMapping.
0332: * @param om the Object Manager
0333: * @param ps the Prepared Statement
0334: * @param param The parameter ids in the statement
0335: */
0336: private void setObjectAsNull(ObjectManager om, Object ps,
0337: int[] param) {
0338: // Null out the PC object
0339: int n = 0;
0340: for (int i = 0; i < javaTypeMappings.length; i++) {
0341: JavaTypeMapping mapping = javaTypeMappings[i];
0342: if (mapping.getNumberOfDatastoreFields() > 0) {
0343: // Only populate the PreparedStatement for the object if it has any datastore mappings
0344: int[] posMapping = new int[mapping
0345: .getNumberOfDatastoreFields()];
0346: for (int j = 0; j < posMapping.length; j++) {
0347: posMapping[j] = param[n++];
0348: }
0349: mapping.setObject(om, ps, posMapping, null);
0350: }
0351: }
0352: }
0353:
0354: /**
0355: * Check if one of the primary key fields of the PC has value attributed by the datastore
0356: * @param mdm the {@link MetaDataManager}
0357: * @param srm the {@link StoreManager}
0358: * @param clr the {@link ClassLoaderResolver}
0359: * @return true if one of the primary key fields of the PC has value attributed by the datastore
0360: */
0361: private boolean hasDatastoreAttributedPrimaryKeyValues(
0362: MetaDataManager mdm, StoreManager srm,
0363: ClassLoaderResolver clr) {
0364: boolean hasDatastoreAttributedPrimaryKeyValues = false;
0365: if (this .fmd != null) {
0366: // Object is associated to a field (i.e not a join table)
0367: AbstractClassMetaData acmd = mdm.getMetaDataForClass(
0368: this .fmd.getType(), clr);
0369: if (acmd.getIdentityType() == IdentityType.APPLICATION) {
0370: for (int i = 0; i < acmd.getPKMemberPositions().length; i++) {
0371: IdentityStrategy strategy = acmd
0372: .getMetaDataForManagedMemberAtAbsolutePosition(
0373: acmd.getPKMemberPositions()[i])
0374: .getValueStrategy();
0375: if (strategy != null) {
0376: //if strategy is null, then it's user attributed value
0377: hasDatastoreAttributedPrimaryKeyValues |= srm
0378: .isStrategyDatastoreAttributed(
0379: strategy, false);
0380: }
0381: }
0382: }
0383: }
0384: return hasDatastoreAttributedPrimaryKeyValues;
0385: }
0386:
0387: /**
0388: * Method to set an object reference (FK) in the datastore.
0389: * @param om The Object Manager
0390: * @param ps The Prepared Statement
0391: * @param param The parameter ids in the statement
0392: * @param value The value to put in the statement at these ids
0393: * @param ownerSM StateManager for the owner object
0394: * @param ownerFieldNumber Field number of this PC object in the owner
0395: * @throws NotYetFlushedException Just put "null" in and throw "NotYetFlushedException",
0396: * to be caught by ParameterSetter and will signal to the PC object being inserted
0397: * that it needs to inform this object when it is inserted.
0398: */
0399: private void setObjectAsValue(ObjectManager om, Object ps,
0400: int[] param, Object value, StateManager ownerSM,
0401: int ownerFieldNumber) {
0402: Object id;
0403:
0404: ApiAdapter api = om.getApiAdapter();
0405: if (!api.isPersistable(value)) {
0406: throw new JPOXException(LOCALISER.msg("041016", value
0407: .getClass(), value)).setFatal();
0408: }
0409:
0410: StateManager sm = om.findStateManager(value);
0411:
0412: try {
0413: ClassLoaderResolver clr = om.getClassLoaderResolver();
0414:
0415: // Check if the field is attributed in the datastore
0416: boolean hasDatastoreAttributedPrimaryKeyValues = hasDatastoreAttributedPrimaryKeyValues(
0417: om.getMetaDataManager(), om.getStoreManager(), clr);
0418:
0419: boolean inserted = false;
0420: if (ownerFieldNumber >= 0) {
0421: // Field mapping : is this field of the related object present in the datastore?
0422: inserted = om.isInserted(value, ownerFieldNumber);
0423: } else if (fmd == null) {
0424: // Identity mapping : is the object inserted far enough to be considered of this mapping type?
0425: inserted = om.isInserted(value, type);
0426: }
0427:
0428: if (sm != null) {
0429: if (om.getApiAdapter().isDetached(value)
0430: && sm.getReferencedPC() != null
0431: && ownerSM != null && fmd != null) {
0432: // 1-1, N-1 mapping
0433: // The value is really still detached but has a temporary StateManager whilst attaching so
0434: // replace this field reference to be for the newly attached object
0435: // Note that we have "fmd != null" here hence omitting any M-N relations where this is a join table
0436: // mapping
0437: ownerSM.replaceField(ownerFieldNumber, sm
0438: .getReferencedPC(), true);
0439: }
0440:
0441: if (sm.isWaitingToBeFlushedToDatastore()) {
0442: // Related object is not yet flushed to the datastore so flush it so we can set the FK
0443: sm.flush();
0444: }
0445: }
0446:
0447: // we can execute this block when
0448: // 1) the pc has been inserted; OR
0449: // 2) is not in process of being inserted; OR
0450: // 3) is being inserted yet is inserted enough to use this mapping; OR
0451: // 4) the PC PK values are not attributed by the database and this mapping is for a PK field (compound identity)
0452: if (inserted
0453: || !om.isInserting(value)
0454: || (!hasDatastoreAttributedPrimaryKeyValues && (this .fmd != null && this .fmd
0455: .isPrimaryKey()))) {
0456: // The PC is either already inserted, or inserted down to the level we need, or not inserted at all,
0457: // or the field is a PK and identity not attributed by the datastore
0458:
0459: // Object either already exists, or is not yet being inserted.
0460: id = api.getIdForObject(value);
0461:
0462: // Check if the PersistenceCapable exists in this datastore
0463: boolean requiresPersisting = false;
0464: if (om.getApiAdapter().isDetached(value)
0465: && ownerSM != null) {
0466: // Detached object so needs attaching
0467: if (ownerSM.isInserting()) {
0468: // Inserting other object, and this object is detached but if detached from this datastore
0469: // we can just return the value now and attach later (in InsertRequest)
0470: if (!om.getOMFContext()
0471: .getPersistenceConfiguration()
0472: .getAttachSameDatastore()) {
0473: if (om.getObjectFromCache(api
0474: .getIdForObject(value)) != null) {
0475: // Object is in cache so exists for this datastore, so no point checking
0476: } else {
0477: try {
0478: Object obj = om.findObject(api
0479: .getIdForObject(value),
0480: true, false, value
0481: .getClass()
0482: .getName());
0483: if (obj != null) {
0484: // Make sure this object is not retained in cache etc
0485: StateManager objSM = om
0486: .findStateManager(obj);
0487: if (objSM != null) {
0488: om
0489: .evictFromTransaction(objSM);
0490: }
0491: om
0492: .removeObjectFromCache(
0493: value,
0494: api
0495: .getIdForObject(value),
0496: true, true);
0497: }
0498: } catch (JPOXObjectNotFoundException onfe) {
0499: // Object doesnt yet exist
0500: requiresPersisting = true;
0501: }
0502: }
0503: }
0504: } else {
0505: requiresPersisting = true;
0506: }
0507: } else if (id == null) {
0508: // Transient object, so we need to persist it
0509: requiresPersisting = true;
0510: } else {
0511: ObjectManager pcPM = ObjectManagerHelper
0512: .getObjectManager(value);
0513: if (pcPM != null && om != pcPM) {
0514: throw new JPOXUserException(LOCALISER
0515: .msg("041015"), id);
0516: }
0517: }
0518:
0519: if (requiresPersisting) {
0520: // PERSISTENCE-BY-REACHABILITY
0521: // This PC object needs persisting (new or detached) to do the "set"
0522: if (fmd != null && !fmd.isCascadePersist()
0523: && !om.getApiAdapter().isDetached(value)) {
0524: // Related PC object not persistent, but cant do cascade-persist so throw exception
0525: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
0526: JPOXLogger.REACHABILITY.debug(LOCALISER
0527: .msg("007006", fmd
0528: .getFullFieldName()));
0529: }
0530: throw new ReachableObjectNotCascadedException(
0531: fmd.getFullFieldName(), value);
0532: }
0533:
0534: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
0535: JPOXLogger.REACHABILITY.debug(LOCALISER.msg(
0536: "007007", fmd != null ? fmd
0537: .getFullFieldName() : null));
0538: }
0539:
0540: try {
0541: Object pcNew = om.persistObjectInternal(value,
0542: null, null, -1, StateManager.PC);
0543: if (hasDatastoreAttributedPrimaryKeyValues) {
0544: om.flushInternal(false);
0545: }
0546: id = api.getIdForObject(pcNew);
0547: if (om.getApiAdapter().isDetached(value)
0548: && ownerSM != null) {
0549: // Update any detached reference to refer to the attached variant
0550: ownerSM.replaceField(ownerFieldNumber,
0551: pcNew, true);
0552: int relationType = fmd.getRelationType(clr);
0553: if (relationType == Relation.MANY_TO_ONE_BI) {
0554: // TODO Update the container to refer to the attached object
0555: if (JPOXLogger.PERSISTENCE
0556: .isInfoEnabled()) {
0557: JPOXLogger.PERSISTENCE
0558: .info("PCMapping.setObject : object "
0559: + ownerSM
0560: .getInternalObjectId()
0561: + " has field "
0562: + ownerFieldNumber
0563: + " that is 1-N bidirectional."
0564: + " Have just attached the N side so should really update the reference in the 1 side collection"
0565: + " to refer to this attached object. Not yet implemented");
0566: }
0567: } else if (relationType == Relation.ONE_TO_ONE_BI) {
0568: AbstractMemberMetaData[] relatedMmds = fmd
0569: .getRelatedMemberMetaData(clr);
0570: // TODO Cater for more than 1 related field
0571: StateManager relatedSM = om
0572: .findStateManager(pcNew);
0573: relatedSM.replaceField(relatedMmds[0]
0574: .getAbsoluteFieldNumber(),
0575: ownerSM.getObject(), true);
0576: }
0577: }
0578: } catch (NotYetFlushedException e) {
0579: setObjectAsNull(om, ps, param);
0580: throw new NotYetFlushedException(value);
0581: }
0582: }
0583:
0584: if (sm != null) {
0585: sm.setStoringPC();
0586: }
0587:
0588: // If the field doesnt map to any datastore fields, omit the set process
0589: if (getNumberOfDatastoreFields() > 0) {
0590: if (id instanceof OID) {
0591: super .setObject(om, ps, param, id);
0592: } else {
0593: // TODO Factor out this PersistenceCapable reference
0594: ((PersistenceCapable) value)
0595: .jdoCopyKeyFieldsFromObjectId(
0596: new AppIDObjectIdFieldConsumer(
0597: param, om, ps,
0598: javaTypeMappings), id);
0599: }
0600: }
0601: } else {
0602: if (sm != null) {
0603: sm.setStoringPC();
0604: }
0605:
0606: if (getNumberOfDatastoreFields() > 0) {
0607: // Object is in the process of being inserted so we cant use its id currently and we need to store
0608: // a foreign key to it (which we cant yet do). Just put "null" in and throw "NotYetFlushedException",
0609: // to be caught by ParameterSetter and will signal to the PC object being inserted that it needs
0610: // to inform this object when it is inserted.
0611: setObjectAsNull(om, ps, param);
0612: throw new NotYetFlushedException(value);
0613: }
0614: }
0615: } finally {
0616: if (sm != null) {
0617: sm.unsetStoringPC();
0618: }
0619: }
0620: }
0621:
0622: /**
0623: * Returns a instance of a PersistenceCapable class.
0624: * Processes a FK field and converts the id stored firstly into an OID/AID
0625: * and then into the object that the FK id relates to.
0626: * @param om The Object Manager
0627: * @param rs The ResultSet
0628: * @param param Array of parameter ids in the ResultSet to retrieve
0629: * @return The Persistence Capable object
0630: */
0631: public Object getObject(ObjectManager om, final Object rs,
0632: int[] param) {
0633: // Check for null FK
0634: try {
0635: //if the first param is null, then the field is null
0636: // TODO Factor this out - using RDBMS-specific code
0637: if (((ResultSet) rs).getObject(param[0]) == null) {
0638: return null;
0639: }
0640: } catch (SQLException e) {
0641: throw new JPOXDataStoreException(e.getMessage(), e);
0642: }
0643:
0644: if (cmd == null) {
0645: cmd = om.getMetaDataManager().getMetaDataForClass(
0646: getType(), om.getClassLoaderResolver());
0647: }
0648:
0649: // Return the object represented by this mapping
0650: if (cmd.getIdentityType() == IdentityType.DATASTORE) {
0651: return getObjectForDatastoreIdentity(om, rs, param, cmd);
0652: } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
0653: return getObjectForApplicationIdentity(om, rs, param, cmd);
0654: } else {
0655: return null;
0656: }
0657: }
0658:
0659: // ---------------------------- JDOQL Query Methods --------------------------------------
0660:
0661: public ScalarExpression newLiteral(QueryExpression qs, Object value) {
0662: ScalarExpression expr = new ObjectLiteral(qs, this , value,
0663: getType());
0664: return expr;
0665: }
0666:
0667: public ScalarExpression newScalarExpression(QueryExpression qs,
0668: LogicSetExpression te) {
0669: if (getNumberOfDatastoreFields() > 0) {
0670: return new ObjectExpression(qs, this , te);
0671: } else {
0672: ClassLoaderResolver clr = qs.getClassLoaderResolver();
0673: MappedStoreManager srm = (MappedStoreManager) qs
0674: .getStoreManager();
0675: int relationType = fmd.getRelationType(clr);
0676: if (relationType == Relation.ONE_TO_ONE_BI) {
0677: // Create an expression joining to the related field in the related table
0678: DatastoreClass targetTable = srm.getDatastoreClass(fmd
0679: .getTypeName(), clr);
0680: AbstractMemberMetaData[] relatedMmds = fmd
0681: .getRelatedMemberMetaData(clr);
0682: // TODO Cater for more than one related field
0683: JavaTypeMapping refMapping = targetTable
0684: .getFieldMapping(relatedMmds[0]);
0685: JavaTypeMapping selectMapping = targetTable
0686: .getIDMapping();
0687: DatastoreIdentifier targetTableIdentifier = srm
0688: .getIdentifierFactory().newIdentifier(
0689: IdentifierFactory.TABLE,
0690: "RELATED"
0691: + fmd.getAbsoluteFieldNumber());
0692: LogicSetExpression targetTe = qs.newTableExpression(
0693: targetTable, targetTableIdentifier);
0694: return new ObjectExpression(qs, this , te, refMapping,
0695: targetTe, selectMapping);
0696: } else if (relationType == Relation.MANY_TO_ONE_BI) {
0697: AbstractMemberMetaData[] relatedMmds = fmd
0698: .getRelatedMemberMetaData(clr);
0699: // TODO Cater for more than one related field
0700: if (fmd.getJoinMetaData() != null
0701: || relatedMmds[0].getJoinMetaData() != null) {
0702: // Join table relation - only allows for Collection/Array
0703: // Create an expression this table to the join table on the element id, selecting the owner id
0704: DatastoreContainerObject targetTable = srm
0705: .getDatastoreContainerObject(relatedMmds[0]);
0706: JavaTypeMapping refMapping = null;
0707: JavaTypeMapping selectMapping = null;
0708: DatastoreElementContainer elementTable = (DatastoreElementContainer) targetTable;
0709: refMapping = elementTable.getElementMapping();
0710: selectMapping = elementTable.getOwnerMapping();
0711: DatastoreIdentifier targetTableIdentifier = srm
0712: .getIdentifierFactory()
0713: .newIdentifier(
0714: IdentifierFactory.TABLE,
0715: "JOINTABLE"
0716: + fmd
0717: .getAbsoluteFieldNumber());
0718: LogicSetExpression targetTe = qs
0719: .newTableExpression(targetTable,
0720: targetTableIdentifier);
0721: return new ObjectExpression(qs, this , te,
0722: refMapping, targetTe, selectMapping);
0723: }
0724: }
0725: }
0726:
0727: // TODO Throw an exception since should be impossible
0728: return null;
0729: }
0730:
0731: // ------------------------------------- Utility Methods ------------------------------------------
0732:
0733: /**
0734: * Get the object instance for a class using datastore identity
0735: * @param om the ObjectManager
0736: * @param rs the ResultSet
0737: * @param param the parameters
0738: * @param cmd the AbstractClassMetaData
0739: * @return the id
0740: */
0741: private Object getObjectForDatastoreIdentity(ObjectManager om,
0742: final Object rs, int[] param, AbstractClassMetaData cmd) {
0743: // Datastore Identity - retrieve the OID for the class.
0744: // Note that this is a temporary OID that is simply formed from the type of base class in the relationship
0745: // and the id stored in the FK. The real OID for the object may be of a different class.
0746: // For that reason we get the object by checking the inheritance (final param in getObjectById())
0747: Object oid = super .getObject(om, rs, param);
0748: ApiAdapter api = om.getApiAdapter();
0749: if (api.isPersistable(oid)) //why check this?
0750: {
0751: return oid;
0752: }
0753: return oid == null ? null : om.findObject(oid, false, true,
0754: null);
0755: }
0756:
0757: /**
0758: * Create a SingleFieldIdentity instance
0759: * @param om the ObjectManager
0760: * @param rs the ResultSet
0761: * @param param the parameters
0762: * @param cmd the AbstractClassMetaData
0763: * @param objectIdClass the object id class
0764: * @param pcClass the PersistenceCapable class
0765: * @return the id
0766: */
0767: private Object createSingleFieldIdentity(ObjectManager om,
0768: final Object rs, int[] param, AbstractClassMetaData cmd,
0769: Class objectIdClass, Class pcClass) {
0770: // SingleFieldIdentity
0771: int paramNumber = param[0];
0772: try {
0773: // TODO Factor this out - using RDBMS-specific code
0774: Object idObj = ((ResultSet) rs).getObject(paramNumber);
0775: if (idObj == null) {
0776: throw new JPOXException(LOCALISER.msg("041039"))
0777: .setFatal();
0778: } else {
0779: // Make sure the key type is correct for the type of SingleFieldIdentity
0780: Class keyType = om.getApiAdapter()
0781: .getKeyTypeForSingleFieldIdentityType(
0782: objectIdClass);
0783: idObj = ClassUtils.convertValue(idObj, keyType);
0784: }
0785: return om.getApiAdapter().getNewSingleFieldIdentity(
0786: objectIdClass, pcClass, idObj);
0787: } catch (Exception e) {
0788: JPOXLogger.PERSISTENCE.error(LOCALISER.msg("041036", cmd
0789: .getObjectidClass(), e));
0790: return null;
0791: }
0792: }
0793:
0794: /**
0795: * Create an object id instance and fill the fields using reflection
0796: * @param om the ObjectManager
0797: * @param rs the ResultSet
0798: * @param param the parameters
0799: * @param cmd the AbstractClassMetaData
0800: * @param objectIdClass the object id class
0801: * @return the id
0802: */
0803: private Object createObjectIdInstanceReflection(ObjectManager om,
0804: final Object rs, int[] param, AbstractClassMetaData cmd,
0805: Class objectIdClass) {
0806: // Users own AID
0807: Object fieldValue = null;
0808: try {
0809: // Create an AID
0810: Object id = objectIdClass.newInstance();
0811:
0812: // Set the fields of the AID
0813: int paramIndex = 0;
0814: for (int i = 0; i < cmd.getPKMemberPositions().length; ++i) {
0815: AbstractMemberMetaData fmd = cmd
0816: .getMetaDataForManagedMemberAtAbsolutePosition(cmd
0817: .getPKMemberPositions()[i]);
0818: Field field = objectIdClass.getField(fmd.getName());
0819:
0820: JavaTypeMapping m = om.getStoreManager()
0821: .getDatastoreClass(cmd.getFullClassName(),
0822: om.getClassLoaderResolver())
0823: .getFieldMapping(fmd);
0824: // NOTE This assumes that each field has one datastore column.
0825: for (int j = 0; j < m.getNumberOfDatastoreFields(); j++) {
0826: // TODO Factor this out - using RDBMS-specific code
0827: Object obj = ((ResultSet) rs)
0828: .getObject(param[paramIndex++]);
0829: if ((obj instanceof BigDecimal)) {
0830: BigDecimal bigDecimal = (BigDecimal) obj;
0831: // Oracle 10g returns BigDecimal for NUMBER columns,
0832: // resulting in IllegalArgumentException when reflective
0833: // setter is invoked for incompatible field type
0834: // (see http://www.jpox.org/servlet/jira/browse/CORE-2624)
0835: Class keyType = om.getApiAdapter()
0836: .getKeyTypeForSingleFieldIdentityType(
0837: field.getType());
0838: obj = ClassUtils.convertValue(bigDecimal,
0839: keyType);
0840: if (!bigDecimal.subtract(
0841: new BigDecimal("" + obj)).equals(
0842: new BigDecimal("0"))) {
0843: throw new JPOXException(
0844: "Cannot convert retrieved BigInteger value to field of object id class!")
0845: .setFatal();
0846: }
0847: }
0848: // field with multiple columns should have values returned from db merged here
0849: fieldValue = obj;
0850: }
0851: field.set(id, fieldValue);
0852: }
0853: return id;
0854: } catch (Exception e) {
0855: JPOXLogger.PERSISTENCE.error(LOCALISER.msg("041037", cmd
0856: .getObjectidClass(), fmd == null ? null : fmd
0857: .getName(), fieldValue, e));
0858: return null;
0859: }
0860: }
0861:
0862: /**
0863: * Create an object id instance and fill the fields using reflection
0864: * @param om the ObjectManager
0865: * @param rs the ResultSet
0866: * @param param the parameters
0867: * @param cmd the AbstractClassMetaData
0868: * @return the id
0869: */
0870: private Object getObjectForAbstractClass(ObjectManager om,
0871: final Object rs, int[] param, AbstractClassMetaData cmd) {
0872: ClassLoaderResolver clr = om.getClassLoaderResolver();
0873:
0874: // Abstract class, so we need to generate an AID before proceeding
0875: Class objectIdClass = clr.classForName(cmd.getObjectidClass());
0876: Class pcClass = clr.classForName(cmd.getFullClassName());
0877: Object id;
0878: if (cmd.usesSingleFieldIdentityClass()) {
0879: id = createSingleFieldIdentity(om, rs, param, cmd,
0880: objectIdClass, pcClass);
0881: } else {
0882: id = createObjectIdInstanceReflection(om, rs, param, cmd,
0883: objectIdClass);
0884: }
0885: return om.findObject(id, false, true, null);
0886: }
0887:
0888: /**
0889: * Get the object instance for a class using application identity
0890: * @param om the ObjectManager
0891: * @param rs the ResultSet
0892: * @param param the parameters
0893: * @param cmd the AbstractClassMetaData
0894: * @return the id
0895: */
0896: private Object getObjectForApplicationIdentity(ObjectManager om,
0897: final Object rs, int[] param, AbstractClassMetaData cmd) {
0898: ClassLoaderResolver clr = om.getClassLoaderResolver();
0899:
0900: // Abstract class
0901: if (((ClassMetaData) cmd).isAbstractPersistenceCapable()
0902: && cmd.getObjectidClass() != null) {
0903: return getObjectForAbstractClass(om, rs, param, cmd);
0904: }
0905:
0906: // Concrete class
0907: int totalFieldCount = cmd.getNoOfManagedMembers()
0908: + cmd.getNoOfInheritedManagedMembers();
0909: final StatementExpressionIndex[] statementExpressionIndex = new StatementExpressionIndex[totalFieldCount];
0910: int paramIndex = 0;
0911:
0912: DatastoreClass datastoreClass = om.getStoreManager()
0913: .getDatastoreClass(cmd.getFullClassName(), clr);
0914: final int[] pkFieldNumbers = cmd.getPKMemberPositions();
0915:
0916: for (int i = 0; i < pkFieldNumbers.length; ++i) {
0917: AbstractMemberMetaData fmd = cmd
0918: .getMetaDataForManagedMemberAtAbsolutePosition(pkFieldNumbers[i]);
0919: JavaTypeMapping m = datastoreClass.getFieldMapping(fmd);
0920: statementExpressionIndex[fmd.getAbsoluteFieldNumber()] = new StatementExpressionIndex();
0921: statementExpressionIndex[fmd.getAbsoluteFieldNumber()]
0922: .setMapping(m);
0923: int expressionsIndex[] = new int[m
0924: .getNumberOfDatastoreFields()];
0925: for (int j = 0; j < expressionsIndex.length; j++) {
0926: expressionsIndex[j] = param[paramIndex++];
0927: }
0928: statementExpressionIndex[fmd.getAbsoluteFieldNumber()]
0929: .setExpressionIndex(expressionsIndex);
0930: }
0931:
0932: final MappedStoreManager storeMgr = (MappedStoreManager) om
0933: .getStoreManager();
0934: return om.findObjectUsingAID(clr.classForName(cmd
0935: .getFullClassName()), new FieldValues() {
0936: // StateManager calls the fetchFields method
0937: public void fetchFields(StateManager sm) {
0938: sm.replaceFields(pkFieldNumbers, storeMgr
0939: .getFieldManagerForResultProcessing(sm, rs,
0940: statementExpressionIndex));
0941: }
0942:
0943: public void fetchNonLoadedFields(StateManager sm) {
0944: sm.replaceNonLoadedFields(pkFieldNumbers, storeMgr
0945: .getFieldManagerForResultProcessing(sm, rs,
0946: statementExpressionIndex));
0947: }
0948:
0949: public FetchPlan getFetchPlanForLoading() {
0950: return null;
0951: }
0952: }, false, true);
0953: }
0954:
0955: // ----------------------- Implementation of MappingCallbacks --------------------------
0956:
0957: /**
0958: * Method executed just after a fetch of the owning object, allowing any necessary action
0959: * to this field and the object stored in it.
0960: * @param sm StateManager for the owner.
0961: */
0962: public void postFetch(StateManager sm) {
0963: }
0964:
0965: /**
0966: * Method executed just after the insert of the owning object, allowing any necessary action
0967: * to this field and the object stored in it.
0968: * @param sm StateManager for the owner
0969: */
0970: public void postInsert(StateManager sm) {
0971: Object pc = sm.provideField(fmd.getAbsoluteFieldNumber());
0972: if (pc == null) {
0973: // Has been set to null so nothing to do
0974: return;
0975: }
0976:
0977: ClassLoaderResolver clr = sm.getObjectManager()
0978: .getClassLoaderResolver();
0979: AbstractMemberMetaData[] relatedMmds = fmd
0980: .getRelatedMemberMetaData(clr);
0981: int relationType = fmd.getRelationType(clr);
0982: if (pc != null) {
0983: if (relationType == Relation.ONE_TO_ONE_BI) {
0984: StateManager otherSM = sm.getObjectManager()
0985: .findStateManager(pc);
0986: AbstractMemberMetaData relatedMmd = fmd
0987: .getRelatedMemberMetaDataForObject(clr, sm
0988: .getObject(), pc);
0989: Object relatedValue = otherSM.provideField(relatedMmd
0990: .getAbsoluteFieldNumber());
0991: if (relatedValue == null) {
0992: // Managed Relations : Other side not set so update it in memory
0993: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
0994: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
0995: "041018", StringUtils.toJVMIDString(sm
0996: .getObject()), fmd
0997: .getFullFieldName(),
0998: StringUtils.toJVMIDString(pc),
0999: relatedMmd.getFullFieldName()));
1000: }
1001: otherSM.replaceField(relatedMmd
1002: .getAbsoluteFieldNumber(), sm.getObject(),
1003: false);
1004: } else if (relatedValue != sm.getObject()) {
1005: // Managed Relations : Other side is inconsistent so throw exception
1006: throw new JPOXUserException(LOCALISER.msg("041020",
1007: StringUtils.toJVMIDString(sm.getObject()),
1008: fmd.getFullFieldName(), StringUtils
1009: .toJVMIDString(pc), StringUtils
1010: .toJVMIDString(relatedValue)));
1011: }
1012: } else if (relationType == Relation.MANY_TO_ONE_BI
1013: && relatedMmds[0].hasCollection()) {
1014: // TODO Make sure we have this PC in the collection at the other side
1015: StateManager otherSM = sm.getObjectManager()
1016: .findStateManager(pc);
1017: if (otherSM != null) {
1018: // Managed Relations : add to the collection on the other side
1019: Collection relatedColl = (Collection) otherSM
1020: .provideField(relatedMmds[0]
1021: .getAbsoluteFieldNumber());
1022: if (relatedColl != null
1023: && !(relatedColl instanceof SCOCollection)) {
1024: // TODO Make sure the collection is a wrapper
1025: boolean contained = relatedColl.contains(sm
1026: .getObject());
1027: if (!contained) {
1028: JPOXLogger.PERSISTENCE.info(LOCALISER.msg(
1029: "041022", StringUtils
1030: .toJVMIDString(sm
1031: .getObject()), fmd
1032: .getFullFieldName(),
1033: StringUtils.toJVMIDString(pc),
1034: relatedMmds[0].getFullFieldName()));
1035: // TODO Enable this. CUrrently causes issues with
1036: // PMImplTest, InheritanceStrategyTest, TCK "inheritance1.conf"
1037: /*relatedColl.add(sm.getObject());*/
1038: }
1039: }
1040: }
1041: }
1042: }
1043: }
1044:
1045: /**
1046: * Method executed just afer any update of the owning object, allowing any necessary action
1047: * to this field and the object stored in it.
1048: * @param sm StateManager for the owner
1049: */
1050: public void postUpdate(StateManager sm) {
1051: Object pc = sm.provideField(fmd.getAbsoluteFieldNumber());
1052: if (pc == null) {
1053: // Has been set to null so nothing to do
1054: return;
1055: }
1056:
1057: if (pc != null) {
1058: StateManager otherSM = sm.getObjectManager()
1059: .findStateManager(pc);
1060: if (otherSM == null) {
1061: ClassLoaderResolver clr = sm.getObjectManager()
1062: .getClassLoaderResolver();
1063: int relationType = fmd.getRelationType(clr);
1064: if (relationType == Relation.ONE_TO_ONE_BI
1065: || relationType == Relation.MANY_TO_ONE_BI) {
1066: // Related object is not yet persisted (e.g 1-1 with FK at other side) so persist it
1067: sm.getObjectManager().persistObjectInternal(pc,
1068: null, null, -1, StateManager.PC);
1069: }
1070: }
1071: }
1072: }
1073:
1074: /**
1075: * Method executed just before the owning object is deleted, allowing tidying up of any
1076: * relation information.
1077: * @param sm StateManager for the owner
1078: */
1079: public void preDelete(StateManager sm) {
1080: // makes sure field is loaded
1081: int fieldNumber = fmd.getAbsoluteFieldNumber();
1082: try {
1083: sm.getObjectManager().getApiAdapter().isLoaded(sm,
1084: fieldNumber);
1085: } catch (JDOObjectNotFoundException onfe) {
1086: // Already deleted so just return
1087: return;
1088: }
1089:
1090: Object pc = sm.provideField(fieldNumber);
1091: if (pc == null) {
1092: // Null value so nothing to do
1093: return;
1094: }
1095:
1096: // Check if the field has a FK defined
1097: ClassLoaderResolver clr = sm.getObjectManager()
1098: .getClassLoaderResolver();
1099: AbstractMemberMetaData[] relatedMmds = fmd
1100: .getRelatedMemberMetaData(clr);
1101: // TODO Cater for more than 1 related field
1102: boolean dependent = fmd.isDependent();
1103: boolean hasFK = false;
1104: if (!dependent) {
1105: // Not dependent, so check if the datastore has a FK and will take care of it for us
1106: if (fmd.getForeignKeyMetaData() != null) {
1107: hasFK = true;
1108: }
1109: if (relatedMmds != null
1110: && relatedMmds[0].getForeignKeyMetaData() != null) {
1111: hasFK = true;
1112: }
1113: if (sm.getObjectManager().getOMFContext()
1114: .getPersistenceConfiguration().getDeletionPolicy()
1115: .equals("JDO2")) {
1116: // JDO2 doesnt currently (2.0 spec) take note of foreign-key
1117: hasFK = false;
1118: }
1119: }
1120:
1121: // Basic rules for the following :-
1122: // 1. If it is dependent then we delete it (maybe after nulling).
1123: // 2. If it is not dependent and they have defined no FK then null it, else delete it
1124: // 3. If it is not dependent and they have a FK, let the datastore handle the delete
1125: // There may be some corner cases that this code doesnt yet cater for
1126: int relationType = fmd.getRelationType(clr);
1127: if (pc != null) {
1128: if (relationType == Relation.ONE_TO_ONE_UNI
1129: || (relationType == Relation.ONE_TO_ONE_BI && fmd
1130: .getMappedBy() == null)) {
1131: // 1-1 with FK at this side (owner of the relation)
1132: if (dependent) {
1133: boolean relatedObjectDeleted = sm
1134: .getObjectManager().getApiAdapter()
1135: .isDeleted(pc);
1136: if (isNullable() && !relatedObjectDeleted) {
1137: // Other object not yet deleted - just null out the FK
1138: // TODO Not doing this would cause errors in 1-1 uni relations (e.g AttachDetachTest)
1139: // TODO Log this since it affects the resultant objects
1140: sm.replaceField(fieldNumber, null, true);
1141: sm.getStoreManager().updateObject(sm,
1142: new int[] { fieldNumber });
1143: }
1144: if (!relatedObjectDeleted) {
1145: // Mark the other object for deletion since not yet tagged
1146: sm.getObjectManager().deleteObjectInternal(pc);
1147: }
1148: } else {
1149: // We're deleting the FK at this side so shouldnt be an issue
1150: AbstractMemberMetaData relatedMmd = fmd
1151: .getRelatedMemberMetaDataForObject(clr, sm
1152: .getObject(), pc);
1153: if (relatedMmd != null) {
1154: StateManager otherSM = sm.getObjectManager()
1155: .findStateManager(pc);
1156: if (otherSM != null) {
1157: // Managed Relations : 1-1 bidir, so null out the object at the other
1158: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
1159: JPOXLogger.PERSISTENCE
1160: .debug(LOCALISER
1161: .msg(
1162: "041019",
1163: StringUtils
1164: .toJVMIDString(pc),
1165: relatedMmd
1166: .getFullFieldName(),
1167: StringUtils
1168: .toJVMIDString(sm
1169: .getObject())));
1170: }
1171: otherSM.replaceField(relatedMmd
1172: .getAbsoluteFieldNumber(), null,
1173: true);
1174: }
1175: }
1176: }
1177: } else if (relationType == Relation.ONE_TO_ONE_BI
1178: && fmd.getMappedBy() != null) {
1179: // 1-1 with FK at other side
1180: DatastoreClass relatedTable = sm.getStoreManager()
1181: .getDatastoreClass(
1182: relatedMmds[0].getClassName(), clr);
1183: JavaTypeMapping relatedMapping = relatedTable
1184: .getFieldMapping(relatedMmds[0]);
1185: boolean isNullable = relatedMapping.isNullable();
1186: StateManager otherSM = sm.getObjectManager()
1187: .findStateManager(pc);
1188: if (dependent) {
1189: if (isNullable) {
1190: // Null out the FK in the datastore using a direct update (since we are deleting)
1191: otherSM.replaceField(relatedMmds[0]
1192: .getAbsoluteFieldNumber(), null, true);
1193: otherSM.getStoreManager().updateObject(
1194: otherSM,
1195: new int[] { relatedMmds[0]
1196: .getAbsoluteFieldNumber() });
1197: }
1198: // Mark the other object for deletion
1199: sm.getObjectManager().deleteObjectInternal(pc);
1200: } else if (!hasFK) {
1201: if (isNullable()) {
1202: // Null out the FK in the datastore using a direct update (since we are deleting)
1203: otherSM.replaceField(relatedMmds[0]
1204: .getAbsoluteFieldNumber(), null, true);
1205: otherSM.getStoreManager().updateObject(
1206: otherSM,
1207: new int[] { relatedMmds[0]
1208: .getAbsoluteFieldNumber() });
1209: } else {
1210: // TODO Remove it
1211: }
1212: } else {
1213: // User has a FK defined (in MetaData) so let the datastore take care of it
1214: }
1215: } else if (relationType == Relation.MANY_TO_ONE_BI) {
1216: StateManager otherSM = sm.getObjectManager()
1217: .findStateManager(pc);
1218: if (relatedMmds[0].getJoinMetaData() == null) {
1219: // N-1 with FK at this side
1220: if (otherSM.isDeleting()) {
1221: // Other object is being deleted too but this side has the FK so just delete this object
1222: } else {
1223: // Other object is not being deleted so delete it if necessary
1224: if (dependent) {
1225: if (isNullable()) {
1226: // TODO Datastore nullability info can be unreliable so try to avoid this call
1227: // Null out the FK in the datastore using a direct update (since we are deleting)
1228: sm
1229: .replaceField(fieldNumber,
1230: null, true);
1231: sm.getStoreManager().updateObject(sm,
1232: new int[] { fieldNumber });
1233: }
1234:
1235: if (sm.getObjectManager().getApiAdapter()
1236: .isDeleted(pc)) {
1237: // Object is already tagged for deletion but we're deleting the FK so leave til flush()
1238: } else {
1239: // Mark the other object for deletion
1240: sm.getObjectManager()
1241: .deleteObjectInternal(pc);
1242: }
1243: } else {
1244: // Managed Relations : remove element from collection/map
1245: if (relatedMmds[0].hasCollection()) {
1246: Collection otherColl = (Collection) otherSM
1247: .provideField(relatedMmds[0]
1248: .getAbsoluteFieldNumber());
1249: if (otherColl != null) {
1250: // TODO Remove from any cached SCO collection
1251: }
1252: } else if (relatedMmds[0].hasMap()) {
1253: // TODO Cater for maps, but what is the key/value pair ?
1254: }
1255: }
1256: }
1257: } else {
1258: // N-1 with join table so no FK here so need to remove from Collection/Map first? (managed relations)
1259: if (dependent) {
1260: // Mark the other object for deletion
1261: sm.getObjectManager().deleteObjectInternal(pc);
1262: } else {
1263: // Managed Relations : remove element from collection/map
1264: if (relatedMmds[0].hasCollection()) {
1265: Collection otherColl = (Collection) otherSM
1266: .provideField(relatedMmds[0]
1267: .getAbsoluteFieldNumber());
1268: if (otherColl != null) {
1269: // TODO Add log message
1270: otherColl.remove(sm.getObject());
1271: }
1272: } else if (relatedMmds[0].hasMap()) {
1273: // TODO Cater for maps, but what is the key/value pair ?
1274: }
1275: }
1276: }
1277: } else {
1278: // No relation so what is this field ?
1279: }
1280: }
1281: }
1282: }
|