【问题标题】:JPA Lazy loading works with final entities and private constructorJPA 延迟加载适用于最终实体和私有构造函数
【发布时间】:2015-09-01 14:40:53
【问题描述】:

我有以下实体

@Entity
@Table(name = "VEHICLE")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public final class Vehicle {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "VEHICLE_ID")
    private int vehicleId;

    @Column(name = "MANUFACTURER")
    private String manufacturer;

    @ManyToOne
    @JoinColumn(name = "personId")
    private Person person;
    //private constructor
    private  Vehicle() { }
    //getters and setters
}

@Entity
@Table(PERSON)
public final class Person {
    @Id
    @Column(name = "PERSON_ID")
    int personId;
    String name;

    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
    List<Vehicle> vehicles = new ArrayList<>();
    //constructor is private
    private Person() {
    }
    //getters and setters
}

这是我的测试课

public class TestClass {
    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("vehiclePU");
        EntityManager entityManager = emf.createEntityManager();

        Person person = entityManager.find(Person.class, 0);
        System.out.println("person = " + person);
        person.getVehicles().forEach(System.out::println);

        emf.close();


    }
}

我的问题是

  1. 为什么它允许将实体声明为 final 并且使用私有的无 arg 构造函数,因为根据 JPA 规范这是不允许的?
  2. 我知道,对于延迟加载,hibernate 使用代理对象,而代理对象只不过是实体的子类,但是由于这里两个实体都被声明为 final,它如何允许它们被子类化?

我也想知道延迟加载是如何工作的? Hibernate默认使用哪个代理库,我们如何知道它使用的是哪个库?

我正在使用 Hibernate 5 和 JPA 2.1。

【问题讨论】:

  • JPA 规范说......为了可移植性,默认构造函数不应该是私有的。仅此而已,如果实现要支持私有,则可以,但用户在其他实现中不能依赖它。 CLASS 可以是最终的。字段/方法不能...为了可移植性。
  • @NeilStockton 如何允许代理和延迟加载?
  • 因为我不使用 Hibernate 并且这是 Hibernate 特定的选项,所以其他人可以回答。并非所有实现都使用“代理”,有些使用包装器,并通过字节码增强来检测此类情况。
  • @NeilStockton 你能分享你找到 jpa Spec 的链接吗?

标签: java hibernate jpa


【解决方案1】:

正如 Neil 所说:不需要使用私有构造函数来支持 final 类——但也没有被禁止。

但除了您失去提供者独立性以及您将看到任何 JPA 验证器发出警告的事实之外 - 您也会失去延迟加载

您确实谈到了延迟加载,但您的示例没有使用它。以下将适用于具有公共或受保护 noargs 构造函数的公共非最终 Person 类:

@ManyToOne(optional = false, fetch = FetchType.LAZY)
private Person person;

但正如 fetch 的 JavaDoc 所说:

EAGER 策略是对持久性提供程序运行时的要求,即必须急切地获取关联实体。 LAZY 策略是对持久性提供程序运行时的提示。

因此,Hibernate 不会抱怨错误的LAZY - 但如果有东西阻止延迟加载,它会默默地忽略。例如最终类,或私有无参数构造函数或optional=true(默认),没有字节码增强。

您可以通过使用不同的选项并检查 Hibernate 日志(检查 load 命令)和使用 vehicle.getPerson().getClass() 来查看所有这些。

一旦你启动了延迟加载并接收到一个代理类,你就可以看到 Hibernate 使用的库:Javassist

【讨论】:

  • 如何使用 cglib 生成代理类?
  • 它已被弃用且不再维护,但根据您的版本,您仍然可以将其与 -Dhibernate.bytecode.provider=cglib 一起使用(经验法则:先搜索,稍后询问)
  • 我检查了 Hibernate 5 的源代码,现在它甚至无法使用 cglib,他们完全删除了它。我已经尝试过您提到的属性,但它不再使用 cglib,然后我在检查源代码后意识到休眠 5 不再使用 cglib。感谢您的回答。
  • 这就是我的意思取决于你的版本。只是好奇:在这里使用过时的 cglib 有什么好处?
猜你喜欢
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-21
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
相关资源
最近更新 更多