S tématem transakcí ve spojení s Hibernate také souvisí téma strategií ukládání dat z paměti do databáze (Flushing the Session), které si nyní představíme.
Změny na perzistentních objektech v perzistentním stavu, tedy na těch, které jsou právě asociovány s Hibernate sezením, nejsou ihned ukládány z paměti na disk. Hibernate se tak snaží snížit počet dotazů nad databází a tím snížit propustnost sítě. Hibernate ukládá data do databáze pouze v následujících případech.
Základní nastavení. Umožňuje dělat mnohé změny na perzistentních objektech, které budou promítnuty do databáze, jakmile je úspěšně dokončena databázová transakce.
Hibernate neukládá data do databáze při každé změně, ale provádí je v paměti. Nicméně, pokud je volán dotaz, který může být tímto chováním ovlivněn, Hibernate synchronizuje stav paměti s databází a až poté je tento dotaz proveden.
V případě explicitního volání metody
flush() nad Hibernate sezením dojde k
synchronizaci paměti a databáze.
Toto chování se dá kontrolovat za běhu nastavením způsobu
synchronizace pomocí Session.flushMode().
Hodnota FlushMode.AUTO nastaví synchronizaci na
vlastnosti popisované výše. FlushMode.COMMIT nastaví
synchronizaci pouze po úspěšném ukončení transakce, což ale může
způsobovat chybné výsledky některých dotazů.
FlushMode.NEVER potom povolí synchronizaci pouze v
případě explicitního volání
Session.flush().
Pokud používáme transakce ve spojení s Hibernate a aplikačním
rámcem Spring, používáme je skrze transakčního manažera. Transakčním manažerem myslíme implementaci rozhraní
PlatformTransactionManager, poskytované
aplikačním rámcem Spring, které nakonfigurujeme následovně, viz.
aplicationContext-hibernate.xml a
applicationContext-business.xml.
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributeSource">
<value>
cz.....managers.CompetitorManager.*=PROPAGATION_REQUIRED
cz.....sportportal.managers.GuidePostCategoryManager.*=PROPAGATION_REQUIRED
cz.....sportportal.managers.RegionManager.*=PROPAGATION_REQUIRED
cz.....sportportal.managers.SportManager.*=PROPAGATION_REQUIRED
cz.....sportportal.managers.UserManager.*=PROPAGATION_REQUIRED
cz.....sportportal.managers.CompetitorManager.*=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Manager" />
<property name="interceptorNames">
<list>
<value>debugInterceptor</value>
<value>performanceMonitorInterceptor</value>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
Aplikační rámec Spring nám nabízí deklarativní správu transakcí, jak si můžete všimnout na našem příkladu.
Naším transakčním manažerem je ve skutečnosti
HibernateTransactionManager, který nastavuje
Hibernate sezení, poskytované továrnou sezení, do vlákna, které
zpracovává uživatelský požadavek na aplikaci. Toto sezení je pod správou
tohoto transakčního manažera a je poskytnuto pro práci jak třídě
HibernateTemplate, tak i třídám
OpenSessionInViewFilter a
OpenSessionInViewInterceptor. Od této chvíle se
mohou tyto třídy účastnit transakčního zpracování dat.
Transakční zpracování je nastaveno na všechny metody manažerů aplikační vrstvy s nastavením uvedeným v deklaraci transakčního interceptoru. Na metodách se dá definovat, jakou část kódu transakce pokrývají apod. V našem případě PROPAGATION_REQUIRED říká, že v jedné transakci bude za běžného stavu všechen kód dané metody manažera. Pokud transakce neexistuje, pak se vytvoří, pokud již transakce existuje, tato metoda se bude na jejím běhu podílet. Samozřejmě můžeme specifikovat i další parametry transakcí, ale to je již nad rámec našeho textu.
Pokud nyní použijeme metodu
getHibernateTemplate().find(), proběhne tato
metoda v transakci.
Představme si však, že pokud jsme modifikovali v této transakci
nějaké perzistentní objekty a jejich stav byl synchronizován s databází
(nastaven FlushMode.AUTO nebo synchronizace byla
vynucena explicitně) a naše transakce poskytovaná transakčním manažerem
bude neúspěšná, nad databází se provede rollback, ale objekty v paměti
zůstanou modifikované. Poté nezbývá nic jiného, než udělat
getHibernateTemplate().clear(), což vymaže tato
data z paměti a nasledně se budou postupně do paměti dostávat správná
data z databáze.
O transakcích se toho dá také napsat mnohem více, než jsme zde uvedli. My jsme si zde transakční zpracování objektů pouze představili a řekli jsme si, jak jej můžeme používat. Více se můžeme dozvědět v dokumentacích k Hibernate [12] i k aplikačnímu rámci Spring [5].