如何将 Hibernate 映射集加载为不可修改的集?

发布于 2024-10-12 10:50:02 字数 1909 浏览 1 评论 0原文

我正在开发的一个应用程序专门使用 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();。我可以想到以下选项:

  1. Getter 包装:使 getInhabitants 首先将 inhabitants 包装在 Collections.unmodifyingSet()< /code> 调用,然后返回。

    • 优点:最少的工作,最少的额外代码
    • 缺点:感觉很老套
  2. 包装类:将Building重命名为MutableBuildingPerson 到 MutablePerson,并提供不可变类 BuildingPerson。由于我的应用程序有一个清晰的快照点,因此我可以将记录作为可变对象获取(就像我现在所做的那样),制作深度不可变的副本并将该对象树呈现给客户端。

    • 优点:直接使用 java,没有 Hibernate 魔法。我开始使用我最喜欢的关键字:final
    • 缺点:需要编写和维护更多代码。另外,Hibernate 会将可变实例保留在内存中吗?
  3. Hibernate 映射魔法:使用该魔法关键字指示 Hibernate 将它在我的实体对象上设置的集合包装在 Collections.unmodifyingSet() 或等效项中。 (注:我使用xml映射文件)

    • 优点:优雅,无需额外代码
    • 缺点:此类关键字可能不存在
  4. 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:

  1. Getter wrapping: Make getInhabitants first wrap inhabitants in a Collections.unmodifiableSet() call, then return it.

    • Pros: Least work, least extra code
    • Cons: Feels hacky
  2. Wrapper classes: Rename Building to MutableBuilding, Person to MutablePerson, and provide immutable classes Building and Person. 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?
  3. 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
  4. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

深海里的那抹蓝 2024-10-19 10:50:02

选项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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文