Spring Data JPA is an amazing product. It has some quirks, but I like to think that I’ve figured out a way to navigate those and love working with it.

My latest endeavour had me searching for a way to call SQL Server stored procedures and grab the result set. I was surprised to find that there isn’t strong support for this type of pattern and moreover there’s a bug logged for the past year.

So, what if we had to get some information about a set of orders for a history report. The data is plentiful and we need to use a stored procedure for performance reasons. Also, this is the only time we will need the data in this fasion so a View would not make sense.

In our pretend case we may find a stored procedure like the one below to be the best approach. However, there are some pitfalls we should avoid.

IF OBJECT_ID ( 'dbo.spOrderHistoryForCustomer', 'P' ) IS NOT NULL
    DROP PROCEDURE dbo.spOrderHistoryForCustomer;
GO

CREATE PROCEDURE dbo.spOrderHistoryForCustomer
  @accountId INT
AS

  SELECT
    O.OrderId,
    O.AccountId,
    O.OrderTotal,
    C.FirstName,
    C.LastName
  FROM Orders WITH(NOLOCK)
    JOIN Customer C WITH(NOLOCK) ON C.AccountId = O.AccountId
  WHERE O.AccountId = @accountId;

GO

At first glance this may look to be a perfect place to use @NamedStoredProcedureQuery and @Procedure, however that approach is suffering from the aforementioned bug and quirks. Check out the following Jira ticket for more information https://jira.spring.io/browse/DATAJPA-1092

The issue is not with pure JPA, it’s actually a Spring Data bug that is blocked by a different Hibernate issue. The original reporter actually provides a solution similar to the one below. We can build on it a bit and morph our newfound quirk and make it a win.

Here’s the code snippet for now, with more to come later.


@PersistenceContext
private EntityManager em;

private List getOrderHistoryForCustomer(Integer accountId) {
    StoredProcedureQuery spq =
      em.createStoredProcedureQuery("spOrderHistoryForCustomer", Orders.class);
    spq.registerStoredProcedureParameter("accountId", Integer.class, ParameterMode.IN);
    spq.setParameter("accountId", accountId);
    return spq.getResultList();
}

Note: This was typed out in a bit of a rush, but I wanted to share before signing off for a bit. Check back soon.