前言:使用Hibernate也有一段时间了,但总没时间好好看看它的细节。现在每天利用一点时间把Hibernate参考手册再好好学习一下,并摘要部分认为比较重要的内容。现在Hibernate已经有大把的资料,翻译参考手册并无太大的价值,本文纯粹是为了加深对Hibernate的理解而已,是写给自己看。
1. 持久化类
在Hibernate中,持久化类就是POJO(Plain Old Java Object)。虽然Hibernate对持久化类没有过多的限制,甚至使用Map实例也可以,但POJO还是最好最常用的方式。
一个表示cat的持久化类示例如下:
package eg;
import java.util.Set;
import java.util.Date;
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.xml: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);
}
}
1.1 持久化类的规则
持久化类主要以下四条主要规则:
- 具有无参构造方法。上述Cat类就具有一个默认的无参构造方法。Hibernate是使用java.lang.reflect.Constructor.newInstance()来示例化持久化类的。为了使运行时proxy创建正确的工作,建议构造方法至少是包可见的。
- 提供一个标识属性。在Cat类中就有一个名为id的标识属性,一般情况下它对应于相应数据库表的主键。也可以对应具有唯一标识的列。Hibernate建议标识属性使用类类型的数据类型,如Long,Integer等。
- 持久化类最好是非final类。Hibernate的核心特征——proxies(lazy loading),就是依靠非final类或者是实现了共有方法的接口的类。除非你不想使用lazy loading,你才使用final类。
- 对持久化域声明访问和赋值方法,即对需要持久化的属性需要有getter和setter方法。
在hbm.xml中关闭proxies的方法:
<class name="Cat" lazy="false"...>...</class>
使用注解关闭proxies的方法:
@Entity @Proxy(lazy=false) public class Cat { ... }
1.2 持久化类的继承
持久化里的子类也遵从相同的规则。
1.3 实现equals()和hashCode()
如果你有一下操作,你必须重写equals()和hashCode()方法:
- 欲把持久化对象放在Set中。
- 欲将离线实例重新加载。
Hibernate能构造保证在一个session范围内持久化标识和Java标识保持一致。当你从不同session中获得混合实例,如果你还希望保持Set语义(Set中每个元素都不相同)的话,你就必须重写equals()和hashCode()方法。
建议使用Business key equality来实现equals()和hashCode()。如:
public class Cat {
...
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;
}
}
1.4 动态模型
动态模型现在被认为是实验性的,有可能将来会发生变化。在这里略过。
1.5 Tuplizer
一个比较奇怪的词。从它的英文愿意理解,应该是负责持久化类创建、存取的类。一般情况下,我们不需要编写Tuplizer,除非你的持久化类很复杂,或者你想定义一个不同的proxy生成策略。可以通过注解或hbm.xml来为一个持久化类指定一个Tuplizer(具体略)。
在实践中常见的和Tuplizer相关的错误是持久化类的getter和setter没有正确编写。前些年和Tuplizer有关的错误是和Spring中的包冲突引起的问题。
1.6 EntityNameResolver
一般情况下我们也不需要实现org.hibernate.EntityNameResolver接口,它有一个唯一的方法resolveEntityName接受一个实体实例参数,它用于解析实体的名称。它通常和动态模型有关。