wtorek, 12 maja 2009

Hibernate i Spring DM Server

Spring Dm Server w wersji 1.0 odpaliłem już prawie rok temu. Udało mi się odpalić aplikację web, która pod spodem korzysta z Hibernate. Nie ukrywam, że nie poszło to gładko, ale w końcu się udało. Niestety rok po tym, chciałem pobawić się jeszcze raz, tym razem pod STS 2.0, cała wiedza wyparowała i musiałem jeszcze raz się przebijac i oglądać wyjątki w stylu java.lang.NoClassDefFoundError. W końcu zdecydowałem się to spisać, aby w przyszłości zaoszczędzic sobie ponownej straty czasu.
Aplikacja składa się z następujących projektów:
  • web - Spring DM web bundle
  • domain - obiekty domeny wykorzystywane przez moduł web oraz moduł api, korzysta z hsqlbd oraz commons dbcp
  • api - definiuje interfejs- kontrakt między warstwą web a warstwą dao
  • dao - implementuję interfejs zdefiniowany przez moduł api, dostarcza implementacji usługi OSGI

  1. No PAR deployment- aplikacja składa się z luźno wrzucanych bundli.
    • konfiguracja ORM określona za pomocą annotacji JPA (AnnotationSessionFactoryBean):
      • dao
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_dao Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_dao
        Import-Package: foo.bar.dao.api,
        foo.bar.domain,
        javax.sql;version="[0.0.0,0.0.0]"
        Import-Bundle: com.springsource.org.apache.commons.dbcp,
        com.springsource.org.hsqldb,
        com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"
        Import-Library: org.springframework.spring.instrumented
        jak się można było spodziewać, importujemy bundle Hibernate

      • domain
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_domain Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_domain
        Export-Package: foo.bar.domain;version="1.0.0"
        Import-Bundle: com.springsource.javax.persistence;version="[1.0.0,1.0.0]",
        com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"

        pomimo, że nie używamy annotacji Hibernate (a jedynie JPA) bundle Hibernate musi zostac zaimportowany - w przeciwnym przypadku przy starcie bundle dao dostaniemy wyjątek: java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy

    • konfiguracja ORM określona w plikach konfiguracyjnych xml (LocalSessionFactoryBean):
      • dao
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_dao Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_dao
        Import-Package: foo.bar.dao.api,
        foo.bar.domain,
        javax.sql;version="[0.0.0,0.0.0]"
        Import-Bundle: com.springsource.org.apache.commons.dbcp,
        com.springsource.org.hsqldb,
        com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"
        Import-Library: org.springframework.spring.instrumented
        bez zmian
      • domain
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_domain Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_domain
        Export-Package: foo.bar.domain;version="1.0.0"
        tym razem bundle domain nie potrzebuje żadnego importu: ani JPA ani Hibernate

    Aby moc korzystać z HQL to dla No-PAR deployment wymagane jest zaimportowanie Hibernate dla bundle web.
    Manifest-Version: 1.0
    Web-DispatcherServletUrlPatterns: *.html
    Module-Type: Web
    Bundle-Version: 1.0.0
    Bundle-Name: HibernateTest_web Bundle
    Bundle-ManifestVersion: 2
    Web-ContextPath: HibernateTest
    Bundle-SymbolicName: foo.bar.HibernateTest_web
    Import-Package: foo.bar.dao.api,
    foo.bar.domain;version="[1.0.0,1.0.0]"
    Import-Bundle: com.springsource.org.apache.taglibs.standard;version="[1.1.2,1.1.2]",
    com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"
    Import-Library: org.springframework.spring.instrumented

    W innym przypadku, przy próbie wykonania zapytania HQL zobaczymy: org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.HqlToken. Hibernate ładuję cześć klas za pomocą Thread context classloadera, czyli w naszym przypadku za pomocą ClassLoadera, który załadował moduł web. Najciekawsze jest to, że zapytania korzystające z Criteria API, operacje get/load działają bez tego importu. Jest on jedna wymagany do odpalania zapytań w HQL.

  2. PAR deployment - nasze bundle zostają zapakowane w archiwum PAR
    • konfiguracja ORM określona za pomocą annotacji JPA (AnnotationSessionFactoryBean):
      • dao
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_dao Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_dao
        Import-Package: foo.bar.dao.api,
        foo.bar.domain,
        javax.sql;version="[0.0.0,0.0.0]"
        Import-Bundle: com.springsource.org.apache.commons.dbcp,
        com.springsource.org.hsqldb,
        com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]";import-scope:=application
        Import-Library: org.springframework.spring.instrumented
        tym razem importujemy bundle Hibernate z dodatkowym atrybutem import-scope:=application, który jest pewnym rozszerzeniem Spring DM Server

      • domain
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_domain Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_domain
        Export-Package: foo.bar.domain;version="1.0.0"
        Import-Bundle: com.springsource.javax.persistence;version="[1.0.0,1.0.0]"

        używamy jedynie annotacji JPA, nie musimy importować bundle Hibernate

    • konfiguracja ORM określona w plikach konfiguracyjnych xml (LocalSessionFactoryBean):
      • dao
        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_dao Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_dao
        Import-Package: foo.bar.dao.api,
        foo.bar.domain,
        javax.sql;version="[0.0.0,0.0.0]"
        Import-Bundle: com.springsource.org.apache.commons.dbcp,
        com.springsource.org.hsqldb,
        com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]";import-scope:=application
        Import-Library: org.springframework.spring.instrumented
        bez zmian
      • domain

        Manifest-Version: 1.0
        Bundle-Version: 1.0.0
        Bundle-Name: HibernateTest_domain Bundle
        Bundle-ManifestVersion: 2
        Bundle-SymbolicName: foo.bar.HibernateTest_domain
        Export-Package: foo.bar.domain;version="1.0.0"
        skoro tym razem nie używamy JPA to nie potrzebujemy importu bundle JPA

    Tym razem do bundle web nie musimy importować bundle Hibernate aby móc korzystać z HQL

    Manifest-Version: 1.0
    Web-DispatcherServletUrlPatterns: *.html
    Module-Type: Web
    Bundle-Version: 1.0.0
    Bundle-Name: HibernateTest_web Bundle
    Bundle-ManifestVersion: 2
    Web-ContextPath: HibernateTest
    Bundle-SymbolicName: foo.bar.HibernateTest_web
    Import-Package: foo.bar.dao.api,
    foo.bar.domain;version="[1.0.0,1.0.0]"
    Import-Bundle: com.springsource.org.apache.taglibs.standard;version="[1.1.2,1.1.2]"
    Import-Library: org.springframework.spring.instrumented

    Dzięki importowaniu bundle Hibernate z atrybutem import-scope:=application nie ma potrzeby importowania Hibernate ani w bundle web ani w bundle domain. Po podłączeniu się za pomocą telnet do konsoli Equinox możemy wyświetlić nagłówki bundli wchodzące w skład naszego PAR, w ten sposób zobaczymy że, każdy z nich "automagicznie" importuje pakiety Hibernate.

Brak komentarzy:

Prześlij komentarz