1. Обзор
Когда мы используем Hibernate для извлечения данных из базы данных, по умолчанию он использует полученные данные для построения всего графа объекта для запрошенного объекта. Но иногда нам может понадобиться получить только часть данных, предпочтительно в плоской структуре.
В этом кратком руководстве мы увидим, как мы можем добиться этого в Hibernate, используя собственный класс.
2. Сущности
Во-первых, давайте посмотрим на сущности, которые мы будем использовать для извлечения данных:
@Entity
public class DeptEmployee {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String employeeNumber;
private String designation;
private String name;
@ManyToOne
private Department department;
// constructor, getters and setters
}
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String name;
@OneToMany(mappedBy="department")
private List<DeptEmployee> employees;
public Department(String name) {
this.name = name;
}
// getters and setters
}
Здесь у нас есть две сущности — DeptEmployee
и Department
. Для простоты предположим, что DeptEmployee
может принадлежать только одному отделу.
Но у отдела
может быть несколько сотрудников отдела
.
3. Пользовательский класс результатов запроса
Допустим, мы хотим напечатать список всех сотрудников, указав только их имена и названия их отделов.
Обычно мы извлекаем эти данные с помощью такого запроса:
Query<DeptEmployee> query = session.createQuery("from com.foreach.hibernate.entities.DeptEmployee");
List<DeptEmployee> deptEmployees = query.list();
Это позволит получить всех сотрудников, все их свойства, связанный отдел и все его свойства.
Но в данном конкретном случае это может быть немного дорого, так как нам нужно только имя сотрудника и название отдела.
Один из способов получить только ту информацию, которая нам нужна, — указать свойства в предложении select.
Но когда мы делаем это, Hibernate возвращает список массивов вместо списка объектов:
Query query = session.createQuery("select m.name, m.department.name from com.foreach.hibernate.entities.DeptEmployee m");
List managers = query.list();
Object[] manager = (Object[]) managers.get(0);
assertEquals("John Smith", manager[0]);
assertEquals("Sales", manager[1]);
Как мы видим, возвращаемые данные немного громоздки для обработки. Но, к счастью, мы можем заставить Hibernate заполнять эти данные в классе.
Давайте посмотрим на класс Result
, который мы будем использовать для заполнения полученных данных:
public class Result {
private String employeeName;
private String departmentName;
public Result(String employeeName, String departmentName) {
this.employeeName = employeeName;
this.departmentName = departmentName;
}
public Result() {
}
// getters and setters
}
Обратите внимание, что класс не является сущностью, а просто POJO. Однако мы также можем использовать сущность, если у нее есть конструктор, который принимает все атрибуты, которые мы хотим заполнить в качестве параметров.
В следующем разделе мы увидим, почему конструктор важен.
4. Использование конструктора в HQL
Теперь давайте посмотрим на HQL, использующий этот класс:
Query<Result> query = session.createQuery("select new com.foreach.hibernate.pojo.Result(m.name, m.department.name)"
+ " from com.foreach.hibernate.entities.DeptEmployee m");
List<Result> results = query.list();
Result result = results.get(0);
assertEquals("John Smith", result.getEmployeeName());
assertEquals("Sales", result.getDepartmentName());
Здесь мы используем конструктор, который мы определили в классе Result
, вместе со свойствами, которые мы хотим получить. Это вернет список объектов Result
с данными, заполненными из столбцов.
Как мы видим, возвращаемый список легче обрабатывать, чем использовать список массивов объектов.
Важно отметить, что мы должны использовать полное имя класса в запросе.
5. Использование ResultTransformer
Альтернативой использованию конструктора в запросе HQL является использование ResultTransformer:
Query query = session.createQuery("select m.name as employeeName, m.department.name as departmentName"
+ " from com.foreach.hibernate.entities.DeptEmployee m");
query.setResultTransformer(Transformers.aliasToBean(Result.class));
List<Result> results = query.list();
Result result = results.get(0);
assertEquals("John Smith", result.getEmployeeName());
assertEquals("Sales", result.getDepartmentName());
Мы используем трансформаторы.
aliasToBean()
, чтобы использовать полученные данные для заполнения объектов Result .
Следовательно, мы должны убедиться, что имена столбцов или их псевдонимы в операторе select соответствуют свойствам класса Result
.
Обратите внимание, что Query.setResultTransformer(
ResultTransformer )
устарел, начиная с Hibernate 5.2.
6. Заключение
В этой статье мы увидели, как можно использовать пользовательский класс для извлечения данных в удобной для чтения форме.
Исходный код, сопровождающий эту статью, доступен на GitHub .