[转帖]别让Hibernate偷走了您的身份_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3460 | 回复: 0   主题: [转帖]别让Hibernate偷走了您的身份        下一篇 
赖文婷
注册用户
等级:少校
经验:1094
发帖:81
精华:0
注册:2012-11-5
状态:离线
发送短消息息给赖文婷 加好友    发送短消息息给赖文婷 发消息
发表于: IP:您无权察看 2012-11-15 15:50:18 | [全部帖] [楼主帖] 楼主

企业级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()方法有关联是因为所有相等的对象都应该返 回相同的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把这个对象 持久存储到数据库的Hibernate映射文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM
"http://hibernate.sourceforge.net/
hibernate-mapping-3.0.dtd">
<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>


Hibernate映射文件指明了Person的id字段代表数据库中的ID列(也就是说,它是PERSON表的主键)。包含在id标签中的 unsaved-value="null"属性告诉Hibernate使用id字段来判断一个Person对象之前是否被保存过。ORM框架必须依靠这个 来判断保存一个对象的时候应该使用SQL的INSERT子句还是UPDATE子句。在这个例子中,Hibernate假定一个新对象的id字段一开始为 null值,当它第一次被保存时id才被赋予一个值。generator标签告诉Hibernate当对象第一次保存时,应该从哪里获得指派的id。在这 个例子中,Hibernate使用数据库序列作为唯一ID的来源。最后,version标签告诉Hibernate使用Person对象的version 字段进行并发控制。Hibernate将会执行乐观锁定方案,根据这个方案,Hibernate在保存对象之前会根据数据库版本号检查对象的版本号。

      我们的Person对象还缺少的是equals()方法和hashCode()方法的实现。既然这是一个持久性对象,我们并不想依赖于这两个方法的默认实 现,因为默认实现并不能分辨代表数据库中同一行的两个不同实例。一种简单而又显然的实现方法是利用id字段来进行equal()方法的比较以及生成 hashCode()方法的结果。

public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || !(o instanceof Person))
      return false;
      Person other = (Person)o;
      if (id == other.getId()) return true;
      if (id == null) return false;
      // equivalence by id
      return id.equals(other.getId());
}
public int hashCode() {
      if (id != null) {
            return id.hashCode();
      } else {
      return super.hashCode();
}
}




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论