org.eclipse.persistence.internal.jpa.JPAQuery 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
// 08/01/2012-2.5 Chris Delahunt
// - 371950: Metadata caching
// 08/24/2012-2.5 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 org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.OptimisticLockException;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import jakarta.persistence.LockModeType;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.EntityResult;
import org.eclipse.persistence.queries.SQLResultSetMapping;
import org.eclipse.persistence.queries.StoredProcedureCall;
import org.eclipse.persistence.sessions.Session;
/**
* Purpose:
* A JPA placeholder Query object to store JPQL strings so that processing the string is delayed
* until Login.
*
* @author Chris Delahunt
* @since TopLink Essentials
*/
public class JPAQuery extends DatabaseQuery {
private String lockMode;
private String jpqlString;
private String sqlString;
private StoredProcedureCall call;
private String resultClassName;
private List resultClassNames;
private List resultSetMappingNames;
private Map hints;
public JPAQuery() {
}
public JPAQuery(String jpqlString) {
this.jpqlString=jpqlString;
}
/**
* JPQL
*/
public JPAQuery(String name, String jpqlString, String lockMode, Map hints) {
this.name = name;
this.jpqlString = jpqlString;
this.flushOnExecute = null;
this.hints = hints;
this.lockMode = lockMode;
if (lockMode == null) {
this.lockMode = "NONE";
}
}
/*
* SQL returning an entity
*/
public JPAQuery(String queryName, String sqlString, Map hints) {
this.name = queryName;
this.sqlString = sqlString;
this.flushOnExecute = null;
this.hints = hints;
this.lockMode = null;
}
/*
* Stored Proc returning an Entity
*/
public JPAQuery(String queryName, StoredProcedureCall call, Map hints) {
this.name = queryName;
this.call = call;
this.flushOnExecute = null;
this.hints = hints;
this.lockMode = null;
}
public void addResultClassNames(String className) {
if (resultClassNames == null) {
resultClassNames = new ArrayList<>();
}
this.resultClassNames.add(className);
}
public void addResultSetMapping(String resultSetMapping){
if (resultSetMappingNames == null) {
resultSetMappingNames = new ArrayList<>();
}
this.resultSetMappingNames.add(resultSetMapping);
}
/**
* INTERNAL:
* This should never be called and is only here because it is needed as an extension
* to DatabaseQuery. Perhaps exception should be thrown to warn users, but for now
* it will execute the resulting query instead, this allows JPA style queries to be executed
* on a normal EclipseLink Session.
*/
@Override
public Object executeDatabaseQuery() throws DatabaseException, OptimisticLockException{
return getSession().executeQuery(getDatabaseQuery());
}
public DatabaseQuery getDatabaseQuery() {
return (DatabaseQuery)getProperty("databasequery");
}
/**
* INTERNAL:
* For table per tenant queries the descriptor list will extracted from
* parsing the jpql query and cached here.
*/
@Override
public List getDescriptors() {
return descriptors;
}
/**
* Return the JPA query hints.
*/
public Map getHints(){
return hints;
}
/**
* Return the JPQL string.
*/
@Override
public String getJPQLString(){
return jpqlString;
}
/**
* Return true if this query is a jpql query.
*/
public boolean isJPQLQuery() {
return jpqlString != null;
}
/**
* Return true if this query is an sql query.
*/
public boolean isSQLQuery() {
return sqlString != null;
}
/**
* INTERNAL:
* Generate the DatabaseQuery query from the JPA named query.
*/
@Override
public void prepare() {
DatabaseQuery query = null;
ClassLoader loader = session.getDatasourcePlatform().getConversionManager().getLoader();
if (isSQLQuery()) {
query = processSQLQuery(getSession());
} else if (isJPQLQuery()) {
query = processJPQLQuery(getSession());
} else if (call != null) {
query = processStoredProcedureQuery(getSession());
if (call.hasParameters() ) {
//convert the type in the parameters; query.convertClassNamesToClasses does not cascade to the call
for (Object value: call.getParameters()) {
if (value instanceof Object[]) {
//must be inout type, and the out portion is a DatabaseField
((DatabaseField) ((Object[])value)[1]).convertClassNamesToClasses(loader);
value = ((Object[])value)[0];
}
if (value instanceof DatabaseField) {
((DatabaseField) value).convertClassNamesToClasses(loader);
}
}
}
}
// Make sure all class names have been converted.
query.convertClassNamesToClasses(loader);
setDatabaseQuery(query);
}
/**
* INTERNAL:
* Convert the JPA query into a DatabaseQuery.
*/
public DatabaseQuery processJPQLQuery(Session session){
ClassLoader classloader = session.getDatasourcePlatform().getConversionManager().getLoader();
LockModeType lockModeEnum = null;
// Must handle errors if a JPA 2.0 option is used in JPA 1.0.
try {
lockModeEnum = LockModeType.valueOf(lockMode);
} catch (Exception ignore) {
// Ignore JPA 2.0 in JPA 1.0, reverts to no lock.
}
DatabaseQuery ejbquery = EJBQueryImpl.buildEJBQLDatabaseQuery(getName(), jpqlString, (AbstractSession) session, lockModeEnum, hints, classloader);
ejbquery.setName(getName());
return ejbquery;
}
/**
* INTERNAL:
* Convert the SQL string into a DatabaseQuery.
*/
public DatabaseQuery processSQLQuery(Session session){
DatabaseQuery query = null;
ClassLoader loader = session.getDatasourcePlatform().getConversionManager().getLoader();
if (resultClassName != null) {
Class> clazz = session.getDatasourcePlatform().getConversionManager().convertClassNameToClass(resultClassName);
query = EJBQueryImpl.buildSQLDatabaseQuery(clazz, sqlString, hints, loader, (AbstractSession)session);
} else if (resultSetMappingNames != null) {
query = EJBQueryImpl.buildSQLDatabaseQuery(resultSetMappingNames.get(0), sqlString, hints, loader, (AbstractSession)session);
} else {
// Neither a resultClass or resultSetMapping is specified so place in a temp query on the session
query = EJBQueryImpl.buildSQLDatabaseQuery(sqlString, hints, loader, (AbstractSession)session);
}
query.setName(this.getName());
return query;
}
/**
* INTERNAL:
* Convert the StoredProc call into a DatabaseQuery.
*/
public DatabaseQuery processStoredProcedureQuery(Session session){
DatabaseQuery query = null;
ClassLoader loader = session.getDatasourcePlatform().getConversionManager().getLoader();
if (resultClassNames != null) {
List resultSetMappings = new ArrayList<>();
for (String resultClass : resultClassNames) {
SQLResultSetMapping mapping = new SQLResultSetMapping(resultClass);
EntityResult entityResult = new EntityResult(resultClass);
mapping.addResult(entityResult);
resultSetMappings.add(mapping);
}
query = StoredProcedureQueryImpl.buildResultSetMappingQuery(resultSetMappings, call, hints, loader, (AbstractSession)session);
} else if (resultSetMappingNames != null) {
query = StoredProcedureQueryImpl.buildResultSetMappingNameQuery(resultSetMappingNames, call, hints, loader, (AbstractSession)session);
} else if (resultClassName != null) {
Class> clazz = session.getDatasourcePlatform().getConversionManager().convertClassNameToClass(resultClassName);
query = StoredProcedureQueryImpl.buildStoredProcedureQuery(clazz, call, hints, loader, (AbstractSession)session);
} else {
// Neither a resultClass or resultSetMapping is specified so place in a temp query on the session.
if (call.isStoredFunctionCall() || call.isStoredPLSQLProcedureCall()) {
// If it is a function (plsql or not) or plsql procedure use the data read query.
query = StoredProcedureQueryImpl.buildStoredProcedureQuery(call, hints, loader, (AbstractSession)session);
} else {
// Otherwise use a result set mapping query for stored procedure calls so users can use the execute
// method on it (JPA 2.1 API). Will return the same result, that is, Object[] in this case.
query = StoredProcedureQueryImpl.buildResultSetMappingQuery(new ArrayList<>(), call, hints, loader, (AbstractSession)session);
}
}
query.setName(getName());
return query;
}
public void setDatabaseQuery(DatabaseQuery databaseQuery) {
setProperty("databasequery", databaseQuery);
}
/**
* INTERNAL:
* For table per tenant queries the descriptor list will extracted from
* parsing the jpql query and cached here.
*/
public void setDescriptors(List descriptors) {
this.descriptors = descriptors;
}
public void setHints(Map hints){
this.hints = hints;
}
@Override
public void setJPQLString(String jpqlString){
this.jpqlString = jpqlString;
}
public void setResultClassName(String className){
this.resultClassName = className;
}
public void setResultSetMappings(List resultSetMappings){
this.resultSetMappingNames = resultSetMappings;
}
}