Spring

advertisement
Persistenz mit Spring
Eberhard Wolff
Saxonia Systems AG
Eberhard.Wolff@saxsys.de
Über mich
➜ Eberhard.Wolff@saxsys.de
➜ Chef Architekt Saxonia Systems
➜ Java Champion
➜ Fokus: Java EE, Spring ...
➜ Autor (z.B. Java Magazin, Bücher...)
– Server Component Pattern (Wiley)
– Java Persistenz Strategien (Software &
Support)
– Spring (dpunkt, Q1/2006)
➜ Blog: http://JandIandMe.blogspot.com/
1
Entweder Java (vor allem EE) wird viel
einfacher, oder wir haben ein Problem.
Teile der Java Community
Überblick
➜ Warum Spring?
➜ Dependency Injection
➜ JDBC mit Spring
➜ iBATIS
➜ Transaktionen
2
Warum Spring?
➜ Einfachheit
➜ Fokus auf POJOs (Plain Old Java Objects)
➜ Vereinfachte Benutzung verschiedener APIs
– Hibernate, JDO, Top Link, iBATIS, JMS, JDBC,
JTA, Java Mail, EJB, ...
➜ Dependency Injection
– Objekte bekommen Ressourcen zugewiesen
– Objekte sind unabhängig von Infrastruktur
Warum Spring?
➜ Keine Zwänge
– Man muss Spring nicht komplett nutzen
– Viele Integrationsmöglichkeiten
– Kann aber One Stop Shop sein
➜ Code weitgehend unabhängig von Spring
➜ Spring - Das neue Java EE?
3
Woher kommt das?
➜ Grundzüge in Rod Johnsons „Expert One-on-
One J2EE Programming“ beschrieben
➜ Als „privates Framework“ in verschiedenen
Projekten geboren
➜ Dann Open Source
➜ Aus der Praxis entwickelt und schon immer
praktisch eingesetzt
– So sollte man Frameworks entwickeln...
– Anders als bei so manchem JCP / JSR
➜ Etliche Anwendungen in Praxis
Dependency Injection
4
Das Beispiel
➜ Kunden, Bestellungen
➜ Alles, was ein Unternehmen braucht!
➜ DAO Pattern: Persistenz in Klasse kapseln
Umsetzung der Klassen
➜ Direktes Erzeugen des DAOs
➜ Sehr einfach
public class BestellungBusinessProcess {
private KundeDAO kundeDAO = new KundeDAO();
…
}
5
Testing
➜ Hm...
public class BestellungBusinessProcessTest
extends TestCase{
public void testBestellen() {
Kunde testKunde = new Kunde();
testKunde.setKontostand(42.0);
KundeDAO kundeDAO = …;
kundeDAO.setTestKunde(testKunde);
// Daten von testKunde setzen
// und jetzt muss ich ihm dem Bestellungprozess
// unterschieben
}
}
OK, das bekomme ich hin...
➜ Refactoring: Factory einziehen
public class BestellungBusinessProcess {
private IKundeDAO kundeDAO = Factory.getKundeDAO();
…
}
public class BestellungBusinessProcessTest
extends TestCase {
public void testBestellen() {
Kunde testKunde = new Kunde();
// Daten von testKunde setzen
IKundeDAO testKundeDAO = …;
testKundeDAO.setKunde(testKunde);
Factory.setKundeDAO(testKundeDAO);
}
}
6
Vorteile
➜ Testdaten unterschieben geht
➜ Objekte können z.B. EJBs sein
– Service Locator Pattern
– Dann aber Änderungen am Code
➜ DAOs können Singletons werden
– Typische Service-basierte Architekturen sind
Singleton-lastig
Probleme: Dieses Factories sind unangenehm
➜ Eine pro Schicht (DAO/BusinessProcess)?
– Mehrmals dasselbe implementieren
– Testdaten Unterschieben redundant
implementiert
➜ Eine allgemeine?
– Jeder hängt davon ab
➜ Typsicher: Zu groß
7
Datenzugriff
➜ Sollte konfigurierbar sein..
public class DAOBase {
protected Connection getConnection() {
Class.forName(…); // Treiber laden
return DriverManager.getConnection(...);
// Connection holen
}
}
public class KundeDAO extends DAOBase {
public Kunde save(Kunde kunde) {
Connection con = getConnection();
…
}
}
Datenzugriff Java EE Umgebungen
➜ Ein Vorschlag, geht sicher auch anders.
public class DAOBase {
protected Connection getConnection() {
if (inJavaEE) { // Wie bekomme ich das raus?
InitialContext ic=new InitialContext();
DataSource ds=(DataSource)ic.lookup(...);
// hier fehlt Caching
return ds.getConnection();
} else {
Class.forName(…);
return DriverManager.getConnection(...);
}
}
}
8
Und jetzt noch mal mit Dependency Injection
➜ „Normale“ Java Klassen
➜ Referenzen durch set-Methoden zuweisbar
➜ Objekte sind passiv, kein aktiver Look-Up
public class BestellungBusinessProcess {
private IKundeDAO kundeDAO;
public void setKundeDAO(IKundeDAO kundeDAO) {
this.kundeDAO = kundeDAO;
}
public void setBestellungDAO(
IBestellungDAO bestellungDAO) {
this.bestellungDAO = bestellungDAO;
}
…
}
Spring Konfigurationsdatei
➜ DI Konfiguration in Spring Konfigurationsdatei
<beans>
<bean id="kundeDAO" class="dao.KundeDAOJDBC" singleton="true">
<property name="datasource" ref="datasource"/>
</bean>
<bean id="bestellungDAO" class="dao.BestellungDAOJDBC" singleton="true">
<property name="datasource" ref="datasource"/>
</bean>
<bean id="bestellung" class="businessprocess.BestellungBusinessProcess">
<property name="bestellungDAO" ref="bestellungDAO"/
<property name="kundeDAO" ref="kundeDAO"/>
</bean>
</beans>
9
DI löst das Umgebungsproblem
➜ Java EE: DataSource im JNDI
<bean id="datasource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/DB" />
</bean>
➜ Java SE: Eigene DataSource
<bean id="datasource“ class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost/spring" />
<property name="username" value="spring" />
<property name="password" value="spring" />
</bean>
Benutzung...
➜ Vorsicht! Nur Main Methode! (eventuell Tests)
➜ ...denn nur die „Top Level Objekte“ müssen so
zugegriffen werden
➜ Abhängige Objekte sind ja konfiguriert
ClassPathResource res =
new ClassPathResource("beans.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(res);
BestellungBusinessProcess bestellung =
(BestellungBusinessProcess)
beanFactory.getBean("bestellung");
bestellung.…;
10
DI: Vorteile
➜ Definiert, was Objekt-Referenzen sind
➜ Klassen automatisch unabhängig von
Umgebung
– Oft Abhängigkeiten zu JNDI
– ...oder zum Service Locator
➜ Flexibilität (Java SE oder EE: schon gesehen)
➜ Testbarkeit (andere Konfiguration / Mocks)
➜ Einziger Unterschied von Singletons: Eine
Instanz, keine
„Man könnte sich ohrfeigen,
globale Variable dass man es nicht immer so gemacht hat!“
Fazit Dependency Injection
➜ DI erleichtert die Strukturierung von
Σ
Anwendungen
➜ Sehr einfaches Prinzip
➜ Spring hat eine mächtige DI Implementierung
➜ Eingebaute Konfiguration
➜ Leichtere Testbarkeit
– Statt echter Klassen Mocks zuweisen
➜ Flexibel: Ressourcen aus dem Application
Server, Java SE, ...
➜ Viele weitere Möglichkeiten (z.B. Auto Wiring)
11
JDBC mit Spring
Beispiel-Code: Wieviele Fehler?
Connection con = null;
ResultSet rs;
Größtenteils schwer diagnostizierbar!
try {
con = …;
Statement stmt = con.createStatement();
rs = stmt.executeQuery("…");
while (rs.next()) {
…
}
rs.close() fehlt (?)
stmt.close();
Wird nicht immer ausgeführt
}catch(SQLException e) {
System.out.println("SQL-Exception:"+e); Fehlerbehandlung?
}finally {
con.close(); NullPointerException?
SQLException?
}
12
Das kann man keinem Entwickler zumuten
„Benutzen Sie JDBC?“
„Ja.“
„Sie haben ein Problem.“
Oder auch:
„Wir haben Garbage Collection, aber was ist mit
den Ressourcen?“
Hilfsklassen: JDBC
➜ Problem bei JDBC: Aufräumen der Ressourcen
vor allem bei Exceptions
➜ SQLException
– Gibt proprietäre Codes zurück
– Ist eine checked Exception (keine
RuntimeException), obwohl man selten
eine sinnvollen Reaktionsmöglichkeiten hat
13
Lösung Allgemein: Template
➜ Auszuführenden Code einem Template
übergeben
➜ Template führt Code aus
– wandelt Exceptions in RuntimeExceptions
um
– Gemeinsame Exception Hierarchie für alle
Datenbanken
➜ Templates sind in Spring für viele APIs
implementiert
– Hibernate, iBATIS, JDO, JMS...
Lösung JDBC: JdbcTemplate
➜ Für Queries: queryForInt() ... für primitive
Daten
➜ queryForList() für komplexe
ResultSets: Gibt List von Maps mit
Spaltenname als Schlüssel zurück
JdbcTemplate insertTemplate = new JdbcTemplate(
datasource);
insertTemplate.update(
"INSERT INTO KUNDE(VORNAME,NAME) VALUES(?,?)",
new Object[] {vorname, name });
14
Hilfsklassen: JDBC Komplexere Queries
➜ Man kann einer query() Methoden auch
übergeben:
– RowMapper: wandelt Zeilen in Objekte
– ResultSetExtractor: gesamtes Ergebnis in
Objekt wandeln
➜ Für den Extremfall gibt es Möglichkeiten auf
Statement / PreparedStatement Ebene
➜ Oder die Entwicklung eigener Klassen für Queries
➜ Also: Ebenfalls Möglichkeiten für komplexe
Ergebnisse
Fazit JDBC Hilfsklasse
➜ Macht Benutzung von JDBC deutlich
Σ
einfacherer und sicherer
➜ Isoliert vom Rest von Spring nutzbar
➜ Beispiel für allgemeinen Template
Mechanismus
➜ (Noch) Nicht gezeigt:
– Integration iBATIS, Hibernate, JDO, Toplink
– Ähnliche Prinzipien (Templates)
– Wesentliche Vereinfachungen (z.B. Hibernate
Session)
15
iBATIS
iBATIS
➜ Eigenes Open Source Framework
➜ Idee: Einfache Abstraktion über JDBC
➜ Direkte Abbildung von Tabellen in Objekte
16
Abbildung: XML Datei
<sqlMap namespace="Ware">
<resultMap id="result" class="businessobjects.Ware">
<result property="id" column="ID" columnIndex="1"/>
<result property="bezeichnung" column="BEZEICHNUNG"
columnIndex="2"/>
<result property="preis" column="PREIS"
columnIndex="3"/>
</resultMap>
<select id="getWareByID" resultMap="result"
parameterClass="int">
SELECT W.ID as ID, BEZEICHNUNG, PREIS FROM WARE W
WHERE W.ID=#id#
</select>
<insert id="saveWare" parameterClass="map">
INSERT INTO WARE(BEZEICHNUNG, PREIS)
VALUES(#bezeichnung#,#preis#)
</insert>
</sqlMap>
Spring Konfiguration
<bean id="sqlMapClient"
class="....SqlMapClientFactoryBean">
<property name="configLocation“
value="sqlmap-config.xml" />
<property name="dataSource" ref="datasource" />
</bean>
<bean id="kundeDAO" class="ibatisdao.KundeDAO" >
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
➜ Injektion der Konfiguration
17
Code im DAO
public class WareDAO extends SqlMapClientDaoSupport
implements IWareDAO {
private static Map getParameterAsMap(
String bezeichnung, double preis, int id) {
Map result = new HashMap();
result.put("bezeichnung", bezeichnung);
result.put("preis", new Double(preis));
result.put("id", new Integer(id));
return result;
}
public void save(Ware ware) {
Map params =
getParameterAsMap(ware.getBezeichnung(),
ware.getPreis());
getSqlMapClientTemplate().insert("saveWare", params);
}
Code im DAO
public Ware getByID(int id) {
return (Ware) getSqlMapClientTemplate().
queryForObject("getWareByID", new Integer(id));
}
}
➜ SqlMapClientTemplate wird von Spring-
Basisklasse verwaltet
18
iBatis Zusammenfassung
➜ Dünne Abstraktionsschicht über JDBC
➜ Direktes Mapping Objekte <-> Tabellen
Σ
➜ Nicht gezeigt: Caching, Lazy Loading usw.
➜ Flexibilität von JDBC ohne die Details
➜ Einige wichtige Features schon vorhanden
Transaktionen
19
Ein paar APIs für Transkationen...
➜ JDBC (Connection)
➜ Persistenz-Frameworks (Hibernate, iBATIS,
JDO..)
➜ JMS (Java Messaging Service)
➜ JCA (Java Connector Architecture)
➜ JTA (Java Transaction API)
➜ ...und alle haben ein anderes Interface
➜ Also: Adapter bauen, der alle Interfaces
vereinheitlicht
PlatformTransactionManager
➜ TransactionDefinition ist z.B.
requires_new, required ...
public interface PlatformTransactionManager {
TransactionStatus
getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status)
throws TransactionException;
}
void rollback(TransactionStatus status)
throws TransactionException;
20
Benutzung des PlatformTransactionManagers
➜ Direkte Benutzung
➜ TransactionTemplate: Code im Template
wird in einem Transaktions-Kontext
ausgeführt
➜ ...oder deklarativ (mit Aspekten hinter den
Kulissen)
– Interceptor fängt Methodenaufrufe ab
– ...und startet / beendet Transaktionen
– Konfiguration über über
TransactionAttributeSource
Methoden in Spring Konfiguration definieren
<bean id="transactionAttributeSource"
class="….NameMatchTransactionAttributeSource">
<property name="nameMap">
<map>
<entry key="getBy*" >
<value>PROPAGATION_REQUIRED</value>
</entry>
<entry key="save" >
<value>PROPAGATION_REQUIRED</value>
</entry>
…
</map>
</property>
</bean>
21
Methoden annotieren
<bean id="transactionAttributeSource"
class="….AnnotationTransactionAttributeSource" />
@Transactional(propagation=Propagation.REQUIRED,
readOnly=false,
rollbackFor=RuntimeException.class,
isolation=Isolation.SERIALIZABLE)
public void bestellenKreditkarteTransactionAnnotation(
final Einkaufswagen einkaufswagen,
final int kreditkartenNummer)
throws BestellungException {
…
}
Transaktionen: Zusammenfassung
Σ
➜ Einheitliche Schnittstelle
➜ TransactionTemplate für automatisches
Handling
➜ Deklarativ über Methodennamen oder
Annotationen
➜ Flexibles Handling von Exceptions
22
Weitere Spring Themen
➜ JMS / JMX / JCA / zeitgesteuerte Aufgaben
➜ Spring MVC Framework
➜ Integration anderer Web Frameworks (JSF, Struts, ...)
➜ Spring Web Flow
➜ Trails statt Ruby on Rails
➜ Acegi Security Framework
➜ Spring IDE
➜ BeanDoc
➜ Spring Rich Client
Wie fängt man an?
➜ http://www.springframework.org
➜ Verschiedene Bücher
– Bald auch meins... 
➜ Java Magazin Artikelserie
– http://www.saxsys.de/kontakt/download_art
ikel_spring.asp
➜ Saxonia bietet Consulting und Training
23
Woran Sie sich erinnern sollten...
➜ Spring ist ein tolles Framework!
➜ iBATIS: Es gibt Leben zwischen Low-Level JDBC
und High-Level O/R Mappern
➜ Wer JDBC ohne JdbcTemplate macht, ist
selbst schuld.
24
Herunterladen