Jedním ze základních stavebních kamenů v OO návrhu je dědičnost. Nikoho tedy nepřekvapí, že tuto vlastnost nabízí i ORM a tedy i Hibernate.
Při použití dědičnosti v Hibernate a současném používání Proxy ovšem nastávají problémy.
Proxy umožňuje mít referenci na objekt aniž by cílový objekt byl z databáze skutečně načten. Velký smysl má hlavně u vazeb typu 1 ku X (kde X může být jak 1 tak i více než 1).
Uvažujme tento příklad:
public class Smlouva { private Zakaznik zakaznik; }
V případě, že přistoupíte k objektu Smlouva, musí hibernate načíst i objekt Zakaznik. Pokud ovšem použijete Proxy, je použit zástupný objekt, který načte objekt Zazkaznik až při prvním přistupu k němu.
K vytvoření proxy Hibernate použivá knihovnu CGLib a vytvoří objekt odvozený od původního – tedy Zakaznik. Výsledek pak vypadá takto:
Zakaznik$$EnhancerByCGLIB$$933ae557.
Uvažujme teď jiný případ. Vytvořme novou třídu:
class FiremniZakaznik extends Zakaznik { public String getIC() { ... };
Při jejím prvním načtení je vrácen objekt typu rodič (tj. Zakaznik), který je zastoupen vygenerovanou proxy. Proxy ovšem nedědí od FiremniZakaznik, a tak nemá nově přidané metody (přetížené metody samozřejmě fungují). Z čistě objektového přístupu je všechno v pořádku.
Pokud se pokusíte přetypovat proxy na FiremniZakaznik dostanete oblíbenou ClassCastException (případně NoSuchPropertyException).
Na stránkách hibernate doporučují použít visitor pattern.
Pro ty z vás, kteří stejně jako my, používáte MVC framework a používáte tzv. expressions (#{smlouva.zakaznik.IC}) není použití tohoto vzoru ideální.
Řešením je přidat metodu getThis().
Upravíme tedy zákazníka tak, aby obsahoval následující:
class Zakaznik { public Zakaznik getThis() { return this; } }
Pak je možné u položek, které nejsou v rodičovské třídě (a jiné řešení by bylo příliš složité), použít výraz #{smlouva.zakaznik.this.IC}.
Obvykle nepoužíváme ve výrazech přímo metodu getThis(), ale při vytváření parametrů pro view předáváme objekt zbavený proxy.