Proxy in Hibernate load() Method – Hibernate load()方法中的代理

最后修改: 2018年 10月 3日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll see what a proxy is in the context of Hibernate’s load() method.

在本教程中,我们将在Hibernate的load()方法中看到什么是代理。

For readers new to Hibernate, consider getting familiar with basics first.

对于刚接触Hibernate的读者,可以考虑先熟悉一下基础知识

2. A Brief Introduction To Proxies and load() Method

2.代理机构和load()方法简介

By definition, a proxy is “a function authorized to act as the deputy or substitute for another”.

根据定义,代理人是 “被授权作为他人的副手或替代者的职能”

This applies to Hibernate when we call Session.load() to create what is called an uninitialized proxy of our desired entity class.

这适用于Hibernate,当我们调用Session.load()来创建我们想要的实体类的所谓uninitialized proxy

Simply put, Hibernate subclasses our entity class, using the CGLib library. Other than the @Id method, the proxy implementation delegates all other property methods to the Hibernate session to populate the instance, somewhat like:

简单地说,Hibernate使用CGLib库对我们的实体类进行子类化。除了@Id方法外,代理实现将所有其他属性方法委托给Hibernate会话来填充实例,有点像。

public class HibernateProxy extends MyEntity {
    private MyEntity target;

    public String getFirstName() {
        if (target == null) {
            target = readFromDatabase();
        }
        return target.getFirstName();
    }
}

This subclass will be the one to be returned instead of querying the database directly.

这个子类将被返回,而不是直接查询数据库。

Once one of the entity methods is called, the entity is loaded and at that point becomes an initialized proxy.

一旦其中一个实体方法被调用,该实体就会被加载,并在此时成为一个初始化的代理。

3. Proxies and Lazy loading

3.代理和懒人加载

3.1. A Single Entity

3.1.一个单一的实体

Let’s think about Employee as an entity. To begin, we’ll assume that it has no relation to any other tables.

让我们考虑一下Employee作为一个实体。首先,我们假设它与任何其他表没有关系。

If we use Session.load() to instantiate an Employee:

如果我们使用Session.load()来实例化一个Employee

Employee albert = session.load(Employee.class, new Long(1));

Then Hibernate will create an uninitialized proxy of Employee. It will contain the ID that we gave it but otherwise will have no other values because we haven’t hit the database yet.

然后,Hibernate将创建一个未初始化的Employee的代理。它将包含我们给它的ID,但除此之外将没有其他值,因为我们还没有打到数据库。

However, once we call a method on albert:

然而,一旦我们对albert调用一个方法。

String firstName = albert.getFirstName();

Then Hibernate will query the employee database table for an entity with a primary key of 1, populating albert with his properties from the corresponding row.

然后,Hibernate将查询employee数据库表,寻找一个主键为1的实体,从相应的行中为albert填充其属性。

If it fails to find a row, then Hibernate throws an ObjectNotFoundException.

如果它不能找到一行,那么Hibernate就会抛出一个ObjectNotFoundException

3.2. One-to-Many Relationships

3.2.一对多的关系

Now, let’s create a Company entity as well, where a Company has many Employees:

现在,让我们也创建一个公司实体,一个公司有许多雇员:

public class Company {
    private String name;
    private Set<Employee> employees;
}

If we this time use Session.load() on the company:

如果我们这次在公司上使用Session.load()

Company bizco = session.load(Company.class, new Long(1));
String name = bizco.getName();

Then the company’s properties are populated as before, except the set of employees is just a bit different.

然后,公司的属性会像以前一样被填充,只是员工的集合有点不同而已。

See, we only queried for the company row, but the proxy will leave the employee set alone until we call getEmployees depending on the fetching strategy.

看,我们只查询了公司行,但代理会让雇员集单独存在,直到我们调用getEmployees,这取决于获取策略。

3.3. Many-to-One Relationships

3.3.多对一的关系

The case is similar in the opposite direction:

在相反的方向上,情况也类似。

public class Employee {
    private String firstName;
    private Company workplace;
}

If we use load() again:

如果我们再次使用load()

Employee bob = session.load(Employee.class, new Long(2));
String firstName = bob.getFirstName();

bob will now be initialized, and actually, workplace will now be set to be an uninitialized proxy depending on the fetching strategy.

bob现在将被初始化,实际上,workplace现在将被设置为一个未初始化的代理,这取决于获取策略。

4. Lazy-ish Loading

4.懒惰的装载

Now, load() won’t always give us an uninitialized proxy. In fact, the Session java doc reminds us (emphasis added):

现在,load() 不会总是给我们一个未初始化的代理。事实上,Session java 文档提醒我们(强调是后加)。

This method might return a proxied instance that is initialized on-demand, when a non-identifier method is accessed.

这个方法可能返回一个代理实例,在访问非标识符方法时按需初始化。

A simple example of when this can happen is with batch size.

发生这种情况的一个简单例子是批量大小。

Let’s say that we are using @BatchSize on our Employee entity:

假设我们在我们的@BatchSize实体上使用Employee

@Entity
@BatchSize(size=5)
class Employee {
    // ...
}

And this time we have three employees:

而这一次,我们有三名雇员。

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));

If we call getFirstName on catherine:

如果我们在catherine上调用getFirstName

String cathy = catherine.getFirstName();

Then, actually, Hibernate may decide to load all three employees at once, turning all three into initialized proxies.

那么,实际上,Hibernate可能决定一次加载所有三个雇员,将所有三个雇员变成初始化的代理。

And then, when we call for darrell‘s first name:

然后,当我们呼唤darrell的名字时。

String darrell = darrell.getFirstName();

Then Hibernate doesn’t hit the database at all.

那么,Hibernate根本就不打数据库。

5. Eager Loading

5.急于加载

5.1. Using get()

5.1.使用get()

We can also bypass proxies entirely and ask Hibernate to load the real thing using Session.get():

我们也可以完全绕过代理,要求Hibernate使用Session.get()加载真正的东西。

Employee finnigan = session.get(Employee.class, new Long(6));

This will call the database right away, instead of returning a proxy.

这将立即调用数据库,而不是返回一个代理。

And actually, instead of an ObjectNotFoundException, it will return null if finnigan doesn’t exist.

实际上,如果finnigan不存在,它将返回null,而不是出现ObjectNotFoundException

5.2. Performance Implications

5.2.性能影响

While get() is convenient, load() can be lighter on the database.

虽然get()很方便,但load()对数据库来说可能会更轻。

For instance, let’s say gerald is going to work for a new company:

例如,假设gerald要为一家新公司工作。

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);

Since we know that we are only going to change the employee record in this situation, calling load() for Company is sensible.

由于我们知道在这种情况下我们只打算改变employee 记录,为Company 调用load()是明智的。

If we called get() on Company, then we’d have loaded all its data needlessly from the database.

如果我们对Company调用get(),那么我们就会不必要地从数据库中加载其所有数据。

6. Conclusion

6.结论

In this article, we briefly learned how Hibernate proxies work and how this impacts the load method with entities and their relationships.

在这篇文章中,我们简要地了解了Hibernate代理如何工作,以及这对load方法与实体及其关系有何影响。

Also, we took a quick look at how load() differs from get().

此外,我们还快速看了一下load()get()的区别。

As usual, the full source code that accompanies the tutorial is available over on GitHub.

像往常一样,伴随着教程的完整源代码可在GitHub上获得