JSPWiki logo
Strona główna
Zbieranina
Serwisy
Porady
Projekty
Humor sektora IT
TODO
Nowości
Ostatnie zmiany
Kontakt

Find pages
Unused pages
Undefined pages
Page Index

Set your name in
UserPreferences

Edit this page


Referenced by
News_blogentry_23100...
Porady




JSPWiki v2.2.33


Hibernate_Struts_dictionary_list


Hibernate/Struts - optymalizacja wykorzystania list słownikowych

Często w aplikacjach używa się różnych tabel słownikowych, których dane są używane najczęściej tylko do pokazania na listach wyboru. Dane te z reguły są stałe, bądź zmieniają się stosunkowo rzadko. Z kolei każdorazowe ładowanie ich z bazy przed wyświetleniem nie jest czymś szczególnie szczęśliwym (bo trzeba powtarzać kod, obciąża się zasoby, itd.). Aż się prosi żeby takie dane zapamiętać...

Poniższy artykuł pokaże jak w prosty sposób używając Hibernate (oraz Struts) osiągnąć przechowywanie takich danych w cache.

Wstęp. Co mamy, co chcemy osiągnąć i jak to zrobimy.

Mamy aplikację webową (tutaj napisaną w Strutsach) z formularzami na których znajduje się sporo pól typu select. Dane pokazywane w tych polach pobierane są ze słownikowych tabel w bazie danych. Dane te są żadko (jeśli wogóle) zmieniane. Chcemy ograniczyć zapytania aplikacji o dane z tych tabel w celu minimalizowania ruchu aplikacja <-> baza danych.

Pożądaną funkcjonalność zrealizujemy za pomocą Hibernate, a dokładniej za pomocą cechy pozwalającej na przechowywanie wyników zapytań w cache. Ma to tę zaletę, że kod aplikacji pozostanie bez zmian, późniejszą konfigurację (co i jak długo trzymać w cache) można przeprowadzać w oderwaniu od kodu aplikacji.

Konfiguracja Hibernate

Włączamy cache'owanie zapytań poprzez dodanie parametrów do pliku hibernate.cfg.xml:

<property name="cache.use_query_cache">true</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

Odpowiednie metody klas *DAO

Oczywiście posiadasz odpowiednie klasy *DAO opakowujące odwołania do bazy danych, prawda? Typowa postać metody DAO zwracającej zawartość tabeli słownikowej:

public static List<Bean> getBeans() {
	try {
		Session s = HibernateUtil.getSession();
		return s.createQuery("from Bean order by name").list();
	}
	catch (HibernateException e) {
		log.info("Error", e);
		throw e;
	}
}

Aby jednak Hibernate przechowywał wyniki zapytań w pamięci należy na obiekcie Query wywołać jeszcze metodę setCacheable(true), kompletna metoda będzie więc wyglądać następująco:

public static List<Bean> getBeans() {
	try {
		Session s = HibernateUtil.getSession();
		return s.createQuery("from Bean order by name").setCacheable(true).list();
	}
	catch (HibernateException e) {
		log.info("Error", e);
		throw e;
	}
}

Można sprawdzić w logach bazy danych - zapytanie do tabeli będzie wykonane tylko raz. Kolejne będą pobierać dane z cache.

Konstrukcje na stronach JSP

Kodując formularze na stronach JSP używamy konstrukcji podobnych do:

<html:form action="..." >
...
<html:select property="beanId">
	<html:optionsCollection name="dictionaryBean" property="beans" label="name" value="id"/>
</html:select>
...

Powyższy kod renderuje formularz HTMLowy z kontrolką typu select. Chcemy aby wyświetlała ona zawartośc tabeli zwracaną przez uprzednio przygotowaną metodę getBeans().

Nic prostrzego. Umieszczamy w applicationScope beana realizującego dostęp do naszej klasy DAO. Beana takiego można umieszczać na każdej stronie za pomocą konstrukcji <jsp:useBean/>, prościej będzie posłużyć się jednak implementacją interfejsu ServletContextListener i na starcie aplikacji umieścić naszego beana w odpowiednim zasięgu.

Integracja stron JSP z klasą DAO

Tworzymy klasę stosującą się do konwencji Jave Bean, ona to będzie poprzez applicationScope pośrednikiem między tagami JSP a klasą DAO. Dla każdej z tablic słownikowych tworzymy odpowiednią metodę.

public class DictionaryBean {
    
    public List<Bean> getBeans() {
        return DictionaryService.getBeans();
    }
}
Następnie umieszczamy instancję ww klasy w kontekście aplikacji:
public class ContextListener implements ServletContextListener {
	
   public void contextInitialized(ServletContextEvent event) {
      event.getServletContext().setAttribute("dictionaryBean", new DictionaryBean());
   }

   public void contextDestroyed(ServletContextEvent event) {
   }
}
Na koniec należy tylko jeszcze podłaczyć Listenera do samej aplikacji, robimy to poprzez dodanie elementu listener do pliku WEB-INF/web.xml określającego pełną nazwę klasy Listenera:
<listener>
	<listener-class>org.rydzewski.ContextListener</listener-class>
</listener>

Koniec.

Jak to działa?
  1. na starcie aplikacji wywoływana jest metoda contextInitialized() Listenera, umieszcza ona w kontekście aplikacji instancję beana realizującego dostęp do klasy DAO
  2. podczas pokazywania strony JSP z elementem select, do tagliba przekazywana jest nazwa beana zawierającego listę wartości do pokazania - http://struts.apache.org/1.2.x/userGuide/struts-html.html#select
  3. taglib <html:select> szuka beana o nazwie dictionaryBean, znajduje go w kontekście aplikacji, następnie wywołuje na nim metodę getBeans(). Ona z kolei skutkuje wywołaniem statycznej metody getBeans() naszej klasy DAO.
  4. a nasza klasa DAO, poprzez Hibernate, do samej bazy danych odwoła się tylko za pierwszym razem. I o to właśnie chodziło

Dopieszczanie, czyli tuning.

Jak coś pracuje dobrze, to może chodzić też lepiej ;-)

Hibernate jako mechanizm cache używa (w podanej tutaj konfiguracji) biblioteki ehcache (która oczywiście powinna być dostępna na CLASSPATH apikacji). Za pomocą konfiguracji tej biblioteki możemy dodatkowo zmienić parametry przechowywania danych w cache. Biblioteka ta posiada rozsądne wartości domyślne, jeśli jednak chcemy je zmienić, to wystarczy dodać do CLASSPATH plik ehcache.xml z odpowiednią zawartością. Tutaj jest podany przykład definiujący cache o rozmiarze 10000 elementów i czasie życia 1 godziny:

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="3600"
            overflowToDisk="true"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>



Go to top   Edit this page   More info...   Attach file...
This page last changed on 23-Oct-2006 11:26:54 GMT by mikolajr.