All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.eis.mappings.EISOneToOneMapping Maven / Gradle / Ivy

There is a newer version: 4.0.2
Show newest version
/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.eis.mappings;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.persistence.annotations.CacheKeyType;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.indirection.ValueHolder;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.identitymaps.CacheId;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ObjectReferenceMapping;
import org.eclipse.persistence.queries.*;

/**
 * 

An EIS one-to-one mapping is a reference mapping that represents the relationship between * a single source object and a single mapped persistent Java object. The source object usually * contains a foreign key (pointer) to the target object (key on source); alternatively, the target * object may contain a foreign key to the source object (key on target). Because both the source * and target objects use interactions, they must both be configured as root object types. * *

* * * * * * * * * * * * * * * * *
Record TypeDescription
IndexedOrdered collection of record elements. The indexed record EIS format * enables Java class attribute values to be retrieved by position or index.
MappedKey-value map based representation of record elements. The mapped record * EIS format enables Java class attribute values to be retrieved by an object key.
XMLRecord/Map representation of an XML DOM element.
* * @see org.eclipse.persistence.eis.EISDescriptor#useIndexedRecordFormat * @see org.eclipse.persistence.eis.EISDescriptor#useMappedRecordFormat * @see org.eclipse.persistence.eis.EISDescriptor#useXMLRecordFormat * * @since Oracle TopLink 10g Release 2 (10.1.3) */ public class EISOneToOneMapping extends ObjectReferenceMapping implements EISMapping { /** Maps the source foreign/primary key fields to the target primary/foreign key fields. */ protected Map sourceToTargetKeyFields; /** Maps the target primary/foreign key fields to the source foreign/primary key fields. */ protected Map targetToSourceKeyFields; /** These are used for non-unit of work modification to check if the value of the 1-1 was changed and a deletion is required. */ protected boolean shouldVerifyDelete; protected transient Expression privateOwnedCriteria; public EISOneToOneMapping() { this.selectionQuery = new ReadObjectQuery(); this.foreignKeyFields = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(1); this.sourceToTargetKeyFields = new HashMap(2); this.targetToSourceKeyFields = new HashMap(2); } /** * INTERNAL: */ @Override public boolean isEISMapping() { return true; } /** * INTERNAL: */ @Override public boolean isOneToOneMapping() { return true; } /** * PUBLIC: * Define the source foreign key relationship in the one-to-one mapping. * This method is used to add foreign key relationships to the mapping. * Both the source foreign key field name and the corresponding * target primary key field name must be specified. */ @Override public void addForeignKeyField(DatabaseField sourceForeignKeyField, DatabaseField targetKeyField) { getSourceToTargetKeyFields().put(sourceForeignKeyField, targetKeyField); getTargetToSourceKeyFields().put(targetKeyField, sourceForeignKeyField); getForeignKeyFields().add(sourceForeignKeyField); setIsForeignKeyRelationship(true); } /** * PUBLIC: * Define the source foreign key relationship in the one-to-one mapping. * This method is used to add foreign key relationships to the mapping. * Both the source foreign key field name and the corresponding * target primary key field name must be specified. */ public void addForeignKeyFieldName(String sourceForeignKeyFieldName, String targetKeyFieldName) { this.addForeignKeyField(new DatabaseField(sourceForeignKeyFieldName), new DatabaseField(targetKeyFieldName)); } /** * INTERNAL: * This methods clones all the fields and ensures that each collection refers to * the same clones. */ @Override public Object clone() { EISOneToOneMapping clone = (EISOneToOneMapping)super.clone(); clone.setForeignKeyFields(org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(getForeignKeyFields().size())); clone.setSourceToTargetKeyFields(new HashMap(getSourceToTargetKeyFields().size())); clone.setTargetToSourceKeyFields(new HashMap(getTargetToSourceKeyFields().size())); Map setOfFields = new HashMap(getTargetToSourceKeyFields().size()); for (Enumeration enumtr = getForeignKeyFields().elements(); enumtr.hasMoreElements();) { DatabaseField field = (DatabaseField)enumtr.nextElement(); DatabaseField fieldClone = field.clone(); setOfFields.put(field, fieldClone); clone.getForeignKeyFields().addElement(fieldClone); } //get clones from set for source hashtable. If they do not exist, create a new one. Iterator sourceKeyIterator = getSourceToTargetKeyFields().keySet().iterator(); while (sourceKeyIterator.hasNext()) { DatabaseField sourceField = (DatabaseField)sourceKeyIterator.next(); DatabaseField targetField = getSourceToTargetKeyFields().get(sourceField); DatabaseField targetClone = (DatabaseField)setOfFields.get(targetField); if (targetClone == null) { targetClone = targetField.clone(); setOfFields.put(targetField, targetClone); } DatabaseField sourceClone = sourceField.clone(); if (sourceClone == null) { sourceClone = sourceField.clone(); setOfFields.put(sourceField, sourceClone); } clone.getSourceToTargetKeyFields().put(sourceClone, targetClone); } //get clones from set for target hashtable. If they do not exist, create a new one. Iterator targetKeyIterator = getTargetToSourceKeyFields().keySet().iterator(); while (targetKeyIterator.hasNext()) { DatabaseField targetField = (DatabaseField)targetKeyIterator.next(); DatabaseField sourceField = getTargetToSourceKeyFields().get(targetField); DatabaseField targetClone = (DatabaseField)setOfFields.get(targetField); if (targetClone == null) { targetClone = targetField.clone(); setOfFields.put(targetField, targetClone); } DatabaseField sourceClone = (DatabaseField)setOfFields.get(sourceField); if (sourceClone == null) { sourceClone = sourceField.clone(); setOfFields.put(sourceField, sourceClone); } clone.getTargetToSourceKeyFields().put(targetClone, sourceClone); } return clone; } /** * INTERNAL: * Return the primary key for the reference object (i.e. the object * object referenced by domainObject and specified by mapping). * This key will be used by a RemoteValueHolder. */ @Override public Object extractPrimaryKeysForReferenceObjectFromRow(AbstractRecord row) { List primaryKeyFields = getReferenceDescriptor().getPrimaryKeyFields(); Object[] result = new Object[primaryKeyFields.size()]; for (int index = 0; index < primaryKeyFields.size(); index++) { DatabaseField targetKeyField = (DatabaseField)primaryKeyFields.get(index); DatabaseField sourceKeyField = getTargetToSourceKeyFields().get(targetKeyField); if (sourceKeyField == null) { return null; } result[index] = row.get(sourceKeyField); if (getReferenceDescriptor().getCachePolicy().getCacheKeyType() == CacheKeyType.ID_VALUE) { return result[index]; } } return new CacheId(result); } /** * INTERNAL: * Initialize the mapping. */ @Override public void initialize(AbstractSession session) throws DescriptorException { super.initialize(session); // Must build foreign keys fields. List foreignKeyFields = getForeignKeyFields(); int size = foreignKeyFields.size(); for (int index = 0; index < size; index++) { DatabaseField foreignKeyField = (DatabaseField)foreignKeyFields.get(index); foreignKeyField = getDescriptor().buildField(foreignKeyField); foreignKeyFields.set(index, foreignKeyField); } initializeForeignKeys(session); if (shouldInitializeSelectionCriteria()) { initializeSelectionCriteria(session); } else { setShouldVerifyDelete(false); } setFields(collectFields()); } /** * INTERNAL: * The foreign keys primary keys are stored as database fields in the hashtable. */ protected void initializeForeignKeys(AbstractSession session) { HashMap newSourceToTargetKeyFields = new HashMap(getSourceToTargetKeyFields().size()); HashMap newTargetToSourceKeyFields = new HashMap(getTargetToSourceKeyFields().size()); Iterator iterator = getSourceToTargetKeyFields().entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry)iterator.next(); DatabaseField sourceField = (DatabaseField)entry.getKey(); DatabaseField targetField = (DatabaseField)entry.getValue(); sourceField = getDescriptor().buildField(sourceField); targetField = getReferenceDescriptor().buildField(targetField); newSourceToTargetKeyFields.put(sourceField, targetField); newTargetToSourceKeyFields.put(targetField, sourceField); } setSourceToTargetKeyFields(newSourceToTargetKeyFields); setTargetToSourceKeyFields(newTargetToSourceKeyFields); } /** * INTERNAL: * Selection criteria is created with source foreign keys and target keys. * This criteria is then used to read target records from the table. * * CR#3922 - This method is almost the same as buildSelectionCriteria() the difference * is that getSelectionCriteria() is called */ protected void initializeSelectionCriteria(AbstractSession session) { if (this.getSourceToTargetKeyFields().isEmpty()) { throw DescriptorException.noForeignKeysAreSpecified(this); } Expression criteria; Expression builder = new ExpressionBuilder(); Iterator keyIterator = getSourceToTargetKeyFields().keySet().iterator(); while (keyIterator.hasNext()) { DatabaseField foreignKey = (DatabaseField)keyIterator.next(); DatabaseField targetKey = getSourceToTargetKeyFields().get(foreignKey); Expression expression = builder.getField(targetKey).equal(builder.getParameter(foreignKey)); criteria = expression.and(getSelectionCriteria()); setSelectionCriteria(criteria); } } /** * INTERNAL: * Reads the private owned object. */ @Override protected Object readPrivateOwnedForObject(ObjectLevelModifyQuery modifyQuery) throws DatabaseException { if (modifyQuery.getSession().isUnitOfWork()) { return getRealAttributeValueFromObject(modifyQuery.getBackupClone(), modifyQuery.getSession()); } else { if (!shouldVerifyDelete()) { return null; } ReadObjectQuery readQuery = (ReadObjectQuery)getSelectionQuery().clone(); readQuery.setSelectionCriteria(getPrivateOwnedCriteria()); return modifyQuery.getSession().executeQuery(readQuery, modifyQuery.getTranslationRow()); } } /** * INTERNAL: * Selection criteria is created with source foreign keys and target keys. */ protected void initializePrivateOwnedCriteria() { if (!isForeignKeyRelationship()) { setPrivateOwnedCriteria(getSelectionCriteria()); } else { Expression pkCriteria = getDescriptor().getObjectBuilder().getPrimaryKeyExpression(); ExpressionBuilder builder = new ExpressionBuilder(); Expression backRef = builder.getManualQueryKey(getAttributeName() + "-back-ref", getDescriptor()); Expression newPKCriteria = pkCriteria.rebuildOn(backRef); Expression twistedSelection = backRef.twist(getSelectionCriteria(), builder); if (getDescriptor().getQueryManager().getAdditionalJoinExpression() != null) { // We don't have to twist the additional join because it's all against the same node, which is our base // but we do have to rebuild it onto the manual query key Expression rebuiltAdditional = getDescriptor().getQueryManager().getAdditionalJoinExpression().rebuildOn(backRef); if (twistedSelection == null) { twistedSelection = rebuiltAdditional; } else { twistedSelection = twistedSelection.and(rebuiltAdditional); } } setPrivateOwnedCriteria(newPKCriteria.and(twistedSelection)); } } /** * INTERNAL: * Return the value of the field from the row or a value holder on the query to obtain the object. * Check for batch + aggregation reading. */ @Override public Object valueFromRow(AbstractRecord row, JoinedAttributeManager joinManager, ObjectBuildingQuery query, CacheKey cacheKey, AbstractSession session, boolean isTargetProtected, Boolean[] wasCacheUsed) throws DatabaseException { if (this.descriptor.getCachePolicy().isProtectedIsolation()) { if (this.isCacheable && isTargetProtected && cacheKey != null) { //cachekey will be null when isolating to uow //used cached collection Object result = null; Object cached = cacheKey.getObject(); if (cached != null) { if (wasCacheUsed != null){ wasCacheUsed[0] = Boolean.TRUE; } return this.getAttributeValueFromObject(cached); } return result; } else if (!this.isCacheable && !isTargetProtected && cacheKey != null) { return this.indirectionPolicy.buildIndirectObject(new ValueHolder(null)); } } // If any field in the foreign key is null then it means there are no referenced objects // Skip for partial objects as fk may not be present. if (!query.hasPartialAttributeExpressions()) { for (Enumeration enumeration = getFields().elements(); enumeration.hasMoreElements();) { DatabaseField field = (DatabaseField)enumeration.nextElement(); if (row.get(field) == null) { return getIndirectionPolicy().nullValueFromRow(); } } } // Call the default which executes the selection query, // or wraps the query with a value holder. //return super.valueFromRow(row, query); ReadQuery targetQuery = getSelectionQuery(); // if the source query is cascading then the target query must use the same settings if (targetQuery.isObjectLevelReadQuery() && (query.shouldCascadeAllParts() || (query.shouldCascadePrivateParts() && isPrivateOwned()) || (query.shouldCascadeByMapping() && this.cascadeRefresh))) { targetQuery = (ObjectLevelReadQuery)targetQuery.clone(); ((ObjectLevelReadQuery)targetQuery).setShouldRefreshIdentityMapResult(query.shouldRefreshIdentityMapResult()); targetQuery.setCascadePolicy(query.getCascadePolicy()); //CR #4365 targetQuery.setQueryId(query.getQueryId()); // For queries that have turned caching off, such as aggregate collection, leave it off. if (targetQuery.shouldMaintainCache()) { targetQuery.setShouldMaintainCache(query.shouldMaintainCache()); } } return getIndirectionPolicy().valueFromQuery(targetQuery, row, query.getSession()); } /** * INTERNAL: * Get a value from the object and set that in the respective field of the row. */ @Override public void writeFromObjectIntoRow(Object object, AbstractRecord Record, AbstractSession session, WriteType writeType) { if (isReadOnly() || (!isForeignKeyRelationship())) { return; } AbstractRecord referenceRow = getIndirectionPolicy().extractReferenceRow(getAttributeValueFromObject(object)); if (referenceRow == null) { // Extract from object. Object referenceObject = getRealAttributeValueFromObject(object, session); for (int i = 0; i < getForeignKeyFields().size(); i++) { DatabaseField sourceKey = getForeignKeyFields().get(i); DatabaseField targetKey = getSourceToTargetKeyFields().get(sourceKey); Object referenceValue = null; // If privately owned part is null then method cannot be invoked. if (referenceObject != null) { referenceValue = getReferenceDescriptor().getObjectBuilder().extractValueFromObjectForField(referenceObject, targetKey, session); } Record.add(sourceKey, referenceValue); } } else { for (int i = 0; i < getForeignKeyFields().size(); i++) { DatabaseField sourceKey = getForeignKeyFields().get(i); Record.add(sourceKey, referenceRow.get(sourceKey)); } } } /** * INTERNAL: * Return the classifiction for the field contained in the mapping. * This is used to convert the row value to a consistent java value. */ @Override public Class getFieldClassification(DatabaseField fieldToClassify) throws DescriptorException { DatabaseField fieldInTarget = getSourceToTargetKeyFields().get(fieldToClassify); if (fieldInTarget == null) { return null;// Can be registered as multiple table secondary field mapping } DatabaseMapping mapping = getReferenceDescriptor().getObjectBuilder().getMappingForField(fieldInTarget); if (mapping == null) { return null;// Means that the mapping is read-only } return mapping.getFieldClassification(fieldInTarget); } /** * INTERNAL: * The private owned criteria is only used outside of the unit of work to compare the previous value of the reference. */ public Expression getPrivateOwnedCriteria() { if (privateOwnedCriteria == null) { initializePrivateOwnedCriteria(); } return privateOwnedCriteria; } /** * INTERNAL: * Private owned criteria is used to verify the deletion of the target. * It joins from the source table on the foreign key to the target table, * with a parameterization of the primary key of the source object. */ protected void setPrivateOwnedCriteria(Expression expression) { privateOwnedCriteria = expression; } /** * PUBLIC: Verify delete is used during delete and update on private 1:1's * outside of a unit of work only. It checks for the previous value of the * target object through joining the source and target tables. By default it * is always done, but may be disabled for performance on distributed * database reasons. In the unit of work the previous value is obtained from * the backup-clone so it is never used. * * @param shouldVerifyDelete * Sets whether delete verification should be performed */ public void setShouldVerifyDelete(boolean shouldVerifyDelete) { this.shouldVerifyDelete = shouldVerifyDelete; } /** * PUBLIC: Verify delete is used during delete and update outside of a unit * of work only. It checks for the previous value of the target object * through joining the source and target tables. * * @return TRUE if verify delete has been enabled */ public boolean shouldVerifyDelete() { return shouldVerifyDelete; } /** * INTERNAL: Gets the foreign key fields. * * @return The mapping from source to target key fields */ public Map getSourceToTargetKeyFields() { return sourceToTargetKeyFields; } /** * INTERNAL: Gets the target foreign key fields. * * @return The mapping from target to source key fields */ public Map getTargetToSourceKeyFields() { return targetToSourceKeyFields; } /** * INTERNAL: Set the source keys to target keys fields association. * * @param sourceToTargetKeyFields * The mapping from source keys to target keys */ public void setSourceToTargetKeyFields(Map sourceToTargetKeyFields) { this.sourceToTargetKeyFields = sourceToTargetKeyFields; } /** * INTERNAL: Set the target keys to source keys fields association. * * @param targetToSourceKeyFields * The mapping from target keys to source keys */ public void setTargetToSourceKeyFields(Map targetToSourceKeyFields) { this.targetToSourceKeyFields = targetToSourceKeyFields; } /** * INTERNAL: * This method is not supported in an EIS environment. */ @Override public void setSelectionSQLString(String sqlString) { throw DescriptorException.invalidMappingOperation(this, "setSelectionSQLString"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy