Основные аннотации JPA (Часть 2)

Основные аннотации JPA (Часть 2)
@OneToOne – связь "один к одному" между сущностями. Например, у пользователя один профиль, и профиль принадлежит одному пользователю. Реализуется либо через Foreign Key с уникальным ограничением, либо совместным первичным ключом. В JPA часто сопровождается @JoinColumn (чтобы указать колонку-внешний ключ) или mappedBy (если связь двусторонняя).
@OneToMany – связь "один ко многим": например, один заказ содержит много товарных позиций; одна категория включает много товаров. Реализуется через отдельную таблицу с внешним ключом на "одного". В JPA ставится на стороне коллекции (списка) в "родительском" объекте. Чаще всего используется вместе с mappedBy (если другая сторона @ManyToOne владеет ключом) или через@JoinTable для промежуточной таблицы.
@ManyToOne – связь "многие к одному": обратная сторона для OneToMany. Например, много товаров относятся к одной категории. В базе данных поддерживается внешним ключом в таблице "многих" записей, указывающим на запись в таблице "одного". В JPA ставится на поле-ссылке на родительский объект.
@ManyToMany – связь "многие ко многим": пример – студенты и курсы (каждый студент может записаться на много курсов, каждый курс включает много студентов). В реляционной модели такая связь реализуется через промежуточную таблицу. В JPA это можно настроить аннотацией @JoinTable, указывая имя связной таблицы и колонки ключей. Если @ManyToMany двусторонняя, одна сторона определяется как mappedBy чтобы избежать дублирования.
@JoinColumn – используется вместе с отношениями @OneToOne, @OneToMany, @ManyToOne для указания имени колонки внешнего ключа. Например, в классе Order с поле customer можно указать @JoinColumn(name="customer_id") чтобы явно задать имя колонки в таблице ORDER, которая хранит ID покупателя. Если не указать, Hibernate по умолчанию сформирует имя из сочетания имени поля и первичного ключа целевой сущности (например, customer_id если PK у Customer это id).
@JoinTable – применяется в связях, требующих промежуточной таблицы (@ManyToMany, или унидирекционное @OneToMany при отсутствии обратной стороны). В аннотации задают название таблицы и пары колонок для обоих сторон связи. Например, @JoinTable(name="student_course", joinColumns=@JoinColumn(name="student_id"), inverseJoinColumns=@JoinColumn(name="course_id")). Hibernate тогда будет знать, что при сохранении связей нужно вставлять записи в student_course.
@Cacheable – включает кэш второго уровня для данной сущности. Если настроен общий кэш-провайдер (EHCache, Infinispan и др.), то при пометке сущности Cacheable Hibernate будет хранить экземпляры этой сущности в кэше, чтобы при повторных запросах (по ID, например) не ходить в базу, а брать из кэша. Это повышает производительность чтения, но требует внимательного управления (устаревание кэша при изменениях и т.д.).
@ElementCollection – обозначает коллекцию в сущности, которая хранит не отдельные сущности, а встраиваемые (Embeddable) или простые типы. Например, у пользователя список телефонов (строки). Можно сделать @ElementCollection List<String> phones – Hibernate создаст отдельную вспомогательную таблицу для этих телефонов с ссылкой на пользователя, и будет её заполнять. Или коллекция встроенных объектов (Embeddable) тоже поддерживается.
@Enumerated(EnumType.STRING) – используется для полей-перечислений (enum). По умолчанию JPA сохраняет enum как число (ordinal), что неочевидно в базе. @Enumerated(EnumType.STRING) заставит хранить строковое название константы. Это предпочтительный способ, так как сохраняет читаемость данных и не ломается, если менять порядок элементов в enum.
@DiscriminatorColumn / @DiscriminatorValue – используются при наследовании сущностей (Single Table или Joined strategy) для указания специальной колонки, хранящей тип сущности. Например, класс Payment может иметь наследников CreditCardPayment, CashPayment, и при стратегии "Single Table" все хранятся в одной таблице, а колонка discriminator (например TYPE) содержит значение "CARD" или "CASH". @DiscriminatorColumn(name="TYPE") задает имя колонки, а @DiscriminatorValue("CARD") на наследнике задает значение. В JPA по умолчанию discriminator включается для Single Table стратегии, а имя колонки можно переопределить этой аннотацией.



Пояснение

 Работа с отношениями – ключевая часть ORM:
@OneToOne: Может быть как в одной таблице (редко, через общую PK), так и через FK. В JPA надо решить, кто владелец: обычно один из двух классов содержит поле с @OneToOne и @JoinColumn – он владеет связью. Второй класс ставит @OneToOne(mappedBy="...") и не имеет отдельной колонки. Например, класс User имеет @OneToOne @JoinColumn(name="profile_id") Profile profile. Класс Profile: @OneToOne(mappedBy="profile") User user. Тогда колонка PROFILE_ID будет в таблице USER. Если указать optional=false, это значит связь обязательна (INNER JOIN при выборках по умолчанию).
@OneToMany / @ManyToOne: Это самая распространенная связь (например, у заказа много позиций, у позиции один заказ). Здесь владеющая сторона обычно ManyToOne (у Position поле order с @ManyToOne и @JoinColumn order_id). А в Order делаем @OneToMany(mappedBy="order") List<Position> positions. mappedBy говорит, что физических колонок для связи в таблице Order нет, они на другой стороне. Можно наоборот: сделать @OneToMany владеющим через @JoinTable, тогда появится отдельная таблица. Но обычно OneToMany делаем невладеющим, т.к. проще.
@ManyToMany: Всегда реализуется через третью таблицу. JPA позволяет не создавать вручную класс для этой таблицы, а описать прямую связь M:N. Но нужно понимать, что за этим скрыта дополнительная таблица. В двусторонней ManyToMany кто-то должен быть владельцем (указывать @JoinTable), другой mappedBy. Пример: у Student есть @ManyToMany @JoinTable(name="student_course", joinColumns=@JoinColumn(name="student_id"), inverseJoinColumns=@JoinColumn(name="course_id")) Set<Course> courses;. В Course тогда @ManyToMany(mappedBy="courses") Set<Student> students;. Так Hibernate знает, что таблица student_course хранит связи.
@Cacheable: Второй уровень кэша может значительно ускорять повторяющиеся чтения, но его использование нужно согласовывать в команде и конфигурировать провайдер кэша. Spring Boot, например, позволяет легко включить его (spring.jpa.properties.hibernate.cache... etc.). Без @Cacheable сущности не кэшируются (кэш только первого уровня — в пределах сессии/EntityManager).
@ElementCollection: Это удобнее, чем создавать отдельную сущность Phone и связь OneToMany для списка телефонов, если телефон – просто значение. Hibernate создает скрытую сущность и таблицу, но для программиста это выглядит как обычный список внутри User. Элементами могут быть встроемые объекты тоже. Пример: класс Order имеет @ElementCollection List<String> specialNotes; Hibernate сделает таблицу ORDER_NOTES(order_id, note) и сохранит там строки.
@Enumerated: Настоятельно рекомендуемое использование – EnumType.STRING. Иначе, если ваш enum [RED, GREEN, BLUE] хранится как 0,1,2, то при перестановке или добавлении нового цвета данные потеряют смысл (напр. вставили новый элемент в enum в середину – все сохраненные числовые значения будут ссылаться уже не на те цвета).
Наследование (@DiscriminatorColumn): JPA позволяет хранить наследование классов тремя способами (Single Table – все в одной таблице, Joined – отдельные таблицы для родителя и дочерних, Table Per Class – нечасто используется). В Single Table обычно добавляют колонку "discriminator", по которой определяют какой это подкласс. Hibernate сам может назвать её DTYPE и значения – именами классов. Но через аннотации, как выше, можно задать свои. Например, @DiscriminatorColumn(name="PAYMENT_TYPE") в базовом классе Payment, и @DiscriminatorValue("CARD") в подклассе CardPayment. Тогда при выборке Payment Hibernate читает колонку PAYMENT_TYPE и понимает, экземпляр какого класса создать.
Отметим, что во всем перечисленном над аннотациями JPA есть соответствующие элементы XML конфигурации (если кто-то не хочет использовать аннотации), но это уже крайне редкая ситуация. Аннотации – основной способ.

Автор: к.п.н., Румянцев Сергей Александрович, доцент Финансового университета при Правительстве РФ; доцент ОЧУВО Международного инновационного университета; Консалтинг, управление разработкой ПО; системный и бизнес анализ; менеджмент; аналитиз данных; управление ИТ. Телефон для связи +79269444818 (мессенджеры)   Короткая ссылка:
Основные аннотации JPA (Часть 2)
@OneToOne – связь "один к одному" между сущностями. Например, у пользователя один профиль, и профиль принадлежит одному пользователю. Реализуется либо через Foreign Key с уникальным ограничением, либо совместным первичным ключом. В JPA часто сопровождается @JoinColumn (чтобы указать колонку-внешний ключ) или mappedBy (если связь двусторонняя).
@OneToMany – связь "один ко многим": например, один заказ содержит много товарных позиций; одна категория включает много товаров. Реализуется через отдельную таблицу с внешним ключом на "одного". В JPA ставится на стороне коллекции (списка) в "родительском" объекте. Чаще всего используется вместе с mappedBy (если другая сторона @ManyToOne владеет ключом) или через@JoinTable для промежуточной таблицы.
@ManyToOne – связь "многие к одному": обратная сторона для OneToMany. Например, много товаров относятся к одной категории. В базе данных поддерживается внешним ключом в таблице "многих" записей, указывающим на запись в таблице "одного". В JPA ставится на поле-ссылке на родительский объект.
@ManyToMany – связь "многие ко многим": пример – студенты и курсы (каждый студент может записаться на много курсов, каждый курс включает много студентов). В реляционной модели такая связь реализуется через промежуточную таблицу. В JPA это можно настроить аннотацией @JoinTable, указывая имя связной таблицы и колонки ключей. Если @ManyToMany двусторонняя, одна сторона определяется как mappedBy чтобы избежать дублирования.
@JoinColumn – используется вместе с отношениями @OneToOne, @OneToMany, @ManyToOne для указания имени колонки внешнего ключа. Например, в классе Order с поле customer можно указать @JoinColumn(name="customer_id") чтобы явно задать имя колонки в таблице ORDER, которая хранит ID покупателя. Если не указать, Hibernate по умолчанию сформирует имя из сочетания имени поля и первичного ключа целевой сущности (например, customer_id если PK у Customer это id).
@JoinTable – применяется в связях, требующих промежуточной таблицы (@ManyToMany, или унидирекционное @OneToMany при отсутствии обратной стороны). В аннотации задают название таблицы и пары колонок для обоих сторон связи. Например, @JoinTable(name="student_course", joinColumns=@JoinColumn(name="student_id"), inverseJoinColumns=@JoinColumn(name="course_id")). Hibernate тогда будет знать, что при сохранении связей нужно вставлять записи в student_course.
@Cacheable – включает кэш второго уровня для данной сущности. Если настроен общий кэш-провайдер (EHCache, Infinispan и др.), то при пометке сущности Cacheable Hibernate будет хранить экземпляры этой сущности в кэше, чтобы при повторных запросах (по ID, например) не ходить в базу, а брать из кэша. Это повышает производительность чтения, но требует внимательного управления (устаревание кэша при изменениях и т.д.).
@ElementCollection – обозначает коллекцию в сущности, которая хранит не отдельные сущности, а встраиваемые (Embeddable) или простые типы. Например, у пользователя список телефонов (строки). Можно сделать @ElementCollection List<String> phones – Hibernate создаст отдельную вспомогательную таблицу для этих телефонов с ссылкой на пользователя, и будет её заполнять. Или коллекция встроенных объектов (Embeddable) тоже поддерживается.
@Enumerated(EnumType.STRING) – используется для полей-перечислений (enum). По умолчанию JPA сохраняет enum как число (ordinal), что неочевидно в базе. @Enumerated(EnumType.STRING) заставит хранить строковое название константы. Это предпочтительный способ, так как сохраняет читаемость данных и не ломается, если менять порядок элементов в enum.
@DiscriminatorColumn / @DiscriminatorValue – используются при наследовании сущностей (Single Table или Joined strategy) для указания специальной колонки, хранящей тип сущности. Например, класс Payment может иметь наследников CreditCardPayment, CashPayment, и при стратегии "Single Table" все хранятся в одной таблице, а колонка discriminator (например TYPE) содержит значение "CARD" или "CASH". @DiscriminatorColumn(name="TYPE") задает имя колонки, а @DiscriminatorValue("CARD") на наследнике задает значение. В JPA по умолчанию discriminator включается для Single Table стратегии, а имя колонки можно переопределить этой аннотацией.



Пояснение

 Работа с отношениями – ключевая часть ORM:
@OneToOne: Может быть как в одной таблице (редко, через общую PK), так и через FK. В JPA надо решить, кто владелец: обычно один из двух классов содержит поле с @OneToOne и @JoinColumn – он владеет связью. Второй класс ставит @OneToOne(mappedBy="...") и не имеет отдельной колонки. Например, класс User имеет @OneToOne @JoinColumn(name="profile_id") Profile profile. Класс Profile: @OneToOne(mappedBy="profile") User user. Тогда колонка PROFILE_ID будет в таблице USER. Если указать optional=false, это значит связь обязательна (INNER JOIN при выборках по умолчанию).
@OneToMany / @ManyToOne: Это самая распространенная связь (например, у заказа много позиций, у позиции один заказ). Здесь владеющая сторона обычно ManyToOne (у Position поле order с @ManyToOne и @JoinColumn order_id). А в Order делаем @OneToMany(mappedBy="order") List<Position> positions. mappedBy говорит, что физических колонок для связи в таблице Order нет, они на другой стороне. Можно наоборот: сделать @OneToMany владеющим через @JoinTable, тогда появится отдельная таблица. Но обычно OneToMany делаем невладеющим, т.к. проще.
@ManyToMany: Всегда реализуется через третью таблицу. JPA позволяет не создавать вручную класс для этой таблицы, а описать прямую связь M:N. Но нужно понимать, что за этим скрыта дополнительная таблица. В двусторонней ManyToMany кто-то должен быть владельцем (указывать @JoinTable), другой mappedBy. Пример: у Student есть @ManyToMany @JoinTable(name="student_course", joinColumns=@JoinColumn(name="student_id"), inverseJoinColumns=@JoinColumn(name="course_id")) Set<Course> courses;. В Course тогда @ManyToMany(mappedBy="courses") Set<Student> students;. Так Hibernate знает, что таблица student_course хранит связи.
@Cacheable: Второй уровень кэша может значительно ускорять повторяющиеся чтения, но его использование нужно согласовывать в команде и конфигурировать провайдер кэша. Spring Boot, например, позволяет легко включить его (spring.jpa.properties.hibernate.cache... etc.). Без @Cacheable сущности не кэшируются (кэш только первого уровня — в пределах сессии/EntityManager).
@ElementCollection: Это удобнее, чем создавать отдельную сущность Phone и связь OneToMany для списка телефонов, если телефон – просто значение. Hibernate создает скрытую сущность и таблицу, но для программиста это выглядит как обычный список внутри User. Элементами могут быть встроемые объекты тоже. Пример: класс Order имеет @ElementCollection List<String> specialNotes; Hibernate сделает таблицу ORDER_NOTES(order_id, note) и сохранит там строки.
@Enumerated: Настоятельно рекомендуемое использование – EnumType.STRING. Иначе, если ваш enum [RED, GREEN, BLUE] хранится как 0,1,2, то при перестановке или добавлении нового цвета данные потеряют смысл (напр. вставили новый элемент в enum в середину – все сохраненные числовые значения будут ссылаться уже не на те цвета).
Наследование (@DiscriminatorColumn): JPA позволяет хранить наследование классов тремя способами (Single Table – все в одной таблице, Joined – отдельные таблицы для родителя и дочерних, Table Per Class – нечасто используется). В Single Table обычно добавляют колонку "discriminator", по которой определяют какой это подкласс. Hibernate сам может назвать её DTYPE и значения – именами классов. Но через аннотации, как выше, можно задать свои. Например, @DiscriminatorColumn(name="PAYMENT_TYPE") в базовом классе Payment, и @DiscriminatorValue("CARD") в подклассе CardPayment. Тогда при выборке Payment Hibernate читает колонку PAYMENT_TYPE и понимает, экземпляр какого класса создать.
Отметим, что во всем перечисленном над аннотациями JPA есть соответствующие элементы XML конфигурации (если кто-то не хочет использовать аннотации), но это уже крайне редкая ситуация. Аннотации – основной способ.

https://webprogr.ru/~zFZ06
Короткая ссылка на новость:https://webprogr.ru/~zFZ06


Последние новости

Основные аннотации JPA (Часть 2)

Основные аннотации JPA (Часть 2)
@OneToOne – связь "один к одному" между сущностями. Например, у пользователя один профиль, и профиль принадлежит одному пользователю. Реализуется либо через Foreign Key с уникальным ограничением, либо совместным первичным ключом. В JPA часто сопровождается @JoinColumn (чтобы указать колонку-внешний ключ) или mappedBy (если связь двусторонняя).
@OneToMany – связь "один ко многим": например, один заказ содержит много товарных позиций; одна категория включает много товаров. Реализуется через отдельную таблицу с внешним ключом на "одного". В JPA ставится на стороне коллекции (списка) в "родительском" объекте. Чаще всего используется вместе с mappedBy (если другая сторона @ManyToOne владеет ключом) или через@JoinTable для промежуточной таблицы.
@ManyToOne – связь "многие к одному": обратная сторона для OneToMany. Например, много товаров относятся к одной категории. В базе данных поддерживается внешним ключом в таблице "многих" записей, указывающим на запись в таблице "одного". В JPA ставится на поле-ссылке на родительский объект.
@ManyToMany – связь "многие ко многим": пример – студенты и курсы (каждый студент может записаться на много курсов, каждый курс включает много студентов). В реляционной модели такая связь реализуется через промежуточную таблицу. В JPA это можно настроить аннотацией @JoinTable, указывая имя связной таблицы и колонки ключей. Если @ManyToMany двусторонняя, одна сторона определяется как mappedBy чтобы избежать дублирования.
@JoinColumn – используется вместе с отношениями @OneToOne, @OneToMany, @ManyToOne для указания имени колонки внешнего ключа. Например, в классе Order с поле customer можно указать @JoinColumn(name="customer_id") чтобы явно задать имя колонки в таблице ORDER, которая хранит ID покупателя. Если не указать, Hibernate по умолчанию сформирует имя из сочетания имени поля и первичного ключа целевой сущности (например, customer_id если PK у Customer это id).
@JoinTable – применяется в связях, требующих промежуточной таблицы (@ManyToMany, или унидирекционное @OneToMany при отсутствии обратной стороны). В аннотации задают название таблицы и пары колонок для обоих сторон связи. Например, @JoinTable(name="student_course", joinColumns=@JoinColumn(name="student_id"), inverseJoinColumns=@JoinColumn(name="course_id")). Hibernate тогда будет знать, что при сохранении связей нужно вставлять записи в student_course.
@Cacheable – включает кэш второго уровня для данной сущности. Если настроен общий кэш-провайдер (EHCache, Infinispan и др.), то при пометке сущности Cacheable Hibernate будет хранить экземпляры этой сущности в кэше, чтобы при повторных запросах (по ID, например) не ходить в базу, а брать из кэша. Это повышает производительность чтения, но требует внимательного управления (устаревание кэша при изменениях и т.д.).
@ElementCollection – обозначает коллекцию в сущности, которая хранит не отдельные сущности, а встраиваемые (Embeddable) или простые типы. Например, у пользователя список телефонов (строки). Можно сделать @ElementCollection List<String> phones – Hibernate создаст отдельную вспомогательную таблицу для этих телефонов с ссылкой на пользователя, и будет её заполнять. Или коллекция встроенных объектов (Embeddable) тоже поддерживается.
@Enumerated(EnumType.STRING) – используется для полей-перечислений (enum). По умолчанию JPA сохраняет enum как число (ordinal), что неочевидно в базе. @Enumerated(EnumType.STRING) заставит хранить строковое название константы. Это предпочтительный способ, так как сохраняет читаемость данных и не ломается, если менять порядок элементов в enum.
@DiscriminatorColumn / @DiscriminatorValue – используются при наследовании сущностей (Single Table или Joined strategy) для указания специальной колонки, хранящей тип сущности. Например, класс Payment может иметь наследников CreditCardPayment, CashPayment, и при стратегии "Single Table" все хранятся в одной таблице, а колонка discriminator (например TYPE) содержит значение "CARD" или "CASH". @DiscriminatorColumn(name="TYPE") задает имя колонки, а @DiscriminatorValue("CARD") на наследнике задает значение. В JPA по умолчанию discriminator включается для Single Table стратегии, а имя колонки можно переопределить этой аннотацией.



Пояснение

 Работа с отношениями – ключевая часть ORM:
@OneToOne: Может быть как в одной таблице (редко, через общую PK), так и через FK. В JPA надо решить, кто владелец: обычно один из двух классов содержит поле с @OneToOne и @JoinColumn – он владеет связью. Второй класс ставит @OneToOne(mappedBy="...") и не имеет отдельной колонки. Например, класс User имеет @OneToOne @JoinColumn(name="profile_id") Profile profile. Класс Profile: @OneToOne(mappedBy="profile") User user. Тогда колонка PROFILE_ID будет в таблице USER. Если указать optional=false, это значит связь обязательна (INNER JOIN при выборках по умолчанию).
@OneToMany / @ManyToOne: Это самая распространенная связь (например, у заказа много позиций, у позиции один заказ). Здесь владеющая сторона обычно ManyToOne (у Position поле order с @ManyToOne и @JoinColumn order_id). А в Order делаем @OneToMany(mappedBy="order") List<Position> positions. mappedBy говорит, что физических колонок для связи в таблице Order нет, они на другой стороне. Можно наоборот: сделать @OneToMany владеющим через @JoinTable, тогда появится отдельная таблица. Но обычно OneToMany делаем невладеющим, т.к. проще.
@ManyToMany: Всегда реализуется через третью таблицу. JPA позволяет не создавать вручную класс для этой таблицы, а описать прямую связь M:N. Но нужно понимать, что за этим скрыта дополнительная таблица. В двусторонней ManyToMany кто-то должен быть владельцем (указывать @JoinTable), другой mappedBy. Пример: у Student есть @ManyToMany @JoinTable(name="student_course", joinColumns=@JoinColumn(name="student_id"), inverseJoinColumns=@JoinColumn(name="course_id")) Set<Course> courses;. В Course тогда @ManyToMany(mappedBy="courses") Set<Student> students;. Так Hibernate знает, что таблица student_course хранит связи.
@Cacheable: Второй уровень кэша может значительно ускорять повторяющиеся чтения, но его использование нужно согласовывать в команде и конфигурировать провайдер кэша. Spring Boot, например, позволяет легко включить его (spring.jpa.properties.hibernate.cache... etc.). Без @Cacheable сущности не кэшируются (кэш только первого уровня — в пределах сессии/EntityManager).
@ElementCollection: Это удобнее, чем создавать отдельную сущность Phone и связь OneToMany для списка телефонов, если телефон – просто значение. Hibernate создает скрытую сущность и таблицу, но для программиста это выглядит как обычный список внутри User. Элементами могут быть встроемые объекты тоже. Пример: класс Order имеет @ElementCollection List<String> specialNotes; Hibernate сделает таблицу ORDER_NOTES(order_id, note) и сохранит там строки.
@Enumerated: Настоятельно рекомендуемое использование – EnumType.STRING. Иначе, если ваш enum [RED, GREEN, BLUE] хранится как 0,1,2, то при перестановке или добавлении нового цвета данные потеряют смысл (напр. вставили новый элемент в enum в середину – все сохраненные числовые значения будут ссылаться уже не на те цвета).
Наследование (@DiscriminatorColumn): JPA позволяет хранить наследование классов тремя способами (Single Table – все в одной таблице, Joined – отдельные таблицы для родителя и дочерних, Table Per Class – нечасто используется). В Single Table обычно добавляют колонку "discriminator", по которой определяют какой это подкласс. Hibernate сам может назвать её DTYPE и значения – именами классов. Но через аннотации, как выше, можно задать свои. Например, @DiscriminatorColumn(name="PAYMENT_TYPE") в базовом классе Payment, и @DiscriminatorValue("CARD") в подклассе CardPayment. Тогда при выборке Payment Hibernate читает колонку PAYMENT_TYPE и понимает, экземпляр какого класса создать.
Отметим, что во всем перечисленном над аннотациями JPA есть соответствующие элементы XML конфигурации (если кто-то не хочет использовать аннотации), но это уже крайне редкая ситуация. Аннотации – основной способ.

Рейтинг@Mail.ru