| /* |
| * Copyright (c) 1998, 2020 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.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. |
| */ |
| @Override |
| protected Object buildBackupCloneFor(Object cloneAttributeValue) { |
| return this.mapping.buildBackupCloneForPartObject(cloneAttributeValue, null, null, getUnitOfWork()); |
| } |
| |
| /** |
| * Clone the original attribute value. |
| */ |
| @Override |
| 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. |
| */ |
| @Override |
| 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 |
| 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; |
| } |
| } |