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 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.
*
* @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.
*
* @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