Рубрики
Без рубрики

Сохранение карт в режиме гибернации

Использование карты для отношений гибернации немного отличается от списка. Узнайте, как это делается.

Автор оригинала: baeldung.

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 Map itemPriceMap;

    // 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 Map itemMap;

}

Обратите внимание, что на этот раз мы будем использовать аннотацию @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 Map sellerItemMap;

    // standard getters and setters

}

Для этого нам нужно добавить @MapKeyJoinColumn , так как эта аннотация позволяет Hibernate сохранять столбец seller_id (ключ карты) в таблице соединения order_item_mapping вместе со столбцом item_id . Таким образом, во время чтения данных из базы данных мы можем легко выполнить операцию GROUP BY .

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

В этой статье мы узнали о нескольких способах сохранения Map в спящем режиме в зависимости от требуемого отображения.

Как всегда, исходный код этого учебника можно найти на Github .