1. Список колекцій

Що ж, ми познайомилися з тим, як мапити прості типи. Тепер настав час перейти до питань цікавіше — як мапити колекції об'єктів.

А об'єкти у нас можуть бути у 5 групах:

  • Array — масив об'єктів
  • List — список об'єктів
  • Set — множина об'єктів
  • Map — словник об'єктів
  • Collection — колекція об'єктів

І приклад класу з полем-колекцією:


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @Чарівна-анотація
   public List<String> messages;
}

То що ж це за чарівна анотація така, яка дозволить нам зберігати не одне поле, а багато значень?

Ця анотація називається @ElementCollection. Приклад:


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @ElementCollection
   public List<String> messages;
}

Пишеться вона дуже просто, а ось працює нетривіально.

2. Допоміжна таблиця

Всі поля Entity-класу, які містять багато елементів і позначаються за допомогою анотації @ElementCollection містяться в базі даних у спеціальній допоміжній таблиці. Що, власне, логічно.

Ця таблиця може містити дані у двох форматах:

  • Упорядковані (List, Map) містять три колонки:
    • Key Column (Foreign Key) — посилання на ID об'єкта-батька.
    • Index Column — позиція/індекс у колекції.
    • Element Column — значення.
  • Неупорядковані (Set) містять дві колонки:
    • Key Column (Foreign Key) — посилання на ID об'єкта-батька.
    • Element Column — значення.

Також ти можеш зазначити ім'я цієї таблиці явно за допомогою анотації:


   @CollectionTable(name="ім'я_таблиці")

Приклад:


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @ElementCollection
   @CollectionTable(name="user_message")
   public List<String> messages;
}

Важливо! Якщо анотація @CollectionTable не вказана, то Hibernate сам побудує ім'я таблиці на основі імені класу та імені поля: клас User та поле messages дадуть ім'я таблиці "User_messages".

3. Колекція Set

Але давай не віддаватимемо на відкуп Hibernate створення допоміжної таблиці та зробимо її самі. Спочатку нам потрібно створити таблицю із двома колонками:


CREATE TABLE user_message {
    user_id INT,
    message VARCHAR(255)
};

Зверни увагу, що ця таблиця не має своєї id-колонки. Це і є основною ознакою допоміжних таблиць. З іншими видами допоміжних таблиць ти познайомишся трохи згодом.

Тепер потрібно замапитти цю таблицю на наше поле messages у класі User. Виглядатиме ця справа так:


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @ElementCollection
   @CollectionTable(name="user_message", joinColumns = @JoinColumn(name = "user_id"))
   @Column(name = "message")
   public Set<String> messages;
}

Тут варто звернути увагу на дві речі.

По-перше, колонка message, вказана за допомогою анотації @Column(name = "message") знаходиться в допоміжній таблиці user_message, а не в таблиці user.

По-друге, в анотації @JoinColumn(name = "user_id") ми вказали ім'я колонки user_id, який посилається на id таблиці user. Це для того, щоб Hibernate знав, як їх правильно об'єднувати.

4. Колекція List

Якщо ти хочеш зберігати у допоміжній таблиці впорядковані елементи списку чи масиву, то тобі знадобиться таблиця з трьома колонками:


CREATE TABLE user_message {
    user_id INT,
    index INT,
    message VARCHAR(255)
};

Якщо не подобається ім'я колонки "index", або ти не можеш його змінити, можна вказати інше ім'я під час мапінгу. Для цього потрібно використовувати анотацію @Index.

Приклад:


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @ElementCollection
   @CollectionTable(name="user_message",
       indexes = { @Index(columnList = "list_index") },
       joinColumns = @JoinColumn(name = "user_id"))
   @Column(name = "message")
   public List<String> messages;
}

5. Колекція Map

І нарешті, ти хочеш зберігати не просто колекцію, а HashMap, і тобі для нього потрібні дві колонки у допоміжній таблиці:


CREATE TABLE user_message {
    user_id INT,
    key VARCHAR(255),
    message VARCHAR(255)
};

Щоб вказати ключ для Map тобі знадобиться анотація @MapKeyColumn.

Приклад:


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @ElementCollection
   @CollectionTable(name="user_message", joinColumns = @JoinColumn(name = "user_id"))
   @MapKeyColumn(name = "key")
   @Column(name = "message")
   public Map<String, String> messages;
}

Детальнішу інформацію ти зможеш знайти в офіційній документації.