Samstag, August 18, 2012

Migration auf Spring Annotations

Heute war der große Tag. Die alten XML Konfigurationsdateien wurden entfernt und durch die Spring Annotationen @Repository, @Service und @Component ersetzt.

Teil 1: @Repository und die DAOs

Dabei bin ich auf ein erstes Problem gestossen: Die HibernateDaoSupport Klasse wird nicht mehr unterstützt. Weiter wird empfohlen, auf die Klasse org.springframework.orm.hibernate.HibernateTemplate zu verzichten und statt dessen direkt org.hibernate.Session zu verwenden. Das ganze funktioniert ab Hibernate 3.0.1. Für die DAOs habe ich eine gemeinsame Oberklasse definiert: AbstractCommonDao. Diese Klasse definiert die typischen Datenoperationen #findById(...), #delete(...), #save(), etc.. Ein erster Migrationsschritt sieht folgendermaßen aus:

public class AbstractCommonDao extends
        org.springframework.orm.hibernate3.support.HibernateDaoSupport {
    ...
    public final void save(final T t) {
        getHibernateTemplate().saveOrUpdate(t);
    }
}
wird ersetzt durch
public class AbstractCommonDao {
    private org.hibernate.SessionFactory sessionFactory;

    SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public final void save(final T t) {
        getSessionFactory().getCurrentSession().saveOrUpdate(t);
    }
}
Im ersten Schritt sind also alle Codestellen #getHibernateTemplate() durch getSessionFactory().getCurrentSession() zu ersetzen. Problematisch sind im Anschluss die #finder Methoden, da diese keine 1:1 Entsprechung in der HibernateSession besitzen. Die Methode #createSQLQuery(...) findet sich in der Klasse HibernateTemplate wie in der Klasse HibernateSession (Mit dieser Methode können native SQL Anfrage formuliert werden). Bei der Umstellung habe ich alle namenlosen SQL Parameter ersetzt. Das Spring HibernateTemplate unterstützt Konstrukte wie
from match where id = ?
Die HibernateSession kennt keine namenlosen Parameter. Diese Ausdrücke habe ich ersetzt durch
from match where id = :matchId
Für den umgebenden Java Code ändert sich der Aufruf:
public SessionFactory getSessionFactory() {
    return sessionFactory;
}

public Team findByName(final String teamName) {
    List teams = getSessionFactory().getCurrentSession()
            .createQuery("from team where name = :teamName")
            .setParameter("teamName", teamName)
            .list();

    if (teams.size() == 0) {
        return null;
    } else {
       return teams.get(0);
    }
}
Erleichtert also sogar die Lesbarkeit.

Teil 2: Server Start

Nach dem die Konfiguration auf Annotationen komplett umgestellt war, alle JUnit Tests erfolgreich durchliefen, ging es an den Server Start. Meine Anwendung besteht grob aus zwei Artefakten: Der Kern mit der Abstraktion für die Datenbank (Dao, Repository, Service-Schicht) und die Web-Anwendung mit JSPs und Servlets. Die Anwendung läuft auf einem Tomcat 7. Nach Deployment der Anwendung trat dabei beim Hochfahren des Servers die folgende Ausnahme auf:

14.08.2012 17:22:23 org.apache.catalina.startup.ContextConfig
    checkHandlesTypes
WARNUNG: Unable to load class [javax.xml.parsers.SecuritySupport12]
    to check against the @HandlesTypes annotation of one or
    more ServletContentInitializers.
java.lang.IllegalAccessError: class javax.xml.parsers.SecuritySupport12
    cannot access its superclass javax.xml.parsers.SecuritySupport
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
        ...
Gefolgt von der Meldung:
INFO: No Spring WebApplicationInitializer types detected on classpath
log4j:WARN No appenders could be found for logger
    (org.springframework.web.context.support.StandardServletEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
14.08.2012 17:22:28 org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring FrameworkServlet 'betofficeform'
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
Und die Lösung ist die folgende: In der web.xml muss das XML Attribut metadata-complete="true" eingetragen sein!
<web-app id="WebApp_9" version="3.0"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  metadata-complete="true">
Das verhindert, dass die deployten JARs nach Annotationen aus dem Package javax.servlet.annotation durchsucht werden. Ein ähnliches Problem ist in diesem Forum Eintrag beschrieben. Das Problem wurde durch das Löschen der activation.jar aus der Maven Dependency Liste gelöst.

Keine Kommentare:

Kommentar veröffentlichen

AssertJ und java.util.List

AssertJ hat eine praktische Möglichkeit, Listen in JUnit Tests abzuprüfen. Insbesondere, wenn in der Liste komplexe Objekte abgelegt sind, s...