Folien zum Einsatz von Annotationen in Spring

Habe bei SlideShare interessante Folien zum Einsatz von Annotationen in der Spring-Entwicklung gefunden. Zunächst werden allgemeine Annotationen aus dem Java SE und besonders aus dem Java EE-Umfeld vorgestellt. Dann werden JPA und REST im Kontext von Spring an Beispielen erklärt. Zum Schluss folgt noch etwa Spring MVC und wie dort Annotationen die Konfiguration vereinfachen.

SpringSource und Groovy/Grails und SpringSource mit Adobe

SpringSource (http://www.springsource.com/) macht große Schritte. Zum einen durch den Aufkauf von G2one (http://www.g2one.com/), die sich der Beratung von Weiterentwicklung von Groovy und Grails verschrieben haben:

SpringSource,
the maker of the Spring framework have just announced they are acquiring G2One, Inc.,
the Groovy/Grails company. You can learn more about this announcement here:

Nun arbeitet SpringSource auch mit Adobe zusammen und Flash näher an Spring zu setzen. Das Spring-Framework integriert dazu BlazeDS (http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/), eine unter LGPL stehende Remoting- und Messaging-Technologie. Mit BlazeDS kann Flex auf Java-Methoden auf der Serverseite zugreifen, so wie es auch JavaScript über DWR (http://directwebremoting.org/) kann. Die Ankündigung zur Zusammenarbeit:

  • http://www.springsource.com/node/1077

Und überall sitzt die OSGi Service Platform drunter…

Unter http://www.earthtimes.org/articles/show/world-leading-enterprise-application-server-providers,541827.shtml und http://www.osgi.org/blog/2008/09/impressive-press-release.html führen die Autoren auf, wie beliebt mittlerweile OSGi als Basis für Java EE Container ist. Peter Kriens führt folgende Container auf:

  1. IBM Websphere. They started in 2006 and aggresively moved their code on OSGi. Obviously it helped that IBM has committed itself to OSGi since day -1.
  2. Oracle WebLogic. Formerly known as BEA weblogic. BEA was one of the first companies public touting the advantages of OSGi, clearing the road for others.
  3. Paremus Infiniflow. Paremus has pioneered the use of OSGi in highly distributed application servers allowing the system to scale to unprecendented heights.
  4. ProSyst ModuleFusion. ProSyst is the key provider of OSGi technology in the embedded worlds but originated from the development of a J2EE server. They are now back in this market with an offering based completely on OSGi.
  5. Redhat JBoss. JBoss already worked with a microkernel approach but recognized the advantages of a standard two years ago.
  6. SAP Netweaver. Well, they are not yet based on OSGi, but they clearly see their future based on the OSGi specifications and are becoming more and more active in the OSGi Alliance.
  7. SpringSource Application Platform. The company that simplified Enterprise development with their Spring Framework decided to create their own offering in the application server market completely based on the OSGi specifications.
  8. Sun Glassfish. And last, but definitely not least, Sun decided to use OSGi in the JEE reference implementation Glassfish. They clearly take OSGi extremely serious nowadays since they also hired Richard S. Hall. It is so good to see Sun back at the OSGi Alliance.

Interessant sind zwei Feststellungen:

  1. In der Regel geben die Container die OSGi-Implementierung nicht nach oben weiter, abstrahieren also von den Internas. Nur einige Produkte erlauben, auch selbst OSGi-Bundles anzumelden.
  2. Java EE und das SpringFramework sind beides Nutznießer der OSGi-Plattform. Man kann nun fragen, ob man sich überhaupt noch mit OSGi selbst beschäftigen muss, wenn etwa GlassFish oder http://www.springsource.com/products/suite/applicationplatform gute Abstraktionen bieten.

Buchkritik: Agile Java Development with Spring, Hibernate and Eclipse

Anil Hemrajani. Sams. ISBN 0-672-32896-8. Mai 2006. 360 Seiten

Lehrbücher, die anhand eines Beispiels versuchen, die agile testgetriebene Softwareentwicklung mit Ant, Hibernate, HSQLDB und Spring (Web mit Spring MVC) zu zeigen, gibt es meines Wissens keins – bis auf dieses. Vielleicht ist das auch kein Wunder, denn schon die Themen Spring/Hibernate füllen dicke Bücher. Hemrajanis Buch will aber gar nicht in die Tiefe gehen, sondern zeigt, wie mit Open-Source Tools ein Online Timesheet System aufgebaut wird. Der Autor streift dabei weitere Technologien, wie die Eclipse IDE, Logging, Profiling, und stellt Links zu vielen Tools übersichtlich am Ende jedes Kapitels vor. Die Zielgruppe für das Buch dürften Personen sein, die bisher mit dem Standard Java gearbeitet haben, und nun lernen wollen, mit welchen Enterprise Technologien sich Web-Anwendungen bauen lassen. Wer eine detaillierte Beschreibung in JUnit, Hibernate und Spring sucht, ist mit dem Buch sicherlich nicht gut beraten. Schade ist auch, dass Hibernate über die HBM.XML-Dateien konfiguriert wird und nicht die Sprachmöglichkeiten über Java 5 (Annotationen) und der JPA-Entity-Manager zum Einsatz kommen. Dass nicht Spring 2 den Kern bildet, ist zu verschmerzen, und das statt Spring MVC nicht Spring WebFlow zum Einsatz kommt ist wohl eher eine Frage des didaktischen Wegs und dem Zeitpunkts des Drucks zu verschulden.

Annotationen in Spring 2.0/2.5 für Autowire und neue Beans

Bis Spring 2.0 gab es im Wesentlichen nur eine Möglichkeit, Spring-Beans zu deklarieren und Bean-Referenzen zu injizieren. Ab Spring 2.0 und besonders in Spring 2.5 gibt es einen Richtungswechsel. Statt Bean-Definitionen und Injizierungsanweisungen ausschließlich über XML-Dokumente zu beschreiben, kommen Annotationen hinzu. Spring nutzt zum Einen Standard-Annotationen aus Java 6 (Common Annoations) aber auch eigene.

Nehmen wir zwei Beans an:

    <bean id="today" class="java.util.Date" /> 
<bean id="calendarPrinter"
class="com.tutego.spring.annotation.CalendarPrinter" />

Die Bean CalendarPrinter soll ein Datum injiziert bekommen.

    public class CalendarPrinter { 
public void setDate( Date date ) …

Über XML lässt sich das einfach beschreiben, doch seit Spring 2.5 veranlasst eine Annotation Spring dazu, die Verweise automatisch zu injizieren:
@Autowired

@Autowired ist eine Annotation für Methoden und Attribute, damit Spring selbständig den Verweis setzt:

    public class CalendarPrinter { 
@Autowired
public void setDate(@Qualifier("today") Date d) …
}

Die Annotation @Qualifier ist nur dann nötig, wenn es mehrere Beans dieses Typs gibt. (Nicht bei uns.)

Damit Spring überhaupt die Verknüpfung vornimmt, ist in der XML-Datei ein Hinweis zu setzen.

Die erste Möglichkeit ist:

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

Eine im Allgemeinen bessere Alternative (die AutowiredAnnotationBeanPostProcessor und CommonAnnotationBeanPostProcessor zusammenfasst) ist

    <context:annotation-config/>

Die Annotation @Autowired ist genauso gültig bei Attributen:

    public class CalendarPrinter 
{
@Autowired Date date;

      public void doIt()
      {
        System.out.println( date );
      }
    }

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
<bean id="today" class="java.util.Date" />
<bean id="calendarPrinter" class="com.tutego.spring.annotation.CalendarPrinter" />
</beans>

Java 6 (für Java 5 ist ein Extra-Jar nötig) führt aus JSR-250 die Lebenszyklus-Annotationen @PostConstruct und @PreDestroy sowie @Resource ein.

    public class CalendarPrinter { 
@PostConstruct public void initialize() {
System.out.println( "init" );
}
/* @PreDestroy public void remove() {
System.out.println( "destroy" );
} */
}

Beans deklarieren

Die Verknüpfung ist durch Autowire automatisiert und minimiert die XML-Konfigurationsdatei.
Zum Deklarieren von neuen Beans bringt Spring ebenfalls Annotationen mit.
Statt in der XML-Datei zu schreiben

    <bean id="calendarPrinter" class="com.tutego.spring.annotation.CalendarPrinter" /> 

können wir annotieren:

    @Component class CalendarPrinter

Damit Spring nach annotierten Beans sucht, ist nötig:

    <context:component-scan base-package="com.tutego.spring" />

Die Annotation @Component ist an allen Typen erlaubt und natürlich zur Laufzeit sichtbar:

    @Target(value=TYPE) 
@Retention(value=RUNTIME)
@Documented
public @interface Component
{
String value();
}

API zur Eigenschaft: „The value may indicate a suggestion for a logical component name, to be turned into a Spring bean in case of an autodetected component.“

Geben wir dem CalendarPrinter den Bean-Namen „ cal-printer“:

    @Component("cal-printer") 
public class CalendarPrinter

Aus dem ApplicationContext genommen folgt:

    ApplicationContext context = 
new ClassPathXmlApplicationContext( … );
Object bean = context.getBean( "cal-printer" );
System.out.println( bean.getClass() );
// class com.tutego.spring.annotation.CalendarPrinter

@Component ist eine sehr allgemeine Annotation.

Besser ist es, semantische Annotationen zu nutzen, die @Component erweitern. Spring liefert drei mit:

  1. @Target(value=TYPE) … @Component
    public @interface Repository
  2. @Target(value=TYPE) … @Component
    public @interface Service
  3. @Target(value=TYPE) … @Component
    public @interface Controller

JAX-WS 2.1 with Spring under Java 6

First you need some jars in your classpath of an web application

  • commons-logging.jar
  • jaxw-api.jar
  • jaxws-rt.jar
  • jaxws-spring-1.1.jar
  • saaj-api.jar
  • saaj-impl.jar
  • spring.jar
  • stax-ex.jar
  • streambuffer.jar
  • xbean-spring-2.8.jar

The Jars are from Spring itself, http://geronimo.apache.org/xbean/, https://jax-ws.dev.java.net/ (jax-ws 2.1, JAXWS2.1_nightly.zip) and https://jax-ws-commons.dev.java.net/spring/ (jaxws-spring-1.1.jar). It is although important to place

  • jaxb-api.jar
  • jaxb-impl.jar

under C:\Programme\Apache Software Foundation\Tomcat 5.5\common\endorsed.

This was the necessary and boring preparation.

Next the Web Service itself

package com.tutego.service;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class GreeterService
{
@WebMethod
public String greet( String name )
{
return "Hello " + name + "!"
}
}

And web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>SpringWeb</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>jaxws-servlet</servlet-name>
<servlet-class>
com.sun.xml.ws.transport.http.servlet.WSSpringServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jaxws-servlet</servlet-name>
<url-pattern>/greet</url-pattern>
</servlet-mapping>
</web-app>

The applicationContext.xml follows the suggestions with the namespace.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ws="http://jax-ws.dev.java.net/spring/core"
xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd
http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet.xsd">
<ws:service id="greeterService"
impl="com.tutego.service.GreeterService" />
<wss:bindings id="jax-ws.http">
<wss:bindings>
<wss:binding url="/greet" service="#greeterService" />
</wss:bindings>
</wss:bindings>
</beans>

Now you can consume your WS. If your Web-Context is SpringWeb use http://localhost:8080/SpringWeb/greet?wsdl. Enjoy it!

Spring in einer Web-Applikationen nutzen – so einfach ist das

Mit dem WTP ist es eine Sache von Minuten.

Lege ein Dynamic-Web-Projekt an. Nenne es etwa spring.

Aus dem Spring-all-in-one-glücklich.zip kopiere spring.jar und common-loggings.jar in das WEB-INF/lib.

Lege in WEB-INF eine Datei applicationContext.xml.

Setze in die Datei:

<?xml version="1.0" encoding="UTF-8"?> 


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>

<bean id="date" class="java.util.Date" />

</beans>

Setzte in WEB-INF/web.xml die Zeilen:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

Schreibe eine index.jsp

<%@page import="org.springframework.web.context.support.WebApplicationContextUtils"%>
<%@page import="org.springframework.context.ApplicationContext"%>
<%
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
%>

<%= appContext.getBean("date") %>

Das war’s. Unter http://localhost:8080/spring/ gibt’s dann das Datum.

SimpleJdbcTemplate aus Spring 2.0

Das org.springframework.jdbc.core.JdbcTemplate compiliert unter Java 1.4 und nutzt keine Eigenschaften von Java 5 wie Generics, Autoboxing und Varags.
Seit Spring 2.0 gibt es mit org.springframework.jdbc.core.simple.SimpleJdbcTemplate eine neue Klasse mit eine überschaubaren Anzahl Methoden für Java 5.
– Es ist eine Java 5-Fassade vor dem JdbcTemplate.
Der Konstruktor von SimpleJdbcTemplate nimmt wie JdbcTemplate eine DataSource an.

Methoden vom SimpleJdbcTemplate

int queryForInt(String sql, Object… args)
long queryForLong(String sql, Object… args)
Erfragt ein int/long über einen SQL-String und Argumenten.
List<Map<String,Object>> queryForList(String sql, Object… args)
Map<String,Object> queryForMap(String sql, Object… args)
Führt die SQL-Anfrage mit optionalen Argumenten aus.
<T> T queryForObject(String sql, ParameterizedRowMapper<T> rm, Object… args)
Die SQL-Anfrage liefert ein Ergebnis, das der Mapper auf ein Objekt überträgt.
<T> T queryForObject(String sql, Class<T> requiredType, Object… args)
Führt eine SQL-Anfrage aus und überträgt die Eigenschaften auf ein Objekt.
<T> List<T> query(String sql, ParameterizedRowMapper<T> rm, Object… args)
Der ParameterizedRowMapper überträgt Ergebnisse auf Objekte.
int update(String sql, Object… args)
Führt ein SQL-Update mit optionalen Argumenten durch.


SimpleJdbcTemplate und JdbcTemplate

Neben dem Konstruktor SimpleJdbcTemplate(DataSource) gibt es einen zweiten:
– SimpleJdbcTemplate( JdbcOperations classicJdbcTemplate )
Als Übergabe ist ein Objekt vom Typ JdbcOperations nötig; JdbcTemplate implementiert diese Schnittstelle.
Soll vom SimpleJdbcTemplate auf das unterliegende JdbcTemplate zurückgegriffen werden liefert das die Methode:
JdbcOperations getJdbcOperations()

SimpleJdbcDaoSupport

Die DAO-Klassen von Spring erlauben Zugriff auf die Template-Objekte.

Während JdbcDaoSupport ein JdbcTemplate liefert, gibt SimpleJdbcDaoSupport ein SimpleJdbcTemplate.

SimpleJdbcDaoSupport ist eine Unterklasse von JdbcDaoSupport:

org.springframework.jdbc.core.support.JdbcDaoSupport
extended by org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport

@ManagedResource in Spring is really cool

First, write a class and annotate it:

package com.javatutor.spring.jmx;

import java.util.HashMap;

import java.util.Map;

import org.springframework.jmx.export.annotation.ManagedAttribute;

import org.springframework.jmx.export.annotation.ManagedOperation;

import org.springframework.jmx.export.annotation.ManagedResource;

@ManagedResource( objectName = "com.javatutor.spring.jmx:name=StringMap" )

public class StringMapImpl

{

private Map<String, String> map = new HashMap<String, String>();

@ManagedAttribute

public int getSize()

{

return map.size();

}

@ManagedOperation

public String get( String key )

{

return map.get( key );

}

@ManagedOperation

public String put( String key, String value )

{

return map.put( key, value );

}

public void remove( String key )

{

map.remove( key );

}

}

Next, write a spring config file:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean id="StringMap" class="com.javatutor.spring.jmx.StringMapImpl" />

<bean id="MBeanExporter"

class="org.springframework.jmx.export.MBeanExporter">

<property name="assembler" ref="MBeanInfoAssembler" />

<property name="autodetect" value="true" />

<property name="namingStrategy" ref="NamingStrategy" />

</bean>

<bean id="AnnotationJmxAttributeSource"

class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"

/>

<bean id="NamingStrategy"

class="org.springframework.jmx.export.naming.MetadataNamingStrategy">

<property name="attributeSource" ref="AnnotationJmxAttributeSource" />

</bean>

<bean id="MBeanInfoAssembler"

class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">

<property name="attributeSource" ref="AnnotationJmxAttributeSource" />

</bean>

</beans>

Now the client:

package com.javatutor;

import javax.swing.JOptionPane;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.javatutor.spring.jmx.StringMapImpl;

public class SpringWithJmxExample

{

public static void main( String[] args )

{

ApplicationContext beFac = new ClassPathXmlApplicationContext( "springJmx.xml"

);

StringMapImpl cacheBean = (StringMapImpl) beFac.getBean( "StringMap" );

cacheBean.put( "Key", "Value" );

System.out.println( cacheBean.get("Key") );

JOptionPane.showMessageDialog( null, "End" );

}

}

Start the program. Do not forgett to start with -Dcom.sun.management.jmxremote.
Start JConsole and enjoy.

 

Spring, Hibernate und HSQLDB

Nach dem das erste Beispiel das Zusammenspiel von Spring und HSQLDB zeigte, soll das zweite Beispiel statt dem JdbcTemplate das HibernateTemplate nutzen, damit Hibernate die OR-Abbildung übernimmt. Zur Vorbereitung (und Erweiterung) des ersten Beispiels wird zunächst log4j.properties um eine Zeile erweitert:

log4j.rootCategory=ERROR, A1
log4j.logger.net.sf.ehcache=ERROR, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Für Hibernate sind ebenso weitere Jar-Dateien nötig, so dass sich insgesamt
im Pfad befinden müssen:

  • hsqldb.jar
  • log4j-*.jar
  • spring.jar
  • common-logging.jar
  • hibernate3.jar
  • dom4j-*.jar
  • jta.jar
  • common-collections-*.jar
  • ehcache-*.jar
  • cglib-*.jar
  • asm.jar
  • antlr-*.jar

Als erstes zeigt die DAO-Implementierung die Verwendung von HibernateTemplate.

package com.javatutor.dao.hibernate;

import java.util.Collection;

import org.springframework.orm.hibernate3.HibernateTemplate;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoHibernateImpl extends HibernateTemplate implements CustomerDao
{
@SuppressWarnings("unchecked")
public Collection<Customer> getCustomers()
{
return loadAll( Customer.class );
}

public Customer findCustomerById( int id )
{
return (Customer) load( Customer.class, id );
}

public void save( Customer customer )
{
saveOrUpdate( customer );
}
}

Zum Mappen der POJOs auf die Datenbank ist eine Mapping-Datei nötig, es sei denn, man arbeitet mit Java 5 Annotationen. In den Klassenpfad kommt die Datei Customer.hbm.xml.

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.javatutor.domain">

<class name="Customer" table="Customers" lazy="false">

<id name="id" column="Id">
<generator class="native" />
</id>

<property name="name" column="Name" />

</class>

</hibernate-mapping>

In der Spring XML-Datei müssen wir den CustomerDaoHibernateImpl anmelden und
mit einer SessionFactory versorgen.

<bean id="CustomerDaoHibernate" class="com.javatutor.dao.hibernate.CustomerDaoHibernateImpl">
<property name="sessionFactory">
<ref local="SessionFactory" />
</property>
</bean>

Die SessionFactory ist für Hibernate nötig und enthält den Hinweis auf die OR-Mappings und die DataSource. Die DataSource haben wir schon verwendet, doch damit alles persistent bleibt, soll HSQLDB diesmal ins Dateisystem schreiben.

<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:file:/c/:Customers;shutdown=true</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

Es ist sehr wichtig, den Parameter ;shutdown=true an die Verbindungs-URL zu setzen. Die LocalSessionFactoryBean ist der Produzent für unsere SessionFactory:

<bean id="SessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="DataSource" />
</property>
<property name="mappingResources">
<list>
<value>Customer.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.HSQLDialect
</prop>
<!-- <prop key="hibernate.show_sql">true</prop>-->
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>

Mit dem hibernate.hbm2ddl.auto ist dann auch in der Applikation das CREATE TABLE unnötig. Und das war’s dann auch schon.

Mit Spring und dem JdbcTemplate auf eine Hibernate-Datenbank

Das folgende Beispiel soll zeigen, wie man mit Spring arbeitet und Daten in einer relationalen Datenbank persistent macht. Um das Bespiel zum Laufen zu bringen müssen im Klassenpfad sein: hsqldb.jar, spring.jar, log4j-1.2.9.jar und common-logging.jar. Für die Log-Meldungen setzen wir in den Klassenpfad die Datei log4j.properties:

log4j.rootCategory=WARN, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Beginnen wir mit unserem Geschäftsobjekt, einem Kunden:

package com.javatutor.domain;

public class Customer
{
private int id;

private String name;

public int getId()
{
return id;
}

public void setId( int id )
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName( String name )
{
this.name = name;
}

@Override
public String toString()
{
return String.format( "Customer[id=%d, name=%s]", id, name );
}
}

Für diesen Kunden definieren wir eine DAO-Schnittstelle, die uns später Exemplare der Geschäftsobjekte gibt und die Speicherung ermöglichen.

package com.javatutor.dao;

import java.util.Collection;

import com.javatutor.domain.Customer;

public interface CustomerDao
{
Collection<Customer> getCustomers();

Customer findCustomerById( int id );

void save( Customer customer );
}

Zum Testen der Applikation beginnen wir mit einer einfach DAO-Implementierung, die Kunden in einer HashMap speichert.


package com.javatutor.dao.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoMapImpl implements CustomerDao
{
private Map<Integer, Customer> map = new HashMap<Integer, Customer>();

public Collection<Customer> getCustomers()
{
return map.values();
}

public Customer findCustomerById( int id )
{
return map.get( id );
}

public void save( Customer customer )
{
map.put( customer.getId(), customer );
}
}

Eine Applikation wird über die Spring-Konfigurationsdatei später mit einem konkreten CustomerDao gespritzt. Die Methode haveFun() legt einige Business-Objekte an und speichert sie über das DAO.


package com.javatutor;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class Application
{
private CustomerDao customerDao;

public void setCustomerDao( CustomerDao customerDao )
{
this.customerDao = customerDao;
}

private void haveFun()
{
System.out.println( customerDao.getCustomers() );

Customer c1 = new Customer();
c1.setId( 0 );
c1.setName( "Christian Ullenboom" );
customerDao.save( c1 );

System.out.println( customerDao.findCustomerById( 0 ) );

System.out.println( customerDao.getCustomers() );

Customer c2 = new Customer();
c2.setId( 1 );
c2.setName( "Tantiana Roujitcher" );
customerDao.save( c2 );

System.out.println( customerDao.getCustomers() );
}

//

public static void main( String[] args )
{
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "spring.xml" );
Application bean = (Application) context.getBean( "Application" );
bean.haveFun();
}
}

Jetzt fehlt nur noch die XML-Datei für Spring:


<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="Application" class="com.javatutor.Application">
<property name="customerDao">
<ref local="CustomerDao" />
</property>
</bean>
<bean id="CustomerDao" class="com.javatutor.dao.map.CustomerDaoMapImpl" />
</beans>

Startet man das Programm, ist die Ausgabe


[]
Customer[id=0, name=Christian Ullenboom]
[Customer[id=0, name=Christian Ullenboom]]
[Customer[id=1, name=Tantiana Roujitcher], Customer[id=0, name=Christian Ullenboom]]

Prima. Es klappt. Jetzt kommt eine DAO-Implementierung für JDBC. Zunächst die Klasse.


package com.javatutor.dao.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoJdbcImpl extends JdbcTemplate implements CustomerDao
{
@SuppressWarnings("unchecked")
public Collection<Customer> getCustomers()
{
return query( "SELECT Id, Name FROM Customers", new CustomerRowMapper() );
}

public Customer findCustomerById( int id )
{
String sql = "SELECT Id, Name FROM Customers WHERE Id = ?";

try
{
return (Customer) queryForObject( sql,
new Object[] { id },
new CustomerRowMapper());
}
catch ( IncorrectResultSizeDataAccessException e ) { }

return null;
}

public void save( Customer customer )
{
if ( findCustomerById( customer.getId() ) == null )
{
Object[] args = { customer.getId(), customer.getName() };
update( "INSERT INTO Customers (Id, Name) VALUES (?,?)", args );
}
else
{
Object[] args = { customer.getName(), customer.getId() };
update( "UPDATE Customers SET Name = ? WHERE Id = ?", args );
}
}
}

class CustomerRowMapper implements RowMapper
{
public Object mapRow( ResultSet rs, int rowNum ) throws SQLException
{
Customer c = new Customer();
c.setId( rs.getInt( "Id" ) );
c.setName( rs.getString( "Name" ) );
return c;
}
}

Und in der XML-Datei ergänzen wir für den JDBC-DAO:

<bean id="CustomerDaoJdbc" class="com.javatutor.dao.jdbc.CustomerDaoJdbcImpl">
<property name="dataSource"><ref local="DataSource" /></property>
</bean>

<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:mem:customers</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

Damit unsere Applikation mit dem neuen JDBC-DAO gespritzt wird setzt man.

 <bean id="Application" class="com.javatutor.Application">
<property name="customerDao">
<ref local="CustomerDaoJdbc" />
</property>
<property name="dataSource">
<ref local="DataSource" />
</property>
</bean>

Gleichzeitig geben wir der Applikation eine DataSource, damit sie die Tabelle für die Datenbank anlegen kann.


public void setDataSource( DataSource dataSource )
{
new JdbcTemplate( dataSource ).execute( "CREATE TABLE Customers( Id INTEGER, Name VARCHAR(128) ) " );
}

Das gestartet Programm gibt nun die gleiche Ausgabe.