1. введение
В Hibernate мы можем представлять отношения один ко многим в наших Java-бобах, если одно из наших полей будет Списком .
В этом кратком руководстве мы рассмотрим различные способы сделать это с помощью Map вместо этого.
2. Карты Отличаются от Списков
Использование Map для представления отношения “один ко многим” отличается от List , потому что у нас есть ключ.
Этот ключ превращает наши отношения сущностей в тернарную ассоциацию, где каждый ключ ссылается на простое значение или встраиваемый объект или сущность. Из-за этого, чтобы использовать Map , нам всегда понадобится объединенная таблица для хранения внешнего ключа, который ссылается на родительскую сущность – ключ и значение.
Но эта таблица соединений будет немного отличаться от других таблиц соединений тем, что первичный ключ не обязательно будет внешним ключом для родительского и целевого объектов. Вместо этого у нас будет первичный ключ, состоящий из внешнего ключа родителя и столбца, который является ключом к нашей карте.
Пара ключ-значение в Карте может быть двух типов: Тип значения и Тип сущности . В следующих разделах мы рассмотрим способы представления этих ассоциаций в режиме гибернации.
3. Использование @MapKeyColumn
Допустим, у нас есть объект Order , и мы хотим отслеживать название и цену всех товаров в заказе. Итак, мы хотим ввести Map Double> в Заказ , который сопоставит имя товара с его ценой: Double>
@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue @Column(name = "id") private int id; @ElementCollection @CollectionTable(name = "order_item_mapping", joinColumns = {@JoinColumn(name = "order_id", referencedColumnName = "id")}) @MapKeyColumn(name = "item_name") @Column(name = "price") private MapitemPriceMap; // standard getters and setters }
Нам нужно указать в спящий режим, где получить ключ и значение. Для ключа мы использовали @ Map Key Column , указывая, что ключ Map является item_name столбцом вашей таблицы объединения, order_item_mapping . Аналогично, @Column указывает, что значение карты соответствует столбцу price таблицы объединения.
Кроме того, карта цен товара объект является картой типа значения, поэтому мы должны использовать аннотацию @ElementCollection|/.
В дополнение к объектам базового типа значений, @ Встраиваемые объекты также могут использоваться в качестве значений Map аналогичным образом.
4. Использование @MapKey
Как мы все знаем, требования со временем меняются — так что теперь, допустим, нам нужно сохранить еще несколько атрибутов Item вместе с ItemName и ItemPrice :
@Entity @Table(name = "item") public class Item { @Id @GeneratedValue @Column(name = "id") private int id; @Column(name = "name") private String itemName; @Column(name = "price") private double itemPrice; @Column(name = "item_type") @Enumerated(EnumType.STRING) private ItemType itemType; @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_on") private Date createdOn; // standard getters and setters }
Соответственно, давайте изменим Map Double> на Map Item> в классе Order entity: Double>
@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue @Column(name = "id") private int id; @OneToMany(cascade = CascadeType.ALL) @JoinTable(name = "order_item_mapping", joinColumns = {@JoinColumn(name = "order_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "item_id", referencedColumnName = "id")}) @MapKey(name = "itemName") private MapitemMap; }
Обратите внимание, что на этот раз мы будем использовать аннотацию @MapKey , чтобы Hibernate использовал Элемент# Имя элемента в качестве ключевого столбца карты вместо введения дополнительного столбца в таблицу объединения. Таким образом, в этом случае объединенная таблица order_item_mapping не имеет ключевого столбца — вместо этого она ссылается на имя I tem .
Это в отличие от @MapKeyColumn. Когда мы используем @MapKeyColumn, ключ карты находится в таблице объединения . Это причина, по которой мы не можем определить наше сопоставление сущностей, используя обе аннотации в сочетании.
Кроме того, карта элементов является картой типа сущности, поэтому мы должны аннотировать отношения с помощью @OneToMany или @ManyToMany .
5. Использование @MapKeyEnumerated и @MapKeyTemporal
Всякий раз, когда мы указываем перечисление в качестве ключа Map , мы используем @MapKeyEnumerated . Аналогично, для временных значений используется @MapKeyTemporal . Поведение очень похоже на стандартные @Перечисляемые и @Временные аннотации соответственно.
По умолчанию они аналогичны @MapKeyColumn в том, что в таблице объединения будет создан ключевой столбец. Если мы хотим повторно использовать значение, уже сохраненное в сохраняемой сущности, мы должны дополнительно пометить поле с помощью @MapKey .
6. Использование @MapKeyJoinColumn
Далее, допустим, нам также нужно отслеживать продавца каждого товара. Один из способов, которым мы могли бы это сделать, – добавить Продавца сущность и привязать ее к нашему Элемент сущность:
@Entity @Table(name = "seller") public class Seller { @Id @GeneratedValue @Column(name = "id") private int id; @Column(name = "name") private String sellerName; // standard getters and setters }
@Entity @Table(name = "item") public class Item { @Id @GeneratedValue @Column(name = "id") private int id; @Column(name = "name") private String itemName; @Column(name = "price") private double itemPrice; @Column(name = "item_type") @Enumerated(EnumType.STRING) private ItemType itemType; @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_on") private Date createdOn; @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "seller_id") private Seller seller; // standard getters and setters }
В этом случае давайте предположим, что наш вариант использования состоит в том, чтобы сгруппировать все Order ‘s Item s по Продавцу. Следовательно, давайте изменим Map Item> на Map Item> : Item>
@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue @Column(name = "id") private int id; @OneToMany(cascade = CascadeType.ALL) @JoinTable(name = "order_item_mapping", joinColumns = {@JoinColumn(name = "order_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "item_id", referencedColumnName = "id")}) @MapKeyJoinColumn(name = "seller_id") private MapsellerItemMap; // standard getters and setters }
Для этого нам нужно добавить @MapKeyJoinColumn , так как эта аннотация позволяет Hibernate сохранять столбец seller_id (ключ карты) в таблице соединения order_item_mapping вместе со столбцом item_id . Таким образом, во время чтения данных из базы данных мы можем легко выполнить операцию GROUP BY .
7. Заключение
В этой статье мы узнали о нескольких способах сохранения Map в спящем режиме в зависимости от требуемого отображения.
Как всегда, исходный код этого учебника можно найти на Github .