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:
@Transactional, and, as a result, we're getting multiple select statements.findById method 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
In the absence of a query cache, a custom query is always executed against the DB. JPA doesn't know what your query is asking for, so it cannot 'guess' the result. It needs to delegate to the DB.
The only time a query is not executed is when the entity has already been loaded into the context and you call EntityManager.find() directly (which is what the default repository method does under the hood).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With