views:

284

answers:

3

Suppose I have the following Domain class mapping to a legacy table, utilizing read-only second-level cache, and having a transient field:

class DomainObject {
 static def transients = ['userId']

 Long id
 Long userId

 static mapping = {
  cache usage: 'read-only'
  table 'SOME_TABLE'
 }
}

I have a problem, references to DomainObject are being shared due to first-level caching, and thus transient fields are writing over each other. For example,

def r1 = DomainObject.get(1)
r1.userId = 22

def r2 = DomainObject.get(1)
r2.userId = 34

assert r1.userId == 34

That is, r1 and r2 are references to the same instance. This is undesirable, I would like to cache the table data without sharing references. Any ideas?

[Edit]

Understanding the situation better now, I believe my question boils down to the following: Is there anyway to disable first level cache for a specific domain class while still using second level cache?

[Edit]

Since there appears to be no clean way to obtain this objective, we've opted instead to redesign around the need for it.

+2  A: 

Please ignore my previous answer, I didn't understand fully your issue.

However, the following will work (code tested) :

def r1 = DomainObject.get(1)
r1.userId = 22
r1.discard() //BE CAREFUL WITH THIS, YOU MIGHT END UP WITH a LazyInitializationException

def r2 = DomainObject.get(1)
r2.userId = 34

assert r1.userId == 22
fabien7474
Thanks. While this does work for my simple example, it's not a practical approach to take throughout the entire application. I'm surprised: p.266 and p.276 in Definitive Guide To Grails acknowledge instance reuse as a part of first-level cache but don't consider the the scenario I presented. I wonder if there is a way to turn off first-level cache for a specific domain object, maybe that's the problem and second-level cache will work the way I want.
Stephen Swensen
I don't know you entire requirements so I cannot really help except from my answer for this small example. That said, even the example seems weird to me...
fabien7474
The idea here may be compared to a multi-set which is augmented (via transient fields) and transformed into a normal set: e.g. [apple, apple, apple, orange, orange] -> [apple', apple'', apple''', orange', orange'']
Stephen Swensen
A: 

I imagine you could, in BootStrap, metaclass on a new get() method (or a method by any other name) that could do the same - call the original get() and discard the object.

What's your usage scenario that requires transient fields not to be shared? You could always get a new Hibernate session for each of the usage locations. The Hibernate session's what maintains the first-level cache.

John Stoneham
Thanks John. That's a good idea, but I couldn't get it to work: def oldGet = SomeDomain. SomeDomain.metaClass.'static' = {id -> def sd = oldGet(id); sd.discard(); sd}; resulted in a stackoverflow, I'm not sure why. As for the usage scenario, my ability to describe it is waning, since we decided to redesign around it, which is probably the best thing to do since it's obviously against the grain of how GORM is designed.
Stephen Swensen
A: 

You can use DomainObject.findById(1) instead of DomainObject.get(1). As the "get" method cache's the query result but the former doesn't.

Amit Jain
Hmm, I was expecting this nice idea to work, but found DomainObject.findById(1).is(DomainObject.findById(1)) to be true.
Stephen Swensen