class Bar { static hasMany = [foos: Foo] static mapping = { foos cascade: 'all-delete-orphan' } } class Foo { static belongsTo = [bar: Bar] }
Bar bar = Bar.createCriteria().get { isNotNull 'id' maxResults 1 } bar.discard() println bar.foosA művelet eredménye egy org.hibernate.LazyInitializationException, hiszen nem töltöttük be a fookat a domain osztállyal együtt, a discard metódus hatására pedig elveszett a kapcsolat a Hibernate Session és a domain osztályban lévő proxy között. Javísuk a problémát.
Bar bar = Bar.createCriteria().get { isNotNull 'id' fetchMode 'foos', org.hibernate.FetchMode.JOIN maxResults 1 }Ez a bejegyzés nem jött volna létre, ha csak ennyivel megúsznánk a dolgot. Bár a Hibernate dokumentációja szerint a FetchMode.JOIN "Fetch using an outer join", mégis ismét egy LazyInitializationException hibára hivatkozva száll el a kérés mint a győzelmi zászló. A problémát a cascade okozza!? Ugyanis ha eltérünk az alap értelmezett viselkedéstől (OTM kapcsolatoknál az alap értelmezett a save-update, tehát a domain osztályt törölve az adatbázisban maradnak a hozzá kapcsolodó entitások), akkor a JOIN hatására nem a domain osztályok kerülnek a domainbe, hanem csak proxyk, tehát akkor szedi fel őket ténylegesen a rendszer, amikor hivatkozunk rájuk. A cascade dokumentációja semmit nem említ erről az igen kellemetlen melléhatásról. Tudom Grailsbe "nem szokás" discard()olni, én mégis azt javaslom, hogy mielőtt átadjuk a vezérlést a nézetnek, csak a próba kedvéért válasszuk le a domaint a sessionről, és győződjünk meg róla, hogy nem fog n+1 lekérdezést indítani a háttérben, miközben mi meg vagyunk róla győződve, hogy minden rendben.
Ahogy említettem FetchMode.JOINnal és nélküle is proxyk jelennek meg a domainbe, egy apró különbség azonban van a két eljárás között. FetchMode.JOIN nélkül az alábbi kódsor hibát eredményez (LazyInitializationException).
Bar bar = Bar.createCriteria().get { isNotNull 'id' maxResults 1 } bar.discard() println bar.foos.size()FetchMode.JOIN megadásával pedig nem, amiből arra lehet következtetni, hogy van eltérés a két viselkedés között. Előbbi esetben az egész kapcsolatot egy proxy helyettesíti, utóbbinál pedig a domaineket helyettesíti egy-egy proxy.
Lefuttattam a tesztet a Grails legfrissebb verziójával (2.4.4), és minden jel arra utal, hogy már megoldották ezt a problémát. Nem tudom ténylegesen melyik verzióval lett javítva, ezért javaslom mindenkinek, hogy ellenőrizze megfelelően működik-e az alkalmazása, vagy frissítsen a legfrissebb verzióra.
Nincsenek megjegyzések:
Megjegyzés küldése