sreda, 20. februar 2008

Zakaj mi dela Hibernate tako počasi?

Hibernate je zakon
Zadnje čase se veliko ukvarjam z tehnologijami: Hibernate, Java, ... Zadolžen sem za postavitev okolja, ki bi bilo uporabno ostalim sodelavcem na tem projektu.
Da bi pokazal primere uporabe razvite rešitve, sem sestavil nekaj "uporabniku prijaznih" unit testov (JUnit). Unit teste sem spisal na hitro, ker se mi že malo mudi; prav tako, nisem ravno bil priden programer in nisem pisal toString, equals, hashCode metod v svojih podatkovnih modelih.
Tako sem si vzel včeraj in danes na poti dom/služba; na vlaku; čas, da sem spisal te manjkajoče metode vsaj v abstraktnih razredih - kjer je največ lastnosti. Sem "goreč" zagovornik pisanja teh metod, vsaj tako sem se naučil iz knjig, ki sem jih prebral.
Unit testi so super: lahko jih pišeš neglede na to ali imaš dostop do baze in/ali logika že obstaja: to je ravno sestavi del uporabnosti unit testa: Jaz vem, kaj mora ta metoda vrnit za znan vhod; kako bo to naredila: znajdi se... To je predmet testranja pri unit testu.
In jaz sem priden in konsistenten in pišem vse tako kot je treba.

Ali je to res pravilno?: Ne.
Danes zjutraj me je pričakalo čudovito presenečenje, ki si ga nekaj trenutkov nisem znal razložit. Oči so mi že skoraj zapustile jamice, ko sem občudoval kako počasi mi delujejo moji unit testi - pa tako ponosen sem bil na vse: da sem jih končno sestavil, ves čas: ki sem ga porabil, da sem spisal toString, hashCode in equals metode za vse te številne lastnosti. In kaj dobim: Eclipse se mi je obesil in pokazal bel zaslon... Nevrončki v možganih so začeli brneti kot star katerpiler: vso kolesje se je začelo vrteti in mleti - mleti vse misli od včeraj, danes. Kaj se je spremenilo od včeraj, ko sem nazadnje izvedel unit teste? Hja: dodal si toString, hashCode, equals... Hmmm.

Čudno se mi je zdelo
Čudno se mi je zdelo, zakaj Hibernate ni naredil pri izgradnji modelov teh metod: zakaj sem jih moral pisat sam? Zakaj je naredil te metode samo za kompozitne ID-je? Včeraj pa sem se tudi začel spraševati: je to pametno, da delam toliko dela v teh metodah. Uporabljam Apache Lang paket, ki ima ToStirngBuilder, HashCodeBuilder in EqualsBuilder util razrede, ki so zelo koristni pri gradnji teh metod. A, kaj ko ima posamezen model tudi 15-30 različnih lastnosti. Malo se mi je zdelo sporno: pa to dela hitro? Tolažil sem se, da sem vedno prebral: te metode morajo biti v vsakem modelu - če imaš toString mora biti obvezno tudi equals in hashCode; to je ključnega pomena za HashMap ali HashSet. No pa dobro, bom spisal te metode. Po parih razredih sem izgubil voljo, ker sem štancel te lastnosti in ... mi ni bilo več.
Hkrati sem opazil nekam zelo velik izpis v logih... kaj te to je? Sem sprva mislil, da so spreminjali podatke v bazi: to je stalnica, tako da najprej pomislim na to. Kmalu sem vse potegnil skupaj in 1+1 = ...
Seveda, najprje sem dobil čudne napake: v stilu:
org.hibernate.LazyInitializationException: illegal access to loading collection
Kaj te s tabo je... kaj te to je. Tukaj so prvič začeli nevrončki skakat čez rob. V zadnji knjigi, ki sem jo prebral; je pisalo o "illegal access" napakah v primeru, da druga nit vleti v map-o in spremeni vrednost (doda/odstrani podatek). Ok, kaj ima sedaj s tem? Ni ravno povezano, ali pa tudi: ne vem. A probelem je bil kr hitro jasen: nekdo dostopa do podatkov, ko še ti niso dokončno sestavljeni - to je hashCode, equals. aaa. Prvi aaa trenutek.
Ko sem odpravil to napako, se je začelo dolgorajtno izvajanje... izvajanje in izvajanje. Spet nisem vedel, kaj je narobe. Pa daj: danes res ni moj dan al kaj... pa sem že mislil, da je, ko sem videl tako lepo deklino na vlaku in končno se mi je malo nasmejala in rekla zdravo... ne mora biti tako hitro konec tako prijetnega dneva. Razmisli!
Hmm: nekam dosti se izpisuje v log: se ti ne zdi. Aaa (drugič). toString metoda ... Vse te metode sem obtal v komentarje in poskusil srečo ponovno. Ccc ded: sprve je paljotka - ko jegermajster po grlu... seksi.

Raziskava
Pri branju loga se mi je zdelo čudno: kaj se tolko izpisuje. Kmalu sem ugotovil, da se je v toString skliceval na drug model (njegov toString); ta se je na tretjega in tako dalje. Model je tako super, da hitro pride v krog in ... začel se je krog izvajanja toString metod. Podobno je verjetno bilo za equals in hashCode.
Verjetno ne bi bilo napačno, če bi vključil nekaj ključnih lastnosti, a če že Hibernate sam ni generiral teh metod - jih niti jaz ne rabim. Nisem še prebral celotne knjige o Hibernate in ne vem točno, kako se obnaša glede takšnih problemov. Hmm, spomnim se, da kao v Hibernate 3 te metode niso obvezne... toliko bolje zame.
Drug povod, da sem odstranil te metode je bil tudi ta: kako bo on vedel določiti hashCode in equals vseh lastnosti če pa je ravno poanta Hibernate-a in modelov, da vrnejo podatke "on-demand" ala lazy fetch. V teh metodah sem za vse te lastnosti sprožil nepotrebna SQL poizvedovanja... za kakšno celo: jaz sem opazil le negativne vrednosti in tako ne vidim smisla, da bi implementiral toString, equals in hashCode.

Zaključek
V Hibernate-ovih modelih pazi kako implementiraš toStirng, equals, hashCode: ne velja vse enako kot velja za "običajne" Java POJO-te.

Ni komentarjev: