org.eclipse.persistence.internal.jpa.EJBQueryImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* 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
// Zoltan NAGY & tware - updated support for MaxRows
// 11/01/2010-2.2 Guy Pelletier
// - 322916: getParameter on Query throws NPE
// 11/09/2010-2.1 Michael O'Brien
// - 329089: PERF: EJBQueryImpl.setParamenterInternal() move indexOf check inside non-native block
// 02/08/2012-2.4 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 08/11/2012-2.5 Guy Pelletier
// - 393867: Named queries do not work when using EM level Table Per Tenant Multitenancy.
package org.eclipse.persistence.internal.jpa;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.LockTimeoutException;
import jakarta.persistence.Parameter;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.TemporalType;
import jakarta.persistence.TypedQuery;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.jpa.querydef.ParameterExpressionImpl;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.queries.JPQLCallQueryMechanism;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaQuery;
import org.eclipse.persistence.queries.Cursor;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.JPAQueryBuilder;
import org.eclipse.persistence.queries.ModifyQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.queries.ResultSetMappingQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
/**
* Concrete JPA query class. The JPA query wraps a DatabaseQuery which is
* executed.
*/
public class EJBQueryImpl extends QueryImpl implements JpaQuery {
/**
* Base constructor for EJBQueryImpl. Initializes basic variables.
*/
protected EJBQueryImpl(EntityManagerImpl entityManager) {
super(entityManager);
}
/**
* Create an EJBQueryImpl with a DatabaseQuery.
*/
public EJBQueryImpl(DatabaseQuery query, EntityManagerImpl entityManager) {
super(query, entityManager);
}
/**
* Build an EJBQueryImpl based on the given jpql string.
*/
public EJBQueryImpl(String jpql, EntityManagerImpl entityManager) {
this(jpql, entityManager, false);
}
/**
* Create an EJBQueryImpl with either a query name or an jpql string.
*
* @param isNamedQuery
* determines whether to treat the queryDescription as jpql or a
* query name.
*/
public EJBQueryImpl(String queryDescription, EntityManagerImpl entityManager, boolean isNamedQuery) {
super(entityManager);
if (isNamedQuery) {
this.queryName = queryDescription;
} else {
if (databaseQuery == null) {
databaseQuery = buildEJBQLDatabaseQuery(queryDescription, entityManager.getActiveSessionIfExists());
}
}
}
/**
* Build a DatabaseQuery from an jpql string.
*
* @param jpql
* @param session
* the session to get the descriptors for this query for.
* @return a DatabaseQuery representing the given jpql.
*/
public static DatabaseQuery buildEJBQLDatabaseQuery(String jpql, AbstractSession session) {
return buildEJBQLDatabaseQuery(null, jpql, session, null, null, session.getDatasourcePlatform().getConversionManager().getLoader());
}
/**
* Build a DatabaseQuery from an JPQL string.
*
* @param jpqlQuery
* the JPQL string.
* @param session
* the session to get the descriptors for this query for.
* @param hints
* a list of hints to be applied to the query.
* @return a DatabaseQuery representing the given jpql.
*/
public static DatabaseQuery buildEJBQLDatabaseQuery(String queryName, String jpqlQuery, AbstractSession session, Enum lockMode, Map hints, ClassLoader classLoader) {
// PERF: Check if the JPQL has already been parsed.
// Only allow queries with default properties to be parse cached.
boolean isCacheable = (queryName == null) && (hints == null);
DatabaseQuery databaseQuery = null;
if (isCacheable) {
databaseQuery = (DatabaseQuery) session.getProject().getJPQLParseCache().get(jpqlQuery);
}
if ((databaseQuery == null) || (!databaseQuery.isPrepared())) {
JPAQueryBuilder queryBuilder = session.getQueryBuilder();
databaseQuery = queryBuilder.buildQuery(jpqlQuery, session);
// If the query uses fetch joins, need to use JPA default of not
// filtering duplicates.
if (databaseQuery.isReadAllQuery()) {
ReadAllQuery readAllQuery = (ReadAllQuery) databaseQuery;
if (readAllQuery.hasJoining() && (readAllQuery.getDistinctState() == ReadAllQuery.DONT_USE_DISTINCT)) {
readAllQuery.setShouldFilterDuplicates(false);
}
} else if (databaseQuery.isModifyQuery()) {
// By default, do not batch modify queries, as row count must be returned.
((ModifyQuery)databaseQuery).setIsBatchExecutionSupported(false);
}
((JPQLCallQueryMechanism) databaseQuery.getQueryMechanism()).getJPQLCall().setIsParsed(true);
// Apply the lock mode.
if (lockMode != null && !lockMode.name().equals(ObjectLevelReadQuery.NONE)) {
if (databaseQuery.isObjectLevelReadQuery()) {
// If setting the lock mode returns true, we were unable to
// set the lock mode, throw an exception.
if (((ObjectLevelReadQuery) databaseQuery).setLockModeType(lockMode.name(), session)) {
throw new PersistenceException(ExceptionLocalization.buildMessage("ejb30-wrong-lock_called_without_version_locking-index", null));
}
} else {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("invalid_lock_query", null));
}
}
// Apply any query hints.
databaseQuery = applyHints(hints, databaseQuery, classLoader, session);
// If a primary key query, switch to read-object to allow cache hit.
if (databaseQuery.isReadAllQuery() && !databaseQuery.isReportQuery() && ((ReadAllQuery)databaseQuery).shouldCheckCache()) {
ReadAllQuery readQuery = (ReadAllQuery)databaseQuery;
if ((readQuery.getContainerPolicy().getContainerClass() == ContainerPolicy.getDefaultContainerClass())
&& (!readQuery.hasHierarchicalExpressions())) {
databaseQuery.checkDescriptor(session);
Expression selectionCriteria = databaseQuery.getSelectionCriteria();
if ((selectionCriteria != null)
&& (databaseQuery.getDescriptor().getObjectBuilder().isPrimaryKeyExpression(true, selectionCriteria, session)
|| (databaseQuery.getDescriptor().getCachePolicy().isIndexableExpression(selectionCriteria, databaseQuery.getDescriptor(), session)))) {
ReadObjectQuery newQuery = new ReadObjectQuery();
newQuery.copyFromQuery(databaseQuery);
databaseQuery = newQuery;
}
}
}
if (isCacheable) {
// Prepare query as hint may cause cloning (but not un-prepare
// as in read-only).
databaseQuery.checkPrepare(session, new DatabaseRecord());
session.getProject().getJPQLParseCache().put(jpqlQuery, databaseQuery);
}
}
return databaseQuery;
}
/**
* Build a ReadAllQuery from a class and sql string.
*/
public static DatabaseQuery buildSQLDatabaseQuery(Class resultClass, String sqlString, ClassLoader classLoader, AbstractSession session) {
return buildSQLDatabaseQuery(resultClass, sqlString, null, classLoader, session);
}
/**
* Build a ReadAllQuery for class and sql string.
*
* @param hints
* a list of hints to be applied to the query.
*/
public static DatabaseQuery buildSQLDatabaseQuery(Class resultClass, String sqlString, Map hints, ClassLoader classLoader, AbstractSession session) {
ReadAllQuery query = new ReadAllQuery(resultClass);
query.setCall(((DatasourcePlatform)session.getPlatform(resultClass)).buildNativeCall(sqlString));
query.setIsUserDefined(true);
// apply any query hints
return applyHints(hints, query, classLoader, session);
}
/**
* Build a DataReadQuery from a sql string.
*/
public static DatabaseQuery buildSQLDatabaseQuery(String sqlString, ClassLoader classLoader, AbstractSession session) {
return buildSQLDatabaseQuery(sqlString, new HashMap(), classLoader, session);
}
/**
* Build a DataReadQuery from a sql string.
*/
public static DatabaseQuery buildSQLDatabaseQuery(String sqlString, Map hints, ClassLoader classLoader, AbstractSession session) {
DataReadQuery query = new DataReadQuery();
query.setResultType(DataReadQuery.AUTO);
query.setSQLString(sqlString);
query.setIsUserDefined(true);
// apply any query hints
return applyHints(hints, query, classLoader, session);
}
/**
* Build a ResultSetMappingQuery from a sql result set mapping name and sql
* string.
*/
public static DatabaseQuery buildSQLDatabaseQuery(String sqlResultSetMappingName, String sqlString, ClassLoader classLoader, AbstractSession session) {
return buildSQLDatabaseQuery(sqlResultSetMappingName, sqlString, null, classLoader, session);
}
/**
* Build a ResultSetMappingQuery from a sql result set mapping name and sql
* string.
*
* @param hints
* a list of hints to be applied to the query.
*/
public static DatabaseQuery buildSQLDatabaseQuery(String sqlResultSetMappingName, String sqlString, Map hints, ClassLoader classLoader, AbstractSession session) {
ResultSetMappingQuery query = new ResultSetMappingQuery();
query.setSQLResultSetMappingName(sqlResultSetMappingName);
query.setCall(((DatasourcePlatform)session.getDatasourcePlatform()).buildNativeCall(sqlString));
query.setIsUserDefined(true);
// apply any query hints
return applyHints(hints, query, classLoader, session);
}
/**
* Set an implementation-specific hint. If the hint name is not recognized,
* it is silently ignored.
*
* @param hintName
* @param value
* @return the same query instance
* @throws IllegalArgumentException
* if the second argument is not valid for the implementation
*/
@Override
public TypedQuery setHint(String hintName, Object value) {
try {
entityManager.verifyOpen();
setHintInternal(hintName, value);
return this;
} catch (RuntimeException e) {
setRollbackOnly();
throw e;
}
}
/**
* Set the lock mode type to be used for the query execution.
*
* @param lockMode
* @throws IllegalStateException
* if not a Java Persistence query language SELECT query
*/
@Override
public EJBQueryImpl setLockMode(LockModeType lockMode) {
return (EJBQueryImpl) super.setLockMode(lockMode);
}
/**
* Non-standard method to return results of a ReadQuery that has a
* containerPolicy that returns objects as a collection rather than a List
*
* @return Collection of results
*/
@Override
public Collection getResultCollection() {
// bug51411440: need to throw IllegalStateException if query
// executed on closed em
this.entityManager.verifyOpenWithSetRollbackOnly();
setAsSQLReadQuery();
propagateResultProperties();
// bug:4297903, check container policy class and throw exception if its
// not the right type
DatabaseQuery query = getDatabaseQueryInternal();
try {
if (query.isReadAllQuery()) {
Class containerClass = ((ReadAllQuery) getDatabaseQueryInternal()).getContainerPolicy().getContainerClass();
if (!Helper.classImplementsInterface(containerClass, ClassConstants.Collection_Class)) {
throw QueryException.invalidContainerClass(containerClass, ClassConstants.Collection_Class);
}
} else if (query.isReadObjectQuery()) {
List resultList = new ArrayList();
Object result = executeReadQuery();
if (result != null) {
resultList.add(executeReadQuery());
}
return resultList;
} else if (!query.isReadQuery()) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_query_for_get_result_collection"));
}
return (Collection) executeReadQuery();
} catch (LockTimeoutException exception) {
throw exception;
} catch (PersistenceException exception) {
setRollbackOnly();
throw exception;
} catch (IllegalStateException exception) {
setRollbackOnly();
throw exception;
} catch (RuntimeException exception) {
setRollbackOnly();
throw new PersistenceException(exception);
}
}
/**
* Non-standard method to return results of a ReadQuery that uses a Cursor.
*
* @return Cursor on results, either a CursoredStream, or ScrollableCursor
*/
@Override
public Cursor getResultCursor() {
// bug51411440: need to throw IllegalStateException if query executed on closed em
this.entityManager.verifyOpenWithSetRollbackOnly();
try {
setAsSQLReadQuery();
propagateResultProperties();
// bug:4297903, check container policy class and throw exception if its
// not the right type
if (getDatabaseQueryInternal() instanceof ReadAllQuery) {
if (!((ReadAllQuery) getDatabaseQueryInternal()).getContainerPolicy().isCursorPolicy()) {
Class containerClass = ((ReadAllQuery) getDatabaseQueryInternal()).getContainerPolicy().getContainerClass();
throw QueryException.invalidContainerClass(containerClass, Cursor.class);
}
} else if (getDatabaseQueryInternal() instanceof ReadObjectQuery) {
// bug:4300879, no support for ReadObjectQuery if a collection is required
throw QueryException.incorrectQueryObjectFound(getDatabaseQueryInternal(), ReadAllQuery.class);
} else if (!(getDatabaseQueryInternal() instanceof ReadQuery)) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_query_for_get_result_collection"));
}
Object result = executeReadQuery();
return (Cursor) result;
} catch (LockTimeoutException e) {
throw e;
} catch (PersistenceException exception) {
setRollbackOnly();
throw exception;
} catch (IllegalStateException exception) {
setRollbackOnly();
throw exception;
} catch (RuntimeException exception) {
setRollbackOnly();
throw new PersistenceException(exception);
}
}
/**
* Execute a query that returns a single result.
*
* @return the result
* @throws jakarta.persistence.EntityNotFoundException
* if there is no result
* @throws jakarta.persistence.NonUniqueResultException
* if more than one result
*/
@Override
public X getSingleResult() {
return (X) super.getSingleResult();
}
/**
* Set the position of the first result to retrieve.
*
* @param startPosition
* position of the first result, numbered from 0
* @return the same query instance
*/
@Override
public EJBQueryImpl setFirstResult(int startPosition) {
return (EJBQueryImpl) super.setFirstResult(startPosition);
}
/**
* Set the flush mode type to be used for the query execution.
*
* @param flushMode
*/
@Override
public EJBQueryImpl setFlushMode(FlushModeType flushMode) {
return (EJBQueryImpl) super.setFlushMode(flushMode);
}
/**
* Set the maximum number of results to retrieve.
*
* @param maxResult
* @return the same query instance
*/
@Override
public EJBQueryImpl setMaxResults(int maxResult) {
return (EJBQueryImpl) super.setMaxResults(maxResult);
}
/**
* Bind an instance of java.util.Calendar to a positional parameter.
*
* @param position
* @param value
* @param temporalType
* @return the same query instance
*/
@Override
public TypedQuery setParameter(int position, Calendar value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
return setParameter(position, convertTemporalType(value, temporalType));
}
/**
* Bind an instance of java.util.Date to a positional parameter.
*
* @param position
* @param value
* @param temporalType
* @return the same query instance
*/
@Override
public TypedQuery setParameter(int position, Date value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
return setParameter(position, convertTemporalType(value, temporalType));
}
/**
* Bind an argument to a positional parameter.
*
* @param position
* @param value
* @return the same query instance
*/
@Override
public TypedQuery setParameter(int position, Object value) {
try {
entityManager.verifyOpen();
setParameterInternal(position, value);
return this;
} catch (RuntimeException e) {
setRollbackOnly();
throw e;
}
}
/**
* Bind an instance of java.util.Calendar to a Parameter object.
*
* @param param
* @param value
* @param temporalType
* @return the same query instance
* @throws IllegalArgumentException
* if position does not correspond to a parameter of the query
*/
@Override
public TypedQuery setParameter(Parameter param, Calendar value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
if (param == null)
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NULL_PARAMETER_PASSED_TO_SET_PARAMETER"));
//bug 402686: type validation
String position = getParameterId(param);
ParameterExpressionImpl parameter = (ParameterExpressionImpl) this.getInternalParameters().get(position);
if (parameter == null ) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NO_PARAMETER_WITH_NAME", new Object[] { param.toString(), this.databaseQuery }));
}
if (!parameter.getParameterType().equals(param.getParameterType())) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("INCORRECT_PARAMETER_TYPE", new Object[] { position, param.getParameterType() }));
}
return this.setParameter(position, value, temporalType);
}
/**
* Bind an instance of java.util.Date to a Parameter object.
*
* @param param
* object
* @param value
* @param temporalType
* @return the same query instance
* @throws IllegalArgumentException
* if position does not correspond to a parameter of the query
*/
@Override
public TypedQuery setParameter(Parameter param, Date value, TemporalType temporalType) {
if (param == null)
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NULL_PARAMETER_PASSED_TO_SET_PARAMETER"));
//bug 402686: type validation
String position = getParameterId(param);
ParameterExpressionImpl parameter = (ParameterExpressionImpl) this.getInternalParameters().get(position);
if (parameter == null ) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NO_PARAMETER_WITH_NAME", new Object[] { param.toString(), this.databaseQuery }));
}
if (!parameter.getParameterType().equals(param.getParameterType())) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("INCORRECT_PARAMETER_TYPE", new Object[] { position, param.getParameterType() }));
}
return this.setParameter(position, value, temporalType);
}
/**
* Set the value of a Parameter object.
*
* @param param
* parameter to be set
* @param value
* parameter value
* @return query instance
* @throws IllegalArgumentException
* if parameter does not correspond to a parameter of the query
*/
@Override
public TypedQuery setParameter(Parameter param, T value) {
if (param == null) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NULL_PARAMETER_PASSED_TO_SET_PARAMETER"));
}
//bug 402686: type validation
String position = getParameterId(param);
ParameterExpressionImpl parameter = (ParameterExpressionImpl) this.getInternalParameters().get(position);
if (parameter == null ) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("NO_PARAMETER_WITH_NAME", new Object[] { param.toString(), this.databaseQuery }));
}
if (!parameter.getParameterType().equals(param.getParameterType())) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("INCORRECT_PARAMETER_TYPE", new Object[] { position, param.getParameterType() }));
}
return this.setParameter(position, value);
}
/**
* Bind an instance of java.util.Calendar to a named parameter.
*
* @param name
* @param value
* @param temporalType
* @return the same query instance
*/
@Override
public TypedQuery setParameter(String name, Calendar value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
return setParameter(name, convertTemporalType(value, temporalType));
}
/**
* Bind an instance of java.util.Date to a named parameter.
*
* @param name
* @param value
* @param temporalType
* @return the same query instance
*/
@Override
public TypedQuery setParameter(String name, Date value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
return setParameter(name, convertTemporalType(value, temporalType));
}
/**
* Bind an argument to a named parameter.
*
* @param name
* the parameter name
* @param value
* @return the same query instance
*/
@Override
public TypedQuery setParameter(String name, Object value) {
try {
entityManager.verifyOpen();
setParameterInternal(name, value, false);
return this;
} catch (RuntimeException e) {
setRollbackOnly();
throw e;
}
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + String.valueOf(this.databaseQuery) + ")";
}
}