不要让Hibernate管理您的id.
试图创建和维护对象及数据库行的各自身份定义是目前为止所有讨论问题的根源。如果我们统一所有身份形式,这些问题都将不复存在。也就是说,作为以数据库为中心和以对象为中心的ID的替代品,我们应该创建一种通用的、特定于实体的ID来代表数据实体,这种ID应该在数据第一次输入的时候创建。无论这个唯一数据实体是保存在数据库中,是作为对象驻留在内存中,还是存储在其他格式的介质中,这个通用ID都应该可以识别它。通过使用数据实体第一次创建时指派的实体ID,我们可以安全地回到equals()和hashCode()的原始定义,它们只需使用这个id:
|
这个例子使用对象id作为equals()方法判断相等的标准,以及hashCode()返回哈希码的来源。这就简单了许多。但是,要让它正常工作,我们需要两样东西。首先,我们需要保证每个对象在被保存之前都有一个id值。在这个例子里,当id变量被声明的时候,它就被指派了一个值。其次,我们需要一种判断这个对象是新生成的还是之前保存过的的手段。在我们最早的例子中,Hibernate通过检查id字段是否为null来判断对象是否为新的。既然对象id永不为null,很显然这种方法不再有效。通过配置Hibernate,让它检查version字段,而不是id字段是否为null, 我们可以很容易地解决这个问题。version字段是一个更恰当的用来判断对象是否被保存过的指示符。
下面是我们改进过的Person类的Hibernate映射文件。
|
注意,id下面的generator标签包含了属性class="assigned".这个属性告诉Hibernate我们不是从数据库指派id值,而是在代码中指派id值。Hibernate会简单地认为即使是新的未保存的对象也有id值。我们也给version标签新增了一个属性:unsaved-value="null".这个属性告诉Hibernate应该把version值而不是id值为null作为对象是新创建而成的指示器。我们也可以简单地告诉Hibernate把负值作为对象未保存的指示符,如果您喜欢把version字段的类型设置为int而不是Integer,这将是很有用的。
我们已经从转移到纯对象id中获取了不少好处。我们对equals()和hashCode()方法的实现更加简单而且更易阅读。这些方法再也不易出错而且无论在保存对象之前还是之后,它们都能与Collection一起正常工作。Hibernate也变得更快一些,这是因为在保存新的对象之前它再也不需要从数据库读取一个序列值。此外,新定义的equals()和hashCode()对于所有包含id对象的对象来说是通用的。这意味着我们可以把这些方法移至一个抽象父类。我们不再需要为每个域对象重新实现equals()和hashCode(),而且我们也不再需要考虑对于每个类来说哪些字段组合是唯一且不变的。我们只要简单地扩展这个抽象父类。当然,我们没必要强迫域对象从父类中扩展出来,所以我们定义了一个接口来保证设计的灵活性。

