Перейти к основному содержимому

Прокси в Hibernate метод load()

· 5 мин. чтения

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.