Skip to content

Commit 610afca

Browse files
committed
restore hibernate.procedure.function_return_jdbc_type_code
and improve Javadoc
1 parent 93e1063 commit 610afca

File tree

3 files changed

+102
-42
lines changed

3 files changed

+102
-42
lines changed

hibernate-core/src/main/java/org/hibernate/jpa/HibernateHints.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,33 @@ public interface HibernateHints {
151151
/**
152152
* Whether to treat a {@link org.hibernate.procedure.ProcedureCall}
153153
* or {@link jakarta.persistence.StoredProcedureQuery} as a call
154-
* to a function rather than a call to a procedure.
154+
* to a function rather than a call to a procedure. Set hint to
155+
* {@link Boolean#TRUE TRUE} or {@code "true"} to indicated that
156+
* the call should be treated as a function call.
157+
* <p>
158+
* When no other return type is indicated, a function is assumed
159+
* to return {@link java.sql.Types#REF_CURSOR REF_CURSOR}.
160+
*
161+
* @see org.hibernate.procedure.ProcedureCall#markAsFunctionCall
162+
* @see #HINT_CALLABLE_FUNCTION_RETURN_TYPE
155163
*/
156164
String HINT_CALLABLE_FUNCTION = "org.hibernate.callableFunction";
157165

166+
/**
167+
* The {@linkplain org.hibernate.type.SqlTypes JDBC type code},
168+
* {@linkplain org.hibernate.query.BindableType type}, or
169+
* {@link Class} of the value returned by a SQL function called
170+
* via {@link org.hibernate.procedure.ProcedureCall} or
171+
* {@link jakarta.persistence.StoredProcedureQuery}. Has the side
172+
* effect of causing the call to be treated as a function call
173+
* rather than a call to a stored procedure.
174+
*
175+
* @see org.hibernate.procedure.ProcedureCall#markAsFunctionCall(int)
176+
* @see org.hibernate.procedure.ProcedureCall#markAsFunctionCall(org.hibernate.query.BindableType)
177+
* @see org.hibernate.procedure.ProcedureCall#markAsFunctionCall(Class)
178+
*/
179+
String HINT_CALLABLE_FUNCTION_RETURN_TYPE = "hibernate.procedure.function_return_jdbc_type_code";
180+
158181
/**
159182
* Hint for specifying the tenant id to use when creating an
160183
* {@link jakarta.persistence.EntityManagerFactory#createEntityManager(java.util.Map) EntityManager}.

hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,53 @@
2121
import org.hibernate.query.procedure.ProcedureParameter;
2222

2323
/**
24-
* Defines support for executing database stored procedures and functions.
24+
* Defines support for executing database stored procedures and functions using the
25+
* {@linkplain java.sql.CallableStatement JDBC stored procedure SQL escape syntax}.
2526
* <p>
26-
* Note that here we use the terms "procedure" and "function" as follows:<ul>
27-
* <li>procedure is a named database executable we expect to call via : {@code {call procedureName(...)}}</li>
28-
* <li>function is a named database executable we expect to call via : {@code {? = call functionName(...)}}</li>
27+
* Here we use the terms "procedure" and "function" as follows:<ul>
28+
* <li>A <em>procedure</em> is a named database executable called via:
29+
* {@code {call procedureName(...)}}</li>
30+
* <li>A <em>function</em> is a named database executable called via:
31+
* {@code {? = call functionName(...)}}</li>
2932
* </ul>
3033
* <p>
31-
* Unless explicitly specified, the ProcedureCall is assumed to follow the
32-
* procedure call syntax. To explicitly specify that this should be a function
33-
* call, use {@link #markAsFunctionCall}.
34+
* Unless explicitly specified, the {@code ProcedureCall} is executed using the
35+
* procedure call syntax. To explicitly specify that the function call syntax
36+
* should be used, call {@link #markAsFunctionCall}. Clients of the JPA-standard
37+
* {@link StoredProcedureQuery} interface may choose between:
38+
* <ul>
39+
* <li>using {@link #unwrap storedProcedureQuery.unwrap(ProcedureCall.class).markAsFunctionCall(returnType)},
40+
* or
41+
* <li>setting the {@value org.hibernate.jpa.HibernateHints#HINT_CALLABLE_FUNCTION}
42+
* or {@value org.hibernate.jpa.HibernateHints#HINT_CALLABLE_FUNCTION_RETURN_TYPE}
43+
* {@linkplain #setHint(String, Object) hint} to avoid the cast to a
44+
* Hibernate-specific class.
45+
* </ul>
3446
* <p>
35-
* When using function-call syntax:<ul>
36-
* <li>parameters must be registered by position (not name)</li>
37-
* <li>The first parameter is considered to be the function return (the `?` before the call)</li>
38-
* <li>the first parameter must have mode of OUT, INOUT or REF_CURSOR; IN is invalid</li>
47+
* When the function call syntax is used:
48+
* <ul>
49+
* <li>parameters must be registered by position (not name),
50+
* <li>the first parameter is considered to represent the function return value
51+
* (corresponding to the {@code ?} which occurs before the {@code =}), and
52+
* <li>the first parameter must have {@linkplain ParameterMode mode} OUT, INOUT,
53+
* or REF_CURSOR; {@linkplain ParameterMode#IN IN} is illegal.
3954
* </ul>
4055
* <p>
41-
* In some cases, based on the Dialect, we will have other validations and
42-
* assumptions as well. For example, on PGSQL, whenever we see a REF_CURSOR mode
43-
* parameter, we know that:<ul>
44-
* <li>
45-
* this will be a function call (so we call {@link #markAsFunctionCall} implicitly) because
46-
* that is the only way PGSQL supports returning REF_CURSOR results.
47-
* </li>
48-
* <li>there can be only one REF_CURSOR mode parameter</li>
56+
* Depending on the {@linkplain org.hibernate.dialect.Dialect SQL dialect},
57+
* further constraints are enforced or inferred. For example, on PostgreSQL:
58+
* <ul>
59+
* <li>If a parameter has mode {@linkplain ParameterMode#REF_CURSOR REF_CURSOR},
60+
* it's automatically inferred that the call is a function call because this
61+
* is the only context in which PostgreSQL returns REF_CURSOR results.
62+
* So it's not necessary to call {@link #markAsFunctionCall} explicitly.
63+
* <li>The restriction that there may be at most one REF_CURSOR mode parameter
64+
* is enforced.
4965
* </ul>
5066
*
5167
* @author Steve Ebersole
68+
*
69+
* @see java.sql.CallableStatement
70+
* @see StoredProcedureQuery
5271
*/
5372
public interface ProcedureCall
5473
extends CommonQueryContract, SynchronizeableQuery, StoredProcedureQuery, AutoCloseable {
@@ -274,10 +293,10 @@ default void close() {
274293
ProcedureCall registerStoredProcedureParameter(String parameterName, Class<?> type, ParameterMode mode);
275294

276295
/**
277-
* The hint key indicating the function's return {@linkplain java.sql.Types JDBC type code}.
296+
* The hint key indicating the return {@linkplain java.sql.Types JDBC type code} of a function.
278297
*
279-
* @deprecated This hint no longer has any effect. Use {@link #markAsFunctionCall(int)}.
298+
* @deprecated Use {@link org.hibernate.jpa.HibernateHints#HINT_CALLABLE_FUNCTION_RETURN_TYPE}.
280299
*/
281300
@Deprecated(since="7", forRemoval = true)
282-
String FUNCTION_RETURN_TYPE_HINT = "hibernate.procedure.function_return_jdbc_type_code";
301+
String FUNCTION_RETURN_TYPE_HINT = org.hibernate.jpa.HibernateHints.HINT_CALLABLE_FUNCTION_RETURN_TYPE;
283302
}

hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import static java.util.Collections.unmodifiableSet;
9696
import static org.hibernate.internal.util.StringHelper.join;
9797
import static org.hibernate.jpa.HibernateHints.HINT_CALLABLE_FUNCTION;
98+
import static org.hibernate.jpa.HibernateHints.HINT_CALLABLE_FUNCTION_RETURN_TYPE;
9899
import static org.hibernate.procedure.internal.NamedCallableQueryMementoImpl.ParameterMementoImpl.fromRegistration;
99100
import static org.hibernate.query.results.ResultSetMapping.resolveResultSetMapping;
100101

@@ -294,17 +295,6 @@ public ProcedureCallImpl(
294295
applyOptions( memento );
295296
}
296297

297-
protected void applyOptions(NamedCallableQueryMemento memento) {
298-
super.applyOptions( memento );
299-
300-
if ( memento.getHints() != null ) {
301-
final Object callableFunction = memento.getHints().get( HINT_CALLABLE_FUNCTION );
302-
if ( callableFunction != null && parseBoolean( callableFunction.toString() ) ) {
303-
applyCallableFunctionHint();
304-
}
305-
}
306-
}
307-
308298
private void applyCallableFunctionHint() {
309299
final List<Class<?>> resultTypes = new ArrayList<>();
310300
resultSetMapping.visitResultBuilders(
@@ -360,7 +350,7 @@ public FunctionReturnImplementor<R> getFunctionReturn() {
360350
}
361351

362352
@Override
363-
public ProcedureCallImpl<R> markAsFunctionCall(int sqlType) {
353+
public ProcedureCallImplementor<R> markAsFunctionCall(int sqlType) {
364354
functionReturn = new FunctionReturnImpl<>( this, sqlType );
365355
return this;
366356
}
@@ -1069,13 +1059,41 @@ public QueryImplementor<R> setLockOptions(LockOptions lockOptions) {
10691059

10701060
@Override
10711061
public ProcedureCallImplementor<R> setHint(String hintName, Object value) {
1072-
if ( HINT_CALLABLE_FUNCTION.equals( hintName ) ) {
1073-
if ( value != null && parseBoolean( value.toString() ) ) {
1074-
applyCallableFunctionHint();
1075-
}
1076-
}
1077-
else {
1078-
super.setHint( hintName, value );
1062+
switch ( hintName ) {
1063+
case HINT_CALLABLE_FUNCTION:
1064+
if ( value != null ) {
1065+
if ( value instanceof Boolean bool ) {
1066+
if ( bool ) {
1067+
applyCallableFunctionHint();
1068+
}
1069+
}
1070+
else if ( parseBoolean( value.toString() ) ) {
1071+
applyCallableFunctionHint();
1072+
}
1073+
}
1074+
break;
1075+
case HINT_CALLABLE_FUNCTION_RETURN_TYPE:
1076+
if ( value != null ) {
1077+
if ( value instanceof Integer code ) {
1078+
//noinspection resource
1079+
markAsFunctionCall( code );
1080+
}
1081+
else if ( value instanceof BindableType<?> type ) {
1082+
//noinspection resource
1083+
markAsFunctionCall( type );
1084+
}
1085+
else if ( value instanceof Class<?> type ) {
1086+
//noinspection resource
1087+
markAsFunctionCall( type );
1088+
}
1089+
else {
1090+
//noinspection resource
1091+
markAsFunctionCall( Integer.parseInt( value.toString() ) );
1092+
}
1093+
}
1094+
break;
1095+
default:
1096+
super.setHint( hintName, value );
10791097
}
10801098
return this;
10811099
}

0 commit comments

Comments
 (0)