Hibernate 可以持久化哪些类型的类?

What types of classes can Hibernate persist?

我想知道 hibernate 是否对特定类型的 classes 强加任何限制来持久化它们。我读过 Hibernate 可以持久化 JavaBean classes 和 Pojos。

这是否意味着 hibernate 无法持久化不属于这两类的 classes?

我们可以在休眠状态下保留非 pojo classes 吗?例如

Class A extends B{}

基本上我需要知道是什么让 hibernate 无法在 RDBMS 中持久化 class?

持久性 classes 是应用程序中实现业务问题实体的 classes(例如,电子商务应用程序中的客户和订单)。并非持久 class 的所有实例都被视为处于持久状态。例如,一个实例可以改为瞬态或分离。

如果这些 classes 遵循一些简单的规则,也称为普通旧 Java 对象 (POJO) 编程模型,则 Hibernate 工作得最好。但是,none 这些规则是硬性要求。实际上,Hibernate3 对持久对象的性质假设得很少。您可以用其他方式表达域模型(例如,使用 Map 实例树)。

大多数 Java 应用程序需要一个持久的 class 代表猫科动物。例如:

public class Cat {
private Long id; // identifier

private Date birthdate;
private Color color;
private char sex;
private float weight;
private int litterId;

private Cat mother;
private Set kittens = new HashSet();

private void setId(Long id) {
    this.id=id;
}
public Long getId() {
    return id;
}

void setBirthdate(Date date) {
    birthdate = date;
}
public Date getBirthdate() {
    return birthdate;
}

void setWeight(float weight) {
    this.weight = weight;
}
public float getWeight() {
    return weight;
}

public Color getColor() {
    return color;
}
void setColor(Color color) {
    this.color = color;
}

void setSex(char sex) {
    this.sex=sex;
}
public char getSex() {
    return sex;
}

void setLitterId(int id) {
    this.litterId = id;
}
public int getLitterId() {
    return litterId;
}

void setMother(Cat mother) {
    this.mother = mother;
}
public Cat getMother() {
    return mother;
}
void setKittens(Set kittens) {
    this.kittens = kittens;
}
public Set getKittens() {
    return kittens;
}

// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
    kitten.setMother(this);
kitten.setLitterId( kittens.size() ); 
    kittens.add(kitten);
}

}

持久性 classes 的四个主要规则: 1.Implement 无参数构造函数

Cat 有一个无参数的构造函数。所有持久性 classes 必须有一个默认构造函数(可以是非 public),以便 Hibernate 可以使用 Constructor.newInstance() 实例化它们。建议您有一个默认构造函数,它至少对 Hibernate 中的运行时代理生成具有包可见性。 2.2.提供标识符 属性(可选)

Cat 有一个名为 id 的 属性。此 属性 映射到数据库 table 的主键列。 属性 可能被称为任何东西,它的类型可能是任何原始类型、任何原始 "wrapper" 类型、java.lang.String 或 java.util.Date。如果您的遗留数据库 table 具有复合键,您可以使用具有这些类型属性的用户定义 class(请参阅本章后面的复合标识符部分。)

标识符 属性 是严格可选的。您可以将它们关闭,让 Hibernate 在内部跟踪对象标识符。但是,我们不推荐这样做。

事实上,某些功能仅适用于声明标识符 属性:

的 classes

分离对象的传递性重新附加(级联更新或级联合并)——参见第 10.11 节,“传递持久性” Session.saveOrUpdate() Session.merge() 我们建议您在持久 classes 上声明一致命名的标识符属性,并使用可空(即非原始)类型。

  1. 首选非最终 classes(可选)

Hibernate 的一个核心特性,代理,取决于持久性 class 是非最终的,还是声明所有 public 方法的接口的实现。

您可以保留未实现 Hibernate 接口的最终 classes。但是,您将无法使用代理进行延迟关联提取,这最终会限制您的性能调整选项。

您还应该避免在非最终 class 上声明 public 最终方法。如果您想使用带有 public 最终方法的 class,您必须通过设置 lazy="false".

显式禁用代理
  1. 声明持久字段的访问器和修改器(可选)

Cat 为其所有持久字段声明访问器方法。许多其他 ORM 工具直接持久化实例变量。最好在 class 的关系模式和内部数据结构之间提供一个间接。默认情况下,Hibernate 保留 JavaBeans 样式属性并识别 getFoo、isFoo 和 setFoo 形式的方法名称。如果需要,您可以切换到特定属性的直接字段访问。

不需要声明属性 public - Hibernate 可以使用默认的、受保护的或私有的 get / set 对来保存 属性。

实现继承

子class也必须遵守第一条和第二条规则。它从 superclass、Cat 继承了它的标识符 属性。例如:

包例如;

    public class DomesticCat extends Cat {
    private String name;

    public String getName() {
            return name;
    }
    protected void setName(String name) {
            this.name=name;
    }
    } 

实施 equals() 和 hashCode()

在以下情况下,您必须重写 equals() 和 hashCode() 方法:

打算将持久性 classes 的实例放在一个 Set 中(表示多值关联的推荐方式);和 打算使用分离实例的重新连接 Hibernate 仅在特定会话范围内保证持久标识(数据库行)和 Java 标识的等价性。当您混合在不同会话中检索的实例时,如果您希望 Set 具有有意义的语义,则必须实现 equals() 和 hashCode()。

最明显的方法是通过比较两个对象的标识符值来实现equals()/hashCode()。如果值相同,则两者必须是相同的数据库行,因为它们是相等的。如果两者都添加到一个集合中,则集合中将只有一个元素)。不幸的是,您不能将这种方法用于生成的标识符。 Hibernate 只会将标识符值分配给持久化的对象;新创建的实例将没有任何标识符值。此外,如果一个实例未保存且当前位于 Set 中,则保存它会为该对象分配一个标识符值。如果 equals() 和 hashCode() 是基于标识符值的,哈希码会改变,从而破坏 Set 的契约。有关此问题的完整讨论,请参阅 Hibernate 网站。这不是 Hibernate 问题,而是对象身份和相等性的正常 Java 语义。

建议您使用业务键相等来实现 equals() 和 hashCode()。业务键相等意味着 equals() 方法只比较构成业务键的属性。它是一个可以在现实世界中识别我们的实例的密钥(自然候选密钥):

public class猫{

...
public boolean equals(Object other) {
    if (this == other) return true;
    if ( !(other instanceof Cat) ) return false;

    final Cat cat = (Cat) other;

    if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
    if ( !cat.getMother().equals( getMother() ) ) return false;

    return true;
}

public int hashCode() {
    int result;
    result = getMother().hashCode();
    result = 29 * result + getLitterId();
    return result;
}

} 业务密钥不必像数据库主键候选 Immutable 一样可靠,或者独特的属性通常是业务密钥的良好候选者。

从实践中我了解到对象持久化的这些限制:

  1. 只有class标有@Entity的可以持久化,但并不是所有的超classes都必须有这个注解。
  2. 您不能将接口或非实体 classes 用于实体中的字段类型(用于持久化目的)。
  3. 这就是为什么您不能在实体中使用泛型的全部功能。

在现实世界中,pojo 和 java beans 并不是您成功工作所需要和想要的。