| /* |
| * Copyright (c) 1998, 2021 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.queries; |
| |
| import org.eclipse.persistence.internal.helper.*; |
| import org.eclipse.persistence.internal.sessions.*; |
| import org.eclipse.persistence.exceptions.*; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| |
| /** |
| * <p><b>Purpose</b>: |
| * Abstract class for all object modify queries. |
| * |
| * <p><b>Responsibilities</b>: |
| * <ul> |
| * <li> Stores {@literal &} retrieves the object to modify. |
| * <li> Stores {@literal &} retrieves the primary key of the objects. |
| * </ul> |
| * |
| * @author Yvon Lavoie |
| * @since TOPLink/Java 1.0 |
| */ |
| public abstract class ObjectLevelModifyQuery extends ModifyQuery { |
| |
| /* Primary key of the object to be modified. */ |
| protected Object primaryKey; |
| |
| /* The object being modified. */ |
| protected Object object; |
| |
| /* A changeSet representing the object being modified */ |
| protected ObjectChangeSet objectChangeSet; |
| |
| /* The clone of the object being modified from unit of work. */ |
| protected Object backupClone; |
| |
| /** |
| * PUBLIC: |
| * Initialize the state of the query. |
| */ |
| protected ObjectLevelModifyQuery() { |
| this.cascadePolicy = CascadePrivateParts; |
| } |
| |
| /** |
| * INTERNAL: |
| * Ensure that the descriptor has been set. |
| */ |
| @Override |
| public void checkDescriptor(AbstractSession session) throws QueryException { |
| if (this.descriptor == null) { |
| if (getObject() == null) { |
| throw QueryException.objectToModifyNotSpecified(this); |
| } |
| |
| //Bug#3947714 Pass the object instead of class in case object is proxy |
| ClassDescriptor referenceDescriptor = session.getDescriptor(getObject()); |
| if (referenceDescriptor == null) { |
| throw QueryException.descriptorIsMissing(getObject().getClass(), this); |
| } |
| setDescriptor(referenceDescriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * All have done is move code from UnitOfWork.internalExecuteQuery |
| */ |
| @Override |
| public Object executeInUnitOfWork(UnitOfWorkImpl unitOfWork, AbstractRecord translationRow) throws DatabaseException { |
| if (unitOfWork.isAfterWriteChangesButBeforeCommit()) { |
| throw ValidationException.illegalOperationForUnitOfWorkLifecycle(unitOfWork.getLifecycle(), "executeQuery(ObjectLevelModifyQuery)"); |
| } |
| return executeInUnitOfWorkObjectLevelModifyQuery(unitOfWork, translationRow); |
| } |
| |
| /** |
| * INTERNAL: |
| * This code was moved from UnitOfWork.internalExecuteQuery. |
| */ |
| protected Object executeInUnitOfWorkObjectLevelModifyQuery(UnitOfWorkImpl unitOfWork, AbstractRecord translationRow) throws DatabaseException, OptimisticLockException { |
| if (!unitOfWork.getCommitManager().isActive()) { |
| throw QueryException.invalidQuery(this); |
| } |
| |
| if ((this.object != null) && (unitOfWork.isClassReadOnly(this.object.getClass(), this.descriptor))) { |
| return this.object; |
| } |
| |
| // CR#3216 - Apply check to ObjectLevelModifyQuery not just WriteObjectQuery |
| if (unitOfWork.shouldPerformNoValidation() && unitOfWork.isUnregisteredExistingObject(this.object)) { |
| //if the object is an unregistered existing object then skip it. This |
| // Will only be in the collection if validation is turned off |
| return null; |
| } |
| |
| return super.executeInUnitOfWork(unitOfWork, translationRow); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the backup clone of the object from the unit of work. |
| */ |
| public Object getBackupClone() { |
| // PERF: A backup clone is only required for the old commit, |
| // So avoid its creation for normal commit. |
| if ((backupClone == null) && this.session.isUnitOfWork()) { |
| setBackupClone(((UnitOfWorkImpl)this.session).getBackupCloneForCommit(this.object, this.descriptor)); |
| } |
| return backupClone; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the object required for modification. |
| */ |
| public Object getObject() { |
| return object; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the ObjectChangeSet representing the object being changed |
| */ |
| public ObjectChangeSet getObjectChangeSet() { |
| return this.objectChangeSet; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the primary key for the query |
| */ |
| public Object getPrimaryKey() { |
| return primaryKey; |
| } |
| |
| /** |
| * Return the domain class associated with this query. |
| */ |
| @Override |
| public Class getReferenceClass() { |
| if (this.object == null) { |
| if (this.descriptor != null) { |
| return this.descriptor.getJavaClass(); |
| } |
| return null; |
| } |
| return this.object.getClass(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the reference class for a query |
| * Note: Although the API is designed to avoid classpath dependencies for the MW, since the object |
| * is specified at runtime, this will not be an issue. |
| */ |
| @Override |
| public String getReferenceClassName() { |
| Class referenceClass = getReferenceClass(); |
| if (referenceClass != null) { |
| return referenceClass.getName(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the name to use for the query in performance monitoring. |
| */ |
| @Override |
| public void resetMonitorName() { |
| this.monitorName = getClass().getSimpleName() + ":" + getReferenceClassName(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if this is an object level modify query. |
| */ |
| @Override |
| public boolean isObjectLevelModifyQuery() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Prepare the receiver for execution in a session. |
| * In particular check that the tables on the descriptor are set. |
| */ |
| @Override |
| protected void prepare() throws QueryException { |
| checkDescriptor(this.session); |
| |
| if (this.object != null) {// Prepare can be called without the object set yet. |
| this.object = this.descriptor.getObjectBuilder().unwrapObject(this.object, this.session); |
| } |
| |
| if (this.descriptor.isAggregateDescriptor()) { |
| throw QueryException.aggregateObjectCannotBeDeletedOrWritten(getDescriptor(), this); |
| } |
| |
| super.prepare(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Prepare the receiver for execution in a session. |
| * In particular check that the tables on the descriptor are set. |
| */ |
| @Override |
| public void prepareForExecution() throws QueryException { |
| super.prepareForExecution(); |
| |
| if (this.object == null) { |
| throw QueryException.objectToModifyNotSpecified(this); |
| } |
| |
| this.object = this.descriptor.getObjectBuilder().unwrapObject(this.object, this.session); |
| |
| if (this.primaryKey == null) { |
| if (this.objectChangeSet != null) { |
| this.primaryKey = this.objectChangeSet.getId(); |
| } else { |
| this.primaryKey = this.descriptor.getObjectBuilder().extractPrimaryKeyFromObject(this.object, this.session); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the backup clone of the object from the unit of work. |
| */ |
| public void setBackupClone(Object backupClone) { |
| this.backupClone = backupClone; |
| } |
| |
| /** |
| * PUBLIC: (REQUIRED) |
| * Set the object required for modification. |
| */ |
| public void setObject(Object object) { |
| this.object = object; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the ObjectChangeSet representing the object to be written |
| */ |
| public void setObjectChangeSet(ObjectChangeSet changeSet) { |
| this.objectChangeSet = changeSet; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the primary key for the query. |
| */ |
| public void setPrimaryKey(Object primaryKey) { |
| this.primaryKey = primaryKey; |
| } |
| |
| @Override |
| public String toString() { |
| return Helper.getShortClassName(getClass()) + "(" + String.valueOf(getObject()) + ")"; |
| } |
| } |