Spring Data Java 8 Support

1. Overview

Spring Data now supports core Java 8 features – such as OptionalStream API and CompletableFuture.

In this quick article, we’ll go through some examples of how we can use these with the framework.

2. Optional

Let’s start with the CRUD repository methods – which now wrap results in an Optional:

public interface CrudRepository<T, ID> extends Repository<T, ID> {
    
    Optional<T> findById(ID id);
    
}

When returning an Optional instance, it’s a useful hint that there’s a possibility that the value might not exist. More information on Optional can be found here.

All we now have to do is to specify return type as an Optional:

public interface UserRepository extends JpaRepository<User, Integer> {
    
    Optional<User> findOneByName(String name);
    
}

3. Stream API

Spring Data also provides the support for one of the most important features of Java 8 – the Stream API.

In the past, whenever we needed to return more than one result, we needed to return a collection:

public interface UserRepository extends JpaRepository<User, Integer> {
    // ...
    List<User> findAll();
    // ...
}

One of the problems with this implementation was the memory consumption.

We had to eagerly load and keep all retrieved objects in it.

We could improve by leveraging paging:

public interface UserRepository extends JpaRepository<User, Integer> {
    // ...
    Page<User> findAll(Pageable pageable);
    // ...
}

In some scenarios, that’s enough, but in others – pagination is really not the way to go, due to the high number of requests necessary to retrieve the data.

Thanks to Java 8 Stream API and JPA providers – we can now define that our repository method returns just a Stream of objects:

public interface UserRepository extends JpaRepository<User, Integer> {
    // ...
    Stream<User> findAllByName(String name);
    // ...
}

Spring Data uses provider-specific implementation to stream the result (Hibernate uses ScrollableResultSet, EclipseLink uses ScrollableCursor). It reduces the amount of memory consumption and query calls to a database. Because of that, it’s also much faster than two solutions mentioned earlier.

Processing data with a Stream requires us to close a Stream when we finish it.

It can be done by calling the close() method on a Stream or by using try-with-resources:

try (Stream<User> foundUsersStream 
  = userRepository.findAllByName(USER_NAME_ADAM)) {
 
assertThat(foundUsersStream.count(), equalTo(3l));

We must also remember to call a repository method within a transaction. Otherwise, we’ll get an exception:

org.springframework.dao.InvalidDataAccessApiUsageException: You’re trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction.

4. CompletableFuture

Spring Data repositories can run asynchronously with the support of Java 8’s CompletableFuture and Spring mechanism for asynchronous method execution:

@Async
CompletableFuture<User> findOneByStatus(Integer status);

A client which calls this method will return a future immediately but a method will continue an execution in a different thread.

More info about CompletableFuture processing can be found here.

5. Conclusion

In this tutorial, we showed how Java 8 features work together with Spring Data.

The full implementation of the examples is available over on Github.

Related posts:

Spring Boot - CORS Support
Testing in Spring Boot
An Example of Load Balancing with Zuul and Eureka
Add Multiple Items to an Java ArrayList
Using JWT with Spring Security OAuth (legacy stack)
Use Liquibase to Safely Evolve Your Database Schema
Reactive WebSockets with Spring 5
Spring Boot Security Auto-Configuration
Debug a JavaMail Program
Spring Boot - Tomcat Port Number
Java Program to Find Maximum Element in an Array using Binary Search
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java
Debug a HttpURLConnection problem
Một số tính năng mới về xử lý ngoại lệ trong Java 7
Configure a RestTemplate with RestTemplateBuilder
Tạo ứng dụng Java RESTful Client với thư viện OkHttp
Java Program to Implement Coppersmith Freivald’s Algorithm
A Quick Guide to Spring MVC Matrix Variables
How to Store Duplicate Keys in a Map in Java?
Java Program to Find the Peak Element of an Array O(n) time (Naive Method)
Jackson – JsonMappingException (No serializer found for class)
Java Program to Create a Balanced Binary Tree of the Incoming Data
Spring Boot: Customize the Jackson ObjectMapper
Spring Boot Change Context Path
Rest Web service: Filter và Interceptor với Jersey 2.x (P1)
Java Program to Find the Minimum Element of a Rotated Sorted Array using Binary Search approach
Lớp Collections trong Java (Collections Utility Class)
Java Program to Implement Stack using Linked List
Java Program to Implement wheel Sieve to Generate Prime Numbers Between Given Range
A Guide to Apache Commons Collections CollectionUtils
Java Program to Represent Graph Using Linked List
Spring @Primary Annotation