How should I use JpaRepository.findOne() with SpringBoot?
findOne()
is defined as <S extends T> Optional<S> findOne(Example<S> example);
.
It means that in your case it accepts a Example<Reader>
and returns an Optional<Reader>
.
You passed to it a String
, which is wrong and you use it as lambda return in AuthenticationManagerBuilder.userDetailsService()
, which is also wrong
because UserDetailsService
is an interface functional defined as
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
So you need to return an UserDetails
instance not an Optional
of it or to throw UsernameNotFoundException
if no matching with the username to be compliant with the javadoc :
Returns:
a fully populated user record (never null)
Throws:
UsernameNotFoundException - if the user could not be found or the user has no GrantedAuthority
Besides you don't need to use findOne()
that is a query by example. A query by ID is enough.
So you could write something like that :
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> readerRepository.findById(username)
.orElseThrow( () -> new UsernameNotFoundException("user with username " + username + " not found"));
}
As a side note, getOne()
is tricky enough as it relies on lazy loading that may give bad surprises in some cases.
The remark of JB Nizet was interesting.
So I tested right now. It happens that the JPA session is not still opened when the entity (namely isAccountNonLocked()
) is accessed by the Spring Security classes.
So a LazyInitializationException
is thrown in any case (username correct or no) :
org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:155) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:268) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73) at davidhxxx.example.angularsboot.model.db.User_$$_jvstd90_5.isAccountNonLocked(User_$$_jvstd90_5.java) at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider$DefaultPreAuthenticationChecks.check(AbstractUserDetailsAuthenticationProvider.java:352) at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165)
This question may interest you.
As others have said, in the latest versions of Spring Data 2.x, you should use findById, instead of findOne, findOne in the latest version of Spring Data (that is part of Spring-Boot 2.x if you are using that) wants an example object. My guess is that the book you were using was written before the recent release of Spring 5 / Spring Boot 2 / Spring Data 2.x.
Hopefully reading the migration guide as a reference alongside your [slightly out-of-date] book will help: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide