I've created a custom (derived) find method in the repository:
Optional<User> findUserById(Long id);
Its signature is the same as the default one (findById(...)) inherited from JpaRepository but their behavior is a bit different:
- Seems like the derived method does not respect the first-level cache (persistent context) if it's being called multiple times inside the method with
@Transactional, and, as a result, we're getting multiple select statements. - On the other hand, the default
findByIdmethod uses the first level cache, and, in the same conditions as above, only one select statement is actually executed (next calls get the cached result).
The same (multiple selects) happens even if my custom method is called findById too (but in this case we obviously cannot use the default one because it's shaded by it).
Does anybody know why this is happening? Thanks in advance.
I'm using Spring Boot 2.3.4.RELEASE / Hibernate 5.4.21.Final
Some meaningful code is below (the full project with tests can be found on Github):
User entity:
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
private Long id;
private String name;
}
UserRepository:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findUserById(Long id);
}
DatabaseService:
@Service
@RequiredArgsConstructor
@Slf4j
public class DatabaseService {
private final UserRepository userRepository;
@Transactional
public void testDefaultMethod() {
log.info("Start to call default findById");
userRepository.findById(1L);
userRepository.findById(1L);
userRepository.findById(1L);
log.info("End to call default findById");
}
@Transactional
public void testCustomMethod() {
log.info("Start to call custom findUserById");
userRepository.findUserById(1L);
userRepository.findUserById(1L);
userRepository.findUserById(1L);
log.info("End to call custom findUserById");
}
}
Output while calling testDefaultMethod():
2020-10-04 03:11:26.379 INFO 19264 --- [ main] c.e.f.domain.DatabaseService : Start to call default findById
Hibernate: select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=?
2020-10-04 03:11:26.388 INFO 19264 --- [ main] c.e.f.domain.DatabaseService : End to call default findById
Output while calling testCustomMethod():
2020-10-04 03:11:26.399 INFO 19264 --- [ main] c.e.f.domain.DatabaseService : Start to call custom findUserById
Hibernate: select user0_.id as id1_0_, user0_.name as name2_0_ from user user0_ where user0_.id=?
Hibernate: select user0_.id as id1_0_, user0_.name as name2_0_ from user user0_ where user0_.id=?
Hibernate: select user0_.id as id1_0_, user0_.name as name2_0_ from user user0_ where user0_.id=?
2020-10-04 03:11:26.500 INFO 19264 --- [ main] c.e.f.domain.DatabaseService : End to call custom findUserById