每个Java对象都继承了equals和hashCode方法,但它们仅对Value对象有用,对面向无状态行为的对象毫无用处。

尽管使用“ ==”运算符比较引用很简单,但是对于对象相等而言,事情要复杂一些。

由于您负责告诉平等性对特定对象类型的含义,因此必须使equals和hashCode实现遵循java.lang.Object JavaDoc( equals()和hashCode() )指定的所有规则。

了解您的应用程序(及其使用的框架)如何利用这两种方法也很重要。

幸运的是,Hibernate不需要它们检查实体是否已更改,为此具有专用的脏检查机制。

浏览Hiberante文档后,我偶然发现了这两个链接: Equals和HashCode和Hiberante 4.3文档指出了需要两种方法的上下文:

  • 将实体添加到Set集合时
  • 将实体重新附加到新的持久性上下文时

这些要求来自Object.equals的“ consistent ”约束,使我们遵循以下原则:

在所有JPA对象状态下,实体必须等于其自身

  • 短暂的
  • 附上
  • 超脱
  • 移除(只要该对象被标记为要移除并且它仍然驻留在堆上)

因此,我们可以得出以下结论:

  1. 我们不能使用自动递增的数据库ID来比较对象,因为瞬态和附加的对象版本不会彼此相等。
  2. 我们不能依赖默认的Object equals / hashCode实现,因为在两个不同的持久性上下文中加载的两个实体最终将成为两个不同的Java对象,因此违反了全状态相等性规则。
  3. 因此,如果Hibernate使用相等性唯一地标识对象,则在整个生命周期中,我们需要找到满足此要求的属性的正确组合。

具有在整个实体对象空间中唯一的属性的那些实体字段通常称为业务密钥。

与合成数据库自动递增的ID相对,业务密钥还独立于我们的项目体系结构中采用的任何持久性技术。

因此,必须从我们创建实体的那一刻起就设置业务密钥,然后再也不要更改它。

让我们以实体相关性为例,并选择适当的业务密钥。

  • 根实体用例(没有任何父依赖项的实体)

这是实现equals / hashCode的方式:

@Entity
public class Company {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(unique = true, updatable = false)private String name;@Overridepublic int hashCode() {HashCodeBuilder hcb = new HashCodeBuilder();hcb.append(name);return hcb.toHashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof Company)) {return false;}Company that = (Company) obj;EqualsBuilder eb = new EqualsBuilder();eb.append(name, that.name);return eb.isEquals();}
}

名称字段代表公司业务密钥,因此被声明为唯一且不可更新。 因此,如果两个Company对象具有相同的名称,则它们相等,而忽略了它可能包含的任何其他字段。

  • 拥有EAGER的父级的子实体
@Entity
public class Product {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(updatable = false)private String code;@ManyToOne(fetch = FetchType.EAGER)@JoinColumn(name = "company_id", nullable = false, updatable = false)private Company company;@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "product", orphanRemoval = true)@OrderBy("index")private Set images = new LinkedHashSet();@Overridepublic int hashCode() {HashCodeBuilder hcb = new HashCodeBuilder();hcb.append(name);hcb.append(company);return hcb.toHashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof Product)) {return false;}Product that = (Product) obj;EqualsBuilder eb = new EqualsBuilder();eb.append(name, that.name);eb.append(company, that.company);return eb.isEquals();}
}

在此示例中,我们始终会获取产品的公司,并且由于产品代码在公司之间并不唯一,因此我们可以在业务密钥中包含父实体。 父引用被标记为不可更新,以防止违反equals / hashCode合同(将产品从一家公司转移到另一家公司毫无意义)。 但是,如果“父级”具有“一组子级”实体,并且您调用类似以下内容的方法,则此模型将中断

public void removeChild(Child child) {children.remove(child);child.setParent(null);
}

由于将父级设置为null,因此这将破坏equals / hashCode合同,并且如果子级对象是Set,则不会在子级集合中找到子对象。 因此,在使用具有此类equals / hashCode的Child实体的双向关联时要小心。

  • 拥有LAZY父级的子实体
@Entity
public class Image {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(updatable = false)private String name;@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "product_id", nullable = false, updatable = false)private Product product;@Overridepublic int hashCode() {HashCodeBuilder hcb = new HashCodeBuilder();hcb.append(name);hcb.append(product);return hcb.toHashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof Image)) {return false;}Image that = (Image) obj;EqualsBuilder eb = new EqualsBuilder();eb.append(name, that.name);eb.append(product, that.product);return eb.isEquals();}
}

如果在没有产品的情况下获取图像并且关闭了持久性上下文,并且将图像加载到集合中,则将得到LazyInitializationException,如以下代码示例所示:

List images = transactionTemplate.execute(new TransactionCallback<List>() {@Overridepublic List doInTransaction(TransactionStatus transactionStatus) {return entityManager.createQuery("select i from Image i ", Image.class).getResultList();}
});
try {assertTrue(new HashSet(images).contains(frontImage));fail("Should have thrown LazyInitializationException!");
} catch (LazyInitializationException expected) {}

因此,我不建议使用该用例,因为它容易出错,并且要正确使用equals和hashCode,我们总是需要始终初始化LAZY关联。

  • 子实体不理父母

在此用例中,我们只需从业务密钥中删除父级引用即可。 只要我们始终通过“父级子代”集合使用“子代”,我们就很安全。 如果我们从多个父级加载子级,并且业务键在这些父级中不唯一,则不应将其添加到Set集合中,因为Set可能会丢弃来自不同父级的具有相同业务键的Child对象。

结论

为实体选择正确的业务密钥并不是一件容易的事,因为它反映了您在Hibernate范围内外的实体使用情况。 使用实体之间唯一的字段组合可能是实现equals和hashCode方法的最佳选择。

使用EqualsBuilder和HashCodeBuilder可以帮助我们编写简洁的equals和hashCode实现,并且似乎也可以与Hibernate Proxies一起使用。

参考: Hibernate Fact:来自我们JCG合作伙伴 Vlad Mihalcea的Equals和HashCode ,位于Vlad Mihalcea的Blog博客中。

翻译自: https://www.javacodegeeks.com/2013/11/hibernate-facts-equals-and-hashcode.html

休眠事实:等于和HashCode相关推荐

  1. hash和hashcode_Hibernate事实:等于和HashCode

    hash和hashcode 每个Java对象都继承了equals和hashCode方法,但它们仅对Value对象有用,对面向无状态行为的对象没有用. 尽管使用" =="运算符比较引 ...

  2. 休眠事实:如何“断言” SQL语句计数

    介绍 Hibernate简化了CRUD操作,尤其是在处理实体图时. 但是任何抽象都有其代价,而Hibernate也不例外. 我已经讨论了获取策略和了解Criteria SQL查询的重要性,但是您可以做 ...

  3. 休眠事实:有利于双向集vs列表

    Hibernate是一个很棒的ORM工具,它极大地简化了开发,但是如果您想正确地使用它,则有很多陷阱. 在大中型项目中,具有双向父子关联非常常见,这使我们能够浏览给定关系的两端. 在控制关联的持久/合 ...

  4. 休眠事实:集成测试策略

    我喜欢集成测试,这是检查Hibernate生成哪些幕后花絮的SQL查询的好方法. 但是集成测试需要运行的数据库服务器,这是您必须要做的第一选择. 1.使用类似生产的本地数据库服务器进行集成测试 对于生 ...

  5. 休眠事实:始终检查Criteria API SQL查询

    Criteria API对于动态构建查询非常有用,但这是我使用它的唯一用例. 每当您有一个带有N个过滤器且可以以任意M个组合到达的UI时,都有一个API动态构造查询是有意义的,因为串联字符串始终是我所 ...

  6. 休眠事实:访存策略的重要性

    在使用ORM工具时,每个人都承认数据库设计和实体到表映射的重要性. 这些方面引起了很多关注,而诸如获取策略之类的事情可能只是推迟了. 我认为,不应将实体获取策略与实体映射设计分开,因为除非经过适当设计 ...

  7. 休眠事实:了解刷新操作顺序很重要

    Hibernate将开发人员的思维方式从思考SQL转变为思考对象状态转换. 根据Hibernate Docs,实体可能处于以下状态之一: new / transient:实体不与持久性上下文关联,因为 ...

  8. hashcode java_hashCode方法的作用?

    (1)前言,想要明白hashCode的作用,你必须要先知道Java中的集合. Java中的集合(Collection)有两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重复 ...

  9. java中hashcode()和equals()的详解[转]

    今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36).  1. 首先equals()和hash ...

最新文章

  1. 实时双频Wi-Fi如何实现下一代车内连接
  2. Delphi控件之---UpDown以及其与TEdit的配合使用(比如限制TEdit只能输入数字,还有Object Inspector之组件属性的介绍)...
  3. SQL Server T-SQL高级查询
  4. 【Python】Pyecharts 组合图形绘制实践
  5. Scala里List(1,2,3)和(1,2,3)的区别
  6. 供应商寄售库存管理_【论文解读】物流联合外包下库存管理模式对供应链运作的影响...
  7. Oracle入门(十四.12)之游标FOR循环
  8. 有“嗅觉”的电脑:英特尔神经拟态芯片能嗅出危险化学品气味
  9. 使用layui中的laypage遇到的各种问题总结
  10. 计算机专业英语学术论文框架,计算机专业英语研究学术论文.pdf
  11. EPLAN中如何画屏蔽双绞线
  12. perl php-serialization install,如何在PHP中反序列化Perl Data :: Dumper输出
  13. node-@hapi/joi校验前端数据
  14. 如何禁用笔记本触摸板
  15. javaScript中console.log()的用法
  16. bzoj 4283 魔法少女伊莉雅
  17. 百度京东加持的新潮传媒 已成为分众传媒最大的敌人
  18. 不懂数学,照样做数据科学家
  19. 鼠标连点器烦人弹窗?我直接爆破
  20. 电脑系统格式化需不需要重装系统

热门文章

  1. db9针232接口波特率标准_理解串口通信以及232,485,422常见问题
  2. 转- java单例模式几种实现方式
  3. DFS——深度优先搜索基础
  4. sql 注射_令人惊讶的注射
  5. 内联脚本被视为是有害的_数据类被认为有害
  6. 谷歌gcp 远程计算机_引导性GCP:带有Google Cloud Pub / Sub的Spring Cloud Stream
  7. junit测试起名字规则_如何在JUnit 5中替换规则
  8. lagom的微服务框架_微服务有麻烦吗? Lagom在这里为您提供帮助。 试试吧!
  9. eap和psk_针对WildFly和EAP运行Java Mission Control和Flight Recorder
  10. 引入我们全新的YouTube频道进行视频课程编程