Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate and Flyweight

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?

like image 891
paweloque Avatar asked Nov 29 '09 01:11

paweloque


People also ask

What is flyweight in Java?

Flyweight is a structural design pattern that allows programs to support vast quantities of objects by keeping their memory consumption low. The pattern achieves it by sharing parts of object state between multiple objects.

Where is Flyweight pattern used?

Flyweight design pattern is a Structural design pattern like Facade pattern, Adapter Pattern and Decorator pattern. Flyweight design pattern is used when we need to create a lot of Objects of a class.

Which of the following is true for flyweight design pattern?

Answer» c. this pattern is primarily used to reduce the number of objects created and to decrease memory footprint and increase performance.

What is the design pattern used in string implementation?

String class is designed with Flyweight design pattern. It has similar structure as above example. When you create a string constant, such constant is stored in a pool.


2 Answers

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.

like image 62
Max Rydahl Andersen Avatar answered Oct 15 '22 19:10

Max Rydahl Andersen


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.

public boolean equals(Object obj){
  Fly other; [..]//check type
  //null checks ommitted
  return other.myState.equals(myState) && other.commonState.equals(commonState); 
}

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).

like image 26
Thomas Jung Avatar answered Oct 15 '22 19:10

Thomas Jung