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

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

Модуль 4. Работа с БД
13 уровень , 0 лекция
Открыта

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

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

А объекты у нас могут быть в 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.
Комментарии (15)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Виктор Уровень 83
27 февраля 2026
2026 год, все что снизу ребята написали, так и не пофикшено. живите с этим
Евгений Уровень 97
30 января 2025
Таблицы с именами колонок key, index работать не будут!!! Это зарезервированные слова в MySql....
Alibek Уровень 1 Expert
5 ноября 2024
это если без create table сразу запускать
Alibek Уровень 1 Expert
5 ноября 2024
@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
Непонятен следующий момент : Если мапим 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 Уровень 4
29 июля 2024
Написано: "две колонки во вспомогательной таблице" - это помимо value. Написано: "Если ты хочешь хранить во вспомогательной таблице упорядоченные элементы списка или массива, то тебе понадобится таблица с тремя колонками" - это говорится о всей таблице
Дмитрий Уровень 100 Expert
23 января 2024
Не очень понял почему Map в упорядоченных. У HashMap же то же, что и у Set. Разве что про LinkedHashMap речь...
Gans Electro Уровень 4
29 июля 2024
Да, наверное там должен был быть слово массив. Но возможно имеется ввиду что Value упорядочено относительно Key
Павел Уровень 111 Expert
5 декабря 2023
Не понял: Во-вторых, в аннотации @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
Я думаю Гипер знает, потому что поле id помечено аннотацией @Id
Anonymous #3322801 Уровень 2 Expert
28 ноября 2023
Исправьте в примерах скобки на круглые.

CREATE TABLE user_message (
    user_id INT,
    message VARCHAR(255)
);
Mitrus Latovous Уровень 41
28 ноября 2023
CREATE TABLE должны быть везде круглые скобки
Anonymous #2717671 Уровень 51
15 марта 2023
не очень понятен подход с индексами. @OrderColumn ?
Эльдар Уровень 108 Expert
8 октября 2023
Ага дали только @MapKeyColumn в лекции, а в решении @OrderColumn. Методисты просто на "5"