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 上声明一致命名的标识符属性,并使用可空(即非原始)类型。
- 首选非最终 classes(可选)
Hibernate 的一个核心特性,代理,取决于持久性 class 是非最终的,还是声明所有 public 方法的接口的实现。
您可以保留未实现 Hibernate 接口的最终 classes。但是,您将无法使用代理进行延迟关联提取,这最终会限制您的性能调整选项。
您还应该避免在非最终 class 上声明 public 最终方法。如果您想使用带有 public 最终方法的 class,您必须通过设置 lazy="false".
显式禁用代理
- 声明持久字段的访问器和修改器(可选)
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 一样可靠,或者独特的属性通常是业务密钥的良好候选者。
从实践中我了解到对象持久化的这些限制:
- 只有class标有@Entity的可以持久化,但并不是所有的超classes都必须有这个注解。
- 您不能将接口或非实体 classes 用于实体中的字段类型(用于持久化目的)。
- 这就是为什么您不能在实体中使用泛型的全部功能。
在现实世界中,pojo 和 java beans 并不是您成功工作所需要和想要的。
我想知道 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 上声明一致命名的标识符属性,并使用可空(即非原始)类型。
- 首选非最终 classes(可选)
Hibernate 的一个核心特性,代理,取决于持久性 class 是非最终的,还是声明所有 public 方法的接口的实现。
您可以保留未实现 Hibernate 接口的最终 classes。但是,您将无法使用代理进行延迟关联提取,这最终会限制您的性能调整选项。
您还应该避免在非最终 class 上声明 public 最终方法。如果您想使用带有 public 最终方法的 class,您必须通过设置 lazy="false".
显式禁用代理- 声明持久字段的访问器和修改器(可选)
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 一样可靠,或者独特的属性通常是业务密钥的良好候选者。
从实践中我了解到对象持久化的这些限制:
- 只有class标有@Entity的可以持久化,但并不是所有的超classes都必须有这个注解。
- 您不能将接口或非实体 classes 用于实体中的字段类型(用于持久化目的)。
- 这就是为什么您不能在实体中使用泛型的全部功能。
在现实世界中,pojo 和 java beans 并不是您成功工作所需要和想要的。