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

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

有帮助吗?

解决方案

取决于。

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

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

但是如果它是(并且这完全不推荐而不知道你在做什么)你可以实现Interceptor.getEntity(),它用于二级缓存。在该方法中,您可以返回一个实体(甚至是其他会话共享的实体),您将有效地为您的实体设置一个flyweight模式。

但是我强烈建议不要为了数据的一致性 - 更好地让实体引用实际的不可变flyweight值,而不是尝试和轻量级实际实体。

其他提示

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

flyweight模式是最小化内存使用量每个实例的方法。策略是尽可能多地分享flyweight实例之间的状态。在您的情况下,可共享状态是除了hibernate对象标识符之外的所有内容以及维护对象标识的一些其他状态

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

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

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

您无法使用hibernate对象标识符来实现等于方法来区分对象。这将使对象标识依赖于持久性状态(持久或瞬态)。

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

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

  

所有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;
}

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top