HHH-8415 - Throw exception types expected by JPA spec wrt StoredProcedureQuery

This commit is contained in:
Steve Ebersole 2013-08-02 19:17:31 -05:00
parent aea6b767ae
commit 6beb5acb4b
14 changed files with 425 additions and 137 deletions

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import org.hibernate.HibernateException;
/**
* @author Steve Ebersole
*/
public class NoSuchParameterException extends HibernateException {
public NoSuchParameterException(String message) {
super( message );
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import org.hibernate.HibernateException;
/**
* @author Steve Ebersole
*/
public class ParameterStrategyException extends HibernateException {
public ParameterStrategyException(String message) {
super( message );
}
}

View File

@ -82,6 +82,9 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery
* @param position The parameter position
*
* @return The parameter registration memento
*
* @throws ParameterStrategyException If the ProcedureCall is defined using named parameters
* @throws NoSuchParameterException If no parameter with that position exists
*/
public ParameterRegistration getParameterRegistration(int position);
@ -122,6 +125,9 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery
* @param name The parameter name
*
* @return The parameter registration memento
*
* @throws ParameterStrategyException If the ProcedureCall is defined using positional parameters
* @throws NoSuchParameterException If no parameter with that name exists
*/
public ParameterRegistration getParameterRegistration(String name);

View File

@ -53,6 +53,9 @@ public interface ProcedureOutputs extends Outputs {
*
* @return The output value.
*
* @throws ParameterStrategyException If the ProcedureCall is defined using positional parameters
* @throws NoSuchParameterException If no parameter with that name exists
*
* @see ProcedureCall#registerParameter(String, Class, javax.persistence.ParameterMode)
*/
public Object getOutputParameterValue(String name);
@ -64,6 +67,9 @@ public interface ProcedureOutputs extends Outputs {
*
* @return The output value.
*
* @throws ParameterStrategyException If the ProcedureCall is defined using named parameters
* @throws NoSuchParameterException If no parameter with that position exists
*
* @see ProcedureCall#registerParameter(int, Class, javax.persistence.ParameterMode)
*/
public Object getOutputParameterValue(int position);

View File

@ -50,7 +50,9 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.NamedParametersNotSupportedException;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.procedure.ParameterStrategyException;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.ProcedureOutputs;
@ -321,21 +323,22 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
@Override
public ParameterRegistrationImplementor getParameterRegistration(int position) {
if ( parameterStrategy != ParameterStrategy.POSITIONAL ) {
throw new IllegalArgumentException( "Positions were not used to register parameters with this stored procedure call" );
throw new ParameterStrategyException(
"Attempt to access positional parameter [" + position + "] but ProcedureCall using named parameters"
);
}
try {
return registeredParameters.get( position );
}
catch ( Exception e ) {
throw new QueryException( "Could not locate parameter registered using that position [" + position + "]" );
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( position == parameter.getPosition() ) {
return parameter;
}
}
throw new NoSuchParameterException( "Could not locate parameter registered using that position [" + position + "]" );
}
@Override
@SuppressWarnings("unchecked")
public <T> ParameterRegistration<T> registerParameter(String name, Class<T> type, ParameterMode mode) {
final NamedParameterRegistration parameterRegistration
= new NamedParameterRegistration( this, name, mode, type );
final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, mode, type );
registerParameter( parameterRegistration );
return parameterRegistration;
}
@ -350,14 +353,14 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
@Override
public ParameterRegistrationImplementor getParameterRegistration(String name) {
if ( parameterStrategy != ParameterStrategy.NAMED ) {
throw new IllegalArgumentException( "Names were not used to register parameters with this stored procedure call" );
throw new ParameterStrategyException( "Names were not used to register parameters with this stored procedure call" );
}
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( name.equals( parameter.getName() ) ) {
return parameter;
}
}
throw new IllegalArgumentException( "Could not locate parameter registered under that name [" + name + "]" );
throw new NoSuchParameterException( "Could not locate parameter registered under that name [" + name + "]" );
}
@Override

View File

@ -68,7 +68,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
}
@Override
protected CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) {
protected CurrentReturnState buildCurrentReturnState(boolean isResultSet, int updateCount) {
return new ProcedureCurrentReturnState( isResultSet, updateCount, refCursorParamIndex );
}
@ -81,8 +81,8 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
}
@Override
public boolean indicatesMoreReturns() {
return super.indicatesMoreReturns()
public boolean indicatesMoreOutputs() {
return super.indicatesMoreOutputs()
|| ProcedureOutputsImpl.this.refCursorParamIndex < ProcedureOutputsImpl.this.refCursorParameters.length;
}
@ -106,7 +106,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
.getService( RefCursorSupport.class )
.getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() );
}
return new ResultSetOutputImpl( extractResults( resultSet ) );
return buildResultSetOutput( extractResults( resultSet ) );
}
}

View File

@ -38,23 +38,18 @@ public interface Outputs {
*
* @return The current Output object. Can be {@code null}
*/
public Output getCurrentOutput();
public Output getCurrent();
/**
* Are there any more Output objects associated with {@code this}?
* Go to the next Output object (if any), returning an indication of whether there was another (aka, will
* the next call to {@link #getCurrent()} return {@code null}?
*
* @return {@code true} means there are more Output objects available via {@link #getNextOutput()}; {@code false}
* indicates that calling {@link #getNextOutput()} will certainly result in an exception.
* @return {@code true} if the next call to {@link #getCurrent()} will return a non-{@code null} value.
*/
public boolean hasMoreOutput();
public boolean goToNext();
/**
* Retrieve the next return
*
* @return The next return.
*
* @throws NoMoreReturnsException Thrown if there are no more returns associated with this Result, as would
* have been indicated by a {@code false} return from {@link #hasMoreOutput()}.
* Eagerly release any resources held by this Outputs.
*/
public Output getNextOutput() throws NoMoreReturnsException;
public void release();
}

View File

@ -43,9 +43,7 @@ import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Outputs;
import org.hibernate.result.ResultSetOutput;
import org.hibernate.result.Output;
import org.hibernate.result.UpdateCountOutput;
import org.hibernate.result.spi.ResultContext;
/**
@ -69,14 +67,14 @@ public class OutputsImpl implements Outputs {
try {
final boolean isResultSet = jdbcStatement.execute();
currentReturnState = buildCurrentReturnDescriptor( isResultSet );
currentReturnState = buildCurrentReturnState( isResultSet );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getMoreResults" );
}
}
private CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet) {
private CurrentReturnState buildCurrentReturnState(boolean isResultSet) {
int updateCount = -1;
if ( ! isResultSet ) {
try {
@ -87,42 +85,57 @@ public class OutputsImpl implements Outputs {
}
}
return buildCurrentReturnDescriptor( isResultSet, updateCount );
return buildCurrentReturnState( isResultSet, updateCount );
}
protected CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) {
protected CurrentReturnState buildCurrentReturnState(boolean isResultSet, int updateCount) {
return new CurrentReturnState( isResultSet, updateCount );
}
@Override
public Output getCurrentOutput() {
if ( currentReturnState == null ) {
return null;
}
return currentReturnState.getReturn();
protected JDBCException convert(SQLException e, String message) {
return context.getSession().getFactory().getSQLExceptionHelper().convert(
e,
message,
context.getSql()
);
}
@Override
public boolean hasMoreOutput() {
public Output getCurrent() {
if ( currentReturnState == null ) {
return null;
}
return currentReturnState.getOutput();
}
@Override
public boolean goToNext() {
if ( currentReturnState == null ) {
return false;
}
if ( currentReturnState.indicatesMoreOutputs() )
// prepare the next return state
try {
final boolean isResultSet = jdbcStatement.getMoreResults();
currentReturnState = buildCurrentReturnDescriptor( isResultSet );
currentReturnState = buildCurrentReturnState( isResultSet );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getMoreResults" );
}
return currentReturnState != null && currentReturnState.indicatesMoreReturns();
// and return
return currentReturnState != null && currentReturnState.indicatesMoreOutputs();
}
@Override
public Output getNextOutput() {
if ( !hasMoreOutput() ) {
throw new NoMoreReturnsException( "Results have been exhausted" );
public void release() {
try {
jdbcStatement.close();
}
catch (SQLException e) {
log.debug( "Unable to close PreparedStatement", e );
}
return getCurrentOutput();
}
private List extractCurrentResults() {
@ -143,14 +156,6 @@ public class OutputsImpl implements Outputs {
}
}
protected JDBCException convert(SQLException e, String message) {
return context.getSession().getFactory().getSQLExceptionHelper().convert(
e,
message,
context.getSql()
);
}
/**
* Encapsulates the information needed to interpret the current return within a result
*/
@ -165,7 +170,7 @@ public class OutputsImpl implements Outputs {
this.updateCount = updateCount;
}
public boolean indicatesMoreReturns() {
public boolean indicatesMoreOutputs() {
return isResultSet() || getUpdateCount() >= 0;
}
@ -177,14 +182,14 @@ public class OutputsImpl implements Outputs {
return updateCount;
}
public Output getReturn() {
public Output getOutput() {
if ( rtn == null ) {
rtn = buildReturn();
rtn = buildOutput();
}
return rtn;
}
protected Output buildReturn() {
protected Output buildOutput() {
if ( log.isDebugEnabled() ) {
log.debugf(
"Building Return [isResultSet=%s, updateCount=%s, extendedReturn=%s",
@ -204,10 +209,10 @@ public class OutputsImpl implements Outputs {
);
if ( isResultSet() ) {
return new ResultSetOutputImpl( extractCurrentResults() );
return buildResultSetOutput( extractCurrentResults() );
}
else if ( getUpdateCount() >= 0 ) {
return new UpdateCountOutputImpl( updateCount );
return buildUpdateCountOutput( updateCount );
}
else if ( hasExtendedReturns() ) {
return buildExtendedReturn();
@ -218,6 +223,14 @@ public class OutputsImpl implements Outputs {
// hooks for stored procedure (out param) processing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected Output buildResultSetOutput(List list) {
return new ResultSetOutputImpl( list );
}
protected Output buildUpdateCountOutput(int updateCount) {
return new UpdateCountOutputImpl( updateCount );
}
protected boolean hasExtendedReturns() {
return false;
}
@ -227,53 +240,9 @@ public class OutputsImpl implements Outputs {
}
}
protected static class ResultSetOutputImpl implements ResultSetOutput {
private final List results;
public ResultSetOutputImpl(List results) {
this.results = results;
}
@Override
public boolean isResultSet() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List getResultList() {
return results;
}
@Override
public Object getSingleResult() {
final List results = getResultList();
if ( results == null || results.isEmpty() ) {
return null;
}
else {
return results.get( 0 );
}
}
}
protected static class UpdateCountOutputImpl implements UpdateCountOutput {
private final int updateCount;
public UpdateCountOutputImpl(int updateCount) {
this.updateCount = updateCount;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean isResultSet() {
return false;
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Hooks into Hibernate's Loader hierarchy for ResultSet -> Object mapping
private static CustomLoaderExtension buildSpecializedCustomLoader(final ResultContext context) {
// might be better to just manually construct the Return(s).. SQLQueryReturnProcessor does a lot of

View File

@ -0,0 +1,63 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.result.internal;
import java.util.List;
import org.hibernate.result.ResultSetOutput;
/**
* Implementation of ResultSetOutput
*
* @author Steve Ebersole
*/
class ResultSetOutputImpl implements ResultSetOutput {
private final List results;
public ResultSetOutputImpl(List results) {
this.results = results;
}
@Override
public boolean isResultSet() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List getResultList() {
return results;
}
@Override
public Object getSingleResult() {
final List results = getResultList();
if ( results == null || results.isEmpty() ) {
return null;
}
else {
return results.get( 0 );
}
}
}

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.result.internal;
import org.hibernate.result.UpdateCountOutput;
/**
* Implementation of UpdateCountOutput
*
* @author Steve Ebersole
*/
class UpdateCountOutputImpl implements UpdateCountOutput {
private final int updateCount;
public UpdateCountOutputImpl(int updateCount) {
this.updateCount = updateCount;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean isResultSet() {
return false;
}
}

View File

@ -169,9 +169,9 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
Session session = openSession();
session.beginTransaction();
ProcedureCall query = session.createStoredProcedureCall( "user");
ProcedureOutputs procedureResult = query.getResult();
Output currentOutput = procedureResult.getCurrentOutput();
ProcedureCall procedureCall = session.createStoredProcedureCall( "user");
ProcedureOutputs procedureOutputs = procedureCall.getResult();
Output currentOutput = procedureOutputs.getCurrent();
assertNotNull( currentOutput );
ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
String name = (String) resultSetReturn.getSingleResult();
@ -188,7 +188,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
ProcedureCall query = session.createStoredProcedureCall( "findOneUser" );
ProcedureOutputs procedureResult = query.getResult();
Output currentOutput = procedureResult.getCurrentOutput();
Output currentOutput = procedureResult.getCurrent();
assertNotNull( currentOutput );
ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
Object result = resultSetReturn.getSingleResult();
@ -207,7 +207,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
ProcedureCall query = session.createStoredProcedureCall( "findUsers" );
ProcedureOutputs procedureResult = query.getResult();
Output currentOutput = procedureResult.getCurrentOutput();
Output currentOutput = procedureResult.getCurrent();
assertNotNull( currentOutput );
ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
List results = resultSetReturn.getResultList();
@ -244,7 +244,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 );
ProcedureOutputs procedureResult = query.getResult();
Output currentOutput = procedureResult.getCurrentOutput();
Output currentOutput = procedureResult.getCurrent();
assertNotNull( currentOutput );
ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
List results = resultSetReturn.getResultList();
@ -269,7 +269,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
ProcedureOutputs procedureResult = query.getResult();
Output currentOutput = procedureResult.getCurrentOutput();
Output currentOutput = procedureResult.getCurrent();
assertNotNull( currentOutput );
ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
List results = resultSetReturn.getResultList();

View File

@ -41,6 +41,8 @@ import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterStrategyException;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.ProcedureOutputs;
@ -199,27 +201,10 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
// outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private ProcedureOutputs outputs() {
if ( procedureResult == null ) {
procedureResult = procedureCall.getResult();
}
return procedureResult;
}
@Override
public Object getOutputParameterValue(int position) {
return outputs().getOutputParameterValue( position );
}
@Override
public Object getOutputParameterValue(String parameterName) {
return outputs().getOutputParameterValue( parameterName );
}
@Override
public boolean execute() {
try {
final Output rtn = outputs().getCurrentOutput();
final Output rtn = outputs().getCurrent();
return rtn != null && ResultSetOutput.class.isInstance( rtn );
}
catch (NoMoreReturnsException e) {
@ -227,23 +212,64 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
}
}
protected ProcedureOutputs outputs() {
if ( procedureResult == null ) {
procedureResult = procedureCall.getResult();
}
return procedureResult;
}
@Override
public int executeUpdate() {
if ( ! entityManager().isTransactionInProgress() ) {
throw new TransactionRequiredException( "javax.persistence.Query.executeUpdate requires active transaction" );
}
return getUpdateCount();
// the expectation is that there is just one Output, of type UpdateCountOutput
try {
execute();
return getUpdateCount();
}
finally {
outputs().release();
}
}
@Override
public Object getOutputParameterValue(int position) {
try {
return outputs().getOutputParameterValue( position );
}
catch (ParameterStrategyException e) {
throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e );
}
catch (NoSuchParameterException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
}
@Override
public Object getOutputParameterValue(String parameterName) {
try {
return outputs().getOutputParameterValue( parameterName );
}
catch (ParameterStrategyException e) {
throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e );
}
catch (NoSuchParameterException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
}
@Override
public boolean hasMoreResults() {
return outputs().hasMoreOutput() && ResultSetOutput.class.isInstance( outputs().getCurrentOutput() );
return outputs().goToNext() && ResultSetOutput.class.isInstance( outputs().getCurrent() );
}
@Override
public int getUpdateCount() {
try {
final Output rtn = outputs().getCurrentOutput();
final Output rtn = outputs().getCurrent();
if ( rtn == null ) {
return -1;
}
@ -262,9 +288,9 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
@Override
public List getResultList() {
try {
final Output rtn = outputs().getCurrentOutput();
final Output rtn = outputs().getCurrent();
if ( ! ResultSetOutput.class.isInstance( rtn ) ) {
throw new IllegalStateException( "Current CallableStatement return was not a ResultSet, but getResultList was called" );
throw new IllegalStateException( "Current CallableStatement ou was not a ResultSet, but getResultList was called" );
}
return ( (ResultSetOutput) rtn ).getResultList();

View File

@ -51,6 +51,8 @@ import org.hibernate.jpa.internal.EntityManagerMessageLogger;
import org.hibernate.jpa.internal.util.CacheModeHelper;
import org.hibernate.jpa.internal.util.ConfigurationHelper;
import org.hibernate.jpa.internal.util.LockModeTypeHelper;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterStrategyException;
import static org.hibernate.jpa.QueryHints.HINT_CACHEABLE;
import static org.hibernate.jpa.QueryHints.HINT_CACHE_MODE;
@ -439,7 +441,8 @@ public abstract class BaseQueryImpl implements Query {
protected abstract boolean isJpaPositionalParameter(int position);
/**
* Hibernate specific extension to the JPA {@link javax.persistence.Parameter} contract.
* Hibernate specific extension to the JPA {@link javax.persistence.Parameter} contract. Used here to track
* information known about the parameter.
*/
protected static interface ParameterRegistration<T> extends Parameter<T> {
/**
@ -450,6 +453,12 @@ public abstract class BaseQueryImpl implements Query {
*/
public ParameterMode getMode();
/**
* Can we bind (set) values on this parameter? Generally this is {@code true}, but would not be in the case
* of parameters with OUT or REF_CURSOR mode.
*
* @return Whether the parameter is bindable (can set be called).
*/
public boolean isBindable();
public void bindValue(T value);
@ -459,6 +468,11 @@ public abstract class BaseQueryImpl implements Query {
public ParameterBind<T> getBind();
}
/**
* Represents the value currently bound to a particular parameter.
*
* @param <T>
*/
protected static interface ParameterBind<T> {
public T getValue();
@ -648,6 +662,12 @@ public abstract class BaseQueryImpl implements Query {
try {
findParameterRegistration( position ).bindValue( value, temporalType );
}
catch (ParameterStrategyException e) {
throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e );
}
catch (NoSuchParameterException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
catch (QueryParameterException e) {
throw new IllegalArgumentException( e );
}

View File

@ -54,7 +54,9 @@ import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests various JPA usage scenarios for performing stored procedures. Inspired by the awesomely well-done JPA TCK
@ -107,6 +109,42 @@ public class JpaTckUsageTest extends BaseUnitTestCase {
}
}
@Test
@FailureExpected( jiraKey = "HHH-8416", message = "JPA TCK challenge" )
public void testHasMoreResultsHandlingTckChallenge() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class );
assertTrue( query.execute() );
assertTrue( query.hasMoreResults() );
query.getResultList();
assertFalse( query.hasMoreResults() );
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testHasMoreResultsHandling() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class );
assertTrue( query.execute() );
query.getResultList();
assertFalse( query.hasMoreResults() );
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testResultClassHandling() {
EntityManager em = entityManagerFactory.createEntityManager();
@ -148,6 +186,38 @@ public class JpaTckUsageTest extends BaseUnitTestCase {
}
}
@Test
public void testSettingNonExistingParams() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
// non-existing positional param
try {
StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" );
query.setParameter( 99, 1 );
fail( "Expecting an exception" );
}
catch (IllegalArgumentException expected) {
// this is the expected condition
}
// non-existing named param
try {
StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" );
query.setParameter( "does-not-exist", 1 );
fail( "Expecting an exception" );
}
catch (IllegalArgumentException expected) {
// this is the expected condition
}
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
@FailureExpected( jiraKey = "HHH-8395", message = "Out of the frying pan into the fire: https://issues.apache.org/jira/browse/DERBY-211" )
public void testExecuteUpdate() {
@ -167,6 +237,10 @@ public class JpaTckUsageTest extends BaseUnitTestCase {
}
}
public void testParameterRegistration() {
}
// todo : look at ways to allow "Auxiliary DB Objects" to the db via EMF bootstrapping.
// public static final String findOneUser_CREATE_CMD = "CREATE ALIAS findOneUser AS $$\n" +
@ -317,6 +391,13 @@ public class JpaTckUsageTest extends BaseUnitTestCase {
conn.close();
}
public static void findUserIds(ResultSet[] results) throws SQLException {
Connection conn = DriverManager.getConnection( "jdbc:default:connection" );
PreparedStatement ps = conn.prepareStatement( "select id from t_user" );
results[0] = ps.executeQuery();
conn.close();
}
public static void deleteAllUsers() throws SQLException {
// afaict the only way to return update counts here is to actually perform some DML
Connection conn = DriverManager.getConnection( "jdbc:default:connection" );