I'm making a practice Spring Boot application.
I've got 3 entities(edited as recommended):
class User extends EntityModelTemplate {
String username; //unique
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
List<UserRoles> userRoles;
}
@IdClass(UserRolesKey.class)
class UserRoles {
@Id
@ManyToOne
@JoinColumn(name = "user_id")
User user;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_id")
Role role;
}
class Role extends EntityModelTemplate{
String role;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "role")
List<UserRoles> userRoles;
}
I wanted to make a single DB call which would fetch user along with roles so I tried using Specifications. I read the documentation and searched examples online before coming up with something that should work like I wanted it to.
Service method(edited as recommended):
public UserDTO getUserForSecurityCheck(String username) throws UsernameNotFoundException {
log.info("Repository call start!");
Optional<User> user = userRepo.findOne(Specification.where(UserSpecifications.usernameEquals(username)));
log.info("Repository call end!");
return user.map(UserDTO::new).orElseThrow(() -> new UsernameNotFoundException("Username "+username+" not found!"));
}
Specification:
public static Specification<User> usernameEquals(String username){
return (root, query, criteriaBuilder) -> {
( (Join<Object, Object>) root.fetch(User_.USER_ROLES)).fetch(UserRoles_.ROLE);
return criteriaBuilder.equal(root.get(User_.USERNAME), username);
};
}
It works perfectly except Hibernate makes two DB calls.
(edit)
The first one selects all that is needed using two joins where username=? (it's rather long, hence the short, short version). Which is exactly what I wanted it to do. But then it makes a second call which literally does Select *(except userRoles) from user where user_id=?.
Why?
Even though I'm still a novice at coding in general, I was pretty sure I understood how Specification and Hibernate work. Basics, at least. Obviously I don't.
So, my question is: is it suppose to make two DB calls or what am I doing wrong?
Thanks in advance.
I've found my mistake and I'm pretty much embarrassed for not noticing it right away: User in UserRoles is loaded eagerly by default. That was the second call. I changed it to lazy and it's working as intended.
If it weren't for M.Deinum asking about a lazy collection, I've wouldn't have noticed it in a million years.