spring-data-java-8
Spring Data Java 8 Support
1. Overview
In this quick article, we’ll go through some examples of how we can use these with the framework.
2. 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
The full implementation of the examples is available over on Github.