У меня есть таблицы:
users (id, name, email, password)
user_statuses (user_id, is_premium, is_advanced, user_rank_id)
user_ranks (id, name, ordinal)
Таким образом, отношение между User и UserStatus равно 1-1, и у меня есть следующие классы сущностей:
@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String email;
private String password;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private UserStatus status;
}
@Entity
@Table(name = "user_statuses")
@Getter
@Setter
@NoArgsConstructor
public class UserStatus {
@Id
private long id;
@MapsId
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
private boolean isPremium;
private boolean isAdvanced;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_rank_id")
private UserRank rank;
}
@Entity
@Table(name = "user_ranks")
@Getter
@Setter
@NoArgsConstructor
public class UserRank {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private int ordinal;
}
Затем я создал конечную точку "/ users / {id} ", который должен возвращать адрес электронной почты пользователя в виде строки:
@GetMapping("/users/{id}")
public String getUserEmail(@PathVariable("id") long userId) {
User user = service.getUser(userId);
return user.getEmail();
}
Когда я вызываю указанную выше конечную точку, я получаю адрес электронной почты пользователя в качестве ответа, однако, глядя в журнал консоли, я вижу, что спящий режим выполнен 2 запроса, но никто не просил его сделать это:
Первый для получения информации о пользователе:
SELECT
user0_.id AS id1_2_0_,
user0_.email AS email2_2_0_,
user0_.name AS name3_2_0_,
user0_.password AS password4_2_0_
FROM
users user0_
WHERE
user0_.id = 1;
И второй для получения статуса пользователя, связанного с этим объектом «Пользователь»:
SELECT
userstatus0_.user_id AS user_id1_1_0_,
userstatus0_.is_advanced AS is_advan2_1_0_,
userstatus0_.is_premium AS is_premi3_1_0_,
userstatus0_.user_rank_id AS user_ran4_1_0_
FROM
user_statuses userstatus0_
WHERE
userstatus0_.user_id = 1;
Итак, я смущен: почему спящий режим запускает второй запрос, когда я устанавливаю fetch = FetchType.LAZY
для каждого отношения ... Похоже, что LAZY игнорируется для аннотации @OneToOne
?
Я делаю не использовать EntityGraph.
Как остановить спящий режим для выполнения второго запроса?
EDIT
Итак, оказывается, что Hibernate игнорирует мою ленивую подсказку, потому что это необходимо решить, следует ли инициализировать свойство с помощью N ULL или ProxyObject, что имеет смысл. Эта ссылка хорошо объясняет это:
https://thorben-janssen.com/hibernate-tip-lazy-loading-one-to-one/
Однако эта ссылка также предполагает, что лучший способ смоделировать это - однонаправленный One to One, и он говорит, что я могу всегда получать UserStatus на основе идентификатора пользователя (поскольку обе таблицы "имеют общий" первичный ключ)
Однако это меня немного смущает, потому что я могу получить обе строки с помощью одного запроса (SELECT * FROM users LEFT JOIN user_statuses ON users.id = user_statuses.user_id
), но с описанным подходом в ссылке мне нужно 2 запроса, и, насколько я знаю (что я могу ошибаться), 1 запрос лучше, чем выполнение 2 запросов, а также, если я хочу получить 25 пользователей и их статусы пользователей, мне также понадобятся 2 запросы, один для выборки пользователей, а затем для выборки основных состояний пользователей и, наконец, записи вложенных для каждого цикла, чтобы присоединиться к этим объектам. Я мог бы просто выполнить один-единственный запрос, чтобы получить все ...