Использование второго поля для того же столбца в сущности для равенства хэш-кода и управления сущностью

В нашем проекте мы используем jpa данных spring с спящим режимом.

Ниже приведен пример представления сущностей.

@Entity
public class Author {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long authorId;
 
    private String firstName;
 
    @OneToOne(mappedBy = "author")
    private Book book;


 
    ...
}


@Entity
public class Book {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String title;
 
    @OneToOne(fetch= FetchType.LAZY)
    private Author author;

    ...
}

Пожалуйста, простите любые проблемы с синтаксисом.

У нас есть только поле ассоциации OneToOne для автора в объекте книги. Это имеет много недостатков для нашего дизайна.

  1. Автор всегда лениво извлекается.
  2. Мы однозначно идентифицируем книгу с идентификатором автора
  3. У нас есть authorId в таблице книг в качестве внешнего ключа.
  4. Если вы внимательно понимаете, мы не можем использовать authorId для уникальной идентификации записи только из-за ленивых ассоциаций гибернации, в равном и контракте хэш-кода, даже если мы хотим его использовать. Если мы используем ассоциацию автора в равных и хэш-коде, он будет охотно вызывать сущность автора для каждой книги, которая нам не нужна.
  5. Здесь у нас ранее был контрольно-пропускной пункт, так как book.author.authorId использовался в слишком многих местах, и это требовало дополнительного соединения или запроса таблицы авторов только для того, чтобы получить только идентификатор.

Чтобы решить эту проблему, мы создали еще одно поле authorId в объекте Book.

  • С этим полем мы можем делать что-то вроде book.authorId во всех запросах, не извлекая сущность автора.
  • Именно так мы делаем insertable и updateable ложными, как упоминалось на многих форумах, чтобы один столбец отображался на несколько полей.
@Entity
public class Book {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    private String title;
 
    @OneToOne
    private Author author;
    
    @Column(name = "authorId", insertable = false,updatable = false)
    private Long authorId

    ...
}

Я тестировал следующее до сих пор:

  1. Получить с authorId отлично работает.
  2. Работает с hql jpql с базовыми тестами
  3. Хотя не уверен насчет ограничений.

Вопросы:

  1. Насколько я понял, это поле предназначено только для чтения и установлено для меня спящим режимом, я никогда не должен явно нигде обновлять это поле, правильно ли я понимаю?
  2. Можем ли мы безопасно использовать authorId, представленный как новое поле в запросах hql/jpql и sql
  3. Будет ли это поле корректно управляться спящим режимом?
  4. Нам нужно установить это явно? Например: когда я создаю книгу, я могу сделать так
Author author = repository.findById();
Book b = new Book();
b.setAuthor(author)
b.setAuthorId(author.getId)

также любой другой случай, когда мне явно нужно установить book.authorId ? Будет ли он правильно управляться с помощью спящего режима?

Вы пробовали изменить поле Bookin Author на List<Book>. Таким образом, должно работать сопоставление один ко многим.

fnymmm 05.06.2023 08:47

Согласен с @fnymmm - почему автор должен быть привязан ровно к одной книге? Лучше использовать отношение «один ко многим».

Arno 05.06.2023 21:28

прежде всего уточните свою функциональную потребность, вы действительно хотите, чтобы у автора было ровно 0 или 1 книга? потому что это то, что мы видим из предоставленного вами кода

gaetan224 06.06.2023 10:00

Будет ровно 1 книга. @fnymmm и Арно, как я уже сказал в посте, сущности - это просто имитация реального сценария.

swapyonubuntu 06.06.2023 13:59
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
113
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. Насколько я понял, это поле предназначено только для чтения и установлено для меня спящим режимом, я никогда не должен явно нигде обновлять это поле, правильно ли я понимаю?

Да, как только одно и то же поле используется для ассоциации, вы не можете его трогать. Если вам нужно обновить значение в БД, вы должны сделать это через обновление Book.author. В своей сущности просто создайте частный сеттер в своей сущности Booke, чтобы убедиться, что поле не будет затронуто.

  1. Можем ли мы безопасно использовать authorId, представленный как новое поле в запросах hql/jpql и sql

Вы можете, но, вероятно, не должны. В своем вопросе выше вы написали:

Здесь у нас ранее был контрольно-пропускной пункт, так как book.author.authorId использовался в слишком многих местах, и это требовало дополнительного объединения или запроса таблицы авторов только для того, чтобы получить только идентификатор.

Если вы посмотрите на сгенерированный код, вы обнаружите, что не сгенерировано предложение join, поскольку Hibernate достаточно умен, чтобы брать значение из столбца author_id таблицы Books. т. е. если вы пишете JPQL-запрос типа

select book from Book book where book.author.auhtorId = :authorId

результирующий запрос будет

select b.* from books b where b.author_id = ?`
  1. Будет ли это поле корректно управляться спящим режимом?

Да, Hibernate в основном полагается на отражение, поэтому даже с закрытыми методами доступа код правильный.

  1. Нам нужно установить это явно?

Нет, вам не нужно этого делать. Вам нужно заботиться о Book.author, потому что это связанная сущность. Как только Book.authorId аннотируется @Column(name = "authorId", insertable = false,updatable = false) Hibernate ожидает, что вы не будете его обновлять, с точки зрения вашего кода этого достаточно:

Author author = repository.findById();
Book b = new Book();
b.setAuthor(author)

P.S. В приведенном выше фрагменте вместо Author author = repository.findById(); вы можете использовать Author author = repository.getById(); сохранение одного вызова БД, поскольку JpaRepository.getById() возвращает прокси с id вместо фактического извлечения данных.

Другие вопросы по теме