blob: e513d53dbda1ae8ff38e149ac0733925ece8610a [file] [log] [blame]
/*
* 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.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()) + "(" + getObject() + ")";
}
}