别让Hibernate偷走了你的标识符

摘要:

当对象持久化到数据库中时,对象的标识符总时很难被恰当的实现。尽管如此,问题其实完全是由存在着在保存之前不持有ID的对象的现象衍生而来的。我们可以通过从诸如Hibernate这样的对象—关系映像框架手中取走指派对象ID的职责来解决这个问题。相对的,一旦对象被实例化,它就应该被指派一个ID。这使对象标识符变成简单而不易出错,也减少了领域模型中需要的代码量。

企业级java应用程序常常把数据在java对象和关系型数据库之间来回移动。从手动编写SQL代码到使用诸如hibernate这样的成熟的对象---关系映像(ORM)解决方案,有很多种方法可以实现这个过程。无论你采用什么样的技术,一旦你开始将java对象持久化到数据库中,对象标识符都将成为一个复杂而且难以管理的课题。可能出现的情况是:你实例化了两个不同的对象,而它们却代表了数据库中的同一行。为了解决这个问题,你可能采取的措施是在你的持久化对象中实现equals() 和hashCode()这两个方法,可是要恰当的实现这两个方法比乍看之下要有技巧一些。让问题更糟糕的是,那些传统的思路(包括hibernate官方文档所提倡的那些)对于新的工程并不一定能提出最实用的解决方案。

对象标识在虚拟机(VM)中和在数据库中的差异是问题滋生的温床。在虚拟机中,你并不会得到对象的id,你只是简单的持有对象的直接引用。而在幕后,虚拟机确实给每个对象指派了一个8字节大小的id,这个id才是对象的真实引用。当你将对象持久化到数据库中的时候,问题开始产生了。假定你创建了一个Person对象并将它存入数据库(我们可以叫它person1)。而你的其它某段代码从数据库中读取了这个Person对象的数据并将它实例化为另一个新的Person对象(我们可以叫它Person2)。现在你的内存中有了两个映像到数据库中同一行的对象。一个对象引用只能指向它们俩的其中一个,可是我们需要一种方法来表示这两个对象实际上表示着同一个实体。这就是(在虚拟机中)引入对象标识符的原因。

在java语言中,对象标识符是由每个对象都持有的equals()方法(以及相关的hashCode()方法)来定义的。无论两个对象(引用)是否为同一个实例,equals()方法都应该能够判别出它们是否表示同一个实体。hashCode()方法和equals()方法有关联是因为所有被判断等价(equal)的对象都应该返回相同的哈希值(hashCode)。在缺省实现中,equals()方法仅仅比较对象的引用,一个对象和它自身是等价的,而和其它任何实例都不等价。对于持久化对象来说,重写这两个方法,让代表着数据库中同一行的两个对象被判为等价是很重要的。而这对于java中的Collection数据结构(Set,Map和List)的正确工作更是尤为重要。

为了阐明实现equal()和hashCode()的不同途径,让我们一起考虑一个准备持久化到数据库中的简单对象Person。

public class Person {
  private Long id;
  private Integer version;
  public Long getId() { return id; }
  public void setId(Long id) {
   this.id = id;
  }
  public Integer getVersion() {
   return version;
  }
  public void setVersion(Integer version) {
   this.version = version;
  }
  // person-specific properties and behavior
}

在这个例子中,我们遵循了同时持有id字段和version字段的最佳实践。Id字段保存了在数据库中作为主键使用的值,而version字段则是一个从0开始增长的增量,随着对象的每次更新而变化(它帮助我们避免并发更新的问题)。为了看的更清楚,我们也一起看一下Hibernate把这个对象持久化到数据库的映像文件。

<?xml version="1.0"?>
<hibernate-mapping package="my.package">
<class name="Person" table="PERSON">
<id name="id" column="ID" unsaved-value="null">
<generator class="sequence">
<param name="sequence">PERSON_SEQ</param>
</generator>
</id>
<version name="version" column="VERSION" />
<!-- Map Person-specific properties here. -->
</class>
</hibernate-mapping>

时间: 2024-08-02 02:07:11

别让Hibernate偷走了你的标识符的相关文章

[Hibernate开发之路](1)Hibernate配置

一 准备工作 首先我们将创建一个简单的基于控制台的(console-based)Hibernate应用程序. 我们所做的第一件事就是创建我们的开发目录,并且把所有需要用到的Java库文件放进去.解压缩从Hibernate网站下载的Hibernate发布包,并把所有需要的库文件拷到我们项目中去. 学习建User-library-hibernate,并加入相应的jar包 (a)项目右键-buildpath-configure build path-add library (b)选择User-libr

hibernate.jdbc.fetch_size 和 hibernate.jdbc.batch_size

  这点我也疑惑过,最初应用hibernate的项目,我也感觉速度很慢,知道后来才知道问题的所在.       其实hibernate的速度性能并不差,比起jdbc来说,又是性能能高2倍.       当然了这和应用的数据库有关,在Oracle上,hibernate支持hibernate.jdbc.fetch_size和 hibernate.jdbc.batch_size,而MySQL却不支持,而我原来的项目绝大多数都是使用MySQL的,所以觉得速度慢,其实在企业级应用,尤其是金融系统大型应用上

【Hibernate那点事儿】—— Hibernate知识总结

前言: 上一篇简单的讲解了下Hibernate的基础知识.这里对Hibernate比较重要的一些知识点,进行总结和归纳. 手码不易,转载请注明!--xingoo 总结的知识点: 1 关于hibernate映射的实体类标识符访问权限 2 关于对象描述标识符OID及其生成策略 3 Session缓存--清理缓存 4 Session中的状态变更 5 Session中的常用方法 首先简单的看下整理的思维导图,还没有整理完,所以仅仅是一部分而已. 关于Hibernate映射 关于Hibernate的映射要

Hibernate配置文件中映射元素详解

详解 本文中将讲述Hibernate的基本配置及配置文件的应用,这对于正确熟练使用Hibernate是相当关键的. 配置文件中映射元素详解 对象关系的映射是用一个XML文档来说明的.映射文档可以使用工具来生成,如XDoclet,Middlegen和AndroMDA等.下面从一个映射的例子开始讲解映射元素,映射文件的代码如下. <?xml version="1.0"?><!--所有的XML映射文件都需要定义如下所示的DOCTYPE.Hibernate会先在它的类路径(c

Java的Hibernate框架中的基本映射用法讲解_java

Hibernate进行了分类整合发现其实Hibernate分为三大部分:核心对象.映射.HQL,这三大部分开发过程中最常使用,前几篇讨论了核心对象及对象之间的转换方法,接下来讨论Hibernate的映射使用方法.   Hibernate一个重要的功能就是映射,它能够在对象模型和关系模型之间转换,是面向对象编程思想提倡使用的,使用映射程序开发人员只需要关心对象模型中代码的编写.对象和关系数据库之间的映射通常是由XML文档来定义的.这个映射文档被设计为易读的,并且可以手动修改.这种映射关系我总结为下

【hibernate框架】hibernate的ID的生成策略剖析

1.设置id的原因 我们表里面一般有一个id作为主键,一般id不需要手动去传值的. 在实际工作中,在数据库中建表的时候,id在数据库中都是设置成自增字段. 对于类的对象里面的字段,就无法指定自增,需要靠程序自动的生成或者靠数据库来帮我们自动的生成. Jpa/hibernate就已经实现了这样的能力,你可以通过设置来告诉它id怎么样生成,这样你写程序的时候就不用设置id了,这就叫"id的生成策略". 2.让数据库自动生成id的方法 在实体类的xml配置文件中,有: <id name

Hibernate学习之------&amp;gt;Hibernate的保存的区别

hibernate的保存 hibernate对于对象的保存提供了太多的方法,他们之间有很多不同,这里细说一下,以便区别: 一.预备知识: 在所有之前,说明一下,对于hibernate,它的对象有三种状态,transient.persistent.detached 下边是常见的翻译办法: transient:瞬态或者自由态 persistent:持久化状态 detached:脱管状态或者游离态 脱管状态的实例可以通过调用save().persist()或者saveOrUpdate()方法进行持久化

懒加载session-多数据源springmvc+hibernate 切换问题 在一次请求中多次切换不成功

问题描述 多数据源springmvc+hibernate 切换问题 在一次请求中多次切换不成功 最近写了一个多数据源的代码,结果在使用的过程中出现了在一个请求中(方法中)两个数据库交替使用的情况,最后情况是数据库切换不过来 并且在项目配置中有懒加载和OpenSessionInViewFilter 数据库一个为本地数据库一个为基金数据库 要做的事情就是在本地查询到基金代码接着再循环去基金库查询基金数据 中间报错为在本地数据库中查询不到某个表(其实是在基金库中) controller层 @Reque

Hibernate学习大全

第1课 课程内容. 6 第2课Hibernate UML图. 6 第3课 风格. 7 第4课 资源. 7 第5课 环境准备. 7 第6课 第一个示例HibernateHelloWorld 7 第7课 建立Annotation版本的HellWorld 9 第8课 什么是O/RMapping 11 一.     定义:. 11 二.     Hibernate的创始人:. 11 三.     Hibernate做什么:. 12 四.     Hibernate存在的原因:. 12 五.     Hi