Spring Data JPA findOne() change to Optional how to use this?
JavaSpringSpring Data-JpaJava Problem Overview
I'm learning SpringBoot2.0
with Java8
.
And I followed some blog-making tutorial example.
The tutorial source code is:
@GetMapping("/{id}/edit")
public String edit(@PathVariable Long id, Model model) {
model.addAttribute("categoryDto", categoryService.findOne(id));
return "category/edit";
}
But this code is throwing this error:
> categoryService.findOne(id)
I'm thinking about changing the JPA findOne()
method to Optional< S >
How to solve that?
More info:
This is the categoryService method:
public Category findOne(Long id) {
return categoryRepository.findOne(id);
}
Java Solutions
Solution 1 - Java
From at least, the 2.0
version, Spring-Data-Jpa
modified findOne()
.
Now, findOne()
has neither the same signature nor the same behavior.
Previously, it was defined in the CrudRepository
interface as:
T findOne(ID primaryKey);
Now, the single findOne()
method that you will find in CrudRepository
is the one defined in the QueryByExampleExecutor
interface as:
<S extends T> Optional<S> findOne(Example<S> example);
That is implemented finally by SimpleJpaRepository
, the default implementation of the CrudRepository
interface.
This method is a query by example search and you don't want that as a replacement.
In fact, the method with the same behavior is still there in the new API, but the method name has changed.
It was renamed from findOne()
to findById()
in the CrudRepository
interface :
Optional<T> findById(ID id);
Now it returns an Optional
, which is not so bad to prevent NullPointerException
.
So, the actual method to invoke is now Optional<T> findById(ID id)
.
How to use that?
Learning Optional
usage.
Here's important information about its specification:
> A container object which may or may not contain a non-null value. If a > value is present, isPresent() will return true and get() will return > the value. > > Additional methods that depend on the presence or absence of a > contained value are provided, such as orElse() (return a default value > if value not present) and ifPresent() (execute a block of code if the > value is present).
Some hints on how to use Optional
with Optional<T> findById(ID id)
.
Generally, as you look for an entity by id, you want to return it or make a particular processing if that is not retrieved.
Here are three classical usage examples.
- Suppose that if the entity is found you want to get it otherwise you want to get a default value.
You could write :
Foo foo = repository.findById(id)
.orElse(new Foo());
or get a null
default value if it makes sense (same behavior as before the API change) :
Foo foo = repository.findById(id)
.orElse(null);
2) Suppose that if the entity is found you want to return it, else you want to throw an exception.
You could write :
return repository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(id));
3) Suppose you want to apply a different processing according to if the entity is found or not (without necessarily throwing an exception).
You could write :
Optional<Foo> fooOptional = fooRepository.findById(id);
if (fooOptional.isPresent()) {
Foo foo = fooOptional.get();
// processing with foo ...
} else {
// alternative processing....
}
Solution 2 - Java
The method has been renamed to findById(…)
returning an Optional
so that you have to handle absence yourself:
Optional<Foo> result = repository.findById(…);
result.ifPresent(it -> …); // do something with the value if present
result.map(it -> …); // map the value if present
Foo foo = result.orElse(null); // if you want to continue just like before
Solution 3 - Java
Indeed, in the latest version of Spring Data, findOne returns an optional. If you want to retrieve the object from the Optional, you can simply use get() on the Optional. First of all though, a repository should return the optional to a service, which then handles the case in which the optional is empty. afterwards, the service should return the object to the controller.
Solution 4 - Java
I always write a default method "findByIdOrError" in widely used CrudRepository repos/interfaces.
@Repository
public interface RequestRepository extends CrudRepository<Request, Integer> {
default Request findByIdOrError(Integer id) {
return findById(id).orElseThrow(EntityNotFoundException::new);
}
}
Solution 5 - Java
Optional
api provides methods for getting the values. You can check isPresent()
for the presence of the value and then make a call to get()
or you can make a call to get()
chained with orElse()
and provide a default value.
The last thing you can try doing is using @Query()
over a custom method.
Solution 6 - Java
The findOne
method of the CrudRepository interface
has been replaced by findById
since version 2.0 of Spring Data Commons.
you replace findOne(id)
by:
findById(id).orElse(null)
Solution 7 - Java
Consider an User
entity and UserRepository
. In service package code like below.
Optional<User> resultUser = UserRepository.findById(userId); //return Optional
User createdUser = resultUser.get(); //return User
Now you can access all the User entity attributes using getter.
createdUser.getId();
createdUser.getName();
like that.