如何将 Hibernate 映射集加载为不可修改的集?
我正在开发的一个应用程序专门使用 Hibernate 将一堆持久对象从数据库获取到内存。应用程序会时不时地从数据库刷新这个内存快照,这应该是与数据库的唯一通信。
然后内存中的对象用于一系列计算。计算不得修改这些对象。除了某个地方的某些课程不小心发生了错误,我不得不花一天的时间来寻找这个错误。现在我想知道使整个对象树不可变的最佳方法是什么。
假设类层次结构如下所示:
public class Building { // persistent entity
private String name; // hibernate-mapped property
private Set<Person> inhabitants; // hibernate-mapped collection
// getters
}
public class Person { // persistent entity
private String name; // hibernate-mapped property
// getters
}
我通过以下方式阻止客户端访问数据库:
- 急切地获取所有实体和集合
- 在 Hibernate 映射中用
mutable=false
标记所有实体和集合 - 不提供任何实例Hibernate 会话,或状态更改的 dao 方法。
现在我想防止的错误是,有人不小心去了building.getInhabitants().clear();
。我可以想到以下选项:
Getter 包装:使
getInhabitants
首先将inhabitants
包装在Collections.unmodifyingSet()< /code> 调用,然后返回。
- 优点:最少的工作,最少的额外代码
- 缺点:感觉很老套
包装类:将
Building
重命名为MutableBuilding
,Person 到 MutablePerson
,并提供不可变类Building
和Person
。由于我的应用程序有一个清晰的快照点,因此我可以将记录作为可变对象获取(就像我现在所做的那样),制作深度不可变的副本并将该对象树呈现给客户端。- 优点:直接使用 java,没有 Hibernate 魔法。我开始使用我最喜欢的关键字:
final
- 缺点:需要编写和维护更多代码。另外,Hibernate 会将可变实例保留在内存中吗?
- 优点:直接使用 java,没有 Hibernate 魔法。我开始使用我最喜欢的关键字:
Hibernate 映射魔法:使用该魔法关键字指示 Hibernate 将它在我的实体对象上设置的集合包装在
Collections.unmodifyingSet()
或等效项中。 (注:我使用xml映射文件)- 优点:优雅,无需额外代码
- 缺点:此类关键字可能不存在
Hibernate 扩展:使用该 Hibernate 扩展点来编写我自己的对象实例化器,并在返回之前将集合包装在
Collections.unmodifyingSet()
中。- 优点:比破解我的 getter 更优雅
- 缺点:此类扩展点可能不存在
现在我倾向于#2,主要是因为我不知道 3 和 4 是否可能。
最好的方法是什么?
An application I'm working on uses Hibernate exclusively to fetch a bunch of persistent objects from a database to memory. The application is to refresh this in-memory snapshot from the database every now and then, and that should be the only communication with the database.
The in-memory objects are then used for a bunch of calculations. The calculations must not modify these objects. Except some class somewhere accidentally did, and I had to spend a day hunting down the bug. Now I'm wondering what the best way to make the entire object tree immutable is.
Suppose the class hierarchy looks like this:
public class Building { // persistent entity
private String name; // hibernate-mapped property
private Set<Person> inhabitants; // hibernate-mapped collection
// getters
}
public class Person { // persistent entity
private String name; // hibernate-mapped property
// getters
}
I've prevented clients from accessing the database by:
- eagerly fetching all entities and collections
- marking all entities and collections with
mutable=false
in Hibernate mappings - not providing any instances of the Hibernate session, or state-changing dao methods.
Now the error I'd like to prevent is, someone accidentally going building.getInhabitants().clear();
. I can think of these options:
Getter wrapping: Make
getInhabitants
first wrapinhabitants
in aCollections.unmodifiableSet()
call, then return it.- Pros: Least work, least extra code
- Cons: Feels hacky
Wrapper classes: Rename
Building
toMutableBuilding
,Person
toMutablePerson
, and provide immutable classesBuilding
andPerson
. Since my application has a clear snapshot point, I can fetch the records as mutable objects (as I do now), make deeply immutable copies and present that object tree to clients.- Pros: Straight java, no Hibernate magic. I get to use my favorite keyword:
final
- Cons: More code to write and maintain. Also, will Hibernate keep the mutable instances in memory?
- Pros: Straight java, no Hibernate magic. I get to use my favorite keyword:
Hibernate mapping magic: Use that one magic keyword to instruct Hibernate to wrap the collections it sets on my entity objects in
Collections.unmodifiableSet()
or equivalent. (Note: I use an xml mapping file)- Pros: Elegant, no extra code
- Cons: Such keyword may not exist
Hibernate extension: Use that one Hibernate extension point to write my own object instantiator, and in there wrap the set in
Collections.unmodifiableSet()
before returning it.- Pros: More elegant than hacking my getters
- Cons: Such extension point may not exist
Right now I'm leaning towards #2, mostly because I don't know if 3 and 4 are possible.
What is the best way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
选项1,当然可以。这不是“hacky”,这正是您首先将属性访问抽象为方法的原因:-) 请注意,您应该在 Hibernate 中使用“字段访问”,而不是“方法”,这样您就不会向 Hibernate 提供不可修改的集合的风险。
不幸的是,Hibernate 没有提供放置不可修改集合的方法,但我认为您可以执行 @PostLoad 事件侦听器来在加载对象后修改所有集合。
Option 1, for sure. It's not "hacky", and that's exactly why you abstracted the property access into methods, in the first place :-) Just note that you should use "field access" with Hibernate, instead of "method", so that you don't risk providing an unmodifiable collection to Hibernate.
Unfortunately, Hibernate doesn't provides a way to place unmodifiable collections, but I think you can do a @PostLoad event listener to modify all collections once the object is loaded.