How to use declare Stream as return type when dealing with JPA Specification and spring-data-jpa

7.4k Views Asked by At

I'm wondering if I can use JPA specification predicates in custom queries?

I've tried but with no success.

Let's say I have an Entity Customer and a repository:

@Repository
public interface CustomerRepository 
    extends JpaRepository<Customer, Long>,
   JpaSpecificationExecutor<Customer> {
}

Querying like this is OK

@Query("select c from Customer c")
Stream<Customer> streamAllCustomers();

This is Not OK

Stream<Customer> streamAllCustomersWithFilter(Specification<Customer> filter);

Is there a way to achieve this ?

NB I know I can put params in the @Query but I would like to stay in the design of the current app and use Specifications all the way.

2

There are 2 best solutions below

1
On BEST ANSWER

TL;DR;

No, and No, but manually Yes

I think issue DATAJPA-906 answers both of your questions

  1. Question (from the title): How to use declare Stream as return type when dealing with JPA Specification and spring-data-jpa?

You don't, at least not in a directly supported way:

Support Java 8 Streams on JpaSpecificationExecutor

[..]

This unfortunately will have to wait for a 2.0 revamp as a Stream in the method signature would render the interface unloadable on versions of Java < 8.

Of course you can always add your custom methods including implementation.

  1. Question Can I use JPA specification predicates in custom queries? (custom queries being queries defined using the @Query annotation

how would you even combine a CriteriaQuery defined through a Specification and a manually defined JPQL query?

In case the problem is not clear: If your custom query contains an inner select, wher should the Criteria from the specification go?

What you can do

Implement a custom method, returning a Stream and taking a specification as an argument, combine it with prepared specifications to call an existing method of the JpaSpecificationExecutor interface and convert the result to a Stream

0
On

There is the way to stream data from Spring Data JPA that I use. This approach is useful to process huge amount of data while avoiding high memory consumption, because whole query result is not loaded to memory.

Create custom individual repository with following implementation

public class YourCustomRepositoryImpl implements YourCustomRepository {

    @PersistenceContext(unitName = "yorEntityManagerFactory")
    private EntityManager em;

    @Override
    public Stream<SomeEntity> streamAll(Specification<SomeEntity> spec) {

            CriteriaBuilder cb = em.getCriteriaBuilder();
            CriteriaQuery<SomeEntity> query = cb.createQuery(SomeEntity.class);
            Root<SomeEntity> root = query.from(SomeEntity.class);
            query.where(spec.toPredicate(root, query, cb));

            return em.createQuery(query).getResultStream();
    }

}

Sure, this requires JPA 2.2 supporting ORM framework (I used Hibernate 5.3).

Also, you should care to provide a connection to be alive while stream is being processed.