1. введение
В этом руководстве мы рассмотрим SqlResultSetMapping из API сохранения Java (JPA).
Основная функциональность здесь включает в себя отображение результирующих наборов из операторов SQL базы данных в объекты Java.
2. Настройка
Прежде чем мы рассмотрим его использование, давайте сделаем некоторые настройки.
2.1. Зависимость Maven
Наши необходимые зависимости Maven-это Hibernate и база данных H2. Hibernate дает нам реализацию спецификации JPA. Мы используем H2 Database для базы данных в памяти.
2.2. База данных
Далее мы создадим две таблицы, как показано здесь:
CREATE TABLE EMPLOYEE (id BIGINT, name VARCHAR(10));
В таблице СОТРУДНИК хранится один результат Сущность объект. SCHEDULE_DAYS содержит записи, связанные с таблицей EMPLOYEE столбцом EmployeeID:
CREATE TABLE SCHEDULE_DAYS (id IDENTITY, employeeId BIGINT, dayOfWeek VARCHAR(10));
Скрипт для создания данных можно найти в коде для этого руководства .
2.3. Объекты Сущностей
Наши Сущности объекты должны выглядеть одинаково:
@Entity public class Employee { @Id private Long id; private String name; }
Объекты Entity могут называться иначе, чем таблицы базы данных. Мы можем аннотировать класс с помощью @ Table , чтобы явно сопоставить их:
@Entity @Table(name = "SCHEDULE_DAYS") public class ScheduledDay { @Id @GeneratedValue private Long id; private Long employeeId; private String dayOfWeek; }
3. Скалярное отображение
Теперь, когда у нас есть данные, мы можем начать сопоставлять результаты запросов.
3.1. ColumnResult
В то время как SqlResultSetMapping и Query аннотации также работают с классами Repository , в этом примере мы используем аннотации для класса Entity .
Для каждой SqlResultSetMapping аннотации требуется только одно свойство, имя. Однако без одного из типов элементов ничего не будет отображено. Типы элементов: Результат столбца , ConstructorResult и EntityResult .
В этом случае Результат столбца сопоставляет любой столбец скалярному типу результата:
@SqlResultSetMapping( name="FridayEmployeeResult", columns={@ColumnResult(name="employeeId")})
Результат столбца свойство имя идентифицирует столбец в нашем запросе:
@NamedNativeQuery( name = "FridayEmployees", query = "SELECT employeeId FROM schedule_days WHERE dayOfWeek = 'FRIDAY'", resultSetMapping = "FridayEmployeeResult")
Обратите внимание, что значение ResultSetMapping в нашей NamedNativeQuery аннотации важно, поскольку оно соответствует свойству name из нашего ResultSetMapping объявления.
В результате результирующий набор NamedNativeQuery отображается, как и ожидалось. Аналогично, Хранимая процедура API требует этой ассоциации.
3.2. Проверка результатов в колонке
Нам понадобятся некоторые специальные объекты Hibernate для запуска нашего кода:
@BeforeAll public static void setup() { emFactory = Persistence.createEntityManagerFactory("java-jpa-scheduled-day"); em = emFactory.createEntityManager(); }
Наконец, мы вызываем именованный запрос для запуска нашего теста:
@Test public void whenNamedQuery_thenColumnResult() { ListemployeeIds = em.createNamedQuery("FridayEmployees").getResultList(); assertEquals(2, employeeIds.size()); }
4. Отображение конструктора
Давайте посмотрим, когда нам нужно сопоставить результирующий набор со всем объектом.
4.1. Результат конструктора
Аналогично нашему примеру Результат столбца , мы добавим аннотацию Sqlresultsetmapping на ваш Объект класс, Запланированный день . Однако для того, чтобы сопоставить с помощью конструктора, нам нужно создать его:
public ScheduledDay ( Long id, Long employeeId, Integer hourIn, Integer hourOut, String dayofWeek) { this.id = id; this.employeeId = employeeId; this.dayOfWeek = dayofWeek; }
Кроме того, сопоставление определяет целевой класс и столбцы (оба обязательны):
@SqlResultSetMapping( name="ScheduleResult", classes={ @ConstructorResult( targetClass=com.baeldung.sqlresultsetmapping.ScheduledDay.class, columns={ @ColumnResult(name="id", type=Long.class), @ColumnResult(name="employeeId", type=Long.class), @ColumnResult(name="dayOfWeek")})})
Порядок результатов столбца очень важен. Если столбцы не в порядке, конструктор не будет идентифицирован. В нашем примере порядок соответствует столбцам таблицы, поэтому на самом деле он не требуется.
@NamedNativeQuery(name = "Schedules", query = "SELECT * FROM schedule_days WHERE employeeId = 8", resultSetMapping = "ScheduleResult")
Еще одно уникальное отличие для ConstructorResult заключается в том, что результирующий экземпляр объекта создается как “новый” или “отсоединенный”. Сопоставленная Сущность будет находиться в отключенном состоянии, когда соответствующий первичный ключ существует в EntityManager в противном случае он будет новым.
Иногда мы можем столкнуться с ошибками во время выполнения из-за несоответствия типов данных SQL типам данных Java. Поэтому мы можем явно объявить его с помощью типа .
4.2. Конструкторрезультатный тест
Давайте проверим результат конструктора в модульном тесте:
@Test public void whenNamedQuery_thenConstructorResult() { ListscheduleDays = Collections.checkedList( em.createNamedQuery("Schedules", ScheduledDay.class).getResultList(), ScheduledDay.class); assertEquals(3, scheduleDays.size()); assertTrue(scheduleDays.stream().allMatch(c -> c.getEmployeeId().longValue() == 3)); }
5. Сопоставление сущностей
Наконец, для простого сопоставления сущностей с меньшим количеством кода давайте рассмотрим EntityResult .
5.1. Единое юридическое лицо
Результат сущности требует, чтобы мы указали класс сущности, Сотрудник . Мы используем необязательное свойство fields для большего контроля. В сочетании с результатом Field мы можем сопоставить псевдонимы и поля, которые не совпадают:
@SqlResultSetMapping( name="EmployeeResult", entities={ @EntityResult( entityClass = com.baeldung.sqlresultsetmapping.Employee.class, fields={ @FieldResult(name="id",column="employeeNumber"), @FieldResult(name="name", column="name")})})
Теперь наш запрос должен включать столбец с псевдонимами:
@NamedNativeQuery( name="Employees", query="SELECT id as employeeNumber, name FROM EMPLOYEE", resultSetMapping = "EmployeeResult")
Аналогично Результату конструктора , Результату сущности требуется конструктор. Однако здесь работает один из них по умолчанию.
5.2. Несколько Сущностей
Сопоставление нескольких сущностей довольно просто, как только мы сопоставили одну сущность:
@SqlResultSetMapping( name = "EmployeeScheduleResults", entities = { @EntityResult(entityClass = com.baeldung.sqlresultsetmapping.Employee.class), @EntityResult(entityClass = com.baeldung.sqlresultsetmapping.ScheduledDay.class)
5.3. Тесты EntityResult
Давайте посмотрим на Результат сущности в действии:
@Test public void whenNamedQuery_thenSingleEntityResult() { Listemployees = Collections.checkedList( em.createNamedQuery("Employees").getResultList(), Employee.class); assertEquals(3, employees.size()); assertTrue(employees.stream().allMatch(c -> c.getClass() == Employee.class)); }
Поскольку результаты нескольких сущностей объединяют две сущности, аннотация запроса только для одного из классов приводит к путанице.
По этой причине мы определяем запрос в тесте:
@Test public void whenNamedQuery_thenMultipleEntityResult() { Query query = em.createNativeQuery( "SELECT e.id, e.name, d.id, d.employeeId, d.dayOfWeek " + " FROM employee e, schedule_days d " + " WHERE e.id = d.employeeId", "EmployeeScheduleResults"); List
6. Заключение
В этом руководстве мы рассмотрели различные варианты использования SqlResultSetMapping аннотации . SqlResultSetMapping является ключевой частью API сохранения Java.
Фрагменты кода можно найти на GitHub .