有没有办法使用具有休眠持久性映射的Flyweight对象?我的数据模型包含许多相同的对象。我不想为每个相同的对象设置单独的实例,而是使用Flyweight设计模式并始终引用相同的物理对象。如何在hibernate中实现这一点?
顺便说一下。是否所有JVM都以某种方式优化字符串的使用,以便当多次使用相同的字符串时,它将始终是相同的物理实例?
答案 0 :(得分:3)
取决于。
对于只读值,您可以通过创建自定义UserType轻松实现flyweight模式,该类型将每次从池中返回对象而不是新实例。
对于实体,Hibernate默认是理智的,并且希望在事务中保持一致,因此不会在Sessions之间共享实体以避免数据的竞争条件 - 我认为这不是您想要的。
但是如果它是(并且这完全不推荐而不知道你在做什么)你可以实现Interceptor.getEntity(),它用于二级缓存。在该方法中,您可以返回一个实体(甚至是其他会话共享的实体),您将有效地为您的实体设置一个flyweight模式。
但是我强烈建议不要为了数据的一致性 - 更好地让实体引用实际的不可变flyweight值,而不是尝试和轻量级实际实体。
答案 1 :(得分:2)
是的,您可以使用Hibernate实现Flyweight模式。
flyweight模式是最小化内存使用量每个实例的方法。策略是尽可能多地分享flyweight实例之间的状态。在您的情况下,可共享状态是除了hibernate对象标识符之外的所有内容以及维护对象标识的一些其他状态。
每个flyweight实例都需要自己的object identity。附加状态是实现标识以区分共享共同状态的对象的方式。
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)
这是非法的。 Equal必须是反身的。如果你违反这个合同,所有依赖合同的库都将被破坏(集合,休眠等)。
您无法使用hibernate对象标识符来实现等于方法来区分对象。这将使对象标识依赖于持久性状态(持久或瞬态)。
在hibernate中建模公共状态的一种方法是共享状态对象和flyweight对象之间的一对多关联。 (也许有人知道如何在不加入两个表的情况下映射数据?)
字符串:仅共享internalized strings。这在大多数情况下不是最好的解决方案。它适用于符号(类名,方法名等)。
内部化的字符串永远不会被垃圾收集,你必须有一个String实例,无论如何都要进行垃圾收集new String("..").intern()
。它不会保存分配。只有一个小优点是基本字符串不能在gc生成中存活或者可以在堆栈上分配(在热点启用和适用的情况下进行逃逸分析)。
答案 2 :(得分: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;
}
答案 3 :(得分:0)
如果您的对象按身份实现相等,则Hibernate将只有与该主键关联的单个实例。我不相信它与Flyweight完全相同,但关键是你不会有很多同一个Hibernate对象的实例。