Support JPA 2.1 stored procedures returning result sets [DATAJPA-1092] #1433
Some databases: notably MS SQL and MySQL (but not Oracle) support SPs returning result sets (one or multiple) via simple SELECTs. In MS Transact-SQL this seems to be a pervasive pattern. JPA 2.1 spec explicitly supports mapping of SP result set(s) e.g. in 18.104.22.168 Named Stored Procedure Queries:
This feature does not seem to be supported by Spring Data JPA: e.g. see https://stackoverflow.com/questions/31097667/illegalargumentexception-type-cannot-be-null. I spent some time trying to make it work with both 1.5.2 and the current 2.0 snapshot to no avail.
Here is a test example working via pure JPA 2.1 (executed in Spring Boot 1.5.2 app):
Affects: 1.11.1 (Ingalls SR1)
2 votes, 5 watchers
The text was updated successfully, but these errors were encountered:
Jeff Sheets commented
I looked into implementing this, but the major stumbling block for
If the code is tested locally against a licensed DB2 database, is there any guidance for how to submit the PR since tests will not be possible?
Spring Data JPA's
The simplest return type I can think of would be to just have the query return List<?> and have the ResultSet(s) be the last items in the List. The other option would be returning a new object like StoredProcedureResult that holds a List of outputParamValues and a List of resultSets
For the thousands of others reading this that are up against the same brick wall, before jumping to another library: I had the most luck using a straight Hibernate Session access with createQuery/createNativeQuery and setParameterList which is NOT available with JPA. However, due to different SQL dialects (e.g. PLPGSQL vs HQL) it's still tricky and you'll get things like mismatch of array notation you have to work through (e.g. missing brackets for postgres on createNativeQuery). I'll follow up with "workaround" code samples when I get it working then turn the code into Foos and Bars.
Annotations in repos like @procedure and @NamedStoredProcedure won't work too well for you for anything beyond basic. If you don't have arrays, you can use a custom JPA method. However, if you have arrays that isn't going to work for you unless you hack it in as a string and manually build the array list to feed into the driver basically building a full SQL string to call the stored proc, which is messy. It also won't work with Hibernate sessions and createStoredProcedureQuery. Stackoverflow will mislead you in a lot of cases because they are talking about simpler cases than you may be facing if you already are reading this.
Hibernate sessions and createQuery/createNativeQuery makes the arrays work with the aforementioned setParameterList method, and some elbow grease.
Before you tear your hair out, you should consider inline SQL or QueryDSL as well. Makes unit testing easier too. However, there are reasons companies often use procedures,views, and functions. Also, I'd like to see someone take a large stored proc or view written by a data architect and re-write that in QueryDSL or even inline SQL. Good luck with that.
I think Spring really needs to make this happen to be in line with other solutions. By the time a developer realizes these limitations, they are already on track to miss deadlines, and architects' estimates destroyed because no one ever expects this limitation to exist in such a widely used product. I figure this story has happened thousands of times.
Sadly, I think the limitation is in JPA, so it's not as simple as it seems. Multiple layers will need to be adjusted.
Follow-up: I thought I should say what I ended up with. It turned out I didn't need setParameterList, but some of you may...
Because this is just an array parameter I was able to do this:
effectively, it's doing something similar to:
Here is the postgres function (with changes to remove specific vars):
To import types-52 for the DataTypeArray.INSTANCE in pom.xml
Warning: Due to changing the code a bit so it was more general, I can't guarantee it will work out of the box, but should only need minor tweaking
I believe you'll need setParameterList for a sql IN clause (correct me if I'm wrong), however that isn't the use case of sending and receiving arrays/lists to functions/procedures:
If you absolutely run up against a wall, you can do something like this and then just toss it into the sql directly (again, postgres)
and use as follows (can be done with entityManager this time)