11

I'm maintaining a cache of objects across hibernate sessions by storing (possibly-detached) objects in a map. When the cache gets a hit, I test if the object is already part of the session with Session.contains(object). If not, I re-attach it with Session.lock(object, LockMode.NONE).

The problem is, if the same object was loaded previously in the session, it throws a org.hibernate.NonUniqueObjectException. Given a detached instance, I see no way to find out in advance if this exception will get thrown, without possibly hitting the database.

There are a couple workarounds:

  1. Reattach all cached objects at the start of every transaction.
  2. Catch the NonUniqueObjectException and then call session.load(object.class, object.getId());

Neither of these are as clean as checking the session in advance for an object class + id.

Is there any way to do that?

Rob
  • 1,355
  • 2
  • 14
  • 17
  • One interrogation: if the object is already loaded, maybe it's fresher than the cached version, so why not update the cache instead when you catch NonUniqueObjectException? – Damien B Jun 24 '10 at 09:20
  • Possible duplicate of [What is the proper way to re-attach detached objects in Hibernate?](http://stackoverflow.com/questions/912659/what-is-the-proper-way-to-re-attach-detached-objects-in-hibernate) – danizmax Dec 24 '15 at 08:27

2 Answers2

4

Session.merge() should do it:

obj = session.merge(obj);
Raj
  • 2,852
  • 4
  • 29
  • 48
  • 2
    I think merge() hits the database, which I was hoping to avoid. Note that session.lock(detached_object, LockMode.NONE) does not hit the database at all (if you don't change it after re-attaching it) – Rob Apr 28 '10 at 19:27
0

I'm maintaining a cache of objects across hibernate sessions

Not a direct answer but why don't you use Hibernate L2 cache for this instead of reinventing the wheel?

Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • I think the L2 cache would not help here because I don't know the id of the persistent entity -- I look up the entities in the cache using .equals(), so it effectively replaces a new entity with an equivalent entity that already exists. It's sort of a flyweight pattern for the database. Unfortunately, this is necessary for my application because there are tons of immutable data that can be reused when creating new entities. – Rob Apr 28 '10 at 19:25
  • @Rob Ok, I see... except for the part "I don't know the id". Are these objects kind of referential data (like, say, countries, currencies, etc)? – Pascal Thivent Apr 28 '10 at 19:38
  • Yes, they are value objects whose identity is not important. They are entities only so that their references can be shared. This allows me to reuse the same 64kb blob from millions of others. They are not anything standard like countries, currencies etc, but it's the same idea. – Rob Apr 28 '10 at 19:57
  • @Rob: In your case you have non-mutable (read-only) entities, right? Anyway, nothing prevents you from having synthetic `Entity.getId() { return this; }` so the entity will act as ID for itself. It should implement `Serializable`, `equals()` and `hashCode()`. However, I think, all persistent entities should have a key (currency code, country code). Maybe you can have your custom entity persister for these entities, then you can control what should be the ID for them. Maybe using Session interceptor can help. – dma_k Sep 26 '10 at 19:45