Chapter 4. Persistent Classes
Table of Contents
4.1. A simple POJO example
4.1.1. Implement a no-argument constructor
4.1.2. Provide an identifier property
4.1.3. Prefer non-final classes (semi-optional)
4.1.4. Declare accessors and mutators for persistent fields (optional)
4.2. Implementing inheritance
4.3. Implementing equals() and hashCode()
4.4. Dynamic models
4.5. Tuplizers
4.6. EntityNameResolvers
如果你的持久类遵循一些简单的规则(POJO编程模型),Hibernate将会很好的工作。但是,这并不是必须的。事实上,Hibernate并没有对你的持久类做过多的假定。你可以用其他的方式来表示你的领域模型(比如说用java.util.Map的实例)
4.1. A simple POJO example
Example 4.1. Simple POJO representing a 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); } }
持久类需要遵循的四个主要规则如下:
4.1.1. Implement a no-argument constructor
实现一个无参的构造函数。这样,Hibernate才可以使用java.lang.reflect.Constructor.newInstance()实例化持久类。建议构造函数的可见性最小要和包的可见性一致,这是为了运行时代理生成能够正确工作。
4.1.2. Provide an identifier property
在Hibernate以前的版本中,这一项被视为可选的。至今也不是强制性的,但是最好将其视为deprecated功能,因为在即将发布的新版中,必须要提供一个identifier property。
标识属性并不非要在数据库中对应主键,只需对应可以唯一区别每一行的列即可。
我们建议你在持久化类中声明名字一致的标识属性并使用可空类型(也就是非原始类型)
4.1.3. Prefer non-final classes (半可选)
Hibernate很重要的一项功能proxies (延迟加载)的前提条件是持久类既不是final的也没有实现接口(声明的全是public方法的接口)。你可以用Hibernate持久化一个final类(没有实现接口),但是你将无法使用延迟加载,这将会影响应用程序的性能。为了持久化一个final类(没有实现"full"接口),你必须关闭proxy generation.如下:
Example 4.2. Disabling proxies in hbm.xml
<class name="Cat" lazy="false"...>...</class>
Example 4.3. Disabling proxies in annotations
@Entity @Proxy(lazy=false) public class Cat { ... }
如果final类实现了合适的接口,你还可以通知Hibernate在生成proxies时使用它的接口。
Example 4.4. Proxying an interface in hbm.xml
<class name="Cat" proxy="ICat"...>...</class>
Example 4.5. Proxying an interface in annotations
@Entity @Proxy(proxyClass=ICat.class) public class Cat implements ICat { ... }
你应该避免声明public final方法,因为这会限制从该类中生成proxies 。如果你想要使用的类中包含public final方法,你必须明确的关闭proxying。
许多其他的ORM工具直接持久化实例变量。但是为类的内部数据提供一个间接地访问会更好。默认情况下,Hibernate持久化JavaBeans形式的属性通过识别getFoo, isFoo 和setFoo这种形式的方法名。如果有必要,你可以切换到对某些特定的类属性进行直接访问。没有必要将类属性声明为public的。Hibernate同样可以持久化访问权限为 package, protected 或者 private 的类属性。
4.2. Implementing inheritance
子类也必须遵守第一条和第二条规则。例:
package eg; public class DomesticCat extends Cat { private String name; public String getName() { return name; } protected void setName(String name) { this.name=name; } }
4.3. Implementing equals() and hashCode()
如果你想要:
将实例放到Set中去并且想要使用游离态实例的reattachment。
那么你不得不重写equals() 和 hashCode()方法。
Hibernate只能在一个特定的Session scope中保证persistent identity (database row)与 Java identity的相等性。当你把从不同的session中检索出来的的实例混合到一起,而又想它们的集合Set有意义,那么你必须重写 equals()和hashCode()。
实现 equals()/hashCode()最简单的方式就是比较对象的identifier。如果值相等,那么他们就代表数据库的同一行。如果它们都被添加到Set中去,那么Set当中实际只包含一个元素。不幸的是,当使用generated identifiers时,你不能使用该方式。因为只有当对象是持久态时,Hibernate才会分配identifier,一个新创建的实例还没有identifier。此外,如果一个实例没有保存并且存在于一个Set当中,保存Set时,Hibernate会给该实例一个identifier。
如果equals() 和hashCode()基于identifier的值,hash code会改变,打破Set的约定。到Hibernate网站查看对这一问题的详细讨论。这不是Hibernate的问题,而是正常的Java对于对象同一性和相等性的语义。
建议你使用Business key相等来实现equals() 和hashCode()。Business key相等意味着 equals() 方法只比较由business key构成的属性。它可以用来在现实世界当中区分我们的实例。例:
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; } }
business key不一定非要是数据库的主键。不可变或者唯一键都可以作为business key。
4.4. Dynamic models
Note
The following features are currently considered experimental and may change in the near future.
4.5. Tuplizers
4.6. EntityNameResolvers(以后看)