какие типы ключей поддерживает jpa

Первичные ключи в JPA

какие типы ключей поддерживает jpa. Смотреть фото какие типы ключей поддерживает jpa. Смотреть картинку какие типы ключей поддерживает jpa. Картинка про какие типы ключей поддерживает jpa. Фото какие типы ключей поддерживает jpa

какие типы ключей поддерживает jpa. Смотреть фото какие типы ключей поддерживает jpa. Смотреть картинку какие типы ключей поддерживает jpa. Картинка про какие типы ключей поддерживает jpa. Фото какие типы ключей поддерживает jpaКаждая JPA сущность должна иметь идентификатор, который её однозначно идентифицирует. В мире SQL подобный идентификатор называется, с некоторыми допущениями, первичный ключ. В качестве такого идентификатора можно использовать примитивные типы и их обёртки, строки, BigDecimal/BigInteger, даты и т.д. Подобный идентификатор может быть:

Естественный ключ или суррогатный ключ

Естественный ключ формируется из полей таблицы, которые идентифицируют запись естественным путём 🙂 Например, если у нас будут пользователи, которые идентифицируются по имени пользователя, это имя и будет естественным первичным ключом:

Естественный ключ вытекает непосредственно из модели данных приложения и, обычно, весьма хорошо в неё вписывается. Теория так же говорит нам, что для любой таблицы/сущности можно сформировать естественный ключ.

С другой стороны, естественный ключ может быть и неудобным в использовании. Например представим, что идентификация пользователей проводится по их номерам телефонов. Вначале эта идея кажется неплохой — у пользователя есть некий номер телефона и он принадлежит только ему, то есть второго такого номера, принадлежащего другому пользователю в тоже самое время, быть не может. Однако, если подумать, в будущем могут возникнуть проблемы — пользователи начнут менять номера телефонов, операторы станут выдавать повторно устаревшие номера и предположение о том, что один номер может быть только у одного человека, станет неверным.

Поэтому зачастую используются суррогатные ключи, которые не связаны явно с моделью данных приложения, а либо порождаются из неё, либо создаются каким-либо другим способом. Типичное решение — добавление к сущности дополнительного целочисленного поля, содержимое которого генерируется автоматически.

Генерируемые автоматически и создаваемые вручную ключи

Значения естественных ключей создаются естественным 🙂 образом при создании экземпляра сущности. А вот суррогатные ключи надо как-то заполнять самому и брать для них откуда-то уникальные значения, что может быть непростым делом в условиях параллельной обработки запросов.

В JPA на этот случай предсмотрены механизмы автоматической генерации значений суррогатных ключей, которые включается аннотацией @GeneratedValue

Источник

Односторонние и двусторонние отношения в Hibernate

Всем нам хорошо известен ответ на вопрос, какими могут быть отношения между сущностями в Hibernate и JPA. Вариантов всего четыре:

Для каждого из отношений есть своя аннотация и, казалось бы, на этом можно закончить разговор, но все не так просто. Да и вообще, может ли быть что-то просто в Hibernate 😉 Каждое из выше перечисленных отношений может быть односторонним (unidirectional) или двусторонним (bidirectional), и если не принимать это во внимание, то можно столкнуться с массой проблем и странностей.

Для примера возьмем две простейшие сущности: пользователь и контакт. Очевидно, что каждый контакт связан с пользователем отношением многие к одному, а пользователь с контактами отношением один ко многим.

Односторонние отношения

Односторонним называется отношение, владельцем которого является только одна из двух сторон. Отсюда и название. Следует заметить, что при этом вторая сторона об этом отношении ничего не знает. Hibernate будет считать владельцем отношения ту сущность, в которой будет поставлена аннотация отношения.

Давайте попробуем сделать владельцем отношения сторону контакта. При этом сущности будут выглядеть следующим образом.

Если запустить этот код, то Hibernate создаст следующую структуру таблиц, которая выглядит для нас вполне привычно. Отношение между таблицами создается при помощи ссылочного поля user_id в таблице contacts.

Но выбор сущности Contact в качестве стороны владельца отношений в данном случае не очень удачен. Очевидно, что нам чаще нужна информация обо всех контактах пользователя чем о том, какому пользователю принадлежит контакт. Попробуем сделать владельцем контакта сущность пользователя. Для этого убираем поле user из класса Contact и добавляем поле со списком контактов в класс User. Получаем следующий код.

Теперь владельцем отношения является сущность пользователя, что более логично, но если запустить данный код и посмотреть на созданную Hibernate структуру таблиц, то мы столкнёмся с одной хорошо известной почти каждому кто использовал эту библиотеку проблемой.

Чтобы связать сущности Hibernate создал дополнительную таблицу связи (join table) с именем users_contacts, хотя сущности вполне можно было бы связать через ссылочное поле в таблице contacts, как в предыдущем случае. Честно говоря, я не совсем понимаю, почему Hibernate поступает именно так. Буду рад, если кто-то поможет с этим разобраться в комментариях к статье.

Проблему можно легко решить добавив аннотацию JoinColumn к полю contacts.

При таких настройках связь будет проводиться при помощи колонки user_id в таблице contacts, а таблица связи создаваться не будет.

Двусторонние отношения

Запускаем код и смотрим на структуру таблиц. Помимо таблиц для пользователей и ролей Hibernate создаст две таблицы связи, хотя нам хватило бы и одной.

Дело в том, что вместо одного двустороннего отношения мы с вами сейчас создали два односторонних. Тоже самое произойдет и для отношения один ко многим. Чтобы Hibernate понял, что мы хотим создать именно двустороннее отношение нам нужно указать, какая из сторон является владельцем отношений, а какая является обратной стороной. Это делается при помощи атрибута mappedBy. Важно отметить, что указывается этот атрибут в аннотации, которая находится на противоположной стороне отношения.

Для отношения многие ко многим любая из сторон может быть владельцем. В случае с ролями и пользователями выберем сущность пользователя в качестве владельца. Для этого изменим описание поля users в классе Role следующим образом.

Теперь Hibernate создаст только одну таблицу связи users_roles.

Для такого кода Hibernate создаст привычную нам структуру из двух таблиц со ссылкой на пользователя в таблице контактов.

На этом все на этот раз! Благодарю, что дочитали до конца и надеюсь, что статья была полезной! Разумеется, очень жду от вас обратную связь в виде голосов и комментариев!

Источник

Собеседование по Java EE — Java Persistence API (JPA) (вопросы и ответы). Часть 1

Общие вопросы и ответы о Java Persistence API — JPA. Часть 1.

Данный раздел был скопирован из статьи хабра https://habrahabr.ru/post/265061/, т.к. там рассмотрены многие стандартные, а также более углубленные темы.
Так же можете посмотреть раздел по Hibernate (фреймворк является реализацией JPA и темы взаимосвязанные), перейдя к списку вопросов раздела EE.

к списку вопросов раздела JEE

Вопросы

1. Что такое JPA?
2. В чем её отличие JPA от Hibernate?
3. Можно ли использовать JPA c noSQl базами?
4. В чем её отличие JPA от JDO?
5. Что такое Entity?
6. Может ли Entity класс наследоваться от не Entity классов (non-entity classes)?
7. Может ли Entity класс наследоваться от других Entity классов?
8. Может ли не Entity класс наследоваться от Entity класса?
9. Может ли Entity быть абстрактным классом?
10. Какие требования JPA к Entity классам вы можете перечислить (не менее шести требований)?
11. Какие два типа элементов есть у Entity классов. Или другими словами перечислите два типа доступа (access) к элементам Entity классов.
12. Что такое атрибут Entity класса в терминологии JPA?
13. Какие типы данных допустимы в атрибутах Entity класса (полях или свойствах)?
14. Какие типы данных можно использовать в атрибутах, входящих в первичный ключ Entity класса (составной или простой), чтобы полученный первичный ключ мог использоваться для любой базы данных? А в случае автогенерируемого первичного ключа (generated primary keys)?
15. Что такое встраиваемый (Embeddable) класс?
16. Может ли встраиваемый (Embeddable) класс содержать другой встраиваемый (Embeddable) класс?
17. Может ли встраиваемый (Embeddable) класс содержать связи (relationship) с другими Entity или коллекциями Entity? Если может, то существуют ли какие-то ограничение на такие связи (relationship)?
18. Какие требования JPA устанавливает к встраиваемым (Embeddable) классам?
19. Какие типы связей (relationship) между Entity вы знаете (перечислите восемь типов, либо укажите четыре типа связей, каждую из которых можно разделить ещё на два вида)?
20. Что такое Mapped Superclass?
21. Какие три типа стратегии наследования мапинга (Inheritance Mapping Strategies) описаны в JPA?
22. Какие два типа fetch стратегии в JPA вы знаете?
23. Что такое EntityManager и какие основные его функции вы можете перечислить?
24. Какие четыре статуса жизненного цикла Entity объекта (Entity Instance’s Life Cycle) вы можете перечислить?
25. Как влияет операция persist на Entity объекты каждого из четырех статусов?
26. Как влияет операция remove на Entity объекты каждого из четырех статусов?
27. Как влияет операция merge на Entity объекты каждого из четырех статусов?
28. Как влияет операция refresh на Entity объекты каждого из четырех статусов?
29. Как влияет операция detach на Entity объекты каждого из четырех статусов?

30. Для чего нужна аннотация Basic?
31. Для чего нужна аннотация Access?
32. Какими аннотациями можно перекрыть связи (override entity relationship) или атрибуты, унаследованные от суперкласса, или заданные в embeddable классе при использовании этого embeddable класса в одном из entity классов и не перекрывать в остальных?
33. Какой аннотацией можно управлять кешированием JPA для данного Entity?
34. Какие аннотации служит для задания класса преобразования basic атрибута Entity в другой тип при сохранении/получении данных их базы (например, работать с атрибутом Entity boolean типа, но в базу сохранять его как число)?
35. Какой аннотацией можно задать класс, методы которого должен выполнится при определенных JPA операциях над данным Entity или Mapped Superclass (такие как удаление, изменение данных и т.п.)?
36. Для чего нужны callback методы в JPA? К каким сущностям применяются аннотации callback методов? Перечислите семь callback методов (или что тоже самое аннотаций callback методов)
37. Какие аннотации служить для установки порядка выдачи элементов коллекций Entity?
38. Какой аннотацей можно исключить поля и свойства Entity из маппинга (property or field is not persistent)?
40. Какие два вида кэшей (cache) вы знаете в JPA и для чего они нужны?
41. Какие есть варианты настройки second-level cache (кэша второго уровня) в JPA или что аналогично опишите какие значения может принимать элемент shared-cache-mode из persistence.xml?
42. Как можно изменить настройки fetch стратегии любых атрибутов Entity для отдельных запросов (query) или методов поиска (find), то если у Entity есть атрибут с fetchType = LAZY, но для конкретного запроса его требуется сделать EAGER или наоборот?
43. Каким способом можно в коде работать с кэшем второго уровня (удалять все или определенные Entity из кеша, узнать закэшировался ли данное Entity и т.п.)?
44. Каким способом можно получить метаданные JPA (сведения о Entity типах, Embeddable и Managed классах и т.п.)?
45. Что такое JPQL (Java Persistence query language) и чем он отличается от SQL?
46. Что означает полиморфизм (polymorphism) в запросах JPQL (Java Persistence query language) и как его «выключить»?
47. Что такое Criteria API и для чего он используется?
48. В чем разница в требованиях к Entity в Hibernate, от требований к Entity, указанных в спецификации JPA (см. вопрос 10)?
49. Какая уникальная стратегия наследования есть в Hibernate, но нет в спецификации JPA?
50. Какие основные новые возможности появились в спецификации JPA 2.1 по сравнению с JPA 2.0 (перечислите хотя бы пять-шесть новых возможностей)?

Ответы

1. Что такое JPA?

JPA (Java Persistence API) это спецификация Java EE и Java SE, описывающая систему управления сохранением java объектов в таблицы реляционных баз данных в удобном виде. Сама Java не содержит реализации JPA, однако существует много реализаций данной спецификации от разных компаний (открытых и нет). Это не единственный способ сохранения java объектов в базы данных (ORM систем), но один из самых популярных в Java мире.

2. В чем её отличие JPA от Hibernate?

Hibernate одна из самых популярных открытых реализаций последней версии спецификации (JPA 2.1). Даже скорее самая популярная, почти стандарт де-факто. То есть JPA только описывает правила и API, а Hibernate реализует эти описания, впрочем у Hibernate (как и у многих других реализаций JPA) есть дополнительные возможности, не описанные в JPA (и не переносимые на другие реализации JPA).

3. Можно ли использовать JPA c noSQl базами?

Вообще, спецификация JPA говорит только об отображении java объектов в таблицы реляционных баз данных, но при этом существует ряд реализаций данного стандарта для noSql баз данных: Kundera, DataNucleus, ObjectDB и ряд других. Естественно, при этом не все специфичные для реляционных баз данных особенности спецификации переносятся на nosql базы полностью.

4. В чем её отличие JPA от JDO?

JPA (Java Persistence API) и Java Data Objects (JDO) две спецификации сохранения java объектов в базах данных. Если JPA сконцентрирована только на реляционных базах, то JDO более общая спецификация которая описывает ORM для любых возможных баз и хранилищ. В принципе можно рассматривать JPA как специализированную на реляционных баз часть спецификации JDO, даже при том что API этих двух спецификаций не полностью совпадает. Также отличаются «разработчики» спецификаций — если JPA разрабатывается как JSR, то JDO сначала разрабатывался как JSR, теперь разрабатывается как проект Apache JDO.

5. Что такое Entity?

Entity это легковесный хранимый объект бизнес логики (persistent domain object). Основная программная сущность это entity класс, который так же может использовать дополнительные классы, которые могут использоваться как вспомогательные классы или для сохранения состояния еntity.

Источник

Spring Data JPA: что такое хорошо, и что такое плохо

Крошка-сын к отцу пришел
И спросила кроха
— Что такое хорошо
и что такое плохо

Владимир Маяковский

Эта статья о Spring Data JPA, а именно в подводных граблях, встретившихся на моём пути, ну и конечно же немного о производительности.

Примеры, описанные в статье можно запустить в проверочном окружении, доступном по ссылке.

select t.* from t where t.id in (. )

Один из наиболее распространённых запросов — это запрос вида «выбери все записи, у которых ключ попадает в переданное множество». Уверен, почти все из вас писали или видели что-то вроде

Это рабочие, годные запросы, здесь нет подвоха или проблем с производительность, но есть небольшой, совсем неприметный недостаток.

Недостаток заключается в использовании слишком «узкого» интерфейса для передачи ключей. «Ну и?» — скажете вы. «Ну список, ну набор, я не вижу здесь проблемы». Однако, если мы посмотрим на методы корневого интерфейса, принимающие множество значений, то везде увидим Iterable :

«Ну и что? А я хочу список. Чем он хуже?»
Ни чем не хуже, только будьте готовы к появлению на вышестоящем уровне вашего приложения подобного кода:

Этот код не делает ничего, кроме перезаворачивания коллекций. Может получиться так, что аргументом метода будет список, а репозиторный метод принимает набор (или наоборот), и перезаворачивать придётся просто для прохода компиляции. Разумеется это не станет проблемой на фоне накладных расходов на сам запрос, речь скорее о ненужных телодвижениях.

Поэтому хорошей практикой является использование Iterable :

Лишний код: неповторяющиеся ключи

В продолжение прошлого раздела хочу обратить внимание на распространённое заблуждение:

Другие проявления этого же заблуждения:

На первый взгляд, ничего необычного, верно?

который всегда вернёт одно и тоже безотносительно наличия повторов в аргументе. Поэтому обеспечивать уникальность ключей не нужно. Есть один особый случай — «Оракл», где попадание >1000 ключей в in приводит к ошибке. Но если вы пытаетесь уменьшить количество ключей исключением повторов, то стоит скорее задуматься о причине их возникновения. Скорее всего ошибка где-то уровнем выше.

Итого, в хорошем коде используйте Iterable :

Самопись

Внимательно посмотрите на этот код и найдите здесь три недостатка и одну возможную ошибку:

Гарри Поттер и составной ключ

Взгляните на два примера и выберите предпочтительный для вас:

На первый взгляд, разницы нет. Теперь попробуем первый способ и запустим простой тест:

В логе запросов (вы ведёте его, не так ли?) увидим вот это:

Теперь второй пример

Журнал запросов выглядит иначе:

Вот и вся разница: в первом случае всегда получаем 1 запрос, во втором — n запросов.
Причина этого поведения кроется в SimpleJpaRepository::findAllById :

Какой из способов лучше — определять вам, исходя из того, насколько важно количество выполняемых запросов.

Лишний CrudRepository::save

Часто в коде встречается такой антипаттерн:

In the copyValues method call, the hydrated state is copied again, so a new array is redundantly created, therefore wasting CPU cycles. If the entity has child associations and the merge operation is also cascaded from parent to child entities, the overhead is even greater because each child entity will propagate a MergeEvent and the cycle continues.

Иными словами делается работа, которую можно не делать. В итоге наш код можно упростить, одновременно улучшив его производительность:

Конечно, постоянно держать это в голове при разработке и вычитке чужого кода неудобно, поэтому нам хотелось бы внести изменения на уровне каркаса, чтобы метод JpaRepository::save утратил свои вредные свойства. Возможно ли это?

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

Теперь положим, что к счёту привязан его владелец:

Существует метод, позволяющий открепить пользователя от счёта и передать последний новому пользователю:

Что произойдёт теперь? Проверка em.contains(entity) вернёт истину, а значит em.merge(entity) не будет вызван. Если ключ сущности User создаётся на основе последовательности (один из наиболее частых случаев), то он не будет создан вплоть до завершения транзакции (или ручного вызова Session::flush ) т. е. пользователь будет пребывать в состоянии DETACHED, а его родительская сущность (счёт) — в состоянии PERSISTENT. В некоторых случаях это может сломать логику приложения, что и произошло:

«Слепой» CrudRepository::findById

Продолжаем рассматривать всё ту же модель данных:

В приложении есть метод, создающий новый счёт для указанного пользователя:

С версией 2.* указанный стрелкой антипаттерн не так бросается в глаза — чётче его видно на старых версиях:

Первым запросом мы достаём пользователя по ключу. Дальше получаем из базы ключ для новорожденного счёта и вставляем его в таблицу. И единственное, что мы берём от пользователя — это ключ, который у нас и так есть в виде аргумента метода. С другой стороны, BankAccount содержит поле «пользователь» и оставить его пустым мы не можем (как порядочные люди мы выставили ограничение в схеме). Опытные разработчики наверняка уже видят способ и рыбку съесть, и на лошадке покататься и пользователя получить, и запрос не делать:

JpaRepository::getOne возвращает обёртку над ключом, имеющую тот же тип, что и живая «сущность». Этот код даёт всего два запроса:

Когда создаваемая сущность содержит множество полей с отношением «многие к одному» / «один к одному» этот приём поможет ускорить сохранение и снизить нагрузку на базу.

Исполнение HQL запросов

Это отдельная и интересная тема :). Доменная модель та же и есть такой запрос:

Рассмотрим «чистый» HQL:

При его исполнении будет создан вот такой SQL запрос:

Проблема здесь не сразу бросается в глаза даже умудрённым жизнью и хорошо понимающим SQL разработчикам: inner join по ключу пользователя исключит из выборки счета с отсутствующим user_id (а по-хорошему вставка таковых должна быть запрещена на уровне схемы), а значит присоединять таблицу user вообще не нужно. Запрос может быть упрощён (и ускорен):

Существует способ легко добиться этого поведения в c помощью HQL:

Этот метод создаёт «облегчённый» запрос.

Аннотация Query против метода

Одна из основных фишек Spring Data — возможность создавать запрос из имени метода, что очень удобно, особенно в сочетании с умным дополнением от IntelliJ IDEA. Запрос, описанный в предыдущем примере может быть легко переписан:

Вроде бы и проще, и короче, и читаемее, а главное — не нужно смотреть сам запрос. Имя метода прочитал — и уже понятно, что он выбирает и как. Но дьявол и здесь в мелочах. Итоговый запрос метода, помеченного @Query мы уже видели. Что же будет во втором случае?

«Какого лешего!?» — воскликнет разработчик. Ведь выше мы уже убедились, что скрипач join не нужен.

Если вы ещё не обновились до версий с исправлением, а присоединение таблицы тормозит запрос здесь и сейчас, то не отчаивайтесь: есть сразу два способа облегчить боль:

хороший способ заключается в добавлении optional = false (если схема позволяет):

Теперь запрос-из-метода станет приятнее:

чего мы и добивались.

Ограничение выборки

Для своих целей нам нужно ограничивать выборку (например, хотим возвращать Optional из метода *RepositoryCustom ):

Указанный код обладает одной неприятной особенностью: в том случае, если запрос вернул пустую выборку будет брошено исключение

В проектах, которые я видел, это решалось двумя основными способами:

И очень редко я видел правильное решение:

EntityManager — часть стандарта JPA, в то время как Session принадлежит Хибернейту и является ИМХО более продвинутым средством, о чём часто забывают.

[Иногда] вредное улучшение

Когда нужно достать одно маленькое поле из «толстой» сущности мы поступаем так:

Запрос позволяет достать одно поле типа boolean без загрузки всей сущности (с добавлением в кэш-первого уровня, проверкой изменений по завершению сессии и прочими расходами). Иногда это не только не улучшает производительность, но и наоборот — создаёт ненужные запросы на пустом месте. Представим код, выполняющий некоторые проверки:

Этот код делает по меньшей мере 2 запроса, хотя второго можно было бы избежать:

Вывод простой: не пренебрегайте кэшем первого уровня, в рамках одной транзакции только первый JpaRepository::findById обращается к базе, т. к. кэш первого уровня включен всегда и привязан к сессии, которая, как правило, привязана к текущей транзакции.

Тесты, на которых можно поиграться (ссылка на репозиторий дана в начале статьи):

Источник

Обзор идентификаторов в Hibernate/JPA

Узнайте, как сопоставлять идентификаторы сущностей с помощью Hibernate и JPA.

1. введение

Идентификаторы в Hibernate представляют собой первичный ключ сущности. Это означает, что значения уникальны, чтобы они могли идентифицировать конкретную сущность, что они не являются нулевыми и что они не будут изменены.

Hibernate предоставляет несколько различных способов определения идентификаторов. В этой статье мы рассмотрим каждый метод сопоставления идентификаторов сущностей с помощью библиотеки.

2. Простые Идентификаторы

Простые идентификаторы сопоставляются с помощью @Id с одним свойством одного из этих типов: примитивы Java и примитивные типы оболочек, String, Date, BigDecimal, BigInteger.

Давайте рассмотрим краткий пример определения сущности с первичным ключом типа long:

3. Сгенерированные Идентификаторы

Это может использовать 4 типа генерации: АВТО, ИДЕНТИФИКАЦИЯ, ПОСЛЕДОВАТЕЛЬНОСТЬ, ТАБЛИЦА.

Если мы не указываем значение явно, тип генерации по умолчанию равен AUTO.

3.1. Автогенерация

Если мы используем тип генерации по умолчанию, поставщик сохраняемости будет определять значения на основе типа атрибута первичного ключа. Этот тип может быть числовым или UUID.

Для числовых значений генерация основана на генераторе последовательностей или таблиц, в то время как UUID значения будут использовать UUIDGenerator.

Давайте рассмотрим пример сопоставления первичного ключа сущности с использованием стратегии автоматической генерации:

В этом случае значения первичного ключа будут уникальными на уровне базы данных.

Интересной функцией, введенной в Hibernate 5, является генератор UUID. Чтобы использовать это, все, что нам нужно сделать, это объявить идентификатор типа UUID с @GeneratedValue аннотацией:

Hibernate сгенерирует идентификатор формы “8dd5f315-9788-4d00-87bb-10eed9eff566”.

3.2. Формирование идентичности

Чтобы использовать этот тип генерации, нам нужно только задать параметр strategy :

Следует отметить, что генерация удостоверений отключает пакетное обновление.

3.3. Генерация последовательностей

Этот генератор использует последовательности, если они поддерживаются нашей базой данных, и переключается на генерацию таблиц, если это не так.

Чтобы настроить имя последовательности, мы можем использовать @GenericGenerator аннотацию со стратегией SequenceStyleGenerator:

В этом примере мы также установили начальное значение для последовательности, что означает, что генерация первичного ключа начнется с 4.

ПОСЛЕДОВАТЕЛЬНОСТЬ – это тип генерации, рекомендуемый документацией Hibernate.

Сгенерированные значения уникальны для каждой последовательности. Если вы не укажете имя последовательности, Hibernate будет повторно использовать один и тот же hibernate_sequence для разных типов.

3.4. Генерация ТАБЛИЦ

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

Давайте настроим имя таблицы с помощью аннотации @TableGenerator :

Недостатком этого метода является то, что он плохо масштабируется и может негативно повлиять на производительность.

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

3.5. Пользовательский генератор

Если мы не хотим использовать ни одну из готовых стратегий, мы можем определить наш пользовательский генератор, реализовав интерфейс IdentifierGenerator |.

Давайте создадим генератор, который строит идентификаторы, содержащие префикс String и число:

В этом примере мы переопределяем метод generate() из IdentifierGenerator interface и сначала находим наибольшее число из существующих первичных ключей формы prefix-XX.

Далее, давайте добавим этот пользовательский генератор в сущность. Для этого мы можем использовать аннотацию @GenericGenerator с параметром strategy , который содержит полное имя класса нашего класса генератора :

Кроме того, обратите внимание, что мы установили для параметра prefix значение “prod”.

Давайте посмотрим быстрый тест JUnit для более четкого понимания сгенерированных значений идентификаторов:

Здесь первое значение, сгенерированное с использованием префикса “prod”, было “prod-1”, за которым следовал “prof-2”.

4. Составные Идентификаторы

Помимо простых идентификаторов, которые мы видели до сих пор, Hibernate также позволяет нам определять составные идентификаторы.

Составной идентификатор представлен классом первичного ключа с одним или несколькими постоянными атрибутами.

Класс первичного ключа должен выполнять несколько условий:

Атрибуты класса могут быть базовыми, составными или ManyToOne, избегая при этом коллекций и OneToOne атрибутов.

4.1. @EmbeddedId

Чтобы определить идентификатор с помощью @EmbeddedId, сначала нам нужен класс первичного ключа с аннотацией @Embeddable:

Затем мы можем добавить идентификатор типа Order Entry PK к сущности с помощью @ EmbeddedId :

Давайте посмотрим, как мы можем использовать этот тип составного идентификатора для установки первичного ключа для сущности:

Здесь объект Order Entry имеет Order Entry PK первичный идентификатор, сформированный из двух атрибутов: OrderID и ProductID.

4.2. @IdClass

Аннотация @IdClass аналогична аннотации @EmbeddedId, за исключением того, что атрибуты определяются в основном классе сущностей с использованием @Id для каждого из них.

Класс первичного ключа будет выглядеть так же, как и раньше.

Давайте перепишем Запись заказа пример с @IdClass:

Затем мы можем установить значения идентификаторов непосредственно в Записи заказа объекте:

Hibernate также позволяет определять первичные ключи, состоящие из @ManyToOne ассоциаций в сочетании с @Id аннотацией. В этом случае класс сущности также должен выполнять условия класса первичного ключа.

Недостатком этого метода является отсутствие разделения между объектом сущности и идентификатором.

5. Производные Идентификаторы

Затем давайте проверим, что экземпляр UserProfile имеет тот же идентификатор, что и связанный с ним экземпляр User :

6. Заключение

В этой статье мы рассмотрели несколько способов определения идентификаторов в режиме гибернации.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *