/******************************************************************************* * 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 * 07/16/2009-2.0 Guy Pelletier * - 277039: JPA 2.0 Cache Usage Settings ******************************************************************************/ package org.eclipse.persistence.queries; import org.eclipse.persistence.exceptions.*; import org.eclipse.persistence.expressions.*; import org.eclipse.persistence.internal.queries.DatabaseQueryMechanism; import org.eclipse.persistence.internal.queries.ExpressionQueryMechanism; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; import org.eclipse.persistence.internal.sessions.AbstractSession; /** * PUBLIC: * Query used to perform a bulk delete using the expression framework. * * @author Andrei Ilitchev * @since August 18, 2005 */ public abstract class ModifyAllQuery extends ModifyQuery { /** Cache usage flags */ public static final int NO_CACHE = 0; public static final int INVALIDATE_CACHE = 1; private int m_cacheUsage = INVALIDATE_CACHE; protected Class referenceClass; protected String referenceClassName; /** Number of modified objects */ protected transient Integer result; /** Indicates whether execution should be deferred in UOW */ private boolean shouldDeferExecutionInUOW; /** Provide a default builder so that it's easier to be consistent */ protected ExpressionBuilder defaultBuilder; /** Indicates whether the query was prepared so that it will execute using temp storage */ protected boolean isPreparedUsingTempStorage; /** * PUBLIC: */ public ModifyAllQuery() { super(); shouldDeferExecutionInUOW = true; } /** * PUBLIC: * Create a new update all query for the class specified. */ public ModifyAllQuery(Class referenceClass) { this(); setReferenceClass(referenceClass); } /** * PUBLIC: * Create a new update all query for the class and the selection criteria * specified. */ public ModifyAllQuery(Class referenceClass, Expression selectionCriteria) { this(); setReferenceClass(referenceClass); setSelectionCriteria(selectionCriteria); } /** * PUBLIC: * Return true if this is a modify all query. */ @Override public boolean isModifyAllQuery() { return true; } /** * INTERNAL: */ public void setIsPreparedUsingTempStorage(boolean isPreparedUsingTempStorage) { this.isPreparedUsingTempStorage = isPreparedUsingTempStorage; } /** * INTERNAL: */ public boolean isPreparedUsingTempStorage() { return isPreparedUsingTempStorage; } /** * INTERNAL * Used to give the subclasses opportunity to copy aspects of the cloned query * to the original query. The clones of all the ModifyAllQueries will be added to modifyAllQueries for validation. */ @Override protected void clonedQueryExecutionComplete(DatabaseQuery query, AbstractSession session) { super.clonedQueryExecutionComplete(query, session); if (session.isUnitOfWork()) { ((UnitOfWorkImpl)session).storeModifyAllQuery(query); } } /** * INTERNAL: * Override query execution where Session is a UnitOfWork. * <p> * If there are objects in the cache return the results of the cache lookup. * * @param unitOfWork - the session in which the receiver will be executed. * @param translationRow - the arguments * @exception DatabaseException - an error has occurred on the database. * @exception OptimisticLockException - an error has occurred using the optimistic lock feature. * @return An object, the result of executing the query. */ @Override public Object executeInUnitOfWork(UnitOfWorkImpl unitOfWork, AbstractRecord translationRow) throws DatabaseException, OptimisticLockException { if (unitOfWork.isNestedUnitOfWork()) { throw ValidationException.nestedUOWNotSupportedForModifyAllQuery(); } //Bug4607551 For UpdateAllQuery, if deferred, add the original query with a translation row to the deferredUpdateAllQueries for execution. //No action for non-deferred. Later on the clones of all the UpdateAllQuery's will be added to modifyAllQueries for validation. if(shouldDeferExecutionInUOW()) { unitOfWork.storeDeferredModifyAllQuery(this, translationRow); result = null; } else { if(!unitOfWork.isInTransaction()) { unitOfWork.beginEarlyTransaction(); } unitOfWork.setWasNonObjectLevelModifyQueryExecuted(true); result = (Integer)super.executeInUnitOfWork(unitOfWork, translationRow); } return result; } /** * PUBLIC: * Return the cache usage for this query. */ public int getCacheUsage() { return m_cacheUsage; } /** * PUBLIC: * Get the expression builder which should be used for this query. * This expression builder should be used to build all expressions used by this query. */ public ExpressionBuilder getExpressionBuilder() { if (defaultBuilder == null) { initializeDefaultBuilder(); } return defaultBuilder; } /** * INTERNAL * Sets the default expression builder for this query. */ public void setExpressionBuilder(ExpressionBuilder builder) { this.defaultBuilder = builder; } /** * INTERNAL: * Return the name of the reference class of the query. * Used by the Mapping Workbench to avoid classpath dependencies */ @Override public String getReferenceClassName() { if ((referenceClassName == null) && (referenceClass != null)) { referenceClassName = referenceClass.getName(); } return referenceClassName; } /** * PUBLIC: * Return the reference class for this query. */ @Override public Class getReferenceClass() { return referenceClass; } /** * INTERNAL: * Invalid the cache, that is, those objects in the cache that were affected * by the query. */ protected void invalidateCache() { if(result != null && result.intValue() == 0) { // no rows modified in the db - nothing to invalidate return; } getSession().getIdentityMapAccessor().invalidateObjects(getSelectionCriteria(), getReferenceClass(), getTranslationRow(), true); } /** * INTERNAL: * After execution we need to merge the changes into the shared cache, * unless the query has been tagged to bypass on the store. */ public void mergeChangesIntoSharedCache() { if (shouldInvalidateCache() && ! shouldStoreBypassCache()) { invalidateCache(); } } /** * PUBLIC: * Set the level of cache support for this query, either NONE or INVALIDATE. */ public void setCacheUsage(int cacheUsage) { m_cacheUsage = cacheUsage; } /** * PUBLIC: * Set the reference class this query. */ public void setReferenceClass(Class referenceClass) { if (this.referenceClass != referenceClass) { setIsPrepared(false); } this.referenceClass = referenceClass; } /** * INTERNAL: * Set the class name of the reference class of this query. * Used by the Mapping Workbench to avoid classpath dependencies. */ public void setReferenceClassName(String className) { referenceClassName = className; } /** * PUBLIC: * Set a flag indicating whether execution should be deferred in UOW until commit. */ public void setShouldDeferExecutionInUOW(boolean shouldDeferExecutionInUOW) { this.shouldDeferExecutionInUOW = shouldDeferExecutionInUOW; } /** * PUBLIC: * Indicates whether execution should be deferred in UOW until commit. */ public boolean shouldDeferExecutionInUOW() { return shouldDeferExecutionInUOW; } /** * INTERNAL: */ protected boolean shouldInvalidateCache() { return m_cacheUsage == INVALIDATE_CACHE; } /** * INTERNAL: * Initialize the expression builder which should be used for this query. If * there is a where clause, use its expression builder, otherwise * generate one and cache it. This helps avoid unnecessary rebuilds. */ protected void initializeDefaultBuilder() { initializeQuerySpecificDefaultBuilder(); if(defaultBuilder == null) { defaultBuilder = new ExpressionBuilder(); } } /** * INTERNAL: * Initialize the expression builder which should be used for this query. If * there is a where clause, use its expression builder. * If after this method defaultBuilder is still null, * then initializeDefaultBuilder method will generate and cache it. */ protected void initializeQuerySpecificDefaultBuilder() { DatabaseQueryMechanism mech = getQueryMechanism(); if (mech.isExpressionQueryMechanism() && ((ExpressionQueryMechanism)mech).getExpressionBuilder() != null) { this.defaultBuilder = ((ExpressionQueryMechanism)mech).getExpressionBuilder(); } } }