7

有没有办法将享元对象与休眠持久性映射一起使用?我的数据模型包含许多相同的对象。我不想为每个相同的对象使用单独的实例,而是使用享元设计模式并始终引用相同的物理对象。如何在休眠中实现这一点?

顺便提一句。是否所有 JVM 都以某种方式优化字符串的使用,即当多次使用相同的字符串时,它总是相同的物理实例?

4

4 回答 4

3

这取决于。

对于只读,您可以通过创建自定义 UserType 轻松实现享元模式,该自定义 UserType 每次都会从池中返回对象而不是新实例。

对于实体,Hibernate 默认情况下是理智的,并且希望在事务之间保持一致,因此不会在会话之间共享实体以避免数据的竞争条件 - 我认为这不是你想要的。

但是如果是这样(并且在不知道自己在做什么的情况下完全不推荐这样做),您可以实现用于二级缓存的 Interceptor.getEntity()。在该方法中,您可以返回一个实体(甚至一些由其他会话共享),并且您将有效地为您的实体提供享元模式。

但我强烈建议不要这样做,以保持数据的一致性 - 让实体引用实际不可变的享元值要比尝试对实际实体进行享元好得多。

于 2009-11-29T10:09:23.523 回答
2

是的,您可以使用 Hibernate 实现享元模式。

享元模式是最小化每个实例的内存使用的方法。该策略是在享元实例之间共享尽可能多的状态。在您的情况下,可共享状态是除了休眠对象标识符和一些用于维护对象身份的附加状态之外的所有内容

每个享元实例都需要自己的对象标识。附加状态是实现标识以区分共享公共状态的对象的方式。

public boolean equals(Object obj){
  Fly other; [..]//check type
  //null checks ommitted
  return other.myState.equals(myState) && other.commonState.equals(commonState); 
}

如果对象身份在实例之间共享,hibernate 会将所有物理实例(引用)解释为同一个实例。Hibernate 使用 equals 方法来检查对象身份,而您的 equals 实现将不得不返回(! a.equals(a) == true)这是非法的。平等必须是自反的。如果您违反此合同,所有依赖于合同的库都将被破坏(集合、休眠等)。

您不能使用 hibernate 对象标识符来实现 equal 方法来区分对象。这将使对象身份依赖于持久状态(持久或瞬态)。

在 hibernate 中对公共状态建模的一种方法是共享状态对象和享元对象之间的一对多关联。(也许有人知道如何在不连接两个表的情况下映射数据?)

字符串:只共享内部化的字符串。大多数时候这不是最好的解决方案。它适用于符号(类名、方法名等)。内部化的字符串永远不会被垃圾收集,您必须有一个无论如何都会被垃圾收集的 String 实例new String("..").intern()。它不会保存分配。只有一个次要的优势是基本字符串不会在 gc 生成中存活或可以在堆栈上分配(启用并适用热点中的转义分析)。

于 2009-11-29T07:15:06.750 回答
1

是否所有 JVM 都以某种方式优化字符串的使用,即当多次使用相同的字符串时,它总是相同的物理实例?

我非常怀疑这一点。在同一个类文件中,定义如下:

String s1 = "Yes";
String s2 = "Yes";

你可能会有 s1 == s1。

但如果你有这样的:

String x = loadTextFromFile(); // file contains es
StringBuilder b = new StringBuilder();
s2 = b.append("Y").append(x).toString(); // s2 = "Yes"

我不认为运行时会检查所有加载的字符串并将其与它们构建器的返回值进行比较。

所以,总是用 equals() 比较对象。无论如何,这是一个很好的建议,因为每个好的平等都始于:

if (this == o) {
    return true;
}
于 2009-11-29T09:35:12.570 回答
0

如果您的对象通过身份实现相等,则 Hibernate 将只有与该主键关联的单个实例。我不相信它与 Flyweight 的想法完全相同,但关键是您不会有很多相同 Hibernate 对象的实例。

于 2009-11-29T01:28:21.840 回答