org.eclipse.persistence.internal.jpa.StoredProcedureQueryImpl 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) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022 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
// 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
// 06/20/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 07/13/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 08/24/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 09/13/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 09/27/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 11/05/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
package org.eclipse.persistence.internal.jpa;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.LockTimeoutException;
import jakarta.persistence.Parameter;
import jakarta.persistence.ParameterMode;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.QueryTimeoutException;
import jakarta.persistence.StoredProcedureQuery;
import jakarta.persistence.TemporalType;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.internal.databaseaccess.*;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall.ParameterType;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.jpa.querydef.ParameterExpressionImpl;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ResultSetMappingQuery;
import org.eclipse.persistence.queries.SQLResultSetMapping;
import org.eclipse.persistence.queries.StoredProcedureCall;
/**
* Concrete JPA query class. The JPA query wraps a StoredProcesureQuery which
* is executed.
*/
public class StoredProcedureQueryImpl extends QueryImpl implements StoredProcedureQuery {
protected boolean hasMoreResults;
// Call will be returned from an execute. From it you can get the result set.
protected DatabaseCall executeCall;
protected Statement executeStatement;
protected int executeResultSetIndex = -1;
// If the procedure returns output cursor(s), we'll use them to satisfy
// getResultList and getSingleResult calls so keep track of our index.
protected int outputCursorIndex = -1;
protected boolean isOutputCursorResultSet = false;
/**
* Base constructor for StoredProcedureQueryImpl. Initializes basic variables.
*/
protected StoredProcedureQueryImpl(EntityManagerImpl entityManager) {
super(entityManager);
}
/**
* Create an StoredProcedureQueryImpl with a DatabaseQuery.
*/
public StoredProcedureQueryImpl(DatabaseQuery query, EntityManagerImpl entityManager) {
super(query, entityManager);
}
/**
* Create an StoredProcedureQueryImpl with a query name.
*/
public StoredProcedureQueryImpl(String name, EntityManagerImpl entityManager) {
super(entityManager);
this.queryName = name;
}
/**
* Build the given result set into a list objects. Assumes there is an
* execute call available and therefore should not be called unless an
* execute statement was issued by the user.
*/
protected List buildResultRecords(ResultSet resultSet) {
try {
AbstractSession session = (AbstractSession) getActiveSession();
DatabaseAccessor accessor = (DatabaseAccessor) executeCall.getQuery().getAccessor();
executeCall.setFields(null);
executeCall.matchFieldOrder(resultSet, accessor, session);
ResultSetMetaData metaData = resultSet.getMetaData();
List result = new Vector<>();
while (resultSet.next()) {
result.add(accessor.fetchRow(executeCall.getFields(), executeCall.getFieldsArray(), resultSet, metaData, session));
}
// The result set must be closed in case the statement is cached and not closed.
resultSet.close();
return result;
} catch (Exception e) {
setRollbackOnly();
throw new PersistenceException(e);
}
}
/**
* Build a ResultSetMappingQuery from a sql result set mapping name and a
* stored procedure call.
*
* This is called from a named stored procedure that employs result set
* mapping name(s) which should be available from the session.
*/
public static DatabaseQuery buildResultSetMappingNameQuery(List resultSetMappingNames, StoredProcedureCall call) {
ResultSetMappingQuery query = new ResultSetMappingQuery();
call.setReturnMultipleResultSetCollections(call.hasMultipleResultSets() && ! call.isMultipleCursorOutputProcedure());
query.setCall(call);
query.setIsUserDefined(true);
query.setSQLResultSetMappingNames(resultSetMappingNames);
return query;
}
/**
* Build a ResultSetMappingQuery from a sql result set mapping name and a
* stored procedure call.
*
* This is called from a named stored procedure that employs result set
* mapping name(s) which should be available from the session.
*/
public static DatabaseQuery buildResultSetMappingNameQuery(List resultSetMappingNames, StoredProcedureCall call, Map hints, ClassLoader classLoader, AbstractSession session) {
// apply any query hints
DatabaseQuery hintQuery = applyHints(hints, buildResultSetMappingNameQuery(resultSetMappingNames, call) , classLoader, session);
// apply any query arguments
applyArguments(call, hintQuery);
return hintQuery;
}
/**
* Build a ResultSetMappingQuery from the sql result set mappings given
* a stored procedure call.
*
* This is called from a named stored procedure query that employs result
* class name(s). The resultSetMappings are build from these class name(s)
* and are not available from the session.
*/
public static DatabaseQuery buildResultSetMappingQuery(List resultSetMappings, StoredProcedureCall call) {
ResultSetMappingQuery query = new ResultSetMappingQuery();
call.setReturnMultipleResultSetCollections(call.hasMultipleResultSets() && ! call.isMultipleCursorOutputProcedure());
query.setCall(call);
query.setIsUserDefined(true);
query.setSQLResultSetMappings(resultSetMappings);
return query;
}
/**
* Build a ResultSetMappingQuery from the sql result set mappings given
* a stored procedure call.
*
* This is called from a named stored procedure query that employs result
* class name(s). The resultSetMappings are build from these class name(s)
* and are not available from the session.
*/
public static DatabaseQuery buildResultSetMappingQuery(List resultSetMappings, StoredProcedureCall call, Map hints, ClassLoader classLoader, AbstractSession session) {
// apply any query hints
DatabaseQuery hintQuery = applyHints(hints, buildResultSetMappingQuery(resultSetMappings, call), classLoader, session);
// apply any query arguments
applyArguments(call, hintQuery);
return hintQuery;
}
/**
* Build a ReadAllQuery from a class and stored procedure call.
*/
public static DatabaseQuery buildStoredProcedureQuery(Class> resultClass, StoredProcedureCall call, Map hints, ClassLoader classLoader, AbstractSession session) {
DatabaseQuery query = new ReadAllQuery(resultClass);
query.setCall(call);
query.setIsUserDefined(true);
// apply any query hints
query = applyHints(hints, query, classLoader, session);
// apply any query arguments
applyArguments(call, query);
return query;
}
/**
* Build a DataReadQuery with the stored procedure call given.
*/
public static DatabaseQuery buildStoredProcedureQuery(StoredProcedureCall call, Map hints, ClassLoader classLoader, AbstractSession session) {
DataReadQuery query = new DataReadQuery();
query.setResultType(DataReadQuery.AUTO);
query.setCall(call);
query.setIsUserDefined(true);
// apply any query hints
DatabaseQuery hintQuery = applyHints(hints, query, classLoader, session);
// apply any query arguments
applyArguments(call, hintQuery);
return hintQuery;
}
/**
* Build a ResultSetMappingQuery from a sql result set mapping name and a
* stored procedure call.
*/
public static DatabaseQuery buildStoredProcedureQuery(String sqlResultSetMappingName, StoredProcedureCall call, Map hints, ClassLoader classLoader, AbstractSession session) {
ResultSetMappingQuery query = new ResultSetMappingQuery();
query.setSQLResultSetMappingName(sqlResultSetMappingName);
query.setCall(call);
query.setIsUserDefined(true);
// apply any query hints
DatabaseQuery hintQuery = applyHints(hints, query, classLoader, session);
// apply any query arguments
applyArguments(call, hintQuery);
return hintQuery;
}
/**
* Call this method to close any open connections to the database.
*/
@Override
public void close() {
if (executeCall != null) {
DatabaseQuery query = executeCall.getQuery();
AbstractSession session = query.getSession();
// Release the accessors acquired for the query.
for (Accessor accessor : query.getAccessors()) {
session.releaseReadConnection(accessor);
}
try {
if (executeStatement != null) {
DatabaseAccessor accessor = (DatabaseAccessor) query.getAccessor();
accessor.releaseStatement(executeStatement, query.getSQLString(), executeCall, session);
}
} catch (SQLException exception) {
// Catch the exception and log a message.
session.log(SessionLog.WARNING, SessionLog.CONNECTION, "exception_caught_closing_statement", exception);
}
}
executeCall = null;
executeStatement = null;
}
/**
* Returns true if the first result corresponds to a result set, and false
* if it is an update count or if there are no results other than through
* INOUT and OUT parameters, if any.
* @return true if first result corresponds to result set
* @throws QueryTimeoutException if the query execution exceeds the query
* timeout value set and only the statement is rolled back
* @throws PersistenceException if the query execution exceeds the query
* timeout value set and the transaction is rolled back
*/
@Override
public boolean execute() {
try {
entityManager.verifyOpen();
if (! getDatabaseQueryInternal().isResultSetMappingQuery()) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_execute"));
}
getResultSetMappingQuery().setIsExecuteCall(true);
executeCall = (DatabaseCall) executeReadQuery();
executeStatement = executeCall.getStatement();
// Add this query to the entity manager open queries list.
// The query will be closed in the following cases:
// Within a transaction:
// - on commit
// - on rollback
// Outside of a transaction:
// - em close
// Other safeguards, we will close the query if/when
// - we hit the end of the results.
// - this query is garbage collected (finalize method)
//
// Deferring closing the call avoids having to go through all the
// results now (and building all the result objects) and things
// remain on a as needed basis from the statement.
entityManager.addOpenQuery(this);
hasMoreResults = executeCall.getExecuteReturnValue();
// If execute returned false but we have output cursors then return
// true and build the results from the output cursors.
if (!hasMoreResults && getCall().hasOutputCursors()) {
hasMoreResults = true;
outputCursorIndex = 0;
isOutputCursorResultSet = true;
}
return hasMoreResults;
} catch (LockTimeoutException exception) {
throw exception;
} catch (PersistenceException exception) {
setRollbackOnly();
throw exception;
} catch (IllegalStateException e){
setRollbackOnly();
throw e;
} catch (RuntimeException exception) {
setRollbackOnly();
throw new PersistenceException(exception);
}
}
/**
* Execute an update or delete statement (from a stored procedure query).
* @return the number of entities updated or deleted
*/
@Override
public int executeUpdate() {
try {
// Need to throw TransactionRequiredException if there is no active transaction
entityManager.checkForTransaction(true);
// Legacy: we could have a data read query or a read all query, so
// clearly we shouldn't be executing an update on it. As of JPA 2.1
// API we always create a result set mapping query to interact with
// a stored procedure.
// Also if the result set mapping query has result set mappings
// defined, then it's clearly expecting result sets and we can be
// preemptive in throwing an exception.
if (! getDatabaseQueryInternal().isResultSetMappingQuery() || getResultSetMappingQuery().hasResultSetMappings()) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_execute_update"));
}
// If the return value is true indicating a result set then throw an exception.
if (execute()) {
if (getActiveSession().getPlatform().isJDBCExecuteCompliant()) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_execute_update"));
} else {
return getUpdateCount();
}
} else {
return getUpdateCount();
}
} catch (LockTimeoutException exception) {
throw exception;
} catch (PersistenceException e) {
setRollbackOnly();
throw e;
} catch (IllegalStateException e){
setRollbackOnly();
throw e;
} catch (RuntimeException exception) {
setRollbackOnly();
throw new PersistenceException(exception);
} finally {
close(); // Close the connection once we're done.
}
}
/**
* Finalize method in case the query is not closed.
*/
@Override
public void finalize() {
close();
}
/**
* Return the stored procedure call associated with this query.
*/
protected StoredProcedureCall getCall() {
return (StoredProcedureCall) getDatabaseQueryInternal().getCall();
}
/**
* Return the internal map of parameters.
*/
@Override
protected Map> getInternalParameters() {
if (parameters == null) {
parameters = new HashMap>();
int index = 0;
for (Object parameter : getCall().getParameters()) {
ParameterType parameterType = getCall().getParameterTypes().get(index);
String argumentName = getCall().getProcedureArgumentNames().get(index);
DatabaseField field = null;
if (parameterType == ParameterType.INOUT) {
field = (DatabaseField) ((Object[]) parameter)[0];
} else if (parameterType == ParameterType.IN) {
field = (DatabaseField) parameter;
} else if (parameterType == ParameterType.OUT || parameterType == ParameterType.OUT_CURSOR) {
if (parameter instanceof OutputParameterForCallableStatement) {
field = ((OutputParameterForCallableStatement) parameter).getOutputField();
} else {
field = (DatabaseField) parameter;
}
}
// If field is not null (one we care about) then add it, otherwise continue.
if (field != null) {
// If the argument name is null then it is a positional parameter.
if (argumentName == null) {
parameters.put(field.getName(), new ParameterExpressionImpl(null, field.getType(), Integer.parseInt(field.getName())));
} else {
parameters.put(field.getName(), new ParameterExpressionImpl(null, field.getType(), field.getName()));
}
}
++index;
}
}
return parameters;
}
/**
* Used to retrieve the values passed back from the procedure through INOUT
* and OUT parameters. For portability, all results corresponding to result
* sets and update counts must be retrieved before the values of output
* parameters.
* @param position parameter position
* @return the result that is passed back through the parameter
* @throws IllegalArgumentException if the position does not correspond to a
* parameter of the query or is not an INOUT or OUT parameter
*/
@Override
public Object getOutputParameterValue(int position) {
entityManager.verifyOpen();
if (isValidCallableStatement()) {
try {
Object obj = executeCall.getOutputParameterValue((CallableStatement) executeStatement, position - 1, entityManager.getAbstractSession());
if (obj instanceof ResultSet) {
// If a result set is returned we have to build the objects.
return getResultSetMappingQuery().buildObjectsFromRecords(buildResultRecords((ResultSet) obj), ++executeResultSetIndex);
} else {
return obj;
}
} catch (Exception exception) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("jpa21_invalid_parameter_position", new Object[] { position, exception.getMessage() }), exception);
}
}
return null;
}
/**
* Used to retrieve the values passed back from the procedure through INOUT
* and OUT parameters. For portability, all results corresponding to result
* sets and update counts must be retrieved before the values of output
* parameters.
* @param parameterName name of the parameter as registered or specified in
* metadata
* @return the result that is passed back through the parameter
* @throws IllegalArgumentException if the parameter name does not
* correspond to a parameter of the query or is not an INOUT or OUT parameter
*/
@Override
public Object getOutputParameterValue(String parameterName) {
entityManager.verifyOpen();
if (isValidCallableStatement()) {
try {
Object obj = executeCall.getOutputParameterValue((CallableStatement) executeStatement, parameterName, entityManager.getAbstractSession());
if (obj instanceof ResultSet) {
// If a result set is returned we have to build the objects.
return getResultSetMappingQuery().buildObjectsFromRecords(buildResultRecords((ResultSet) obj), ++executeResultSetIndex);
}
return obj;
} catch (Exception exception) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("jpa21_invalid_parameter_name", new Object[] { parameterName, exception.getMessage() }), exception);
}
}
return null;
}
private boolean hasPositionalParameters() {
for (Parameter parameter: this.getParameters()) {
if (parameter.getName() != null) {
return false;
}
}
return true;
}
/**
* Execute the query and return the query results as a List.
* @return a list of the results
*/
@Override
public List getResultList() {
// bug51411440: need to throw IllegalStateException if query
// executed on closed em
this.entityManager.verifyOpenWithSetRollbackOnly();
try {
// If there is no execute statement, the user has not called
// execute and is simply calling getResultList directly on the query.
if (executeStatement == null) {
// If it's not a result set mapping query (as of JPA 2.1 we
// always create a result set mapping query to interact with a
// stored procedure) then throw an exception.
if (! getDatabaseQueryInternal().isResultSetMappingQuery()) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_get_result_list"));
}
// If the return value is false indicating no result set then throw an exception.
if (execute()) {
return getResultList();
} else {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_get_result_list"));
}
} else {
if (hasMoreResults()) {
if (isOutputCursorResultSet) {
// Return result set list for the current outputCursorIndex.
List results = null;
if (hasPositionalParameters()) {
results = (List) getOutputParameterValue(getCall().getOutputCursors().get(outputCursorIndex++).getIndex() + 1);
} else {
results = (List) getOutputParameterValue(getCall().getOutputCursors().get(outputCursorIndex++).getName());
}
// Update the hasMoreResults flag.
hasMoreResults = (outputCursorIndex < getCall().getOutputCursors().size());
return results;
} else {
// Build the result records first.
List result = buildResultRecords(executeStatement.getResultSet());
// Move the result pointer.
moveResultPointer();
return getResultSetMappingQuery().buildObjectsFromRecords(result, ++executeResultSetIndex);
}
} else {
return null;
}
}
} catch (LockTimeoutException e) {
throw e;
} catch (PersistenceException e) {
setRollbackOnly();
throw e;
} catch (IllegalStateException e) {
setRollbackOnly();
throw e;
} catch (Exception e) {
setRollbackOnly();
throw new PersistenceException(e);
}
}
/**
* Return the ResultSetMappingQuery for this stored procedure query.
* NOTE: Methods assumes associated database query is a ResultSetMappingQuery.
*/
protected ResultSetMappingQuery getResultSetMappingQuery() {
if (executeCall != null) {
return (ResultSetMappingQuery) executeCall.getQuery();
} else {
return (ResultSetMappingQuery) getDatabaseQuery();
}
}
/**
* Execute the query and return the single query result.
* @return a single result object.
*/
@Override
public Object getSingleResult() {
// bug51411440: need to throw IllegalStateException if query
// executed on closed em
this.entityManager.verifyOpenWithSetRollbackOnly();
try {
// If there is no execute statement, the user has not called
// execute and is simply calling getSingleResult directly on the query.
if (executeStatement == null) {
// If it's not a result set mapping query (as of JPA 2.1 we
// always create a result set mapping query to interact with a
// stored procedure) then throw an exception.
if (! getDatabaseQueryInternal().isResultSetMappingQuery()) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_get_single_result"));
}
// If the return value is true indicating a result set then
// build and return the single result.
if (execute()) {
return getSingleResult();
} else {
throw new IllegalStateException(ExceptionLocalization.buildMessage("incorrect_spq_query_for_get_result_list"));
}
} else {
if (hasMoreResults()) {
// Build the result records first.
List results;
if (isOutputCursorResultSet) {
// Return result set list for the current outputCursorIndex.
if (hasPositionalParameters()) {
results = (List) getOutputParameterValue(getCall().getOutputCursors().get(outputCursorIndex++).getIndex() + 1);
} else {
results = (List) getOutputParameterValue(getCall().getOutputCursors().get(outputCursorIndex++).getName());
}
// Update the hasMoreResults flag.
hasMoreResults = (outputCursorIndex < getCall().getOutputCursors().size());
} else {
// Build the result records first.
List result = buildResultRecords(executeStatement.getResultSet());
// Move the result pointer.
moveResultPointer();
results = getResultSetMappingQuery().buildObjectsFromRecords(result, ++executeResultSetIndex);
}
if (results.size() > 1) {
throwNonUniqueResultException(ExceptionLocalization.buildMessage("too_many_results_for_get_single_result", null));
} else if (results.isEmpty()) {
throwNoResultException(ExceptionLocalization.buildMessage("no_entities_retrieved_for_get_single_result", null));
}
// TODO: if hasMoreResults is true, we 'could' and maybe should throw an exception here.
return results.get(0);
} else {
return null;
}
}
} catch (LockTimeoutException e) {
throw e;
} catch (PersistenceException e) {
setRollbackOnly();
throw e;
} catch (IllegalStateException e) {
setRollbackOnly();
throw e;
} catch (Exception e) {
setRollbackOnly();
throw new PersistenceException(e);
} finally {
close(); // Close the connection once we're done.
}
}
/**
* Returns the update count or -1 if there is no pending result
* or if the next result is not an update count.
* @return update count or -1 if there is no pending result or
* if the next result is not an update count
* @throws QueryTimeoutException if the query execution exceeds
* the query timeout value set and only the statement is
* rolled back
* @throws PersistenceException if the query execution exceeds
* the query timeout value set and the transaction
* is rolled back
*/
@Override
public int getUpdateCount() {
entityManager.verifyOpenWithSetRollbackOnly();
if (executeStatement != null) {
try {
int updateCount = executeStatement.getUpdateCount();
// Moving the result pointer when -1 is reached doesn't seem
// to be an issue for the jbdc driver, however as a safeguard,
// once -1 is reached don't bother trying to move the pointer
// as there is no need to do so.
if (updateCount > -1) {
moveResultPointer();
}
return updateCount;
} catch (SQLException e) {
throw getDetailedException(DatabaseException.sqlException(e, executeCall, executeCall.getQuery().getAccessor(), executeCall.getQuery().getSession(), false));
}
}
return -1;
}
/**
* Returns true if the next result corresponds to a result set, and false if
* it is an update count or if there are no results other than through INOUT
* and OUT parameters, if any.
*
* @return true if next result corresponds to result set
* @throws QueryTimeoutException if the query execution exceeds the query
* timeout value set and only the statement is rolled back
* @throws PersistenceException if the query execution exceeds the query
* timeout value set and the transaction is rolled back
*/
@Override
public boolean hasMoreResults() {
entityManager.verifyOpen();
return hasMoreResults;
}
/**
* Returns true if the execute statement for this query is 1) not null (i.e.
* query has been executed and 2) is an instance of callable statement,
* meaning there are out parameters associated with it.
*/
protected boolean isValidCallableStatement() {
if (executeStatement == null) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("jpa21_invalid_call_on_un_executed_query"));
}
if (! (executeStatement instanceof CallableStatement)) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("jpa21_invalid_call_with_no_output_parameters"));
}
return true;
}
/**
* INTERNAL:
* Move the pointer up and update our has more results flag.
* Once there are no result sets left, this will always return false.
*/
private void moveResultPointer() {
try {
hasMoreResults = executeStatement.getMoreResults();
} catch (SQLException e) {
// swallow it.
hasMoreResults = false;
}
}
/**
* Register a positional parameter. All positional parameters must be
* registered.
*
* @param position parameter position
* @param type type of the parameter
* @param mode parameter mode
* @return the same query instance
*/
@Override
@SuppressWarnings({"rawtypes"})
public StoredProcedureQuery registerStoredProcedureParameter(int position, Class type, ParameterMode mode) {
entityManager.verifyOpenWithSetRollbackOnly();
StoredProcedureCall call = (StoredProcedureCall) getDatabaseQuery().getCall();
if (mode.equals(ParameterMode.IN)) {
call.addUnamedArgument(String.valueOf(position), type);
} else if (mode.equals(ParameterMode.OUT)) {
call.addUnamedOutputArgument(String.valueOf(position), type);
} else if (mode.equals(ParameterMode.INOUT)) {
call.addUnamedInOutputArgument(String.valueOf(position), String.valueOf(position), type);
} else if (mode.equals(ParameterMode.REF_CURSOR)) {
call.useUnnamedCursorOutputAsResultSet(position);
}
// Force a re-calculate of the parameters.
this.parameters = null;
return this;
}
/**
* Register a named parameter. When using parameter names, all parameters
* must be registered in the order in which they occur in the parameter list
* of the stored procedure.
*
* @param parameterName name of the parameter as registered or
* specified in metadata
* @param type type of the parameter
* @param mode parameter mode
* @return the same query instance
*/
@Override
@SuppressWarnings({"rawtypes"})
public StoredProcedureQuery registerStoredProcedureParameter(String parameterName, Class type, ParameterMode mode) {
entityManager.verifyOpenWithSetRollbackOnly();
StoredProcedureCall call = (StoredProcedureCall) getDatabaseQuery().getCall();
if (mode.equals(ParameterMode.IN)) {
call.addNamedArgument(parameterName, parameterName, type);
} else if (mode.equals(ParameterMode.OUT)) {
call.addNamedOutputArgument(parameterName, parameterName, type);
} else if (mode.equals(ParameterMode.INOUT)) {
call.addNamedInOutputArgument(parameterName, parameterName, parameterName, type);
} else if (mode.equals(ParameterMode.REF_CURSOR)) {
call.useNamedCursorOutputAsResultSet(parameterName);
}
// Force a re-calculate of the parameters.
this.parameters = null;
return this;
}
/**
* 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 StoredProcedureQueryImpl setFirstResult(int startPosition) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("operation_not_supported", new Object[]{"setFirstResult", "StoredProcedureQuery"}));
}
/**
* Set the flush mode type to be used for the query execution.
* The flush mode type applies to the query regardless of the
* flush mode type in use for the entity manager.
* @param flushMode flush mode
* @return the same query instance
*/
@Override
public StoredProcedureQueryImpl setFlushMode(FlushModeType flushMode) {
return (StoredProcedureQueryImpl) super.setFlushMode(flushMode);
}
/**
* Set a query property or hint. The hints elements may be used to specify
* query properties and hints. Properties defined by this specification must
* be observed by the provider. Vendor-specific hints that are not
* recognized by a provider must be silently ignored. Portable applications
* should not rely on the standard timeout hint. Depending on the database
* in use, this hint may or may not be observed.
*
* @param hintName name of the property or hint
* @param value value for the property or hint
* @return the same query instance
* @throws IllegalArgumentException if the second argument is not valid for
* the implementation
*/
@Override
public StoredProcedureQuery 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 StoredProcedureQueryImpl setLockMode(LockModeType lockMode) {
return (StoredProcedureQueryImpl) super.setLockMode(lockMode);
}
/**
* Set the maximum number of results to retrieve.
*
* @return the same query instance
*/
@Override
public StoredProcedureQueryImpl setMaxResults(int maxResult) {
throw new IllegalStateException(ExceptionLocalization.buildMessage("operation_not_supported", new Object[]{"setMaxResults", "StoredProcedureQuery"}));
}
/**
* Bind an instance of java.util.Calendar to a positional parameter.
*
* @return the same query instance
* @throws IllegalArgumentException if position does not correspond to a
* positional parameter of the query or if the value argument is of
* incorrect type
*/
@Override
public StoredProcedureQuery 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.
*
* @return the same query instance
* @throws IllegalArgumentException if position does not correspond to a
* positional parameter of the query or if the value argument is of
* incorrect type
*/
@Override
public StoredProcedureQuery setParameter(int position, Date value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
return setParameter(position, convertTemporalType(value, temporalType));
}
/**
* Bind an argument to a positional parameter.
*
* @return the same query instance
* @throws IllegalArgumentException if position does not correspond to a
* positional parameter of the query or if the argument is of incorrect type
*/
@Override
public StoredProcedureQuery 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.
*
* @return the same query instance
* @throws IllegalArgumentException if the parameter does not correspond to
* a parameter of the query
*/
@Override
public StoredProcedureQuery setParameter(Parameter param, Calendar 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);
}
/**
* Bind an instance of java.util.Date to a Parameter object.
*
* @return the same query instance
* @throws IllegalArgumentException if the parameter does not correspond to
* a parameter of the query
*/
@Override
public StoredProcedureQuery 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);
}
/**
* Bind the value of a Parameter object.
*
* @return the same query instance
* @throws IllegalArgumentException if the parameter does not correspond to
* a parameter of the query
*/
@Override
public StoredProcedureQuery 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.
*
* @return the same query instance
* @throws IllegalArgumentException if the parameter name does not
* correspond to a parameter of the query or if the value argument is of
* incorrect type
*/
@Override
public StoredProcedureQuery 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.
*
* @return the same query instance
* @throws IllegalArgumentException if the parameter name does not
* correspond to a parameter of the query or if the value argument is of
* incorrect type
*/
@Override
public StoredProcedureQuery setParameter(String name, Date value, TemporalType temporalType) {
entityManager.verifyOpenWithSetRollbackOnly();
return setParameter(name, convertTemporalType(value, temporalType));
}
/**
* Bind an argument to a named parameter.
*
* @return the same query instance
* @throws IllegalArgumentException if the parameter name does not
* correspond to a parameter of the query or if the argument is of incorrect
* type
*/
@Override
public StoredProcedureQuery setParameter(String name, Object value) {
try {
entityManager.verifyOpen();
setParameterInternal(name, value, false);
return this;
} catch (RuntimeException e) {
setRollbackOnly();
throw e;
}
}
/**
* Bind an argument to a named or indexed parameter.
*
* @param name
* the parameter name
* @param value
* to bind
* @param isIndex
* defines if index or named
*/
@Override
protected void setParameterInternal(String name, Object value, boolean isIndex) {
Parameter> parameter = this.getInternalParameters().get(name);
StoredProcedureCall call = (StoredProcedureCall) getDatabaseQuery().getCall();
if (parameter == null) {
if (isIndex) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("ejb30-wrong-argument-index", new Object[] { name, call.getProcedureName() }));
} else {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("ejb30-wrong-argument-name", new Object[] { name, call.getProcedureName() }));
}
}
if (!isValidActualParameter(value, parameter.getParameterType())) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("ejb30-incorrect-parameter-type", new Object[] { name, value.getClass(), parameter.getParameterType(), call.getProcedureName() }));
}
this.parameterValues.put(name, value);
}
}