/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.internal.indirection; import java.util.Collection; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.indirection.*; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; import org.eclipse.persistence.mappings.*; /** * UnitOfWorkQueryValueHolder wraps a database-stored object and * implements behavior to access it. The object is read from * the database by invoking a user-specified query. * This value holder is used only in the unit of work. * * @author Sati */ public class UnitOfWorkQueryValueHolder extends UnitOfWorkValueHolder { protected UnitOfWorkQueryValueHolder() { super(); } protected UnitOfWorkQueryValueHolder(ValueHolderInterface attributeValue, Object clone, DatabaseMapping mapping, UnitOfWorkImpl unitOfWork) { super(attributeValue, clone, mapping, unitOfWork); } public UnitOfWorkQueryValueHolder(ValueHolderInterface attributeValue, Object clone, ForeignReferenceMapping mapping, AbstractRecord row, UnitOfWorkImpl unitOfWork) { this(attributeValue, clone, mapping, unitOfWork); this.row = row; } /** * Backup the clone attribute value. */ protected Object buildBackupCloneFor(Object cloneAttributeValue) { return this.mapping.buildBackupCloneForPartObject(cloneAttributeValue, null, null, getUnitOfWork()); } /** * Clone the original attribute value. */ public Object buildCloneFor(Object originalAttributeValue) { Integer refreshCascade = null; if (wrappedValueHolder instanceof QueryBasedValueHolder){ refreshCascade = ((QueryBasedValueHolder)getWrappedValueHolder()).getRefreshCascadePolicy(); } Object clone = this.mapping.buildCloneForPartObject(originalAttributeValue, null, null, this.relationshipSourceObject, getUnitOfWork(), refreshCascade, true, true); // Bug 414801 if (wrappedValueHolder.isInstantiated() && refreshCascade != null) { ((QueryBasedValueHolder)getWrappedValueHolder()).setRefreshCascadePolicy(null); } return clone; } /** * Ensure that the backup value holder is populated. */ public void setValue(Object theValue) { // Must force instantiation to be able to compare with the old value. if (!this.isInstantiated) { instantiate(); } Object oldValue = getValue(); super.setValue(theValue); updateForeignReferenceSet(theValue, oldValue); } /** * INTERNAL: * Here we now must check for bi-directional relationship. * If the mapping has a relationship partner then we must maintain the original relationship. * We only worry about ObjectReferenceMappings as the collections mappings will be handled by transparentIndirection */ public void updateForeignReferenceRemove(Object value) { DatabaseMapping sourceMapping = this.getMapping(); if (sourceMapping == null) { //mapping is a transient attribute. If it does not exist then we have been serialized return; } if (sourceMapping.isPrivateOwned()) { // don't null out backpointer on private owned relationship because it will cause an // extra update. return; } // ForeignReferenceMapping partner = (ForeignReferenceMapping)getMapping().getRelationshipPartner(); ForeignReferenceMapping partner = this.getRelationshipPartnerFor(value); if (partner != null) { if (value != null) { Object unwrappedValue = partner.getDescriptor().getObjectBuilder().unwrapObject(value, getSession()); Object oldParent = partner.getRealAttributeValueFromObject(unwrappedValue, getSession()); Object sourceObject = getRelationshipSourceObject(); if (oldParent == null) { // value has already been set return; } // PERF: If the collection is not instantiated, then do not instantiated it. if (partner.isCollectionMapping()) { if ((!(oldParent instanceof IndirectContainer)) || ((IndirectContainer)oldParent).isInstantiated()) { if (!partner.getContainerPolicy().contains(sourceObject, oldParent, getSession())) { // value has already been set return; } } } if (partner.isObjectReferenceMapping()) { // Check if it's already been set to null partner.setRealAttributeValueInObject(unwrappedValue, null); } else if (partner.isCollectionMapping()) { // If it is not in the collection then it has already been removed. partner.getContainerPolicy().removeFrom(sourceObject, oldParent, getSession()); } } } } /** * INTERNAL: * Here we now must check for bi-directional relationship. * If the mapping has a relationship partner then we must maintain the original relationship. * We only worry about ObjectReferenceMappings as the collections mappings will be handled by transparentIndirection */ public void updateForeignReferenceSet(Object value, Object oldValue) { if ((value != null) && (value instanceof Collection)) { //I'm passing a collection into the valueholder not an object return; } if (getMapping() == null) { //mapping is a transient attribute. If it does not exist then we have been serialized return; } // ForeignReferenceMapping partner = (ForeignReferenceMapping)getMapping().getRelationshipPartner(); ForeignReferenceMapping partner = this.getRelationshipPartnerFor(value); if (partner != null) { if (value != null) { Object unwrappedValue = partner.getDescriptor().getObjectBuilder().unwrapObject(value, getSession()); Object oldParent = partner.getRealAttributeValueFromObject(unwrappedValue, getSession()); Object sourceObject = getRelationshipSourceObject(); Object wrappedSource = getMapping().getDescriptor().getObjectBuilder().wrapObject(sourceObject, getSession()); if (oldParent == sourceObject) { // value has already been set return; } // PERF: If the collection is not instantiated, then do not instantiated it. if (partner.isCollectionMapping()) { if ((!(oldParent instanceof IndirectContainer)) || ((IndirectContainer)oldParent).isInstantiated()) { if (partner.getContainerPolicy().contains(sourceObject, oldParent, getSession())) { // value has already been set return; } } } // Set the Object that was referencing this value to reference null, or remove value from its collection if (oldParent != null) { if (getMapping().isObjectReferenceMapping()) { if (!partner.isCollectionMapping()) { // If the back pointer is a collection it's OK that I'm adding myself into the collection ((ObjectReferenceMapping)getMapping()).setRealAttributeValueInObject(oldParent, null); } } else if (getMapping().isCollectionMapping() && (!partner.isManyToManyMapping())) { getMapping().getContainerPolicy().removeFrom(unwrappedValue, getMapping().getRealAttributeValueFromObject(oldParent, getSession()), getSession()); } } if (oldValue != null) { // CR 3487 Object unwrappedOldValue = partner.getDescriptor().getObjectBuilder().unwrapObject(oldValue, getSession()); // if this object was referencing a different object reset the back pointer on that object if (partner.isObjectReferenceMapping()) { partner.setRealAttributeValueInObject(unwrappedOldValue, null); } else if (partner.isCollectionMapping()) { partner.getContainerPolicy().removeFrom(sourceObject, partner.getRealAttributeValueFromObject(unwrappedOldValue, getSession()), getSession()); } } // Now set the back reference of the value being passed in to point to this object if (partner.isObjectReferenceMapping()) { partner.setRealAttributeValueInObject(unwrappedValue, wrappedSource); } else if (partner.isCollectionMapping()) { partner.getContainerPolicy().addInto(wrappedSource, oldParent, getSession()); } } else { updateForeignReferenceRemove(oldValue); } } } /** * Helper method to retrieve the relationship partner mapping. This will take inheritance * into account and return the mapping associated with correct subclass if necessary. This * is needed for EJB 2.0 inheritance */ private ForeignReferenceMapping getRelationshipPartnerFor(Object partnerObject) { ForeignReferenceMapping partner = (ForeignReferenceMapping)getMapping().getRelationshipPartner(); if ((partner == null) || (partnerObject == null)) { // no partner, nothing to do return partner; } // if the target object is not an instance of the class type associated with the partner // mapping, try and look up the same partner mapping but as part of the partnerObject's // descriptor. Only check if inheritance is involved... if (partner.getDescriptor().hasInheritance()) { ClassDescriptor partnerObjectDescriptor = this.getSession().getDescriptor(partnerObject); if (!(partner.getDescriptor().getJavaClass().isAssignableFrom(partnerObjectDescriptor.getJavaClass()))) { return (ForeignReferenceMapping)partnerObjectDescriptor.getObjectBuilder().getMappingForAttributeName(partner.getAttributeName()); } } return partner; } }