1. Обзор
В этом руководстве мы увидим, что такое прокси в контексте метода load()
Hibernate .
Читателям, плохо знакомым с Hibernate, рекомендуется сначала ознакомиться с основами .
2. Краткое введение в прокси и метод load()
По определению, доверенное лицо — это «функция, уполномоченная выступать в качестве заместителя или замещать другую» .
Это относится к Hibernate, когда мы вызываем Session.load()
для создания так называемого неинициализированного прокси
-сервера нашего желаемого класса сущностей.
Проще говоря, Hibernate создает подклассы нашего класса сущностей, используя библиотеку CGLib
. Помимо метода @Id
, реализация прокси-сервера делегирует все другие методы свойств сеансу Hibernate для заполнения экземпляра, примерно так:
public class HibernateProxy extends MyEntity {
private MyEntity target;
public String getFirstName() {
if (target == null) {
target = readFromDatabase();
}
return target.getFirstName();
}
}
Этот подкласс будет возвращаться вместо прямого запроса к базе данных.
После вызова одного из методов объекта объект загружается и в этот момент становится инициализированным прокси.
3. Прокси и отложенная загрузка
3.1. Единая сущность
Давайте подумаем о Сотруднике
как о сущности. Для начала предположим, что она не имеет отношения ни к каким другим таблицам.
Если мы используем Session.load()
для создания экземпляра Employee
:
Employee albert = session.load(Employee.class, new Long(1));
Затем Hibernate создаст неинициализированный прокси Employee
. Он будет содержать идентификатор, который мы ему дали, но в остальном не будет иметь других значений, потому что мы еще не попали в базу данных.
Однако, как только мы вызываем метод для albert
:
String firstName = albert.getFirstName();
Затем Hibernate запросит в таблице базы данных сотрудников сущность с первичным ключом 1,
заполнив Альберта
его свойствами из соответствующей строки.
Если не удается найти строку, Hibernate генерирует исключение ObjectNotFoundException
.
3.2. Отношения «один ко многим»
Теперь давайте также создадим сущность Company , где у
компании
много сотрудников:
public class Company {
private String name;
private Set<Employee> employees;
}
Если на этот раз мы используем Session.load()
для компании:
Company bizco = session.load(Company.class, new Long(1));
String name = bizco.getName();
Затем свойства компании заполняются так же, как и раньше, за исключением того, что набор сотрудников немного отличается.
Видите ли, мы запросили только строку компании, но прокси оставит сотрудника в покое, пока мы не вызовем getEmployees
в зависимости от стратегии выборки.
3.3. Отношения «многие к одному»
Случай аналогичен в обратном направлении:
public class Employee {
private String firstName;
private Company workplace;
}
Если мы снова используем load() :
Employee bob = session.load(Employee.class, new Long(2));
String firstName = bob.getFirstName();
bob
теперь будет инициализирован, и фактически, рабочее место
теперь будет настроено как неинициализированный прокси-сервер в зависимости от стратегии выборки.
4. Ленивая загрузка
Теперь load()
не всегда будет давать нам неинициализированный прокси. На самом деле документ Session
java напоминает нам (выделено мной):
Этот метод
может
возвращать прокси-экземпляр, который инициализируется по запросу при доступе к методу без идентификатора.
Простым примером того, когда это может произойти, является размер пакета.
Допустим, мы используем @BatchSize
для нашего объекта Employee
:
@Entity
@BatchSize(size=5)
class Employee {
// ...
}
И на этот раз у нас три сотрудника:
Employee catherine = session.load(Employee.class, new Long(3));
Employee darrell = session.load(Employee.class, new Long(4));
Employee emma = session.load(Employee.class, new Long(5));
Если мы вызовем getFirstName
для catherine
:
String cathy = catherine.getFirstName();
Тогда, собственно, Hibernate может решить загрузить сразу всех трех сотрудников, превратив всех троих в инициализированные прокси.
И потом, когда мы зовем Даррелла
по имени:
String darrell = darrell.getFirstName();
Тогда Hibernate вообще не попадает в базу данных.
5. Нетерпеливая загрузка
5.1. Использование get()
Мы также можем полностью обойти прокси и попросить Hibernate загрузить реальную вещь, используя Session.get()
:
Employee finnigan = session.get(Employee.class, new Long(6));
Это вызовет базу данных сразу, вместо возврата прокси.
И на самом деле вместо ObjectNotFoundException
он вернет null
, если finnigan
не существует.
5.2. Влияние на производительность
В то время как get()
удобен, load()
может быть легче для базы данных.
Например, предположим, что Джеральд
собирается работать в новой компании:
Employee gerald = session.get(Employee.class, new Long(7));
Company worldco = (Company) session.load(Company.class, new Long(2));
employee.setCompany(worldco);
session.save(employee);
Поскольку мы знаем, что в этой ситуации мы собираемся изменить только запись о сотруднике
,
вызов load()
для Company
имеет смысл.
Если бы мы вызвали get()
для Company
, мы бы без необходимости загрузили все ее данные из базы данных.
6. Заключение
В этой статье мы вкратце узнали, как работают прокси-серверы Hibernate
и как это влияет на метод загрузки
с сущностями и их отношениями.
Кроме того, мы быстро рассмотрели, чем load()
отличается от get().
Как обычно, полный исходный код, сопровождающий руководство, доступен на GitHub.