blob: 109a652b1652df8ce2a7bae5bb8b406ca229d4ae [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2021 IBM Corporation. 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
// 10/15/2010-2.2 Guy Pelletier
// - 322008: Improve usability of additional criteria applied to queries at the session/EM
// 04/01/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 2)
// 05/24/2011-2.3 Guy Pelletier
// - 345962: Join fetch query when using tenant discriminator column fails.
// 08/18/2011-2.3.1 Guy Pelletier
// - 355093: Add new 'includeCriteria' flag to Multitenant metadata
// 09/09/2011-2.3.1 Guy Pelletier
// - 356197: Add new VPD type to MultitenantType
// 08/01/2012-2.5 Chris Delahunt
// - 371950: JPA Metadata caching
// 09/03/2015 - Will Dazey
// - 456067 : Added support for defining query timeout units
// IBM - Bug 537795: CASE THEN and ELSE scalar expression Constants should not be casted to CASE operand type
package org.eclipse.persistence.descriptors;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.expressions.CompoundExpression;
import org.eclipse.persistence.internal.expressions.FunctionExpression;
import org.eclipse.persistence.internal.expressions.ParameterExpression;
import org.eclipse.persistence.internal.expressions.SubSelectExpression;
import org.eclipse.persistence.internal.helper.ConcurrentFixedCache;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.helper.NonSynchronizedVector;
import org.eclipse.persistence.internal.queries.ReportItem;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.ChangeRecord;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.queries.Call;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.DeleteObjectQuery;
import org.eclipse.persistence.queries.DoesExistQuery;
import org.eclipse.persistence.queries.InsertObjectQuery;
import org.eclipse.persistence.queries.JPAQueryBuilder;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.QueryResultsCachePolicy;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.queries.ReportQuery;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.queries.WriteObjectQuery;
/**
* <p><b>Purpose</b>: The query manager allows for the database operations that EclipseLink
* performs to be customized by the application. For each descriptor a query can be
* given that controls how a operation will occur. A common example is if the application
* requires a stored procedure to be used to insert the object, it can override the SQL call
* in the insert query that EclipseLink will use to insert the object.
* Queries can be customized to extend EclipseLink behavior, access non-relational data or use stored
* procedures or customized SQL calls.
* <p>
* The queries that can be customized include:
* <ul>
* <li> insertQuery - used to insert the object
* <li> updateQuery - used to update the object
* <li> readObjectQuery - used to read a single object by primary key
* <li> readAllQuery - used to read all of the objects of the class
* <li> doesExistQuery - used to determine whether an insert or update should occur
* <li> deleteQuery - used to delete the object
* </ul>
*
* @see ClassDescriptor
*/
public class DescriptorQueryManager implements Cloneable, Serializable {
protected InsertObjectQuery insertQuery;
protected UpdateObjectQuery updateQuery;
protected ReadObjectQuery readObjectQuery;
protected ReadAllQuery readAllQuery;
protected DeleteObjectQuery deleteQuery;
protected DoesExistQuery doesExistQuery;
protected ClassDescriptor descriptor;
protected boolean hasCustomMultipleTableJoinExpression;
protected String additionalCriteria;
protected transient Expression additionalJoinExpression;
protected transient Expression multipleTableJoinExpression;
protected Map<String, List<DatabaseQuery>> queries;
protected transient Map<DatabaseTable, Expression> tablesJoinExpressions;
/** PERF: Update call cache for avoiding regenerated update SQL. */
protected transient ConcurrentFixedCache cachedUpdateCalls;
/** PERF: Expression query call cache for avoiding regenerated dynamic query SQL. */
protected transient ConcurrentFixedCache cachedExpressionQueries;
/**
* queryTimeout has three possible settings: DefaultTimeout, NoTimeout, and 1..N
* This applies to both DatabaseQuery.queryTimeout and DescriptorQueryManager.queryTimeout
*
* DatabaseQuery.queryTimeout:
* - DefaultTimeout: get queryTimeout from DescriptorQueryManager
* - NoTimeout, 1..N: overrides queryTimeout in DescriptorQueryManager
*
* DescriptorQueryManager.queryTimeout:
* - DefaultTimeout: get queryTimeout from parent DescriptorQueryManager. If there is no
* parent, default to NoTimeout
* - NoTimeout, 1..N: overrides parent queryTimeout
*/
public static final int NoTimeout = 0;
public static final int DefaultTimeout = -1;
protected int queryTimeout;
public static final TimeUnit DefaultTimeoutUnit = TimeUnit.MILLISECONDS;
protected TimeUnit queryTimeoutUnit;
/**
* INTERNAL:
* Initialize the state of the descriptor query manager
*/
public DescriptorQueryManager() {
this.queries = new LinkedHashMap<>(5);
setDoesExistQuery(new DoesExistQuery());// Always has a does exist.
this.setQueryTimeout(DefaultTimeout);
this.setQueryTimeoutUnit(DefaultTimeoutUnit);
}
/**
* ADVANCED:
* Set the max size of the expression query cache for avoiding regenerated dynamic query SQL.
*/
public void setExpressionQueryCacheMaxSize(int maxSize) {
this.cachedExpressionQueries = new ConcurrentFixedCache(maxSize);
}
/**
* ADVANCED:
* Return the max size of the expression query cache for avoiding regenerated dynamic query SQL.
*/
public int getExpressionQueryCacheMaxSize() {
return getCachedExpressionQueries().getMaxSize();
}
/**
* PUBLIC:
* Add the query to the descriptor queries with the given name
* @param name This is the name of the query. It will be set on the query and used to look it up.
* @param query This is the query that will be added. If the query being added has parameters, the
* existing list of queries will be checked for matching queries. If a matching query exists,
* it will be replaced.
*/
public void addQuery(String name, DatabaseQuery query) {
query.setName(name);
addQuery(query);
}
/**
* PUBLIC:
* Add the query to the session queries
* @param query DatabaseQuery This is the query that will be added. If the query being added has parameters, the
* existing list of queries will be checked for matching queries. If a matching query exists,
* it will be replaced.
*/
public synchronized void addQuery(DatabaseQuery query) {
if (query instanceof ObjectLevelReadQuery && (query.getReferenceClassName() == null)) {
((ObjectLevelReadQuery)query).setReferenceClassName(getDescriptor().getJavaClassName());
// try to set the reference ClassNotFoundException since it should only happen on the MW in which
// case we will lazily initialize the reference class at a later point.
try {
((ObjectLevelReadQuery)query).setReferenceClass(getDescriptor().getJavaClass());
} catch (ConversionException exception) {
}
//this is an optimization
query.setDescriptor(getDescriptor());
}
// Add query has been synchronized for bug 3355199.
// Additionally code has been added to ensure that the same query is not added twice.
List<DatabaseQuery> queriesByName = getQueries().get(query.getName());
if (queriesByName == null) {
// lazily create Vector in Hashtable.
queriesByName = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance();
getQueries().put(query.getName(), queriesByName);
} else {
int argumentTypesSize = 0;
if (query.getArguments() != null) {
argumentTypesSize = query.getArguments().size();
}
List<String> argumentTypes = new ArrayList<>(argumentTypesSize);
for (int i = 0; i < argumentTypesSize; i++) {
argumentTypes.add(query.getArgumentTypeNames().get(i));
}
// Search for a query with the same parameters and replace it if one is found
for (int i = 0; i < queriesByName.size(); i++) {
DatabaseQuery currentQuery = queriesByName.get(i);
// Here we are checking equality instead of assignability. If you look at getQuery()
// it is the other way around.
// The reason we do this is we are replacing a query and we want to make sure we are
// replacing the exact same one. - TW
if (argumentTypes.equals(currentQuery.getArgumentTypeNames())) {
queriesByName.set(i, query);
return;
}
}
}
queriesByName.add(query);
}
/**
* PUBLIC:
* Assume that if the objects primary key does not include null then it must exist.
* This may be used if the application guarantees or does not care about the existence check.
*/
public void assumeExistenceForDoesExist() {
getDoesExistQuery().assumeExistenceForDoesExist();
}
/**
* PUBLIC:
* Assume that the object does not exist. This may be used if the application guarantees or
* does not care about the existence check. This will always force an insert to be called.
*/
public void assumeNonExistenceForDoesExist() {
getDoesExistQuery().assumeNonExistenceForDoesExist();
}
/**
* PUBLIC:
* Default behavior.
* Assume that if the objects primary key does not include null and it
* is in the cache, then is must exist.
*/
public void checkCacheForDoesExist() {
getDoesExistQuery().checkCacheForDoesExist();
}
/**
* PUBLIC:
* Perform does exist check on the database
*/
public void checkDatabaseForDoesExist() {
getDoesExistQuery().checkDatabaseForDoesExist();
}
/**
* INTERNAL:
* Clone the query manager
*/
@Override
public Object clone() {
DescriptorQueryManager manager = null;
try {
manager = (DescriptorQueryManager)super.clone();
} catch (Exception exception) {
throw new AssertionError(exception);
}
// Bug 3037701 - clone the queries
manager.setQueries(new LinkedHashMap<>(getQueries().size()));//bug5677655
Iterator<List<DatabaseQuery>> iterator = queries.values().iterator();
while (iterator.hasNext()) {
Iterator<DatabaseQuery> queriesForKey = iterator.next().iterator();
while (queriesForKey.hasNext()) {
DatabaseQuery initialQuery = queriesForKey.next();
DatabaseQuery clonedQuery = (DatabaseQuery)initialQuery.clone();
clonedQuery.setDescriptor(manager.getDescriptor());
manager.addQuery(clonedQuery);
}
}
manager.setDoesExistQuery((DoesExistQuery)getDoesExistQuery().clone());
if (getReadAllQuery() != null) {
manager.setReadAllQuery((ReadAllQuery)getReadAllQuery().clone());
}
if (getReadObjectQuery() != null) {
manager.setReadObjectQuery((ReadObjectQuery)getReadObjectQuery().clone());
}
if (getUpdateQuery() != null) {
manager.setUpdateQuery((UpdateObjectQuery)getUpdateQuery().clone());
}
if (getInsertQuery() != null) {
manager.setInsertQuery((InsertObjectQuery)getInsertQuery().clone());
}
if (getDeleteQuery() != null) {
manager.setDeleteQuery((DeleteObjectQuery)getDeleteQuery().clone());
}
return manager;
}
/**
* PUBLIC:
* Return true if the query is defined on the session
*/
public boolean containsQuery(String queryName) {
return queries.containsKey(queryName);
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this Query Manager to actual class-based
* settings
* This method is implemented by subclasses as necessary.
*/
public void convertClassNamesToClasses(ClassLoader classLoader){
Iterator<List<DatabaseQuery>> queryVectors = getQueries().values().iterator();
while (queryVectors.hasNext()){
Iterator<DatabaseQuery> queries = queryVectors.next().iterator();
while (queries.hasNext()){
queries.next().convertClassNamesToClasses(classLoader);
}
}
if (getReadObjectQuery() != null) {
getReadObjectQuery().convertClassNamesToClasses(classLoader);
}
if (getReadAllQuery() != null) {
getReadAllQuery().convertClassNamesToClasses(classLoader);
}
}
/**
* ADVANCED:
* Returns the join expression that should be appended to all of the descriptors expressions
* Contains any multiple table or inheritance dependencies
*/
public Expression getAdditionalJoinExpression() {
return additionalJoinExpression;
}
/**
* ADVANCED:
* Return the receiver's delete query.
* This should be an instance of a valid subclass of DeleteObjectQuery.
* If specified this is used by the descriptor to delete itself and its private parts from the database.
* This gives the user the ability to define exactly how to delete the data from the database,
* or access data external from the database or from some other framework.
*/
public DeleteObjectQuery getDeleteQuery() {
return deleteQuery;
}
/**
* ADVANCED:
* Return the receiver's delete SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the source row,
* through replacing the field names marked by '#' with the values for those fields.
* <p>
* Example, "delete from EMPLOYEE where EMPLOYEE_ID = #EMPLOYEE_ID".
*/
public String getDeleteSQLString() {
if (getDeleteQuery() == null) {
return null;
}
return getDeleteQuery().getSQLString();
}
/**
* INTERNAL:
* Return the descriptor associated with this descriptor query manager
*/
public ClassDescriptor getDescriptor() {
return descriptor;
}
/**
* ADVANCED:
* Return the receiver's does exist query.
* This should be an instance of a valid subclass of DoesExistQuery.
* If specified this is used by the descriptor to query existence of an object in the database.
* This gives the user the ability to define exactly how to query existence from the database,
* or access data external from the database or from some other framework.
*/
public DoesExistQuery getDoesExistQuery() {
return doesExistQuery;
}
/**
* ADVANCED:
* Return the receiver's does exist SQL string.
* This allows the user to override the SQL generated by EclipseLink, with there own SQL or procedure call.
* The arguments are translated from the fields of the source row, through replacing the field names marked by '#'
* with the values for those fields.
* This must return null if the object does not exist, otherwise return a database row.
* <p>
* Example, "select EMPLOYEE_ID from EMPLOYEE where EMPLOYEE_ID = #EMPLOYEE_ID".
*/
public String getDoesExistSQLString() {
if (getDoesExistQuery() == null) {
return null;
}
return getDoesExistQuery().getSQLString();
}
/**
* INTERNAL:
* This method is explicitly used by the Builder only.
*/
public String getExistenceCheck() {
if (getDoesExistQuery().shouldAssumeExistenceForDoesExist()) {
return "Assume existence";
} else if (getDoesExistQuery().shouldAssumeNonExistenceForDoesExist()) {
return "Assume non-existence";
} else if (getDoesExistQuery().shouldCheckCacheForDoesExist()) {
return "Check cache";
} else if (getDoesExistQuery().shouldCheckDatabaseForDoesExist()) {
return "Check database";
} else {
// Default.
return "Check cache";
}
}
/**
* ADVANCED:
* Return the receiver's insert query.
* This should be an instance of a valid subclass of InsertObjectQuery.
* If specified this is used by the descriptor to insert itself into the database.
* If the receiver uses sequence numbers, this query must return the updated sequence value.
* This gives the user the ability to define exactly how to insert the data into the database,
* or access data external from the database or from some other framework.
*/
public InsertObjectQuery getInsertQuery() {
return insertQuery;
}
/**
* ADVANCED:
* Return the receiver's insert SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the source row,
* through replacing the field names marked by '#' with the values for those fields.
* <p>
* Example, "insert into EMPLOYEE (F_NAME, L_NAME) values (#F_NAME, #L_NAME)".
*/
public String getInsertSQLString() {
if (getInsertQuery() == null) {
return null;
}
return getInsertQuery().getSQLString();
}
/**
* ADVANCED:
* This is normally generated for descriptors that have multiple tables.
* However, if the additional table does not reference the primary tables primary key,
* this expression may be set directly.
*/
public Expression getMultipleTableJoinExpression() {
return multipleTableJoinExpression;
}
/**
* PUBLIC:
* Return the pre-defined queries for the descriptor.
* The Map returned contains Lists of queries.
*
* @see #getAllQueries()
*/
public Map<String, List<DatabaseQuery>> getQueries() {
return queries;
}
/**
* PUBLIC:
* Return the pre-defined queries for the descriptor. The Vector returned
* contains all queries for this descriptor.
*
* @see #getQueries()
*/
public Vector getAllQueries() {
Vector<DatabaseQuery> allQueries = new Vector<>();
for (Iterator<List<DatabaseQuery>> vectors = getQueries().values().iterator(); vectors.hasNext();) {
allQueries.addAll(vectors.next());
}
return allQueries;
}
/**
* INTERNAL:
* Set pre-defined queries for the descriptor. Converts the Vector to a hashtable
*/
public void setAllQueries(Vector vector) {
for (Enumeration enumtr = vector.elements(); enumtr.hasMoreElements();) {
addQuery((DatabaseQuery)enumtr.nextElement());
}
}
/**
* PUBLIC:
* set the pre-defined queries for the descriptor. Used to write out deployment XML
*/
public void setQueries(Map map) {
queries = map;
}
/**
* PUBLIC:
* Return the query name from the set of pre-defined queries
* If only one query exists with this name, it will be returned.
* If there are multiple queries of this name, this method will search for a query
* with no arguments and return the first one it finds.
*
* @see #getQuery(String, Vector)
*/
public DatabaseQuery getQuery(String queryName) {
return getQuery(queryName, null);
}
/**
* PUBLIC:
* Return the query from the set of pre-defined queries with the given name and argument types.
* This allows for common queries to be pre-defined, reused and executed by name.
* This method should be used if the Session has multiple queries with the same name but
* different arguments.
* If only one query exists, it will be returned regardless of the arguments.
* If multiple queries exist, the first query that has corresponding argument types will be returned
*
* @see #getQuery(String)
*/
public DatabaseQuery getQuery(String name, Vector arguments) {
DatabaseQuery query = getLocalQuery(name, arguments);
// CR#3711: Check if a query with the same name exists for this descriptor.
// If not, recursively check descriptors of parent classes. If nothing is
// found in parents, return null.
if (query == null) {
DatabaseQuery parentQuery = getQueryFromParent(name, arguments);
if ((parentQuery != null) && parentQuery.isReadQuery()) {
parentQuery = (DatabaseQuery)parentQuery.clone();
((ObjectLevelReadQuery)parentQuery).setReferenceClass(this.descriptor.getJavaClass());
addQuery(name, parentQuery);
}
return parentQuery;
}
return query;
}
/**
* INTENAL:
* Return the query from the set of pre-defined queries with the given name and argument types.
* This allows for common queries to be pre-defined, reused and executed by name.
* Only returns those queries locally defined, not superclass's queries
* If only one query exists, it will be returned regardless of the arguments.
* If multiple queries exist, the first query that has corresponding argument types will be returned
*
* @see #getQuery(String)
*/
public DatabaseQuery getLocalQuery(String name, Vector arguments) {
List<DatabaseQuery> queries = getQueries().get(name);
if (queries == null) {
return null;
}
// Short circuit the simple, most common case of only one query.
if (queries.size() == 1) {
return queries.get(0);
}
// CR#3754; Predrag; mar 19/2002;
// We allow multiple named queries with the same name but
// different argument set; we can have only one query with
// no arguments; Vector queries is not sorted;
// When asked for the query with no parameters the
// old version did return the first query - wrong:
// return (DatabaseQuery) queries.firstElement();
int argumentTypesSize = 0;
if (arguments != null) {
argumentTypesSize = arguments.size();
}
Vector argumentTypes = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(argumentTypesSize);
for (int i = 0; i < argumentTypesSize; i++) {
argumentTypes.addElement(arguments.elementAt(i).getClass());
}
return getLocalQueryByArgumentTypes(name, argumentTypes);
}
/**
* INTERNAL:
* Return the query from the set of pre-defined queries with the given name and argument types.
* This allows for common queries to be pre-defined, reused and executed by name.
* Only returns those queries locally defined, not superclass's queries
* If only one query exists, it will be returned regardless of the arguments.
* If multiple queries exist, the first query that has corresponding argument types will be returned
*
* @see #getQuery(String)
*/
public DatabaseQuery getLocalQueryByArgumentTypes(String name, List argumentTypes) {
List<DatabaseQuery> queries = getQueries().get(name);
if (queries == null) {
return null;
}
// Short circuit the simple, most common case of only one query.
if (queries.size() == 1) {
return queries.get(0);
}
for (DatabaseQuery query : queries) {
// BUG#2698755
// This check was backward, we default the type to Object
// Was checking Object is descendant of String not other way.
if (Helper.areTypesAssignable(query.getArgumentTypes(), argumentTypes)) {
return query;
}
}
return null;
}
/**
* INTERNAL:
* CR#3711: Check if the class for this descriptor has a parent class.
* Then search this parent's descriptor for a query with the same name
* and arguments. If nothing found, return null.
*
* This method should only be used recursively by getQuery().
*/
protected DatabaseQuery getQueryFromParent(String name, Vector arguments) {
ClassDescriptor descriptor = this.descriptor;
if (descriptor.hasInheritance()) {
InheritancePolicy inheritancePolicy = descriptor.getInheritancePolicy();
ClassDescriptor parent = inheritancePolicy.getParentDescriptor();
// if parent exists, check for the query
if (parent != null) {
return parent.getQueryManager().getQuery(name, arguments);
}
}
return null;
}
/**
* ADVANCED:
* Return the receiver's read query.
* This should be an instance of a valid subclass of ReadAllQuery.
*/
public ReadAllQuery getReadAllQuery() {
return readAllQuery;
}
/**
* ADVANCED:
* Return the receiver's read SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the read arguments row,
* through replacing the field names marked by '#' with the values for those fields.
* Note that this is only used on readAllObjects(Class), and not when an expression is provided.
* <p>
* Example, "select * from EMPLOYEE"
*/
public String getReadAllSQLString() {
if (getReadAllQuery() == null) {
return null;
}
return getReadAllQuery().getSQLString();
}
/**
* ADVANCED:
* Return the receiver's read query.
* This should be an instance of a valid subclass of ReadObjectQuery.
* If specified this is used by the descriptor to read itself from the database.
* The read arguments must be the primary key of the object only.
* This gives the user the ability to define exactly how to read the object from the database,
* or access data external from the database or from some other framework.
*/
public ReadObjectQuery getReadObjectQuery() {
return readObjectQuery;
}
/**
* ADVANCED:
* Return the receiver's read SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the read arguments row,
* through replacing the field names marked by '#' with the values for those fields.
* This must accept only the primary key of the object as arguments.
* <p>
* Example, "select * from EMPLOYEE where EMPLOYEE_ID = #EMPLOYEE_ID"
*/
public String getReadObjectSQLString() {
if (getReadObjectQuery() == null) {
return null;
}
return getReadObjectQuery().getSQLString();
}
/**
* ADVANCED:
* Return the receiver's update query.
* This should be an instance of a valid subclass of UpdateObjectQuery.
* If specified this is used by the descriptor to insert itself into the database.
* If the receiver uses optimistic locking this must raise an error on optimistic lock failure.
* This gives the user the ability to define exactly how to update the data into the database,
* or access data external from the database or from some other framework.
*/
public UpdateObjectQuery getUpdateQuery() {
return updateQuery;
}
/**
* ADVANCED:
* Return the receiver's update SQL string.
* This allows the user to override the SQL generated by EclipseLink, with there own SQL or procedure call.
* The arguments are translated from the fields of the source row,
* through replacing the field names marked by '#' with the values for those fields.
* This must check the optimistic lock field and raise an error on optimistic lock failure.
* <p>
* Example, "update EMPLOYEE set F_NAME to #F_NAME, L_NAME to #L_NAME where EMPLOYEE_ID = #EMPLOYEE_ID".
*/
public String getUpdateSQLString() {
if (getUpdateQuery() == null) {
return null;
}
return getUpdateQuery().getSQLString();
}
/**
* ADVANCED:
* Return true if an additional criteria has been set on this query manager.
*/
public boolean hasAdditionalCriteria() {
return additionalCriteria != null;
}
/**
* INTERNAL:
* Return if a custom join expression is used.
*/
public boolean hasCustomMultipleTableJoinExpression() {
return hasCustomMultipleTableJoinExpression;
}
/**
* INTERNAL:
* Flag that specifies if a delete query is available
*/
public boolean hasDeleteQuery() {
return (deleteQuery != null);
}
/**
* INTERNAL:
* Flag that specifies if a does exist query is available
*/
public boolean hasDoesExistQuery() {
return (doesExistQuery != null);
}
/**
* INTERNAL:
* Flag that specifies if a insert query is available
*/
public boolean hasInsertQuery() {
return (insertQuery != null);
}
/**
* INTERNAL:
* Flag that specifies if a read all query is available
*/
public boolean hasReadAllQuery() {
return (readAllQuery != null);
}
/**
* INTERNAL:
* Flag that specifies if a read object query is available
*/
public boolean hasReadObjectQuery() {
return (readObjectQuery != null);
}
/**
* INTERNAL:
* Flag that specifies if a update query is available
*/
public boolean hasUpdateQuery() {
return (updateQuery != null);
}
/**
* INTERNAL:
* populate the queries with the descriptor.
*/
private void populateQueries() {
/* CR2260
* Description:
* NullPointerException accessing null descriptor
* Fix:
* Initialize queries with an instantiated descriptor at this point
*/
if (getInsertQuery() != null) {
getInsertQuery().setDescriptor(descriptor);
}
if (getUpdateQuery() != null) {
getUpdateQuery().setDescriptor(descriptor);
}
if (getReadObjectQuery() != null) {
getReadObjectQuery().setReferenceClass(getDescriptor().getJavaClass());
getReadObjectQuery().setDescriptor(descriptor);
}
if (getDeleteQuery() != null) {
getDeleteQuery().setDescriptor(descriptor);
}
if (getReadAllQuery() != null) {
getReadAllQuery().setReferenceClass(getDescriptor().getJavaClass());
getReadAllQuery().setDescriptor(descriptor);
}
for (Iterator it = getAllQueries().iterator(); it.hasNext();) {
((DatabaseQuery)it.next()).setDescriptor(descriptor);
}
}
/**
* INTERNAL:
* Post initialize the mappings
*/
public void initialize(AbstractSession session) {
this.initializeQueryTimeout(session);
if (getDescriptor().isAggregateDescriptor()) {
return;
}
if (getMultipleTableJoinExpression() != null) {
// Combine new multiple table expression to additional join expression
setAdditionalJoinExpression(getMultipleTableJoinExpression().and(getAdditionalJoinExpression()));
}
if (getDescriptor().isAggregateCollectionDescriptor()) {
return;
}
// Configure default query cache for all named queries.
QueryResultsCachePolicy defaultQueryCachePolicy = session.getProject().getDefaultQueryResultsCachePolicy();
if (defaultQueryCachePolicy != null && !getDescriptor().getCachePolicy().isIsolated()) {
for (List<DatabaseQuery> queries : getQueries().values()) {
for (DatabaseQuery query : queries) {
if (query.isReadQuery()) {
ReadQuery readQuery = (ReadQuery)query;
if (!readQuery.shouldCacheQueryResults()) {
readQuery.setQueryResultsCachePolicy(defaultQueryCachePolicy.clone());
}
}
}
}
}
getDescriptor().initialize(this, session);
}
/**
* INTERNAL:
* Initialize the queryTimeout to:
*
* NoTimeout: If queryTimeout is DefaultTimeout, either directly or via inheritance.
* Parent's Timeout: If queryTimeout is something other than DefaultTimeout via my parent.
*/
public void initializeQueryTimeout(AbstractSession session) {
//if queryTimeout is DefaultTimeout, try to get my parent's queryTimeout
if (getQueryTimeout() == DefaultTimeout) {
if (getDescriptor().hasInheritance() && (this.getDescriptor().getInheritancePolicy().getParentDescriptor() != null)) {
setQueryTimeout(this.getParentDescriptorQueryManager().getQueryTimeout());
}
}
if (getQueryTimeoutUnit().equals(DefaultTimeoutUnit)) {
if (getDescriptor().hasInheritance() && (this.getDescriptor().getInheritancePolicy().getParentDescriptor() != null)) {
setQueryTimeoutUnit(this.getParentDescriptorQueryManager().getQueryTimeoutUnit());
}
}
//if I have DefaultTimeout (via parent or not), set to NoTimeout
if (getQueryTimeout() == DefaultTimeout) {
setQueryTimeout(NoTimeout);
}
}
/**
* INTERNAL:
* Get the parent DescriptorQueryManager.
* Caution must be used in using this method as it expects the descriptor
* to have inheritance.
* Calling this when the descriptor that does not use inheritance will cause problems, #hasInheritance() must
* always first be called.
*/
public DescriptorQueryManager getParentDescriptorQueryManager() {
return this.descriptor.getInheritancePolicy().getParentDescriptor().getQueryManager();
}
/**
* INTERNAL:
* Execute the post delete operation for the query
*/
public void postDelete(DeleteObjectQuery query) {
ObjectBuilder builder = this.descriptor.getObjectBuilder();
// PERF: Only process relationships.
if (!builder.isSimple()) {
List<DatabaseMapping> mappings = builder.getRelationshipMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
mappings.get(index).postDelete(query);
}
}
}
/**
* INTERNAL:
* Post initializations after mappings are initialized.
*/
public void postInitialize(AbstractSession session) throws DescriptorException {
// If the additional criteria is specified, append it to the additional
// join expression. We do this in postInitialize after all the mappings
// have been fully initialized.
if (additionalCriteria != null) {
if (getDescriptor().hasInheritance() && getDescriptor().getInheritancePolicy().hasView()) {
throw DescriptorException.additionalCriteriaNotSupportedWithInheritanceViews(getDescriptor());
}
JPAQueryBuilder queryBuilder = session.getQueryBuilder();
Expression selectionCriteria = queryBuilder.buildSelectionCriteria(
getDescriptor().getAlias(),
additionalCriteria,
session
);
updatePropertyParameterExpression(selectionCriteria);
additionalJoinExpression = selectionCriteria.and(additionalJoinExpression);
}
if (additionalJoinExpression != null) {
// The make sure the additional join expression has the correct
// context, rebuild the additional join expression on a new
// expression builder.
additionalJoinExpression = additionalJoinExpression.rebuildOn(new ExpressionBuilder());
}
}
/**
* INTERNAL:
* This method will walk the given expression and mark any parameter
* expressions as property expressions. This is done when additional
* criteria has been specified and parameter values must be resolved
* through session properties.
*
* @see #postInitialize
*/
protected void updatePropertyParameterExpression(Expression exp) {
if (exp.isCompoundExpression()) {
updatePropertyParameterExpression(((CompoundExpression) exp).getFirstChild());
updatePropertyParameterExpression(((CompoundExpression) exp).getSecondChild());
} else if (exp.isFunctionExpression()) {
for (Expression e : ((FunctionExpression) exp).getChildren()) {
updatePropertyParameterExpression(e);
}
} else if (exp.isSubSelectExpression()) {
ReportQuery subSelectQuery = ((SubSelectExpression) exp).getSubQuery();
for (ReportItem item : subSelectQuery.getItems()) {
updatePropertyParameterExpression(item.getAttributeExpression());
}
}
if (exp.isParameterExpression()) {
((ParameterExpression) exp).setIsProperty(true);
}
}
/**
* INTERNAL:
* Execute the post insert operation for the query
*/
public void postInsert(WriteObjectQuery query) {
ObjectBuilder builder = this.descriptor.getObjectBuilder();
// PERF: Only process relationships.
if (!builder.isSimple()) {
List<DatabaseMapping> mappings = builder.getRelationshipMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
mappings.get(index).postInsert(query);
}
}
}
/**
* INTERNAL:
* Execute the post update operation for the query
*/
public void postUpdate(WriteObjectQuery query) {
ObjectBuilder builder = this.descriptor.getObjectBuilder();
// PERF: Only process relationships.
if (!builder.isSimple()) {
// PERF: Only process changed mappings.
ObjectChangeSet changeSet = query.getObjectChangeSet();
if ((changeSet != null) && (!changeSet.isNew())) {
List<org.eclipse.persistence.sessions.changesets.ChangeRecord> changeRecords = changeSet.getChanges();
int size = changeRecords.size();
for (int index = 0; index < size; index++) {
ChangeRecord record = (ChangeRecord)changeRecords.get(index);
record.getMapping().postUpdate(query);
}
} else {
List<DatabaseMapping> mappings = builder.getRelationshipMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
mappings.get(index).postUpdate(query);
}
}
}
}
/**
* INTERNAL:
* Execute the pre delete operation for the query
*/
public void preDelete(DeleteObjectQuery query) {
ObjectBuilder builder = this.descriptor.getObjectBuilder();
// PERF: Only process relationships.
if (!builder.isSimple()) {
List<DatabaseMapping> mappings = builder.getRelationshipMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
mappings.get(index).preDelete(query);
}
}
}
/**
* INTERNAL:
* Initialize the query manager.
* Any custom queries must be inherited from the parent before any initialization.
*/
public void preInitialize(AbstractSession session) {
if (getDescriptor().isAggregateDescriptor()) {
return;
}
// Must inherit parent query customization if not redefined.
if (getDescriptor().isChildDescriptor()) {
DescriptorQueryManager parentQueryManager = getDescriptor().getInheritancePolicy().getParentDescriptor().getQueryManager();
if ((!hasInsertQuery()) && (parentQueryManager.hasInsertQuery())) {
setInsertQuery((InsertObjectQuery)parentQueryManager.getInsertQuery().clone());
}
if ((!hasUpdateQuery()) && (parentQueryManager.hasUpdateQuery())) {
setUpdateQuery((UpdateObjectQuery)parentQueryManager.getUpdateQuery().clone());
}
if ((!hasDeleteQuery()) && (parentQueryManager.hasDeleteQuery())) {
setDeleteQuery((DeleteObjectQuery)parentQueryManager.getDeleteQuery().clone());
}
if ((!hasReadObjectQuery()) && (parentQueryManager.hasReadObjectQuery())) {
setReadObjectQuery((ReadObjectQuery)parentQueryManager.getReadObjectQuery().clone());
}
if ((!hasReadAllQuery()) && (parentQueryManager.hasReadAllQuery())) {
setReadAllQuery((ReadAllQuery)parentQueryManager.getReadAllQuery().clone());
}
if ((!getDoesExistQuery().isUserDefined()) && getDoesExistQuery().shouldCheckCacheForDoesExist()) {
setDoesExistQuery(((DoesExistQuery)parentQueryManager.getDoesExistQuery().clone()));
}
}
}
/**
* INTERNAL:
* Execute the pre insert operation for the query.
*/
public void preInsert(WriteObjectQuery query) {
ObjectBuilder builder = this.descriptor.getObjectBuilder();
// PERF: Only process relationships.
if (!builder.isSimple()) {
List<DatabaseMapping> mappings = builder.getRelationshipMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
mappings.get(index).preInsert(query);
}
}
}
/**
* INTERNAL:
* Execute the pre update operation for the query
*/
public void preUpdate(WriteObjectQuery query) {
ObjectBuilder builder = this.descriptor.getObjectBuilder();
// PERF: Only process relationships.
if (!builder.isSimple()) {
// PERF: Only process changed mappings.
ObjectChangeSet changeSet = query.getObjectChangeSet();
if ((changeSet != null) && (!changeSet.isNew())) {
List<org.eclipse.persistence.sessions.changesets.ChangeRecord> changeRecords = changeSet.getChanges();
int size = changeRecords.size();
for (int index = 0; index < size; index++) {
ChangeRecord record = (ChangeRecord)changeRecords.get(index);
record.getMapping().preUpdate(query);
}
} else {
List<DatabaseMapping> mappings = builder.getRelationshipMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
mappings.get(index).preUpdate(query);
}
}
}
}
/**
* PUBLIC:
* Remove all queries with the given query name from the set of pre-defined queries
*
* @see #removeQuery(String, Vector)
*/
public void removeQuery(String queryName) {
queries.remove(queryName);
}
/**
* PUBLIC:
* Remove the specific query with the given queryName and argumentTypes.
*
* @see #removeQuery(String)
*/
public void removeQuery(String queryName, Vector argumentTypes) {
List<DatabaseQuery> queries = getQueries().get(queryName);
if (queries != null) {
DatabaseQuery query = null;
for (DatabaseQuery q : queries) {
if (Helper.areTypesAssignable(argumentTypes, q.getArgumentTypes())) {
query = q;
break;
}
}
if (query != null) {
queries.remove(query);
}
}
}
/**
* ADVANCED:
* Set the additional join criteria that will be used to form the additional
* join expression. The additionalCriteria is a jpql fragment at this point.
* @see #setAdditionalJoinExpression
*/
public void setAdditionalCriteria(String additionalCriteria) {
this.additionalCriteria = additionalCriteria;
}
/**
* ADVANCED:
* Set the additional join expression. Used in conjunction with
* multiple tables and inheritance relationships.
* This can also be used if a sub-expression is always required to be
* appended to all queries. Such as tables that are shared based on a type field
* without inheritance.
*/
public void setAdditionalJoinExpression(Expression additionalJoinExpression) {
this.additionalJoinExpression = additionalJoinExpression;
}
/**
* ADVANCED:
* Set the receiver's delete query.
* This should be an instance of a valid subclass of DeleteObjectQuery.
* If specified this is used by the descriptor to delete itself and its private parts from the database.
* This gives the user the ability to define exactly how to delete the data from the database,
* or access data external from the database or from some other framework.
*/
public void setDeleteQuery(DeleteObjectQuery query) {
this.deleteQuery = query;
if (query == null) {
return;
}
query.setIsUserDefined(true);
query.setDescriptor(getDescriptor());
if (query.isCallQuery()) {
query.setIsFullRowRequired(true);
}
}
/**
* ADVANCED:
* Set the receiver's delete SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the source row,
* through replacing the field names marked by '#' with the values for those fields.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>
* Example, "delete from EMPLOYEE where EMPLOYEE_ID = #EMPLOYEE_ID".
*/
public void setDeleteSQLString(String sqlString) {
if (sqlString == null) {
return;
}
DeleteObjectQuery query = new DeleteObjectQuery();
query.setSQLString(sqlString);
setDeleteQuery(query);
}
/**
* ADVANCED:
* Set the receiver's delete call.
* This allows the user to override the delete operation.
*/
public void setDeleteCall(Call call) {
if (call == null) {
return;
}
DeleteObjectQuery query = new DeleteObjectQuery();
query.setCall(call);
setDeleteQuery(query);
}
/**
* INTERNAL:
* Set the descriptor.
*/
public void setDescriptor(ClassDescriptor descriptor) {
this.descriptor = descriptor;
//Gross alert: This is for the case when we are reading from XML, and
//we have to compensate for no descriptor available at read time. - JL
populateQueries();
}
/**
* ADVANCED:
* Set the receiver's does exist query.
* This should be an instance of a valid subclass of DoesExistQuery.
* If specified this is used by the descriptor to query existence of an object in the database.
* This gives the user the ability to define exactly how to query existence from the database,
* or access data external from the database or from some other framework.
*/
public void setDoesExistQuery(DoesExistQuery query) {
this.doesExistQuery = query;
if (query == null) {
return;
}
this.doesExistQuery.setIsUserDefined(true);
this.doesExistQuery.setDescriptor(getDescriptor());
}
/**
* ADVANCED:
* Set the receiver's does exist SQL string.
* This allows the user to override the SQL generated by EclipseLink, with there own SQL or procedure call.
* The arguments are translated from the fields of the source row, through replacing the field names marked by '#'
* with the values for those fields.
* This must return null if the object does not exist, otherwise return a database row.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>
* Example, "select EMPLOYEE_ID from EMPLOYEE where EMPLOYEE_ID = #EMPLOYEE_ID".
*/
public void setDoesExistSQLString(String sqlString) {
if (sqlString == null) {
return;
}
getDoesExistQuery().setSQLString(sqlString);
getDoesExistQuery().checkDatabaseForDoesExist();
}
/**
* ADVANCED:
* Set the receiver's does exist call.
* This allows the user to override the does exist operation.
*/
public void setDoesExistCall(Call call) {
if (call == null) {
return;
}
getDoesExistQuery().setCall(call);
}
/**
* INTERNAL:
* This method is explicitly used by the Builder only.
*/
public void setExistenceCheck(String token) throws DescriptorException {
if (token.equals("Check cache")) {
checkCacheForDoesExist();
} else if (token.equals("Check database")) {
checkDatabaseForDoesExist();
} else if (token.equals("Assume existence")) {
assumeExistenceForDoesExist();
} else if (token.equals("Assume non-existence")) {
assumeNonExistenceForDoesExist();
} else {
throw DescriptorException.setExistenceCheckingNotUnderstood(token, getDescriptor());
}
}
/**
* INTENAL:
* Set if a custom join expression is used.
*/
protected void setHasCustomMultipleTableJoinExpression(boolean hasCustomMultipleTableJoinExpression) {
this.hasCustomMultipleTableJoinExpression = hasCustomMultipleTableJoinExpression;
}
/**
* ADVANCED:
* Set the receiver's insert query.
* This should be an instance of a valid subclass of InsertObjectQuery.
* If specified this is used by the descriptor to insert itself into the database.
* This gives the user the ability to define exactly how to insert the data into the database,
* or access data external from the database or from some other framework.
*/
public void setInsertQuery(InsertObjectQuery insertQuery) {
this.insertQuery = insertQuery;
if (insertQuery == null) {
return;
}
this.insertQuery.setIsUserDefined(true);
this.insertQuery.setDescriptor(getDescriptor());
}
/**
* ADVANCED:
* Set the receiver's insert call.
* This allows the user to override the insert operation.
*/
public void setInsertCall(Call call) {
if (call == null) {
return;
}
InsertObjectQuery query = new InsertObjectQuery();
query.setCall(call);
setInsertQuery(query);
}
/**
* ADVANCED:
* Set the receiver's insert SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the source row,
* through replacing the field names marked by '#' with the values for those fields.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>
* Example, "insert into EMPLOYEE (F_NAME, L_NAME) values (#F_NAME, #L_NAME)".
*/
public void setInsertSQLString(String sqlString) {
if (sqlString == null) {
return;
}
InsertObjectQuery query = new InsertObjectQuery();
query.setSQLString(sqlString);
setInsertQuery(query);
}
/**
* ADVANCED:
* Return the receiver's insert call.
* This allows the user to override the insert operation.
*/
public Call getInsertCall() {
if (getInsertQuery() == null) {
return null;
}
return getInsertQuery().getDatasourceCall();
}
/**
* ADVANCED:
* Return the receiver's update call.
* This allows the user to override the update operation.
*/
public Call getUpdateCall() {
if (getUpdateQuery() == null) {
return null;
}
return getUpdateQuery().getDatasourceCall();
}
/**
* ADVANCED:
* Return the receiver's delete call.
* This allows the user to override the delete operation.
*/
public Call getDeleteCall() {
if (getDeleteQuery() == null) {
return null;
}
return getDeleteQuery().getDatasourceCall();
}
/**
* ADVANCED:
* Return the receiver's read-object call.
* This allows the user to override the read-object operation.
*/
public Call getReadObjectCall() {
if (getReadObjectQuery() == null) {
return null;
}
return getReadObjectQuery().getDatasourceCall();
}
/**
* ADVANCED:
* Return the receiver's read-all call.
* This allows the user to override the read-all operation.
*/
public Call getReadAllCall() {
if (getReadAllQuery() == null) {
return null;
}
return getReadAllQuery().getDatasourceCall();
}
/**
* ADVANCED:
* Return the receiver's does-exist call.
* This allows the user to override the does-exist operation.
*/
public Call getDoesExistCall() {
if (getDoesExistQuery() == null) {
return null;
}
return getDoesExistQuery().getDatasourceCall();
}
/**
* INTERNAL:
* Used in case descriptor has additional tables:
* each additional table mapped to an expression joining it.
*/
public Map<DatabaseTable, Expression> getTablesJoinExpressions() {
if (tablesJoinExpressions == null) {
tablesJoinExpressions = new HashMap<>();
}
return tablesJoinExpressions;
}
/**
* INTERNAL:
* Used to set the multiple table join expression that was generated by EclipseLink as opposed
* to a custom one supplied by the user.
* @see #setMultipleTableJoinExpression(Expression)
*/
public void setInternalMultipleTableJoinExpression(Expression multipleTableJoinExpression) {
this.multipleTableJoinExpression = multipleTableJoinExpression;
}
/**
* ADVANCED:
* This is normally generated for descriptors that have multiple tables.
* However, if the additional table does not reference the primary table's primary key,
* this expression may be set directly.
*/
public void setMultipleTableJoinExpression(Expression multipleTableJoinExpression) {
this.multipleTableJoinExpression = multipleTableJoinExpression;
setHasCustomMultipleTableJoinExpression(true);
}
/**
* ADVANCED:
* Set the receiver's read all query.
* This should be an instance of a valid subclass of ReadAllQuery.
* If specified this is used by the descriptor to read all instances of its class from the database.
* This gives the user the ability to define exactly how to read all objects from the database,
* or access data external from the database or from some other framework.
* Note that this is only used on readAllObjects(Class), and not when an expression is provided.
*/
public void setReadAllQuery(ReadAllQuery query) {
this.readAllQuery = query;
if (query == null) {
return;
}
this.readAllQuery.setIsUserDefined(true);
/* CR2260 - Steven Vo
* Description:
* NullPointerException accessing null descriptor
* Fix:
* Setting query's descriptor and reference class when descriptor is not null.
* Otherwise, wait until the descriptor is set.See populateQueries() that is
* called by setDescriptor()
*/
if (this.getDescriptor() != null) {
this.readAllQuery.setDescriptor(getDescriptor());
this.readAllQuery.setReferenceClassName(getDescriptor().getJavaClassName());
try {
readAllQuery.setReferenceClass(getDescriptor().getJavaClass());
} catch (ConversionException exception) {
}
}
}
/**
* ADVANCED:
* Set the receiver's read SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the read arguments row,
* through replacing the field names marked by '#' with the values for those fields.
* Note that this is only used on readAllObjects(Class), and not when an expression is provided.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>
* Example, "select * from EMPLOYEE"
*/
public void setReadAllSQLString(String sqlString) {
if (sqlString == null) {
return;
}
ReadAllQuery query = new ReadAllQuery();
query.setSQLString(sqlString);
setReadAllQuery(query);
}
/**
* ADVANCED:
* Set the receiver's read all call.
* This allows the user to override the read all operation.
* Note that this is only used on readAllObjects(Class), and not when an expression is provided.
*/
public void setReadAllCall(Call call) {
if (call == null) {
return;
}
ReadAllQuery query = new ReadAllQuery();
query.setCall(call);
setReadAllQuery(query);
}
/**
* ADVANCED:
* Set the receiver's read query.
* This should be an instance of a valid subclass of ReadObjectQuery
* If specified this is used by the descriptor to read itself from the database.
* The read arguments must be the primary key of the object only.
* This gives the user the ability to define exactly how to read the object from the database,
* or access data external from the database or from some other framework.
*/
public void setReadObjectQuery(ReadObjectQuery query) {
this.readObjectQuery = query;
if (query == null) {
return;
}
this.readObjectQuery.setIsUserDefined(true);
/* CR2260 - Steven Vo
* Description:
* NullPointerException accessing null descriptor
* Fix:
* Setting query's descriptor and reference class when descriptor is not null.
* Otherwise, wait until the descriptor is set.See populateQueries() that is
* called by setDescriptor()
*/
if (this.getDescriptor() != null) {
this.readObjectQuery.setDescriptor(getDescriptor());
this.readObjectQuery.setReferenceClassName(getDescriptor().getJavaClassName());
try {
readObjectQuery.setReferenceClass(getDescriptor().getJavaClass());
} catch (ConversionException exception) {
}
}
}
/**
* ADVANCED:
* Set the receiver's read SQL string.
* This allows the user to override the SQL generated by EclipseLink, with their own SQL or procedure call.
* The arguments are translated from the fields of the read arguments row,
* through replacing the field names marked by '#' with the values for those fields.
* This must accept only the primary key of the object as arguments.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>
* Example, "select * from EMPLOYEE where EMPLOYEE_ID = #EMPLOYEE_ID"
*/
public void setReadObjectSQLString(String sqlString) {
if (sqlString == null) {
return;
}
ReadObjectQuery query = new ReadObjectQuery();
query.setSQLString(sqlString);
setReadObjectQuery(query);
}
/**
* ADVANCED:
* Set the receiver's read object call.
* This allows the user to override the read object operation.
* This must accept only the primary key of the object as arguments.
*/
public void setReadObjectCall(Call call) {
if (call == null) {
return;
}
ReadObjectQuery query = new ReadObjectQuery();
query.setCall(call);
setReadObjectQuery(query);
}
/**
* ADVANCED:
* Set the receiver's update query.
* This should be an instance of a valid subclass of UpdateObjectQuery.
* If specified this is used by the descriptor to update itself in the database.
* If the receiver uses optimistic locking this must raise an error on optimistic lock failure.
* This gives the user the ability to define exactly how to update the data into the database,
* or access data external from the database or from some other framework.
*/
public void setUpdateQuery(UpdateObjectQuery updateQuery) {
this.updateQuery = updateQuery;
if (updateQuery == null) {
return;
}
this.updateQuery.setIsUserDefined(true);
this.updateQuery.setDescriptor(getDescriptor());
}
/**
* ADVANCED:
* Set the receiver's update SQL string.
* This allows the user to override the SQL generated by EclipseLink, with there own SQL or procedure call.
* The arguments are translated from the fields of the source row,
* through replacing the field names marked by '#' with the values for those fields.
* This must check the optimistic lock field and raise an error on optimistic lock failure.
* Warning: Allowing an unverified SQL string to be passed into this
* method makes your application vulnerable to SQL injection attacks.
* <p>
* Example, "update EMPLOYEE set F_NAME to #F_NAME, L_NAME to #L_NAME where EMPLOYEE_ID = #EMPLOYEE_ID".
*/
public void setUpdateSQLString(String sqlString) {
if (sqlString == null) {
return;
}
UpdateObjectQuery query = new UpdateObjectQuery();
query.setSQLString(sqlString);
setUpdateQuery(query);
}
/**
* ADVANCED:
* Set the receiver's update call.
* This allows the user to override the update operation.
*/
public void setUpdateCall(Call call) {
if (call == null) {
return;
}
UpdateObjectQuery query = new UpdateObjectQuery();
query.setCall(call);
setUpdateQuery(query);
}
/**
* PUBLIC:
* Return the number of seconds queries will wait for their Statement to execute.
*
* - DefaultTimeout: get queryTimeout from parent DescriptorQueryManager. If there is no
* parent, default to NoTimeout
* - NoTimeout, 1..N: overrides parent queryTimeout
*/
public int getQueryTimeout() {
return queryTimeout;
}
public TimeUnit getQueryTimeoutUnit() {
return queryTimeoutUnit;
}
/**
* PUBLIC:
* Set the number of seconds that queries will wait for their Statement to execute.
* If the limit is exceeded, a DatabaseException is thrown.
*
* - DefaultTimeout: get queryTimeout from parent DescriptorQueryManager. If there is no
* parent, default to NoTimeout
* - NoTimeout, 1..N: overrides parent queryTimeout
*/
public void setQueryTimeout(int queryTimeout) {
this.queryTimeout = queryTimeout;
}
public void setQueryTimeoutUnit(TimeUnit queryTimeoutUnit) {
this.queryTimeoutUnit = queryTimeoutUnit;
}
/**
* INTERNAL:
* Returns the collection of cached Update calls.
*/
private ConcurrentFixedCache getCachedUpdateCalls() {
if (cachedUpdateCalls == null) {
this.cachedUpdateCalls = new ConcurrentFixedCache(10);
}
return this.cachedUpdateCalls;
}
/**
* INTERNAL:
* Returns the collection of cached expression queries.
*/
private ConcurrentFixedCache getCachedExpressionQueries() {
if (cachedExpressionQueries == null) {
this.cachedExpressionQueries = new ConcurrentFixedCache(20);
}
return this.cachedExpressionQueries;
}
/**
* ADVANCED:
* Return the size of the update call cache.
* The update call cache is used to cache the update SQL to avoid regeneration.
* Since every update with different fields produces different SQL,
* this cache allows caching of the update SQL based on the fields being updated.
* The default cache size is 10, the update call cache can be disabled through setting the size to 0.
*/
public int getUpdateCallCacheSize() {
return getCachedUpdateCalls().getMaxSize();
}
/**
* ADVANCED:
* Set the size of the update call cache.
* The update call cache is used to cache the update SQL to avoid regeneration.
* Since every update with different fields produces different SQL,
* this cache allows caching of the update SQL based on the fields being updated.
* The default cache size is 10, the update call cache can be disabled through setting the size to 0.
*/
public void setUpdateCallCacheSize(int updateCallCacheSize) {
getCachedUpdateCalls().setMaxSize(updateCallCacheSize);
}
/**
* INTERNAL:
* Return the cached update SQL call based on the updated fields.
* PERF: Allow caching of the update SQL call to avoid regeneration.
*/
public Vector getCachedUpdateCalls(Vector updateFields) {
return (Vector) getCachedUpdateCalls().get(updateFields);
}
/**
* INTERNAL:
* Cache a clone of the update SQL calls based on the updated fields.
* If the max size is reached, do not cache the call.
* The call's query must be dereferenced in order to allow the GC of a related session.
* PERF: Allow caching of the update SQL call to avoid regeneration.
*/
public void putCachedUpdateCalls(Vector updateFields, Vector updateCalls) {
Vector vectorToCache = updateCalls;
if (!updateCalls.isEmpty()) {
int updateCallsSize = updateCalls.size();
vectorToCache = new NonSynchronizedVector(updateCallsSize);
for (int i = 0; i < updateCallsSize; i++) {
DatasourceCall updateCall = (DatasourceCall)updateCalls.get(i);
// clone call and dereference query for DatasourceCall and EJBQLCall
DatasourceCall clonedUpdateCall = (DatasourceCall) updateCall.clone();
clonedUpdateCall.setQuery(null);
vectorToCache.add(clonedUpdateCall);
}
}
getCachedUpdateCalls().put(updateFields, vectorToCache);
}
/**
* INTERNAL:
* Return the cached SQL call for the expression query.
* PERF: Allow caching of expression query SQL call to avoid regeneration.
*/
public DatabaseQuery getCachedExpressionQuery(DatabaseQuery query) {
return (DatabaseQuery) getCachedExpressionQueries().get(query);
}
/**
* INTERNAL:
* Set the cached SQL call for the expression query.
* PERF: Allow caching of expression query SQL call to avoid regeneration.
*/
public void putCachedExpressionQuery(DatabaseQuery query) {
getCachedExpressionQueries().put(query, query);
}
/**
* INTERNAL:
* Remove the cached expression query.
* PERF: Allow caching of expression query SQL call to avoid regeneration.
*/
public void removeCachedExpressionQuery(DatabaseQuery query) {
getCachedExpressionQueries().remove(query);
}
}