| /* |
| * 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 java.util.*; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.exceptions.*; |
| import org.eclipse.persistence.expressions.*; |
| |
| /** |
| * PUBLIC: |
| * A Query Class used to perform a bulk update using TopLink's expression framework. |
| * This class is provided to help optimize performance. It can be used in place |
| * of reading in all the objects to be changed and issuing single updates per |
| * instance. With this approach a single SQL UPDATE statement can be issued and |
| * then, based on the Expression provided, any objects in the cache that are |
| * effected by the update can be invalidated. |
| * <p> |
| * Notes: <ul> |
| * <li>By default, if a UOW is being used, this query will be deferred until the UOW commits. |
| * <li>UpdateAllQuery does not support foreign key updates |
| * unless the relationship is 1-1 (without back pointers.)</ul> |
| * <p> |
| * <b>Example of Usage:</b> Adding an area code. <br> |
| * <code> |
| * UpdateAllQuery updateQuery = new UpdateAllQuery(Employee.class); <br> |
| * updateQuery.setSelectionCriteria(eb.get("areaCode").isNull()); <br> |
| * updateQuery.addUpdate(eb.get("areaCode"), "613"); <br> |
| * </code> |
| * |
| * @author Guy Pelletier |
| * @since March 1, 2004 |
| */ |
| public class UpdateAllQuery extends ModifyAllQuery { |
| |
| protected HashMap m_updateClauses; |
| |
| /** |
| * PUBLIC: |
| * Constructs a default update all query. |
| */ |
| public UpdateAllQuery() { |
| super(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Constructs an update all query for the Class type specified. |
| * @param referenceClass Class |
| */ |
| public UpdateAllQuery(Class<?> referenceClass) { |
| super(referenceClass); |
| } |
| |
| /** |
| * PUBLIC: |
| * Constructs an update all query for the specified Class type and selection criteria. |
| * @param referenceClass Class type to be considered |
| * @param selectionCriteria Expression |
| */ |
| public UpdateAllQuery(Class<?> referenceClass, Expression selectionCriteria) { |
| super(referenceClass, selectionCriteria); |
| } |
| |
| /** |
| * PUBLIC: |
| * Constructs an update all query for the Class type specified and the given |
| * ExpressionBuilder. This sets the default builder which is used for all associated |
| * expressions in the query.<br> |
| * @param referenceClass Class type to be considered |
| * @param expressionBuilder ExpressionBuilder |
| */ |
| public UpdateAllQuery(Class<?> referenceClass, ExpressionBuilder expressionBuilder) { |
| super(referenceClass); |
| this.defaultBuilder = expressionBuilder; |
| } |
| |
| /** |
| * PUBLIC: |
| * Adds the update (SET) clause to the query. Uses default ExpressionBuilder. |
| * @param field Expression Object level representation of a database query 'where' clause |
| * @param value Object, the new value |
| */ |
| public void addUpdate(Expression field, Object value) { |
| addUpdateInternal(field, value); |
| } |
| |
| /** |
| * PUBLIC: |
| * Adds the update (SET) clause to the query. Uses default ExpressionBuilder. |
| * @param attributeName String, the name of the attribute |
| * @param value Object, the new value |
| */ |
| public void addUpdate(String attributeName, Object value) { |
| addUpdateInternal(attributeName, value); |
| } |
| |
| /** |
| * PUBLIC: |
| * Adds the update (SET) clause to the query. This method ensures that |
| * the builder has the session and reference class set for both given Expressions. |
| * Uses default ExpressionBuilder. |
| * @param field Expression, representation of a database query 'where' clause that describes the field |
| * @param value Expression, representation of a database query 'where' clause that describes the new value |
| */ |
| public void addUpdate(Expression field, Expression value) { |
| addUpdateInternal(field, value); |
| } |
| |
| /** |
| * PUBLIC: |
| * Adds the update (SET) clause to the query. Uses default ExpressionBuilder. |
| * @param attributeName String, the name of the attribute |
| * @param value Expression, the new value |
| */ |
| public void addUpdate(String attributeName, Expression value) { |
| addUpdateInternal(attributeName, value); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void addUpdateInternal(Object fieldObject, Object valueObject) { |
| if(fieldObject == null) { |
| throw QueryException.updateAllQueryAddUpdateFieldIsNull(this); |
| } |
| if(m_updateClauses == null) { |
| m_updateClauses = new HashMap(); |
| } |
| m_updateClauses.put(fieldObject, valueObject); |
| } |
| |
| /** |
| * INTERNAL: |
| * Issue the SQL to the database and then merge into the cache. |
| * If we are within a UoW, the merge to the cache must not be done until |
| * the UoW merges into the parent. The UoW will trigger the merge to occur |
| * at the correct time and will ensure the cache setting is set to none at |
| * that time. |
| */ |
| @Override |
| public Object executeDatabaseQuery() throws DatabaseException { |
| result = getQueryMechanism().updateAll();// fire the SQL to the database |
| mergeChangesIntoSharedCache(); |
| return result; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the specific default redirector for this query type. There are numerous default query redirectors. |
| * See ClassDescriptor for their types. |
| */ |
| @Override |
| protected QueryRedirector getDefaultRedirector(){ |
| return descriptor.getDefaultUpdateObjectQueryRedirector(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the updates stored for an update all query |
| */ |
| public HashMap getUpdateClauses() { |
| return m_updateClauses; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if this is an update all query. |
| */ |
| @Override |
| public boolean isUpdateAllQuery() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| protected void prepare() throws QueryException { |
| super.prepare();// will tell the query mechanism to prepare itself as well. |
| |
| Class<?> referenceClass = getReferenceClass(); |
| |
| // Check the reference class, must be set |
| if (referenceClass == null) { |
| throw QueryException.referenceClassMissing(this); |
| } |
| |
| // Check the descriptor is set, if not try to get it from the session |
| if (getDescriptor() == null) { |
| ClassDescriptor desc = getSession().getDescriptor(referenceClass); |
| |
| if (desc == null) { |
| throw QueryException.descriptorIsMissing(referenceClass, this); |
| } |
| |
| setDescriptor(desc); |
| } |
| |
| ClassDescriptor descriptor = getDescriptor(); |
| |
| // Check the descriptor for an aggregate |
| if (descriptor.isAggregateDescriptor()) { |
| throw QueryException.aggregateObjectCannotBeDeletedOrWritten(descriptor, this); |
| } |
| |
| // Check that the update statement is set. That is the bare minimum, |
| // the user can execute this query without a selection criteria though. |
| if ((getUpdateClauses() == null || getUpdateClauses().isEmpty()) && isExpressionQuery()) { |
| throw QueryException.updateStatementsNotSpecified(); |
| } |
| |
| getQueryMechanism().prepareUpdateAll(); |
| } |
| /** |
| * 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. |
| */ |
| @Override |
| protected void initializeQuerySpecificDefaultBuilder() { |
| super.initializeQuerySpecificDefaultBuilder(); |
| if(this.defaultBuilder == null && m_updateClauses != null) { |
| Iterator it = m_updateClauses.values().iterator(); |
| while(it.hasNext() ) { |
| Object value = it.next(); |
| if(value != null) { |
| if(value instanceof Expression) { |
| Expression valueExpression = (Expression)value; |
| this.defaultBuilder = valueExpression.getBuilder(); |
| if(this.defaultBuilder != null) { |
| return; |
| } |
| } |
| } |
| } |
| it = m_updateClauses.keySet().iterator(); |
| while(it.hasNext() ) { |
| Object field = it.next(); |
| if(field instanceof Expression) { |
| Expression fieldExpression = (Expression)field; |
| this.defaultBuilder = fieldExpression.getBuilder(); |
| if(this.defaultBuilder != null) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| |