HHH-8445 - Implement REF_CURSOR support for StoredProcedureQuery

This commit is contained in:
Steve Ebersole 2013-08-26 13:50:22 -05:00
parent 7146435a42
commit 939c1f2743
3 changed files with 171 additions and 7 deletions

View File

@ -25,7 +25,6 @@ package org.hibernate.procedure.internal;
import javax.persistence.ParameterMode;
import javax.persistence.TemporalType;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.util.Calendar;
@ -119,6 +118,10 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
this.mode = mode;
this.type = type;
if ( mode == ParameterMode.REF_CURSOR ) {
return;
}
setHibernateType( hibernateType );
}
@ -286,6 +289,10 @@ public abstract class AbstractParameterRegistrationImpl<T> implements ParameterR
}
public int[] getSqlTypes() {
if ( mode == ParameterMode.REF_CURSOR ) {
// we could use the Types#REF_CURSOR added in Java 8, but that would require requiring Java 8...
throw new IllegalStateException( "REF_CURSOR parameters do not have a SQL/JDBC type" );
}
return sqlTypes;
}

View File

@ -398,10 +398,19 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
.append( "(" );
String sep = "";
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
for ( int i = 0; i < parameter.getSqlTypes().length; i++ ) {
if ( parameter == null ) {
throw new QueryException( "Registered stored procedure parameters had gaps" );
}
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
buffer.append( sep ).append( "?" );
sep = ",";
}
else {
for ( int i = 0; i < parameter.getSqlTypes().length; i++ ) {
buffer.append( sep ).append( "?" );
sep = ",";
}
}
}
buffer.append( ")}" );
@ -413,13 +422,15 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
// prepare parameters
int i = 1;
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( parameter == null ) {
throw new QueryException( "Registered stored procedure parameters had gaps" );
}
for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
parameter.prepare( statement, i );
i += parameter.getSqlTypes().length;
if ( parameter.getMode() == ParameterMode.REF_CURSOR ) {
i++;
}
else {
i += parameter.getSqlTypes().length;
}
}
return new ProcedureOutputsImpl( this, statement );

View File

@ -0,0 +1,146 @@
/*
* 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.test.sql.storedproc;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ParameterMode;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.math.BigDecimal;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.dialect.PostgreSQL82Dialect;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureOutputs;
import org.hibernate.result.ResultSetOutput;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.RequiresDialects;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
/**
* @author Steve Ebersole
*/
@RequiresDialect( value = PostgreSQL81Dialect.class, strictMatching = false )
@FailureExpected( jiraKey = "HHH-8445", message = "Waiting on EG clarification" )
public class PostgresRefCursorSupportTest extends BaseUnitTestCase {
public static class ProcedureDefinitions implements AuxiliaryDatabaseObject {
/**
* Singleton access
*/
public static final ProcedureDefinitions INSTANCE = new ProcedureDefinitions();
@Override
public void addDialectScope(String dialectName) {
throw new IllegalStateException( "Not expecting addition of dialects to scope" );
}
@Override
public boolean appliesToDialect(Dialect dialect) {
return PostgreSQL81Dialect.class.isInstance( dialect )
|| PostgreSQL82Dialect.class.isInstance( dialect );
}
@Override
public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
return "create function all_items() return refcursor as \n" +
" 'declare someCursor refcursor;\n" +
" begin\n" +
" open someCursor for select * from ITEM;\n" +
" return someCursor;\n" +
" end;' language plpgsql;";
}
@Override
public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
return "drop function FIND_ITEMS()";
}
}
@Entity
@Table( name = "PROC_ITEM" )
public static class Item {
@Id
private Integer id;
private String stockCode;
private String name;
private BigDecimal unitCost;
@Temporal( TemporalType.TIMESTAMP )
private Date availabilityStartDate;
@Temporal( TemporalType.TIMESTAMP )
private Date availabilityEndDate;
}
private SessionFactory sf;
@Before
public void beforeTest() {
Configuration cfg = new Configuration()
.addAnnotatedClass( Item.class )
.setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
cfg.addAuxiliaryDatabaseObject( ProcedureDefinitions.INSTANCE );
sf = cfg.buildSessionFactory();
}
@After
public void afterTest() {
if ( sf != null ) {
sf.close();
}
}
@Test
public void testExplicitClassReturn() {
Session session = sf.openSession();
session.beginTransaction();
ProcedureCall call = session.createStoredProcedureCall( "all_items", Item.class );
call.registerParameter( 1, void.class, ParameterMode.REF_CURSOR );
ProcedureOutputs outputs = call.getOutputs();
ResultSetOutput results = assertTyping( ResultSetOutput.class, outputs.getCurrent() );
session.getTransaction().commit();
session.close();
}
}