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

oks*_*ayt 6 java collections hibernate immutability

我正在处理的应用程序使用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
}
Run Code Online (Sandbox Code Playgroud)

我已阻止客户通过以下方式访问数据库:

  • 热切地获取所有实体和集合
  • 使用mutable=falseHibernate映射标记所有实体和集合
  • 不提供Hibernate会话的任何实例,或状态改变dao方法.

现在我想防止的错误是,有人不小心走了building.getInhabitants().clear();.我可以想到这些选择:

  1. 吸气剂包装:在通话中getInhabitants首先换行,然后返回.inhabitantsCollections.unmodifiableSet()

    • 优点:最少的工作,最少的额外代码
    • 缺点:感觉hacky
  2. 包装类:重命名BuildingMutableBuilding,PersonMutablePerson,并提供一成不变的类BuildingPerson.由于我的应用程序有一个清晰的快照点,我可以将记录作为可变对象(我现在这样做)获取,制作深度不可变的副本并将该对象树呈现给客户端.

    • 优点:直接java,没有Hibernate魔法.我可以使用我最喜欢的关键字:final
    • 缺点:编写和维护更多代码.另外,Hibernate会将可变实例保留在内存中吗?
  3. Hibernate映射魔术:使用一个魔术关键字来指示Hibernate将其设置的集合包装在我的实体对象中Collections.unmodifiableSet()或等效.(注意:我使用的是xml映射文件)

    • 优点:优雅,没有额外的代码
    • 缺点:此类关键字可能不存在
  4. Hibernate扩展:使用一个Hibernate扩展点来编写我自己的对象实例化器,然后在Collections.unmodifiableSet()返回之前将其包装好.

    • 优点:比黑客入侵更优雅
    • 缺点:此类扩展点可能不存在

现在我倾向于#2,主要是因为我不知道3和4是否可能.

什么是最好的方法?

jpk*_*ing 6

选项1,当然.这不是"hacky",这正是你将属性访问抽象为方法的原因,首先:-)请注意你应该使用Hibernate的"field access"而不是"method",这样你就不会为Hibernate提供不可修改的集合的风险.

不幸的是,Hibernate没有提供放置不可修改集合的方法,但我认为你可以在加载对象后执行@PostLoad事件监听器来修改所有集合.