čtvrtek 31. července 2008

Hibernate a One-to-One (ne)lazy loading

Vztahy jedna ku jedné nepoužívám příliš často (aspoň co se databázových aplikací týče), takže mě zarazila následující věc: ve výchozím nastavení totiž Hibernate po vytažení jedné entity selectem ihned dalším selectem natáhne i druhou entitu ze vztahu. To by nebylo tak zlé, horší je, že po jednom dotazu na 1000 entit provede ihned dalších 1000 dotazů na vázané entity.
K pochopení proč to tak je a jak z toho ven je potřeba udělat úkrok stranou a uvědomit si, jak funguje lazy loading (líné načítání) ve vztazích Many-to-One. V něm je entita, obsahující kolekci (set, list,...) dalších entit. Při načtení "hlavní" entity nejsou prvky kolekce ihned načteny, ale na místo kolekce vloží Hibernate svůj speciální proxy objekt, který umí podřízené entity načíst z databáze teprve až při prvním přístupu k prvkům. Důležité je, že kolekce jako taková nikdy není null -- pokud vztah na straně Many má nulový počet prvků, má kolekce velikost nula.
U vztahu One-to-One ale takové řešení není možné proto, že nejde nasimulovat hodnotu null: Hibernate nemůže vložit "svůj speciální proxy objekt" a při prvním přístupu ho načíst, protože pokud by entita v databází chyběla, není způsob, jak proxy vyměnit za null. A právě z tohoto důvodu se musí jít do databáze zjistit, jestli objekt je nebo není. (Tady bych poněkud čekal, že bude aspoň možné objekty načítat nastavením BatchSize po větších kusech než po jednom, ale to se mi nepodařilo rozchodit).
V případě, že máme opravdový vztah One-to-One (tj. obě strany jsou vždy přítomné), nejjednoduším řešením je přidat k anotaci parametr optional=false (případně v mapovacím souboru uvést constrained="true") a lazy loading bude fungovat tak, jak člověk čeká.
Some explanations on lazy loading

Žádné komentáře:

Okomentovat