001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.queryframework;
038:
039: import java.util.*;
040: import oracle.toplink.essentials.exceptions.*;
041: import oracle.toplink.essentials.expressions.*;
042: import oracle.toplink.essentials.descriptors.DescriptorEvent;
043: import oracle.toplink.essentials.descriptors.DescriptorEventManager;
044: import oracle.toplink.essentials.internal.sessions.AbstractRecord;
045: import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
046: import oracle.toplink.essentials.internal.sessions.AbstractSession;
047: import oracle.toplink.essentials.descriptors.ClassDescriptor;
048:
049: /**
050: * <p><b>Purpose</b>:
051: * Query used to delete a collection of objects
052: *
053: * <p><b>Responsibilities</b>:
054: * <ul>
055: * <li> Stores & retrieves the objects to delete.
056: * <li> Store the where clause used for the deletion.
057: * </ul>
058: *
059: * @author Yvon Lavoie
060: * @since TOPLink/Java 1.0
061: */
062: public class DeleteAllQuery extends ModifyAllQuery {
063:
064: /* Vector containing objects to be deleted, these should be removed from the identity map after deletion. */
065: protected Vector objects;
066:
067: /**
068: * PUBLIC:
069: */
070: public DeleteAllQuery() {
071: super ();
072: }
073:
074: /**
075: * PUBLIC:
076: * Create a new delete all query for the class specified.
077: */
078: public DeleteAllQuery(Class referenceClass) {
079: super (referenceClass);
080: }
081:
082: /**
083: * PUBLIC:
084: * Create a new delete all query for the class and the selection criteria
085: * specified.
086: */
087: public DeleteAllQuery(Class referenceClass,
088: Expression selectionCriteria) {
089: super (referenceClass, selectionCriteria);
090: }
091:
092: /**
093: * PUBLIC:
094: * Return if this is a delete all query.
095: */
096: public boolean isDeleteAllQuery() {
097: return true;
098: }
099:
100: /**
101: * INTERNAL:
102: * This method has to be broken. If commit manager is not active either
103: * an exception should be thrown (ObjectLevelModify case), or a transaction
104: * should be started early and execute on parent if remote (dataModify case).
105: * A modify query is NEVER executed on the parent, unless remote session.
106: * @param unitOfWork
107: * @param translationRow
108: * @return
109: * @throws oracle.toplink.essentials.exceptions.DatabaseException
110: * @throws oracle.toplink.essentials.exceptions.OptimisticLockException
111: */
112: public Object executeInUnitOfWork(UnitOfWorkImpl unitOfWork,
113: AbstractRecord translationRow) throws DatabaseException,
114: OptimisticLockException {
115: if (getObjects() != null) {
116: if (unitOfWork.isAfterWriteChangesButBeforeCommit()) {
117: throw ValidationException
118: .illegalOperationForUnitOfWorkLifecycle(
119: unitOfWork.getLifecycle(),
120: "executeQuery(DeleteAllQuery)");
121: }
122:
123: // This must be broken, see comment.
124: if (!unitOfWork.getCommitManager().isActive()) {
125: return unitOfWork.getParent().executeQuery(this ,
126: translationRow);
127: }
128: result = (Integer) super
129: .execute(unitOfWork, translationRow);
130: return result;
131: } else {
132: return super
133: .executeInUnitOfWork(unitOfWork, translationRow);
134: }
135: }
136:
137: /**
138: * INTERNAL:
139: * Perform the work to delete a collection of objects.
140: * This skips the optimistic lock check and should not called for objects using locking.
141: * @exception DatabaseException - an error has occurred on the database.
142: * @return Integer the number of objects (rows) deleted.
143: */
144: public Object executeDatabaseQuery() throws DatabaseException {
145: // CR# 4286
146: if (getObjects() != null) {
147:
148: if (isExpressionQuery() && getSelectionCriteria() == null) {
149: // DeleteAllQuery has objects so it *must* have selectionCriteria, too
150: throw QueryException
151: .deleteAllQuerySpecifiesObjectsButNotSelectionCriteria(
152: getDescriptor(), this , getObjects()
153: .toString());
154: }
155:
156: // Optimistic lock check not required because objects are deleted individually in that case.
157: try {
158: getSession().beginTransaction();
159:
160: // Need to run pre-delete selector if available.
161: // PERF: Avoid events if no listeners.
162: if (getDescriptor().getEventManager()
163: .hasAnyEventListeners()) {
164: for (Enumeration deletedObjectsEnum = getObjects()
165: .elements(); deletedObjectsEnum
166: .hasMoreElements();) {
167: DescriptorEvent event = new DescriptorEvent(
168: deletedObjectsEnum.nextElement());
169: event
170: .setEventCode(DescriptorEventManager.PreDeleteEvent);
171: event.setSession(getSession());
172: event.setQuery(this );
173: getDescriptor().getEventManager().executeEvent(
174: event);
175: }
176: }
177:
178: result = getQueryMechanism().deleteAll();
179:
180: // Need to run post-delete selector if available.
181: // PERF: Avoid events if no listeners.
182: if (getDescriptor().getEventManager()
183: .hasAnyEventListeners()) {
184: for (Enumeration deletedObjectsEnum = getObjects()
185: .elements(); deletedObjectsEnum
186: .hasMoreElements();) {
187: DescriptorEvent event = new DescriptorEvent(
188: deletedObjectsEnum.nextElement());
189: event
190: .setEventCode(DescriptorEventManager.PostDeleteEvent);
191: event.setSession(getSession());
192: event.setQuery(this );
193: getDescriptor().getEventManager().executeEvent(
194: event);
195: }
196: }
197:
198: if (shouldMaintainCache()) {
199: // remove from the cache.
200: for (Enumeration objectsEnum = getObjects()
201: .elements(); objectsEnum.hasMoreElements();) {
202: Object deleted = objectsEnum.nextElement();
203: if (getSession().isUnitOfWork()) {
204: //BUG #2612169: Unwrap is needed
205: deleted = getDescriptor()
206: .getObjectBuilder().unwrapObject(
207: deleted, getSession());
208: ((UnitOfWorkImpl) getSession())
209: .addObjectDeletedDuringCommit(
210: deleted, getDescriptor());
211: } else {
212: getSession().getIdentityMapAccessor()
213: .removeFromIdentityMap(deleted);
214: }
215: }
216: }
217:
218: getSession().commitTransaction();
219:
220: } catch (RuntimeException exception) {
221: getSession().rollbackTransaction();
222: throw exception;
223: }
224: } else {
225: result = getQueryMechanism().deleteAll();// fire the SQL to the database
226: mergeChangesIntoSharedCache();
227: }
228:
229: return result;
230: }
231:
232: /**
233: * INTERNAL:
234: * Delete all queries are executed specially to avoid cloning and ensure preparing.
235: */
236: public void executeDeleteAll(AbstractSession session,
237: AbstractRecord translationRow, Vector objects)
238: throws DatabaseException {
239: this .checkPrepare(session, translationRow);
240: DeleteAllQuery queryToExecute = (DeleteAllQuery) clone();
241:
242: // Then prapared for the single execution.
243: queryToExecute.setTranslationRow(translationRow);
244: queryToExecute.setSession(session);
245: queryToExecute.setObjects(objects);
246: queryToExecute.prepareForExecution();
247: queryToExecute.executeDatabaseQuery();
248: }
249:
250: /**
251: * PUBLIC:
252: * Return the objects that are to be deleted
253: */
254: public Vector getObjects() {
255: return objects;
256: }
257:
258: /**
259: * INTERNAL:
260: * Prepare the receiver for execution in a session.
261: */
262: protected void prepare() throws QueryException {
263: super .prepare();
264:
265: if (getReferenceClass() == null) {
266: throw QueryException.referenceClassMissing(this );
267: }
268:
269: if (getDescriptor() == null) {
270: ClassDescriptor referenceDescriptor = getSession()
271: .getDescriptor(getReferenceClass());
272: if (referenceDescriptor == null) {
273: throw QueryException.descriptorIsMissing(
274: getReferenceClass(), this );
275: }
276: setDescriptor(referenceDescriptor);
277: }
278:
279: if (getDescriptor().isAggregateDescriptor()) {
280: throw QueryException
281: .aggregateObjectCannotBeDeletedOrWritten(
282: getDescriptor(), this );
283: }
284:
285: getQueryMechanism().prepareDeleteAll();
286: }
287:
288: /**
289: * PUBLIC (REQUIRED):
290: * Set the objects to be deleted.
291: * Also REQUIRED is a selection criteria or SQL string that performs the deletion of the objects.
292: * This does not generate the SQL call from the deleted objects.
293: * #setObject() should not be called.
294: *
295: * Vector objects used as an indicator of one of two possible
296: * ways the query may behave:
297: * objects != null - the "old" functionality used by OneToMany mapping
298: * objects deleted from the cache, either selection expression or custom sql
299: * should be provided for deletion from db;
300: * objects == null - the "new" functionality (on par with UpdateAllQuery)
301: * the cache is either left alone or in-memory query finds the cached objects to be deleted,
302: * and these objects are invalidated in cache.
303: *
304: * Note that empty objects is still objects != case.
305: * Signal that no cache altering is required.
306: * Used by AggregationCollectionMapping and OneToManyMapping in case they use indirection
307: * and the ValueHolder has not been instantiated.
308: */
309: public void setObjects(Vector objectCollection) {
310: objects = objectCollection;
311: }
312: }
|