Search in sources :

Example 21 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class PersistableMapping method setObjectAsValue.

/**
 * Method to set an object reference (FK) in the datastore.
 * @param ec The ExecutionContext
 * @param ps The Prepared Statement
 * @param param The parameter ids in the statement
 * @param value The value to put in the statement at these ids
 * @param ownerOP ObjectProvider for the owner object
 * @param ownerFieldNumber Field number of this PC object in the owner
 * @throws NotYetFlushedException Just put "null" in and throw "NotYetFlushedException", to be caught by ParameterSetter and will signal to the
 *     PC object being inserted that it needs to inform this object when it is inserted.
 */
private void setObjectAsValue(ExecutionContext ec, PreparedStatement ps, int[] param, Object value, ObjectProvider ownerOP, int ownerFieldNumber) {
    Object id;
    ApiAdapter api = ec.getApiAdapter();
    if (!api.isPersistable(value)) {
        throw new NucleusException(Localiser.msg("041016", value.getClass(), value)).setFatal();
    }
    ObjectProvider valueOP = ec.findObjectProvider(value);
    try {
        ClassLoaderResolver clr = ec.getClassLoaderResolver();
        // Check if the field is attributed in the datastore
        boolean hasDatastoreAttributedPrimaryKeyValues = hasDatastoreAttributedPrimaryKeyValues(ec.getMetaDataManager(), storeMgr, clr);
        boolean inserted = false;
        if (ownerFieldNumber >= 0) {
            // Field mapping : is this field of the related object present in the datastore?
            inserted = storeMgr.isObjectInserted(valueOP, ownerFieldNumber);
        } else if (mmd == null) {
            // Identity mapping : is the object inserted far enough to be considered of this mapping type?
            inserted = storeMgr.isObjectInserted(valueOP, type);
        }
        if (valueOP != null) {
            if (ec.getApiAdapter().isDetached(value) && valueOP.getReferencedPC() != null && ownerOP != null && mmd != null) {
                // Still detached but started attaching so replace the field with what will be the attached
                // Note that we have "fmd != null" here hence omitting any M-N relations where this is a join table
                // mapping
                ownerOP.replaceFieldMakeDirty(ownerFieldNumber, valueOP.getReferencedPC());
            }
            if (valueOP.isWaitingToBeFlushedToDatastore()) {
                try {
                    // Related object is not yet flushed to the datastore so flush it so we can set the FK
                    valueOP.flush();
                } catch (NotYetFlushedException nfe) {
                    // Could not flush it, maybe it has a relation to this object! so set as null TODO check nullability
                    if (ownerOP != null) {
                        ownerOP.updateFieldAfterInsert(value, ownerFieldNumber);
                    }
                    setObjectAsNull(ec, ps, param);
                    return;
                }
            }
        } else {
            if (ec.getApiAdapter().isDetached(value)) {
                // Field value is detached and not yet started attaching, so attach
                Object attachedValue = ec.persistObjectInternal(value, null, -1, ObjectProvider.PC);
                if (attachedValue != value && ownerOP != null) {
                    // Replace the field value if using copy-on-attach
                    ownerOP.replaceFieldMakeDirty(ownerFieldNumber, attachedValue);
                    // Work from attached value now that it is attached
                    value = attachedValue;
                }
                valueOP = ec.findObjectProvider(value);
            }
        }
        // 5) the value is the same object as we are inserting anyway and has its identity set
        if (inserted || !ec.isInserting(value) || (!hasDatastoreAttributedPrimaryKeyValues && (this.mmd != null && this.mmd.isPrimaryKey())) || (!hasDatastoreAttributedPrimaryKeyValues && ownerOP == valueOP && api.getIdForObject(value) != null)) {
            // The PC is either already inserted, or inserted down to the level we need, or not inserted at all,
            // or the field is a PK and identity not attributed by the datastore
            // Object either already exists, or is not yet being inserted.
            id = api.getIdForObject(value);
            // Check if the persistable object exists in this datastore
            boolean requiresPersisting = false;
            if (ec.getApiAdapter().isDetached(value) && ownerOP != null) {
                // Detached object so needs attaching
                if (ownerOP.isInserting()) {
                    // we can just return the value now and attach later (in InsertRequest)
                    if (!ec.getBooleanProperty(PropertyNames.PROPERTY_ATTACH_SAME_DATASTORE)) {
                        if (ec.getObjectFromCache(api.getIdForObject(value)) != null) {
                        // Object is in cache so exists for this datastore, so no point checking
                        } else {
                            try {
                                Object obj = ec.findObject(api.getIdForObject(value), true, false, value.getClass().getName());
                                if (obj != null) {
                                    // Make sure this object is not retained in cache etc
                                    ObjectProvider objOP = ec.findObjectProvider(obj);
                                    if (objOP != null) {
                                        ec.evictFromTransaction(objOP);
                                    }
                                    ec.removeObjectFromLevel1Cache(api.getIdForObject(value));
                                }
                            } catch (NucleusObjectNotFoundException onfe) {
                                // Object doesn't yet exist
                                requiresPersisting = true;
                            }
                        }
                    }
                } else {
                    requiresPersisting = true;
                }
            } else if (id == null) {
                // Transient object, so we need to persist it
                requiresPersisting = true;
            } else {
                ExecutionContext pcEC = ec.getApiAdapter().getExecutionContext(value);
                if (pcEC != null && ec != pcEC) {
                    throw new NucleusUserException(Localiser.msg("041015"), id);
                }
            }
            if (requiresPersisting) {
                // This PC object needs persisting (new or detached) to do the "set"
                if (mmd != null && !mmd.isCascadePersist() && !ec.getApiAdapter().isDetached(value)) {
                    // Related PC object not persistent, but cant do cascade-persist so throw exception
                    if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
                        NucleusLogger.PERSISTENCE.debug(Localiser.msg("007006", mmd.getFullFieldName()));
                    }
                    throw new ReachableObjectNotCascadedException(mmd.getFullFieldName(), value);
                }
                if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
                    NucleusLogger.PERSISTENCE.debug(Localiser.msg("007007", mmd != null ? mmd.getFullFieldName() : null));
                }
                try {
                    Object pcNew = ec.persistObjectInternal(value, null, -1, ObjectProvider.PC);
                    if (hasDatastoreAttributedPrimaryKeyValues) {
                        ec.flushInternal(false);
                    }
                    id = api.getIdForObject(pcNew);
                    if (ec.getApiAdapter().isDetached(value) && ownerOP != null && mmd != null) {
                        // Update any detached reference to refer to the attached variant
                        ownerOP.replaceFieldMakeDirty(ownerFieldNumber, pcNew);
                        RelationType relationType = mmd.getRelationType(clr);
                        if (relationType == RelationType.MANY_TO_ONE_BI) {
                            // TODO Update the container to refer to the attached object
                            if (NucleusLogger.PERSISTENCE.isInfoEnabled()) {
                                NucleusLogger.PERSISTENCE.info("PCMapping.setObject : object " + ownerOP.getInternalObjectId() + " has field " + ownerFieldNumber + " that is 1-N bidirectional." + " Have just attached the N side so should really update the reference in the 1 side collection" + " to refer to this attached object. Not yet implemented");
                            }
                        } else if (relationType == RelationType.ONE_TO_ONE_BI) {
                            AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
                            // TODO Cater for more than 1 related field
                            ObjectProvider relatedOP = ec.findObjectProvider(pcNew);
                            relatedOP.replaceFieldMakeDirty(relatedMmds[0].getAbsoluteFieldNumber(), ownerOP.getObject());
                        }
                    }
                } catch (NotYetFlushedException e) {
                    setObjectAsNull(ec, ps, param);
                    throw new NotYetFlushedException(value);
                }
            }
            if (valueOP != null) {
                valueOP.setStoringPC();
            }
            // If the field doesn't map to any datastore fields (e.g remote FK), omit the set process
            if (getNumberOfDatastoreMappings() > 0) {
                if (IdentityUtils.isDatastoreIdentity(id)) {
                    Object idKey = IdentityUtils.getTargetKeyForDatastoreIdentity(id);
                    try {
                        // Try as a Long
                        getDatastoreMapping(0).setObject(ps, param[0], idKey);
                    } catch (Exception e) {
                        // Must be a String
                        getDatastoreMapping(0).setObject(ps, param[0], idKey.toString());
                    }
                } else {
                    boolean fieldsSet = false;
                    if (IdentityUtils.isSingleFieldIdentity(id) && javaTypeMappings.length > 1) {
                        Object key = IdentityUtils.getTargetKeyForSingleFieldIdentity(id);
                        AbstractClassMetaData keyCmd = ec.getMetaDataManager().getMetaDataForClass(key.getClass(), clr);
                        if (keyCmd != null && keyCmd.getIdentityType() == IdentityType.NONDURABLE) {
                            // Embedded ID - Make sure these are called starting at lowest first, in order
                            // We cannot just call OP.provideFields with all fields since that does last first
                            ObjectProvider keyOP = ec.findObjectProvider(key);
                            int[] fieldNums = keyCmd.getAllMemberPositions();
                            FieldManager fm = new AppIDObjectIdFieldManager(param, ec, ps, javaTypeMappings);
                            for (int i = 0; i < fieldNums.length; i++) {
                                keyOP.provideFields(new int[] { fieldNums[i] }, fm);
                            }
                            fieldsSet = true;
                        }
                    }
                    if (!fieldsSet) {
                        // Copy PK fields from identity to the object
                        FieldManager fm = new AppIDObjectIdFieldManager(param, ec, ps, javaTypeMappings);
                        api.copyKeyFieldsFromIdToObject(value, new AppIdObjectIdFieldConsumer(api, fm), id);
                    }
                }
            }
        } else {
            if (valueOP != null) {
                valueOP.setStoringPC();
            }
            if (getNumberOfDatastoreMappings() > 0) {
                // Object is in the process of being inserted so we cant use its id currently and we need to store
                // a foreign key to it (which we cant yet do). Just put "null" in and throw "NotYetFlushedException",
                // to be caught by ParameterSetter and will signal to the PC object being inserted that it needs
                // to inform this object when it is inserted.
                setObjectAsNull(ec, ps, param);
                throw new NotYetFlushedException(value);
            }
        }
    } finally {
        if (valueOP != null) {
            valueOP.unsetStoringPC();
        }
    }
}
Also used : ApiAdapter(org.datanucleus.api.ApiAdapter) AppIDObjectIdFieldManager(org.datanucleus.store.rdbms.mapping.AppIDObjectIdFieldManager) SingleValueFieldManager(org.datanucleus.store.fieldmanager.SingleValueFieldManager) FieldManager(org.datanucleus.store.fieldmanager.FieldManager) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) NotYetFlushedException(org.datanucleus.exceptions.NotYetFlushedException) NucleusObjectNotFoundException(org.datanucleus.exceptions.NucleusObjectNotFoundException) NucleusObjectNotFoundException(org.datanucleus.exceptions.NucleusObjectNotFoundException) ReachableObjectNotCascadedException(org.datanucleus.exceptions.ReachableObjectNotCascadedException) NucleusException(org.datanucleus.exceptions.NucleusException) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) NotYetFlushedException(org.datanucleus.exceptions.NotYetFlushedException) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) ReachableObjectNotCascadedException(org.datanucleus.exceptions.ReachableObjectNotCascadedException) ExecutionContext(org.datanucleus.ExecutionContext) RelationType(org.datanucleus.metadata.RelationType) AppIDObjectIdFieldManager(org.datanucleus.store.rdbms.mapping.AppIDObjectIdFieldManager) ObjectProvider(org.datanucleus.state.ObjectProvider) NucleusException(org.datanucleus.exceptions.NucleusException) AppIdObjectIdFieldConsumer(org.datanucleus.state.AppIdObjectIdFieldConsumer)

Example 22 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class ReferenceIdMapping method setObject.

/**
 * Method to set the object based on an input identity.
 * @param ec execution context
 * @param ps PreparedStatement
 * @param param Parameter positions to populate when setting the value
 * @param value The identity
 */
public void setObject(ExecutionContext ec, PreparedStatement ps, int[] param, Object value) {
    if (value == null) {
        super.setObject(ec, ps, param, null);
        return;
    }
    if (mappingStrategy == ID_MAPPING || mappingStrategy == XCALIA_MAPPING) {
        String refString = getReferenceStringForObject(ec, value);
        getJavaTypeMapping()[0].setString(ec, ps, param, refString);
        return;
    }
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    int colPos = 0;
    for (int i = 0; i < javaTypeMappings.length; i++) {
        int[] cols = new int[javaTypeMappings[i].getNumberOfDatastoreMappings()];
        for (int j = 0; j < cols.length; j++) {
            cols[j] = param[colPos++];
        }
        Class cls = clr.classForName(javaTypeMappings[i].getType());
        AbstractClassMetaData implCmd = ec.getMetaDataManager().getMetaDataForClass(cls, clr);
        if (implCmd.getObjectidClass().equals(value.getClass().getName())) {
            if (IdentityUtils.isDatastoreIdentity(value)) {
                Object key = IdentityUtils.getTargetKeyForDatastoreIdentity(value);
                if (key instanceof String) {
                    javaTypeMappings[i].setString(ec, ps, cols, (String) key);
                } else {
                    javaTypeMappings[i].setObject(ec, ps, cols, key);
                }
            } else if (IdentityUtils.isSingleFieldIdentity(value)) {
                Object key = IdentityUtils.getTargetKeyForSingleFieldIdentity(value);
                if (key instanceof String) {
                    javaTypeMappings[i].setString(ec, ps, cols, (String) key);
                } else {
                    javaTypeMappings[i].setObject(ec, ps, cols, key);
                }
            } else {
                // TODO Cater for compound identity
                String[] pkMemberNames = implCmd.getPrimaryKeyMemberNames();
                for (int j = 0; j < pkMemberNames.length; j++) {
                    Object pkMemberValue = ClassUtils.getValueForIdentityField(value, pkMemberNames[j]);
                    if (pkMemberValue instanceof Byte) {
                        getDatastoreMapping(j).setByte(ps, param[j], (Byte) pkMemberValue);
                    } else if (pkMemberValue instanceof Character) {
                        getDatastoreMapping(j).setChar(ps, param[j], (Character) pkMemberValue);
                    } else if (pkMemberValue instanceof Integer) {
                        getDatastoreMapping(j).setInt(ps, param[j], (Integer) pkMemberValue);
                    } else if (pkMemberValue instanceof Long) {
                        getDatastoreMapping(j).setLong(ps, param[j], (Long) pkMemberValue);
                    } else if (pkMemberValue instanceof Short) {
                        getDatastoreMapping(j).setShort(ps, param[j], (Short) pkMemberValue);
                    } else if (pkMemberValue instanceof String) {
                        getDatastoreMapping(j).setString(ps, param[j], (String) pkMemberValue);
                    } else {
                        getDatastoreMapping(j).setObject(ps, param[j], pkMemberValue);
                    }
                }
            }
        } else {
            // Set columns to null for this implementation
            javaTypeMappings[i].setObject(ec, ps, cols, null);
        }
    }
}
Also used : ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Example 23 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class ReferenceMapping method getObjectForReferenceString.

/**
 * Method to convert a "reference string" into the associated object.
 * Reference string is of the form :
 * <ul>
 * <li>ID_MAPPING : "{classname}:{id}"</li>
 * <li>XCALIA_MAPPING (datastore-id) : "{definer}:{id-key}" where definer is discriminator/classname</li>
 * <li>XCALIA_MAPPING (app-id) : "{definer}:{id}" where definer is discriminator/classname</li>
 * </ul>
 * @param ec execution context
 * @param refString The reference string
 * @return The referenced object
 */
protected Object getObjectForReferenceString(ExecutionContext ec, String refString) {
    int sepPos = refString.indexOf(':');
    String refDefiner = refString.substring(0, sepPos);
    String refClassName = null;
    String refId = refString.substring(sepPos + 1);
    AbstractClassMetaData refCmd = null;
    if (mappingStrategy == ID_MAPPING) {
        refCmd = ec.getMetaDataManager().getMetaDataForClass(refDefiner, ec.getClassLoaderResolver());
    } else {
        refCmd = ec.getMetaDataManager().getMetaDataForClass(refDefiner, ec.getClassLoaderResolver());
        if (refCmd == null) {
            refCmd = ec.getMetaDataManager().getMetaDataForDiscriminator(refDefiner);
        }
    }
    if (refCmd == null) {
        throw new NucleusException("Reference field contains reference to class of type " + refDefiner + " but no metadata found for this class");
    }
    refClassName = refCmd.getFullClassName();
    // Obtain the identity
    Object id = null;
    if (refCmd.getIdentityType() == IdentityType.DATASTORE) {
        if (mappingStrategy == ID_MAPPING) {
            // refId is the OID.toString() form
            id = ec.getNucleusContext().getIdentityManager().getDatastoreId(refId);
        } else if (mappingStrategy == XCALIA_MAPPING) {
            // refId is simply the OID key in this case
            id = ec.getNucleusContext().getIdentityManager().getDatastoreId(refCmd.getFullClassName(), refId);
        }
    } else if (refCmd.getIdentityType() == IdentityType.APPLICATION) {
        id = ec.getNucleusContext().getIdentityManager().getApplicationId(ec.getClassLoaderResolver(), refCmd, refId);
    }
    // Retrieve the referenced object with this id
    return ec.findObject(id, true, false, refClassName);
}
Also used : NucleusException(org.datanucleus.exceptions.NucleusException) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Example 24 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class ReferenceMapping method prepareDatastoreMapping.

/**
 * Convenience method to create the necessary columns to represent this reference in the datastore.
 * With "per-implementation" mapping strategy will create columns for each of the possible implementations.
 * With "identity"/"xcalia" will create a single column to store a reference to the implementation value.
 * @param clr The ClassLoaderResolver
 */
protected void prepareDatastoreMapping(ClassLoaderResolver clr) {
    if (mappingStrategy == PER_IMPLEMENTATION_MAPPING) {
        // Mapping per reference implementation, so create columns for each possible implementation
        if (roleForMember == FieldRole.ROLE_ARRAY_ELEMENT) {
            // Creation of columns in join table for array of references
            ColumnMetaData[] colmds = null;
            ElementMetaData elemmd = mmd.getElementMetaData();
            if (elemmd != null && elemmd.getColumnMetaData() != null && elemmd.getColumnMetaData().length > 0) {
                // Column mappings defined at this side (1-N, M-N)
                colmds = elemmd.getColumnMetaData();
            }
            createPerImplementationColumnsForReferenceField(false, false, false, false, roleForMember, colmds, clr);
        } else if (roleForMember == FieldRole.ROLE_COLLECTION_ELEMENT) {
            // Creation of columns in join table for collection of references
            ColumnMetaData[] colmds = null;
            AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
            ElementMetaData elemmd = mmd.getElementMetaData();
            if (elemmd != null && elemmd.getColumnMetaData() != null && elemmd.getColumnMetaData().length > 0) {
                // Column mappings defined at this side (1-N, M-N)
                colmds = elemmd.getColumnMetaData();
            } else if (relatedMmds != null && relatedMmds[0].getJoinMetaData() != null && relatedMmds[0].getJoinMetaData().getColumnMetaData() != null && relatedMmds[0].getJoinMetaData().getColumnMetaData().length > 0) {
                // Column mappings defined at other side (M-N) on <join>
                colmds = relatedMmds[0].getJoinMetaData().getColumnMetaData();
            }
            createPerImplementationColumnsForReferenceField(false, false, false, false, roleForMember, colmds, clr);
        } else if (roleForMember == FieldRole.ROLE_MAP_KEY) {
            // Creation of columns in join table for map of references as keys
            ColumnMetaData[] colmds = null;
            KeyMetaData keymd = mmd.getKeyMetaData();
            if (keymd != null && keymd.getColumnMetaData() != null && keymd.getColumnMetaData().length > 0) {
                // Column mappings defined at this side (1-N, M-N)
                colmds = keymd.getColumnMetaData();
            }
            createPerImplementationColumnsForReferenceField(false, false, false, false, roleForMember, colmds, clr);
        } else if (roleForMember == FieldRole.ROLE_MAP_VALUE) {
            // Creation of columns in join table for map of references as values
            ColumnMetaData[] colmds = null;
            ValueMetaData valuemd = mmd.getValueMetaData();
            if (valuemd != null && valuemd.getColumnMetaData() != null && valuemd.getColumnMetaData().length > 0) {
                // Column mappings defined at this side (1-N, M-N)
                colmds = valuemd.getColumnMetaData();
            }
            createPerImplementationColumnsForReferenceField(false, false, false, false, roleForMember, colmds, clr);
        } else {
            if (mmd.getMappedBy() == null) {
                // Unidirectional 1-1
                boolean embedded = (mmd.isEmbedded() || mmd.getEmbeddedMetaData() != null);
                createPerImplementationColumnsForReferenceField(false, true, false, embedded, roleForMember, mmd.getColumnMetaData(), clr);
            } else {
                // Bidirectional 1-1/N-1
                AbstractClassMetaData refCmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForInterface(mmd.getType(), clr);
                if (refCmd != null && refCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
                    // TODO Is this block actually reachable ? Would we specify "inheritance" under "interface" elements?
                    // Find the actual tables storing the other end (can be multiple subclasses)
                    AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(refCmd, clr);
                    if (cmds != null && cmds.length > 0) {
                        if (cmds.length > 1) {
                            NucleusLogger.PERSISTENCE.warn("Field " + mmd.getFullFieldName() + " represents either a 1-1 relation, or a N-1 relation where the other end uses" + " \"subclass-table\" inheritance strategy and more than 1 subclasses with a table. " + "This is not fully supported currently");
                        }
                    } else {
                        // TODO Throw an exception ?
                        return;
                    }
                    // TODO We need a mapping for each of the possible subclass tables
                    /*JavaTypeMapping referenceMapping = */
                    storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr).getIdMapping();
                } else {
                    String[] implTypes = MetaDataUtils.getInstance().getImplementationNamesForReferenceField(mmd, FieldRole.ROLE_FIELD, clr, storeMgr.getMetaDataManager());
                    for (int j = 0; j < implTypes.length; j++) {
                        JavaTypeMapping refMapping = storeMgr.getDatastoreClass(implTypes[j], clr).getIdMapping();
                        JavaTypeMapping mapping = storeMgr.getMappingManager().getMapping(clr.classForName(implTypes[j]));
                        mapping.setReferenceMapping(refMapping);
                        this.addJavaTypeMapping(mapping);
                    }
                }
            }
        }
    } else if (mappingStrategy == ID_MAPPING || mappingStrategy == XCALIA_MAPPING) {
        // Single (String) column storing the identity of the related object
        MappingManager mapMgr = storeMgr.getMappingManager();
        JavaTypeMapping mapping = mapMgr.getMapping(String.class);
        mapping.setMemberMetaData(mmd);
        mapping.setTable(table);
        mapping.setRoleForMember(roleForMember);
        Column col = mapMgr.createColumn(mapping, String.class.getName(), 0);
        mapMgr.createDatastoreMapping(mapping, mmd, 0, col);
        this.addJavaTypeMapping(mapping);
    }
}
Also used : ElementMetaData(org.datanucleus.metadata.ElementMetaData) KeyMetaData(org.datanucleus.metadata.KeyMetaData) Column(org.datanucleus.store.rdbms.table.Column) ValueMetaData(org.datanucleus.metadata.ValueMetaData) ColumnMetaData(org.datanucleus.metadata.ColumnMetaData) MappingManager(org.datanucleus.store.rdbms.mapping.MappingManager) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Example 25 with AbstractClassMetaData

use of org.datanucleus.metadata.AbstractClassMetaData in project datanucleus-rdbms by datanucleus.

the class FKListStore method validateElementForWriting.

/**
 * Method to validate that an element is valid for writing to the datastore.
 * TODO Minimise differences to super.validateElementForWriting()
 * @param op ObjectProvider for the List
 * @param element The element to validate
 * @param index The position that the element is being stored at in the list
 * @return Whether the element was inserted
 */
protected boolean validateElementForWriting(final ObjectProvider op, final Object element, final int index) {
    final Object newOwner = op.getObject();
    ComponentInfo info = getComponentInfoForElement(element);
    final DatastoreClass elementTable;
    if (storeMgr.getNucleusContext().getMetaDataManager().isPersistentInterface(elementType)) {
        elementTable = storeMgr.getDatastoreClass(storeMgr.getNucleusContext().getMetaDataManager().getImplementationNameForPersistentInterface(elementType), clr);
    } else {
        elementTable = storeMgr.getDatastoreClass(element.getClass().getName(), clr);
    }
    final JavaTypeMapping orderMapping;
    if (info != null) {
        orderMapping = info.getDatastoreClass().getExternalMapping(ownerMemberMetaData, MappingType.EXTERNAL_INDEX);
    } else {
        orderMapping = this.orderMapping;
    }
    // Check if element is ok for use in the datastore, specifying any external mappings that may be required
    boolean inserted = super.validateElementForWriting(op.getExecutionContext(), element, new FieldValues() {

        public void fetchFields(ObjectProvider elemOP) {
            // Find the (element) table storing the FK back to the owner
            if (elementTable != null) {
                JavaTypeMapping externalFKMapping = elementTable.getExternalMapping(ownerMemberMetaData, MappingType.EXTERNAL_FK);
                if (externalFKMapping != null) {
                    // The element has an external FK mapping so set the value it needs to use in the INSERT
                    elemOP.setAssociatedValue(externalFKMapping, op.getObject());
                }
                if (relationDiscriminatorMapping != null) {
                    elemOP.setAssociatedValue(relationDiscriminatorMapping, relationDiscriminatorValue);
                }
                if (orderMapping != null && index >= 0) {
                    if (ownerMemberMetaData.getOrderMetaData() != null && ownerMemberMetaData.getOrderMetaData().getMappedBy() != null) {
                        // Order is stored in a field in the element so update it
                        // We support mapped-by fields of types int/long/Integer/Long currently
                        Object indexValue = null;
                        if (orderMapping.getMemberMetaData().getTypeName().equals(ClassNameConstants.JAVA_LANG_LONG) || orderMapping.getMemberMetaData().getTypeName().equals(ClassNameConstants.LONG)) {
                            indexValue = Long.valueOf(index);
                        } else {
                            indexValue = Integer.valueOf(index);
                        }
                        elemOP.replaceFieldMakeDirty(orderMapping.getMemberMetaData().getAbsoluteFieldNumber(), indexValue);
                    } else {
                        // Order is stored in a surrogate column so save its vaue for the element to use later
                        elemOP.setAssociatedValue(orderMapping, Integer.valueOf(index));
                    }
                }
            }
            if (ownerMemberMetaData.getMappedBy() != null) {
                // TODO This is ManagedRelations - move into RelationshipManager
                // Managed Relations : 1-N bidir, so make sure owner is correct at persist
                // TODO Support DOT notation in mappedBy
                ObjectProvider ownerHolderOP = elemOP;
                int ownerFieldNumberInHolder = -1;
                if (ownerMemberMetaData.getMappedBy().indexOf('.') > 0) {
                    AbstractMemberMetaData otherMmd = null;
                    AbstractClassMetaData otherCmd = info.getAbstractClassMetaData();
                    String remainingMappedBy = ownerMemberMetaData.getMappedBy();
                    while (remainingMappedBy.indexOf('.') > 0) {
                        int dotPosition = remainingMappedBy.indexOf('.');
                        String thisMappedBy = remainingMappedBy.substring(0, dotPosition);
                        otherMmd = otherCmd.getMetaDataForMember(thisMappedBy);
                        Object holderValueAtField = ownerHolderOP.provideField(otherMmd.getAbsoluteFieldNumber());
                        ownerHolderOP = op.getExecutionContext().findObjectProviderForEmbedded(holderValueAtField, ownerHolderOP, otherMmd);
                        remainingMappedBy = remainingMappedBy.substring(dotPosition + 1);
                        otherCmd = storeMgr.getMetaDataManager().getMetaDataForClass(otherMmd.getTypeName(), clr);
                        if (remainingMappedBy.indexOf('.') < 0) {
                            otherMmd = otherCmd.getMetaDataForMember(remainingMappedBy);
                            ownerFieldNumberInHolder = otherMmd.getAbsoluteFieldNumber();
                        }
                    }
                } else {
                    ownerFieldNumberInHolder = info.getAbstractClassMetaData().getAbsolutePositionOfMember(ownerMemberMetaData.getMappedBy());
                }
                Object currentOwner = ownerHolderOP.provideField(ownerFieldNumberInHolder);
                if (currentOwner == null) {
                    // No owner, so correct it
                    NucleusLogger.PERSISTENCE.info(Localiser.msg("056037", op.getObjectAsPrintable(), ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(ownerHolderOP.getObject())));
                    ownerHolderOP.replaceFieldMakeDirty(ownerFieldNumberInHolder, newOwner);
                } else if (currentOwner != newOwner && op.getReferencedPC() == null) {
                    // Inconsistent owner, so throw exception
                    throw new NucleusUserException(Localiser.msg("056038", op.getObjectAsPrintable(), ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(ownerHolderOP.getObject()), StringUtils.toJVMIDString(currentOwner)));
                }
            }
        }

        public void fetchNonLoadedFields(ObjectProvider op) {
        }

        public FetchPlan getFetchPlanForLoading() {
            return null;
        }
    });
    return inserted;
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ObjectProvider(org.datanucleus.state.ObjectProvider) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) FieldValues(org.datanucleus.store.FieldValues) FetchPlan(org.datanucleus.FetchPlan) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Aggregations

AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)204 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)90 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)69 ClassLoaderResolver (org.datanucleus.ClassLoaderResolver)59 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)55 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)42 RDBMSStoreManager (org.datanucleus.store.rdbms.RDBMSStoreManager)42 NucleusException (org.datanucleus.exceptions.NucleusException)37 MetaDataManager (org.datanucleus.metadata.MetaDataManager)37 ArrayList (java.util.ArrayList)31 SQLExpression (org.datanucleus.store.rdbms.sql.expression.SQLExpression)26 SQLTable (org.datanucleus.store.rdbms.sql.SQLTable)23 SQLExpressionFactory (org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory)22 ClassLoaderResolverImpl (org.datanucleus.ClassLoaderResolverImpl)19 MapTable (org.datanucleus.store.rdbms.table.MapTable)18 List (java.util.List)16 ObjectProvider (org.datanucleus.state.ObjectProvider)16 PersistableMapping (org.datanucleus.store.rdbms.mapping.java.PersistableMapping)16 SelectStatement (org.datanucleus.store.rdbms.sql.SelectStatement)16 Iterator (java.util.Iterator)15