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:

Java Program to Generate a Sequence of N Characters for a Given Specific Case
Validate email address exists or not by Java Code
Giới thiệu Google Guice – Dependency injection (DI) framework
What is Thread-Safety and How to Achieve it?
Multipart Upload with HttpClient 4
Lập trình mạng với java
Spring Boot - Actuator
Java Program to Implement Repeated Squaring Algorithm
Java Program to Implement Coppersmith Freivald’s Algorithm
Copy a List to Another List in Java
Java Program to Find Whether a Path Exists Between 2 Given Nodes
Tips for dealing with HTTP-related problems
Java Program to Implement the RSA Algorithm
Spring’s RequestBody and ResponseBody Annotations
Debugging Reactive Streams in Java
Loại bỏ các phần tử trùng trong một ArrayList như thế nào?
Deploy a Spring Boot App to Azure
Spring Cloud AWS – RDS
Lập trình đa luồng với CompletableFuture trong Java 8
Java Program to Search Number Using Divide and Conquer with the Aid of Fibonacci Numbers
Control the Session with Spring Security
Java Program to Check whether Graph is a Bipartite using DFS
Một số ký tự đặc biệt trong Java
Guide to @ConfigurationProperties in Spring Boot
Array to String Conversions
Java Program to Implement Horner Algorithm
Java Program to Implement Pairing Heap
Java Program to Perform Left Rotation on a Binary Search Tree
Java Program to Find the Longest Path in a DAG
Working with Network Interfaces in Java
The Difference Between map() and flatMap()
Spring Boot - Tracing Micro Service Logs