JavaRush/Курсы/Модуль 4. Работа с БД/Маппинг коллекций

Маппинг коллекций

Открыта

Список коллекций

Что ж, ты познакомился с тем, как мапить простые типы. Теперь пора перейти к вопросам поинтереснее – как мапить коллекции объектов.

А объекты у нас могут быть в 5 группах:

  • Array – массив объектов
  • List – список объектов
  • Set – множество объектов
  • Map – словарь объектов
  • Collection – коллекция объектов

И пример класса с полем-коллекцией:

@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;

   @Волшебная-аннотация
   public List






    messages; }

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

Эта аннотация называется @ElementCollection. Пример:

@Entity
@Table(name="user")
class User  {
   @Id
   @Column(name="id")
   public Integer id;

   @ElementCollection
   public List<String> messages;
}

Пишется она очень просто, а вот работает нетривиально.

Вспомогательная таблица

Все поля 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".

Коллекция 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 знал, как их правильно объединять.

Коллекция 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;
}

Коллекция 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;
}

Более подробную информацию ты сможешь найти в официальной документации.

1
Задача
Модуль 4. Работа с БД,  13 уровень0 лекция
Недоступна
Расстановка аннотаций
Есть класс-энтити Author, у автора могут быть статьи articles. Добавь аннотации перед полем articles в классе Author, чтобы программа корректно обрабатывала данные из таблиц:
1
Задача
Модуль 4. Работа с БД,  13 уровень0 лекция
Недоступна
Расстановка аннотаций 2
Есть класс-энтити Author, у автора могут быть достижения achievements. Не все достижения одинаково ценные, поэтому они упорядочены с помощью achievement_index и хранятся в списке List.
Комментарии (14)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Евгений
Уровень 97
30 января, 07:21
Таблицы с именами колонок key, index работать не будут!!! Это зарезервированные слова в MySql....
Alibek
Уровень 1
Expert
5 ноября 2024, 07:01
это если без create table сразу запускать
Alibek
Уровень 1
Expert
5 ноября 2024, 07:00
@CollectionTable(name="user_message", indexes = { @Index(columnList = "list_index") } joinColumns = @JoinColumn(name = "user_id")) выдает ошибку Эта ошибка означает, что в таблице user_message_list не существует столбца list_index, который используется для создания индекса. При попытке выполнить DDL-команду create index IDXepqkvbiv2mq5v6khxmwnrs552 on user_message_list (list_index), Hibernate не смог найти указанный столбец и выдал ошибку SQL-синтаксиса. исправил на @CollectionTable( name="user_message_list", joinColumns = @JoinColumn(name = "user_id") ) @Column(name = "message_list") @OrderColumn(name = "list_index") // Явно указываем столбец для индексации списка и ошибка изчезла
Олег
Уровень 109
Expert
23 июля 2024, 12:51
Непонятен следующий момент : Если мапим List - понадобится 3 колонки
CREATE TABLE user_message {
    user_id INT,
    index INT,
    message VARCHAR(255)
};
Если мапим HashMap - понадобится 2 колонки
CREATE TABLE user_message {
    user_id INT,
    key VARCHAR(255),
    message VARCHAR(255)
};
... в обоих примерах по 3 колонки. Поясните этот момент.
Gans Electro
Уровень 40
29 июля 2024, 13:24
Написано: "две колонки во вспомогательной таблице" - это помимо value. Написано: "Если ты хочешь хранить во вспомогательной таблице упорядоченные элементы списка или массива, то тебе понадобится таблица с тремя колонками" - это говорится о всей таблице
Дмитрий
Уровень 100
Expert
23 января 2024, 04:04
Не очень понял почему Map в упорядоченных. У HashMap же то же, что и у Set. Разве что про LinkedHashMap речь...
Gans Electro
Уровень 40
29 июля 2024, 13:25
Да, наверное там должен был быть слово массив. Но возможно имеется ввиду что Value упорядочено относительно Key
Павел
Уровень 111
Expert
5 декабря 2023, 06:35
Не понял: Во-вторых, в аннотации @JoinColumn(name = "user_id") мы указали имя колонки user_id, которая ссылается на id таблицы user. Это чтобы Hibernate знал, как их правильно объединять. Откуда Hibernate знает что "user_id" ссылается на поле id, ведь у поля id колонка называется "id", не "user_id". В Hibernate при работе с коллекциями вшита функция распознавания "название_id" и сопоставления с полем id? По-другому это не объяснить, т.к. имя поля и колонки "id" а джойн через "user_id". В чистом SQL это было бы через ON и далее указание колонок. В Hibernate - ни намёка.
Андрей
Уровень 109
4 июля 2024, 05:00
Я думаю Гипер знает, потому что поле id помечено аннотацией @Id
Anonymous #3322801
Уровень 2
Expert
28 ноября 2023, 11:17
Исправьте в примерах скобки на круглые.
CREATE TABLE user_message (
    user_id INT,
    message VARCHAR(255)
);
Mitrus Latovous
Уровень 41
28 ноября 2023, 07:51
CREATE TABLE должны быть везде круглые скобки
Anonymous #2717671
Уровень 51
15 марта 2023, 09:35
не очень понятен подход с индексами. @OrderColumn ?
Эльдар
Уровень 108
Expert
8 октября 2023, 06:45
Ага дали только @MapKeyColumn в лекции, а в решении @OrderColumn. Методисты просто на "5"
Dinis Shayslamov
Уровень 108
Expert
22 января 2023, 20:07
В примере кода про List в конце 10-й строчки не хватает запятой