Почему Hibernate со спецификацией делает два запроса, когда первый получает все, что было задано? - PullRequest
1 голос
/ 28 января 2020

Я делаю тренировочное приложение Spring Boot.

У меня есть 3 объекта (отредактировано в соответствии с рекомендациями):

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;
}

Я хотел сделать сингл вызов БД, который выбирает пользователя вместе с ролями, поэтому я попытался использовать спецификации. Я прочитал документацию и искал примеры в Интернете, прежде чем придумал что-то, что должно работать так, как я хотел.

Метод обслуживания (отредактированный в соответствии с рекомендациями):

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!"));
}

Спецификация:

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);
    };
}

Он отлично работает, за исключением того, что Hibernate делает два вызова БД.

(правка)

Logs

Первый выбирает все, что нужно, используя два объединения, где username =? (это довольно долго, следовательно, короткая, короткая версия). Именно это я и хотел. Но затем он делает второй вызов, который буквально делает Select * (кроме userRoles) от пользователя, где user_id =?.

Почему?

Хотя я все еще новичок в кодировании в целом, Я был почти уверен, что понял, как работают Спецификация и Hibernate. Основы, по крайней мере. Очевидно, что нет.

Итак, мой вопрос: предполагается ли сделать два вызова БД или что я делаю не так?

Заранее спасибо.

1 Ответ

0 голосов
/ 28 января 2020

Я обнаружил свою ошибку и очень смущен, что сразу ее не заметил: по умолчанию пользователь в UserRoles загружен. Это был второй звонок. Я изменил его на ленивый, и он работает как задумано.

Если бы М.Дейн не спрашивал о ленивой коллекции, я бы не заметил этого через миллион лет.

...