休眠和蝇量级
有没有办法将 Flyweight 对象与休眠持久性映射一起使用?我的数据模型包含许多相同的对象。我不想为每个相同的对象拥有单独的实例,而是使用享元设计模式并始终引用相同的物理对象。如何在休眠中实现这一目标?
顺便提一句。是否所有 JVM 都以某种方式优化字符串的使用,使得当多次使用同一个字符串时,它始终是相同的物理实例?
Is there a way to use Flyweight objects with the hibernating persistence mapping? My data model contains many objects that will be the same. Instead of having a separate instance for each of those same objects I'd like to use the Flyweight Design Pattern and reference always the same physical object. How to achieve this in hibernate?
Btw. do all JVMs optimize the usage of Strings in a way such that when the same string is used several times, it will always be the same physical instance?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这取决于。
对于只读值,您可以通过创建自定义 UserType 轻松实现享元模式,该自定义 UserType 将从池中返回对象而不是每次都返回新实例。
对于实体,Hibernate 默认情况下是理智的,并且希望在事务之间保持一致,因此不会在会话之间共享实体以避免数据的竞争条件 - 我认为这不是您想要的。
但如果是这样(在不真正知道你在做什么的情况下完全不建议这样做),你可以实现 Interceptor.getEntity() ,它用于二级缓存。在该方法中,您可以返回一个实体(甚至一些由其他会话共享的实体),并且您将有效地为您的实体拥有享元模式。
但为了数据的一致性,我强烈建议不要这样做——让实体引用实际的不可变的享元值比尝试对实际实体进行享元要好得多。
It depends.
For readonly values you can easily implement a flyweight pattern by creating a custom UserType which will return objects from a pool instead of new instances every time.
For entities Hibernate is by default sane and wants to be consistent across transactions and thus won't share entities between Sessions to avoid race conditions of your data - and I don't think that is what you want.
But in case it is (and this is totally non-recommended without really knowing what you are doing) you can implement Interceptor.getEntity() which is intended for second level caching. In that method you can return an entity (even some shared by other sessions) and you will effectively have a flyweight pattern for your entities.
BUT I highly recommend against this for the consistency of your data - much better to have actual immutable flyweight values referenced by entities than also try and flyweight the actual entities.
是的,您可以使用 Hibernate 实现 Flyweight 模式。
享元模式是最小化每个实例内存使用的方法。该策略是在享元实例之间共享尽可能多的状态。在您的情况下,可共享状态是除了休眠对象标识符和维护对象标识的一些附加状态之外的所有状态。
每个享元实例都需要自己的对象标识。附加状态是实现身份以区分共享公共状态的对象的方法。
如果对象标识在实例之间共享,hibernate 会将所有物理实例(引用)解释为同一实例。 Hibernate 使用 equals 方法来检查对象身份,并且您的 equal 实现必须返回
(!a.equals(a) == true)
这是非法的。 等于必须是自反的。如果您违反此合同,则所有依赖于该合同的库都将被破坏(集合、休眠等)。不能使用hibernate对象标识符实现equal方法来区分对象。这将使对象标识依赖于持久性状态(持久或瞬态)。
在 hibernate 中对公共状态进行建模的一种方法是共享状态对象和享元对象之间的一对多关联。 (也许有人知道如何在不连接两个表的情况下映射数据?)
字符串:仅 内部化字符串将被共享。大多数时候这不是最好的解决方案。它适用于符号(类名、方法名等)。
内部化的字符串永远不会被垃圾收集,并且您必须有一个无论如何都会被垃圾收集的 String 实例
new String("..").intern()
。它不会保存分配。只有一个很小的优点,即基本字符串不会在 gc 生成后保留下来,或者可以在堆栈上分配(启用并适用热点中的逃逸分析)。Yes, you can implement the Flyweight pattern with Hibernate.
The flyweight pattern is way to minimize memory usage per instance. The strategy is to share as much state between flyweight instances as possible. In your case the shareable state is everything except the hibernate object identifier and some additional state to maintain object identity.
Each flyweight instance needs its own object identity. The additional state is the way to implement identity to distinguish between objects that share common state.
If the object identity is shared between instances hibernate would interpret all physical instances (references) as the same instance. Hibernate uses the equals method to check object identity and your equal implementation would have to return
(! a.equals(a) == true)
which is illegal. Equal has to be reflexive. If you would break this contract all libraries that depend on the contract will be broken (collections, hibernate, etc.).You cannot implement the equal method using the hibernate object identifier to distinguish between objects. This would make the object identity dependent on the persistence state (persisted or transient).
One way to model the common state in hibernate is a one-to-many association between shared state objects and flyweight objects. (Maybe someone has an idea how to map the data without joining two tables?)
String: Only internalized strings will be shared. This is not the best solution most of the time. It is appropriate for symbols (class name, method name, etc.).
The internalized strings will never by garbage collected and you have to have a String instance that will to be garbage collected anyway
new String("..").intern()
. It will not save allocations. There is only the minor advantage that the base string will not survive a gc generation or could be allocated on the stack (with escape analysis in hot spot enabled and applicable).我对此非常怀疑。在同一个类文件中,当定义如下时:
您可能会有 s1 == s1。
但如果你有这样的想法:
我不认为运行时会检查和比较所有加载的字符串
它们构建器的返回值。
因此,总是使用 equals() 来比较对象。无论如何,这是一个很好的建议,因为每一个好的平等都是从以下开始的:
I doubt that very much. In the same class file, when definined like:
you'll probably have s1 == s1.
But if you have like:
I don't think the runtime is going to check and compare all the loaded strings to
the return value of them builder.
So, always compare objects with equals(). That's good advice anyway since every good equals starts with:
如果您的对象通过身份实现相等,则 Hibernate 将仅具有与该主键关联的单个实例。我不认为它与 Flyweight 的想法完全相同,但重点是您不会拥有同一个 Hibernate 对象的许多实例。
If your objects implement equality by identity, Hibernate will only have the single instance associated with that primary key. I don't believe it's exactly the same idea as Flyweight, but the point is that you won't have many instances of the same Hibernate object.