Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I load a Hibernate-mapped set as an unmodifiable set?

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?

like image 409
oksayt Avatar asked Jan 13 '11 15:01

oksayt


People also ask

How can a class be mapped as immutable in Hibernate?

You can tell Hibernate that a specific entity is immutable by using @Entity(mutable=false) or @Immutable annotations. Note that both are Hibernate extensions to JPA standard.

Can we obtain an immutable class in Hibernate?

There's one obstacle on the way of creating immutable entity classes with an ORM framework like Hibernate. Hibernate uses reflection to instantiate entities, it, therefore requires a default, no argument constructor. This means class fields can never be final.

What is mutable false in Hibernate?

In hibernate, 'mutable' is default to 'true' in class and its related collection, it mean the class or collection are allow to add, update and delete. On the other hand, if the mutable is changed to false, it has different meaning in class and its related collection.


1 Answers

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.

like image 187
jpkrohling Avatar answered Sep 30 '22 14:09

jpkrohling