IMO that is actually worse. So if we returnOutParameter==false, does that mean we ignore all (IN/)OUT parameters? Because that's what that name implies to me
On Mon, Feb 15, 2016 at 10:23 AM Vlad Mihalcea <mihalcea.v...@gmail.com> wrote: > After giving it more thought, I think the method should be called: > > ProcedureCall#returnOutParameter(boolean returnOut) > > On Mon, Feb 15, 2016 at 6:16 PM, Vlad Mihalcea <mihalcea.v...@gmail.com> > wrote: > >> The problem with naming the method as >> "ProcedureCall#setTreatAsFunction(boolean isFunction)" is that the term >> "function" is very leaky. >> >> PostgreSQL uses functions only even when using OUT or REF_CURSOR >> parameter types: >> >> statement.executeUpdate( >> "CREATE OR REPLACE FUNCTION post_comments(postId BIGINT) " + >> " RETURNS REFCURSOR AS " + >> "$BODY$ " + >> " DECLARE " + >> " postComments REFCURSOR; " + >> " BEGIN " + >> " OPEN postComments FOR " + >> " SELECT * " + >> " FROM post_comment " + >> " WHERE post_id = postId; " + >> " RETURN postComments; " + >> " END; " + >> "$BODY$ " + >> "LANGUAGE plpgsql" >> ); >> >> Which even works with the JPA syntax: >> >> StoredProcedureQuery query = >> entityManager.createStoredProcedureQuery("post_comments"); >> query.registerStoredProcedureParameter(1, void.class, >> ParameterMode.REF_CURSOR); >> query.registerStoredProcedureParameter(2, Long.class, ParameterMode.IN); >> >> query.setParameter(2, 1L); >> >> List<Object[]> postComments = query.getResultList(); >> assertEquals(2, postComments.size()); >> >> In fact, PostgreSQL is the only one where REF_CURSOR worked for the >> database that I tested. >> For SQL Server and MySQL, I got: "Dialect .*? not known to support >> REF_CURSOR parameters", >> while for Oracle I got: ".*?Dialect does not support resultsets via >> stored procedures". >> >> To summarize, we can have the discriminator method for knowing we should >> handle a return-like SQL function: >> >> 1. ProcedureCall#returnResultSet(boolean isReturn) >> >> Now, considering the options that you proposed, I'd go for the 2nd one: >> >> "2. use the existing ProcedureCall param methods and just assume that in >> the case of a function that the first parameter represents the function >> return" >> >> This is actually very close to JDBC too, so it would be easier for a >> developer to recall the syntax because in JDBC the syntax is: >> >> try (CallableStatement function = connection.prepareCall( >> "{ ? = call fn_count_comments(?) }" )) { >> function.registerOutParameter( 1, Types.INTEGER ); >> function.setInt( 2, 1 ); >> function.execute(); >> int result = function.getInt( 1 ); >> } >> >> But then, it means that we register the return type and the fact that we >> use the first index like with a stored procedure: >> >> query.registerStoredProcedureParameter(1, Integer.class, >> ParameterMode.OUT); >> >> This way we have only a new methods being added (e.g. returnResultSet) >> and we alter the callable statement syntax based on it. >> When it comes to fetching the result set, we need to do it just like in >> the JDBC example. >> >> Vlad >> >> On Mon, Feb 15, 2016 at 5:29 PM, Steve Ebersole <st...@hibernate.org> >> wrote: >> >>> A function can also define (IN/)OUT arguments depending on database, so >>> I'd rather not get into validating that. I have no idea whether any >>> databases support REFCURSOR arguments for a function. >>> >>> I think the registerFunctionReturnType suggestion works on this >>> assumptions that (1) we are only returning the function RETURN (aka, not >>> allowing OUT args) and (2) that the function only returns "simple" types. >>> I think that the normal parameter registration could work for the function >>> return. The only thing I worry about is whether it is better to handle the >>> param registration representing the return separately (see below). >>> >>> So I'd suggest the following... >>> >>> For the Hibernate native ProcedureCall I would add a method to indicate >>> whether the call is a procedure or a function. Just a simple boolean, >>> something like: >>> >>> ProcedureCall#setTreatAsFunction(boolean isFunction) >>> >>> The the question becomes how to deal with the function-return. To me it >>> makes the most sense to reuse the notion of a ParameterRegistration, >>> although I can see 2 specific options: >>> >>> 1. hold a separate >>> "ProcedureCallImpl#functionReturnParameterRegistration" >>> field with appropriate ProcedureCall methods exposed >>> 2. use the existing ProcedureCall param methods and just assume that >>> in the case of a function that the first parameter represents the >>> function >>> return >>> >>> >>> Another, more drastic, option on the native side is a >>> specialized ProcedureCall type for functions: `FunctionCall >>> extends ProcedureCall`. But that effectively requires a new method (set >>> of?) on Session to create the FunctionCall. >>> >>> And, unless I am mistaken, drivers support calling functions differently >>> in terms of the "call string" we pass it so we'd also have to have a >>> Dialect/CallableStatementSupport hook here. This is the place where the >>> difference in how we handle the ParameterRegistration for the function >>> return is important. >>> >>> On a completely separate tangent... I wonder if we maybe want to wander >>> into trying to >>> leverage >>> java.sql.DatabaseMetaData#getFunctions/java.sql.DatabaseMetaData#getProcedures >>> to just automatically handle the difference (as opposed to the need for the >>> user to explicitly hand us a boolean)? >>> >>> On Mon, Feb 15, 2016 at 8:58 AM Vlad Mihalcea <mihalcea.v...@gmail.com> >>> wrote: >>> >>>> Hi Steve, >>>> >>>> I'm glad we have plans to improve this, so let's use this conversation >>>> to gather as much info as we need to open a JIRA issue for this task. >>>> >>>> For the Hibernate-specific ProcedureCall, should add a method like this: >>>> >>>> ProcedureCall#registerFunctionReturnType(Class type) >>>> >>>> This way we can define the result type and also know that we should >>>> expect a function and not a stored procedure. >>>> We can add a validation so that we disallow registering an >>>> OUT/REF_CURSOR and a function-return-type for the same ProcedureCall >>>> instance. >>>> >>>> For @javax.persistence.NamedStoredProcedureQuery and >>>> StoredProcedureQuery, we could add the following hint: >>>> >>>> org.hibernate.registerFunctionReturnType >>>> >>>> The logic should be just like for the >>>> ProcedureCall#registerFunctionReturnType(Class type). >>>> >>>> Does it sound reasonable? >>>> >>>> Vlad >>>> >>>> On Mon, Feb 15, 2016 at 4:34 PM, Steve Ebersole <st...@hibernate.org> >>>> wrote: >>>> >>>>> So to be clear... >>>>> >>>>> I absolutely think we should add support for this. The question >>>>> really is how to expose this, both in the native API >>>>> (org.hibernate.procedure.ProcedureCall) and the JPA API >>>>> (javax.persistence.StoredProcedureQuery), as well as >>>>> @javax.persistence.NamedStoredProcedureQuery. As far as the JPA >>>>> contracts, >>>>> obviously this requires a hint since we cannot change them (of course we >>>>> could offer an extension to build a StoredProcedureQuery that models >>>>> a function rather than a procedure. >>>>> >>>>> Notice too that there is another concern though: namely defining the >>>>> spec for the output parameter. >>>>> >>>>> On Mon, Feb 15, 2016 at 8:27 AM Steve Ebersole <st...@hibernate.org> >>>>> wrote: >>>>> >>>>>> Well as my todo comment says: >>>>>> >>>>>> // todo : how to identify calls which should be in the form `{? = >>>>>> call procName...}` ??? (note leading param marker) >>>>>> >>>>>> // more than likely this will need to be a method on the native >>>>>> API. I can see this as a trigger to >>>>>> // both: (1) add the `? = ` part and also (2) register a REFCURSOR >>>>>> parameter for DBs (Oracle, PGSQL) that >>>>>> // need it. >>>>>> >>>>>> :) >>>>>> >>>>>> >>>>>> On Mon, Feb 15, 2016 at 7:54 AM Vlad Mihalcea < >>>>>> mihalcea.v...@gmail.com> wrote: >>>>>> >>>>>>> Hi, >>>>>>> >>>>>>> While writing the stored procedure section, I found a way to improve >>>>>>> the >>>>>>> current implementation to FUNCTIONS as well. >>>>>>> >>>>>>> Considering the following function: >>>>>>> >>>>>>> CREATE FUNCTION fn_post_comments(postId integer) >>>>>>> RETURNS integer >>>>>>> DETERMINISTIC >>>>>>> READS SQL DATA >>>>>>> BEGIN >>>>>>> DECLARE commentCount integer; >>>>>>> SELECT COUNT(*) INTO commentCount >>>>>>> FROM post_comment >>>>>>> WHERE post_comment.post_id = postId; >>>>>>> RETURN commentCount; >>>>>>> END >>>>>>> >>>>>>> We could call this function and fetch the result ith plain-old JDBC: >>>>>>> >>>>>>> session.doWork(connection -> { >>>>>>> try (CallableStatement function = connection.prepareCall("{ ? = >>>>>>> call fn_count_comments(?) }")) { >>>>>>> function.registerOutParameter(1, Types.INTEGER); >>>>>>> function.setInt(2, 1); >>>>>>> function.execute(); >>>>>>> int commentCount = function.getInt(1); >>>>>>> assertEquals(2, commentCount); >>>>>>> } >>>>>>> }); >>>>>>> >>>>>>> When using the JPA 2.1 API: >>>>>>> >>>>>>> StoredProcedureQuery query = >>>>>>> entityManager.createStoredProcedureQuery("fn_count_comments"); >>>>>>> query.registerStoredProcedureParameter("postId", Long.class, >>>>>>> ParameterMode.IN); >>>>>>> >>>>>>> query.setParameter("postId", 1L); >>>>>>> >>>>>>> Long commentCount = (Long) query.getSingleResult(); >>>>>>> >>>>>>> We get a "PROCEDURE fn_count_comments does not exist" exception >>>>>>> because the >>>>>>> SQL statement is built as "{call fn_count_comments(?)}" instead of >>>>>>> "{ ? = >>>>>>> call fn_count_comments(?) }". >>>>>>> I think we could define a hint like this: >>>>>>> >>>>>>> query.setHint(QueryHints.HINT_CALL_FUNCTION, true); >>>>>>> >>>>>>> So we could adjust the callable statement to work like a function. >>>>>>> >>>>>>> What do you think of this? >>>>>>> >>>>>>> Vlad >>>>>>> _______________________________________________ >>>>>>> hibernate-dev mailing list >>>>>>> hibernate-dev@lists.jboss.org >>>>>>> https://lists.jboss.org/mailman/listinfo/hibernate-dev >>>>>>> >>>>>> >>>> >> > _______________________________________________ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev