JPA — создание нетипизированных запросов и запросов со специальным типом
При работе с базой данных часто возникает потребность получить набор данных, не относящихся к какой-либо сущности напрямую. Для этих целей можно создать специальный запрос.
Используемые технологии
- Spring 4.1.5.RELEASE
- Hibernate 5.0.1.Final
- JPA 2.1
- MySQL 5.6.25
- IntelliJ IDEA 14
- Maven 3.2.5
1. Описание задачи
Допустим у нас есть таблица с контактными данными и телефонными номерами. Нам нужно сделать выборку из базы данных, которая будет содержать определенное количество столбцов (но не все). Такая задача может возникать, к примеру, при создании отчета. Пусть необходимо выбрать только те контакты, у которых есть домашний телефон и при этом отобразить только имя, фамилию и номер этого телефона.
Если сделать запрос к БД напрямую, то мы получим массив данных (разные сущности) из разных таблиц (контакты и телефоны), который потом придется перебирать и составлять отчет вручную.
Мы пойдем другим путем:
- создадим запрос с нетипизированным результатом.
- а так же рассмотрим вариант в виде запроса со специальным типом результата и конструирующим выражением.
2. Структура проекта
Все таблицы, настройки и другое взяты из JPA – пример приложения Hello World.
3. JPA — запрос с нетипизированным результатом
Создадим класс и запрос с нетипизированным результатом ContactSummaryUntypeImpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package ru.javastudy.impl; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.util.Iterator; import java.util.List; /* Получение нетипизированного результата (несколько данных из разных таблиц) */ @Service("contactSummaryUntype") @Repository @Transactional public class ContactSummaryUntypeImpl { @PersistenceContext private EntityManager em; @Transactional(readOnly = true) public void displayAllContactSummary() { List result = em.createQuery("select c.firstName, c.lastName, t.telNumber " + "from ContactEntity c left join c.contactTelDetails t " + "where t.telType='Домашний'").getResultList(); int count = 0; for (Iterator i = result.iterator(); i.hasNext();) { Object[] values = (Object[]) i.next(); System.out.println("#" + ++count + ": " +values[0] + ", " + values[1] + ", " + values[2]); } } } |
При указании конкретных столбцов (firstName, lastName, telNumber) в операторе JPQL будет возвращен итератор с результирующим списком. Обычным перебором выводим в консоль результат.
4. JPA — Запрос со специальным типом результата и конструирующим выражением
Более интересное решение заключается в создании специального класса, который будет содержать в себе запрашиваемые данные (данные для каждой колонки). Другими словами мы заставим JPA конструировать POJO класс для каждой полученной записи.
4.1 Сервис запроса со специальным типом
1 2 3 4 5 6 7 8 |
package ru.javastudy.intf; import ru.javastudy.supportClasses.ContactSummary; import java.util.List; public interface ContactSummaryService { public List<ContactSummary> findAllSummary(); } |
4.2 Конструирующий класс
Для нашего примера был создан класс, который содержит три запрашиваемых свойства:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package ru.javastudy.supportClasses; import java.io.Serializable; public class ContactSummary implements Serializable { private String firstNameSummary; private String lastNameSummary; private String homeTelNumber; public ContactSummary(String firstNameSummary, String lastNameSummary, String homeTelNumber) { this.firstNameSummary = firstNameSummary; this.lastNameSummary = lastNameSummary; this.homeTelNumber = homeTelNumber; } @Override public String toString() { return "ContactSummary{" + "firstNameSummary='" + firstNameSummary + '\'' + ", lastNameSummary='" + lastNameSummary + '\'' + ", homeTelNumber='" + homeTelNumber + '\'' + '}'; } } |
4.3. Реализация интерфейса
Класс реализующий интерфейс, в котором содержится запрос к базе данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package ru.javastudy.impl; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.javastudy.intf.ContactSummaryService; import ru.javastudy.supportClasses.ContactSummary; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.util.List; /* Запрос со специальным типом результата и конструирующим выражением */ @Service("contactSummaryService") @Repository @Transactional public class ContactSummaryServiceImpl implements ContactSummaryService { @PersistenceContext private EntityManager em; public List<ContactSummary> findAllSummary() { List<ContactSummary> contactSummaryList = em.createQuery( "select new ru.javastudy.supportClasses.ContactSummary(c.firstName, c.lastName, t.telNumber)" +" from ContactEntity c left join c.contactTelDetails t where t.telType='Домашний'", ContactSummary.class).getResultList(); return contactSummaryList; } } |
Тот же самый запрос, но с добавлением конструктора ContactSummary() и указания типа возвращаемого результата ContactSummary.class.
5. Тест запроса
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class Main { public static void main(String[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); ctx.load("classpath:spring-config.xml"); ctx.refresh(); ContactService contactService = ctx.getBean("jpaContactService", ContactService.class); /*untype select */ ContactSummaryUntypeImpl summaryUntype = ctx.getBean("contactSummaryUntype", ContactSummaryUntypeImpl.class); summaryUntype.displayAllContactSummary(); /*select with consturctor */ ContactSummaryService contactSummaryService = ctx.getBean("contactSummaryService", ContactSummaryService.class); List<ContactSummary> contactSummaryList = contactSummaryService.findAllSummary(); for (ContactSummary cs : contactSummaryList) { System.out.println(cs); } } |
Напомню, что этот метод main() был взять из статьи, указанной вначале. Здесь просто добавлены два рассмотренных здесь запроса.