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

Руководство по SqlResultSetMapping

Узнайте, как использовать SqlResultSetMapping JPA для сопоставления результатов базы данных с объектами Java

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

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() {
    List employeeIds = 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() {
  List scheduleDays
    = 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() {
    List employees = 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 results = query.getResultList();
    assertEquals(4, results.size());
    assertTrue(results.get(0).length == 2);

    Employee emp = (Employee) results.get(1)[0];
    ScheduledDay day = (ScheduledDay) results.get(1)[1];

    assertTrue(day.getEmployeeId() == emp.getId());
}

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

В этом руководстве мы рассмотрели различные варианты использования SqlResultSetMapping аннотации . SqlResultSetMapping является ключевой частью API сохранения Java.

Фрагменты кода можно найти на GitHub .