JAXB und Anpassungen des JAXB Bindings

Veröffentlicht in: Java | 0

JAXB – Java Architecture for XML Binding – (http://jaxb.java.net/) wird eingesetzt um ein Mapping zwischen XML und Java-Klassen herzustellen und aus einer XSD-Schema-Datei Java-Klassen zu generieren. Hierfür kann das Programm xjc verwendet werden, dass seit Java 6 Bestandteil vom Java SE JDK ist.

Im nun folgenden Beispiel sollen die Daten eines Benutzers von XML in ein Java-Objekt konvertiert werden (marshalling). Auch der Umgekehrte Weg – also das Umwandeln eines Java-Objektes in XML (unmarshalling) – soll möglich sein. Ein Benutzer hat verschiedene Eigenschaften, wie z.B. Vor- und Nachname, die beim Konvertieren berücksichtigt werden sollen. Außerdem soll ein Benutzer beliebig viele Adress-Informationen besitzen können.

Zunächst wird ein Maven-Projekt erstellt. Die Beschreibung der Benutzer-Eigenschaften wird in der Schema-Datei src/main/resources/user.xsd hinterlegt. Die Definition sieht wie folgt aus:

<xs:schema xmlns:xs=“http://www.w3.org/2001/XMLSchema“
targetNamespace=“http://www.mz.de/user/schema“
xmlns=“http://www.mz.de/user/schema“
elementFormDefault=“qualified“
attributeFormDefault=“unqualified“>

<xs:element name=“user“ type=“UserType“ />

<xs:complexType name=“UserType“>
<xs:sequence>
<xs:element name=“forename“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“lastname“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“title“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“phonenumber“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“mail“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“age“ type=“xs:int“ minOccurs=“0″ />
<xs:element name=“addresses“ type=“AddressType“ minOccurs=“0″ maxOccurs=“unbounded“ />
</xs:sequence>
</xs:complexType>

<xs:complexType name=“AddressType“>
<xs:sequence>
<xs:element name=“street“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“housenumber“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“citycode“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“country“ type=“xs:string“ minOccurs=“0″ />
</xs:sequence>
</xs:complexType>
</xs:schema>

Eine XML-Datei die zu obigem Schema passen würde, sähe folgendermaßen aus:

<user xmlns=“http://www.mz.de/user/schema“>
<forename>Max</forename>
<lastname>Mustermann</lastname>
<addresses>
<street>Musterweg</street>
<citycode>50123</citycode>
</addresses>
<addresses>
<street>Musterstrasse</street>
<citycode>50123</citycode>
</addresses>
</user>

Aus der Schema-Datei lassen sich auch ohne Maven die entsprechenden Java-Klassen generieren. Hierfür führt man im Projekt-Verzeichnis xjc mit folgenden Parametern aus:

xjc -d generated-sources/xjc src/main/resources/user.xsd

Sofern in der Projekt-Struktur die Verzeichnisse generated-sources/xjc existieren, werden hier nun folgende Dateien erzeugt:

  • AddressType
  • ObjectFactory
  • package-info
  • UserType

Die Klassen UserType und AddressType ergeben sich aus der XSD-Beschreibung. Die ObjectFactory wird mit Methoden zum Erstellen der automatisch generierten Klassen versehen und bietet zudem die Möglichkeit alle Elemente, die auf nicht anonyme komplexe Typen verweisen – das name-Attribut muss gesetzt sein – zu JAXBElementen zu transformieren. In diesem Beispiel trifft das also auf das Element UserType zu.

Statt xjc nach jeder Änderung der XSD-Datei manuell auszuführen, kann alternativ Maven zum Bauen des Projektes und zur Generierung der Java-Klassen eingesetzt werden. Hierfür kommt das Maven JAXB2 Plugin (http://java.net/projects/maven-jaxb2-plugin/) zum Einsatz, wofür der build-Eintrag der pom.xml-Datei ergänzt werden muss:

<build>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Wie bei Maven üblich, werden die gebauten Klassen im target-Verzeichnis abgelegt. Die von xjc generierten Klassen sind hier unter generated-sources/xjc zu finden, wobei das Verzeichnis automatisch einen ClassPathEntry erhält und die hier liegenden Klassen somit im Projekt direkt verwendet werden können. Sollte dies in der Eclipse IDE einmal nicht der Fall sein, so reicht es Maven – Update Project Configuration auszuführen.

Nun wird der oben aufgeführte XML-Inhalt in der Datei src/main/resources/user.xml gespeichert, die anschließend unmarshallt werden soll.

JAXBContext jaxbContext = JAXBContext.newInstance(UserType.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
UserType user = (UserType) unmarshaller.unmarshal(getClass().getResourceAsStream(„/user.xml“));

Das Root-Element der XML-Datei ist vom Typ UserType, daher wird zunächst eine JAXBContext-Instanz für dieses Objekt erstellt. Anschließend wird auf dieser Instanz ein Unmarshaller erstellt, der den Inhalt der XML-Datei in Form eines InputStreams unmarshallen soll. Hierbei kommt es jedoch zu einer Exception, deren Ursache im StackTrace nicht direkt ersichtlich ist:

Exception in thread „Main“ javax.xml.bind.UnmarshalException: unexpected element (uri:“http://www.mz.de/user/schema“, local:“user“). Expected elements are (none) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:631)

Ursache für diesen Fehler ist die fehlende Annotation XmlRootElement in der Klasse UserType. Klassen die mit JAXB unmarshallt werden sollen (Root-Elemente), müssen diese Annotation besitzen. Im Fall der Klasse UserType sähe die korrekte Annotation so aus:

@XmlRootElement(name = „UserType“)

Es gibt verschiedene Möglichkeiten, um xjc dazu zu bewegen, die Klasse UserType wie gewünscht zu annotieren.
Eine Möglichkeit ist es, die XSD-Datei anzupassen:

<xs:element name=“UserType“>
<xs:complexType>
<xs:sequence>
<xs:element name=“forename“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“lastname“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“title“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“phonenumber“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“mail“ type=“xs:string“ minOccurs=“0″ />
<xs:element name=“age“ type=“xs:int“ minOccurs=“0″ />
<xs:element name=“addresses“ type=“AddressType“ minOccurs=“0″ maxOccurs=“unbounded“ />
</xs:sequence>
</xs:complexType>
</xs:element>

Hintergrund ist, dass das Element UserType nun einen anonymen complexType beinhaltet. Damit ist gewährleistet, dass der complexType nicht nur nicht in dieser XSD-Datei, sondern auch in keiner anderen referenziert werden kann. Damit ist der complexType zwangsläufig ein Root-Element und wird auch als solches von xjc erkannt und annotiert.

Bevor die obige XML-Datei unmarshallt werden kann, muss für diese Lösung noch das Root-Element von User auf UserType unbenannt werden:

<UserType xmlns=“http://www.mz.de/user/schema“>
<lastname>Mustermann</lastname>

</UserType>

Nun kann die XML-Datei unmarshallt werden und deren Inhalt steht im Objekt UserType zur Verfügung.

Die Lösung hat allerdings einen Nachteil. Da der complexType des UserType-Elementes annonym ist, erhält die ObjectFactory keine Methode zum Transformieren des UserTypes in ein JAXBElement. Sofern das Unmarshallen von XML-Inhalt in Java Objekte ausreicht, kann das vernachlässigt werden. Ist der umgekehrte Weg ebenfalls gewünscht – Java Objekt in XML marshallen – muss eine andere Lösung gefunden werden.

So gibt es die Möglichkeit das Standardverhalten des JAXB Bindings anzupassen, wofür die Datei src/main/resources/jxbBindings.xjb mit folgendem Inhalt erstellt wird:

<jxb:bindings version=“1.0″
xmlns:jxb=“http://java.sun.com/xml/ns/jaxb“
xmlns:xs=“http://www.w3.org/2001/XMLSchema“
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
xmlns:xjc=“http://java.sun.com/xml/ns/jaxb/xjc“
xmlns:annox=“http://annox.dev.java.net“
xsi:schemaLocation=“http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd“
jxb:extensionBindingPrefixes=“xjc“>

<jxb:bindings schemaLocation=“user.xsd“ node=“/xs:schema/xs:complexType[@name = ‚UserType‘]“>
<annox:annotate>
<annox:annotate annox:class=“javax.xml.bind.annotation.XmlRootElement“ name=“user“/>
</annox:annotate>
</jxb:bindings>

</jxb:bindings>

Die schemaLocation des jxb:binding bestimmt für welches Schema Anpassungen vorgenommen werden sollen, in diesem Fall also für die XSD-Datei user.xsd. Das Attribut node definiert mittels XPath, welche XSD-Einträge anzupassen sind. Der gewählte XPath Ausdruck matched auf alle complexType-Einträge, die sich direkt unterhalb des Schemas befinden. Da dies auch den AddressType betreffen würde, muss zusätzlich mit dem name-Attribut gearbeitet werden, welches den Wert UserType besitzt.

Über annox:annotate wird letztendlich bestimmt, was an den zuvor ausgewählten XSD-Einträgen modifiziert werden soll. Die Klasse UserType erhält die Annotation XmlRootElement mit dem name-Attribut user:

@XmlRootElement(name = „user“)

Der Anschließende Maven-Build wird allerdings fehlschlagen, da weitere Anpassungen an der build-Einstellung in der pom.xml-Datei vorzunehmen sind:

<build>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<configuration>
<strict>false</strict>
<extension>true</extension>
<args>
<arg>-Xannotate</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.8.0</version>
</plugin>
<plugin>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics-annotate</artifactId>
<version>0.8.0</version>
</plugin>
</plugins>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

Zunächst muss eine Konfiguration hinzugefügt werden. Damit komplexe Typen als Root-Element behandelt werden, auf die theoretisch von anderen XSD-Dateien verwiesen werden könnte, darf nicht der strict-Mode zum Einsatz kommen. Weiterhin müssen Extensions von Fremd-Anbietern zugelassen werden, da andernfalls die annotate-Anweisung nicht verwendet werden darf. Für diese Library ist außerdem der Argument-Eintrag Xannotate nötigt. Außerdem werden die Plugins jaxb2-basics und jaxb2-basics-annotate und die Dependency activation benötigt, damit der Build-Prozess wie gewünscht durchläuft.

Als Ergebnis ist die Klasse UserType korrekt annotiert und die ObjectFactory bietet nun wieder die Möglichkeit Objekte vom Typ UserType zu JAXBElementen zu transformieren. Damit ist auch die Möglichkeit gegeben, Java-Objekte zu marshallen und so in XML-Inhalt umzuwandeln. Hierzu wird das entsprechende Java-Objekt über die ObjectFactory erstellt:

ObjectFactory factory = new ObjectFactory();
UserType userType = factory.createUserType();
userType.setForename(„Max“);
userType.setLastname(„Mustermann“);

AddressType addressType = factory.createAddressType();
addressType.setCitycode(„50123“);
addressType.setStreet(„Musterweg“);
userType.getAddresses().add(addressType);

addressType = factory.createAddressType();
addressType.setCitycode(„50123“);
addressType.setStreet(„Musterstrasse“);
userType.getAddresses().add(addressType);

Nun kann der UserType in ein JAXBElement umgewandelt werden:

JAXBElement<UserType> jaxbElement = factory.createUser(userType);

Das marshallen ähnelt dem unmarshallen aus obigem Beispiel. Es wird zunächst der JAXBContext für die Klasse UserType und im Anschluss der entsprechende Marshaller erzeugt. Dieser erhält das JAXBElement zum marshallen und einen OutputStream in den der XML-Inhalt geschrieben wird:

JAXBContext jaxbContext = JAXBContext.newInstance(UserType.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(jaxbElement, outputStream);

Benötigt man den XML-Inhalt in einer Datei, so kann dem Marshaller statt dem OutputStream auch ein File übergeben werden.

Weiter Information zur Anpassung des JAXB Bindinings sind auf der Oracle Webseite zu finden:

http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html

REST Services mit Spring 3

Veröffentlicht in: Java, spring | 0

Ging vor ein paar Jahren der Trend bei der Übertragung von Informationen zwischen Softwaresystemen noch in Richtung Web-Services, so geht mittlerweile die Tendenz klar in Richtung REST.

REST selber basiert auf dem HTTP-Protokoll und ist zustandslos, da alle benötigten Informationen Bestandteil des jeweiligen HTTP-Requests sind. Die einzelnen Ressourcen die ein REST-Service zur Verfügung stellt, sind über eindeutige URIs erreichbar. Auf den Ressourcen können unter anderem folgende Operationen ausgeführt werden:

  • GET – Abfrage einer Ressource
  • POST- Zur aufgerufenen Ressource wird eine Sub-Ressource erzeugt
  • PUT – Verändern einer bestehende Ressource
  • DELETE – Löschen einer Ressource

Aufgrund der Entwicklung der vergangenen Jahren und der immer weiteren Verbreitung von REST wurde JAX-RS mit der Version 1.1 in den Java EE 6 Standard aufgenommen. Aktuell wird an der Version 2.0 gearbeitet, welche in den Java EE 7 Standard einfließen soll. Die gängigsten JAX-RS Implementierungen sind:

  • Jersey – Referenzimplementierung von Oracle
  • RESTEasy von JBoss
  • Restlet

Alle diese JAX-RS Implementierungen unterstützen Spring, was auch der Grund dafür ist, dass Spring selber keine eigene JAX-RS Implementierung anbietet. Man ist daher also auf eines der oben genannten Frameworks angewiesen.

Da ich mich bereits mit Restlet beschäftigt habe (siehe http://martinzimmermann1979.wordpress.com/2011/11/01/restlet-rest-im-osgi-kontext/), habe ich mich im folgenden Beispiel für dieses REST-Framework entschieden. Restlet kann unter http://www.restlet.org/ in der aktuellen Version 2.0.11 heruntergeladen werden.

Natürlich gibt es auch die Möglichkeit Restlet als Maven Dependency zu laden. Hier kann man zusätzlich die Restlet-Spring-Extension verwenden, die Spring in der Version 3.0.1 verwendet. Will man eine neuere Spring Version einbinden – aktuell ist Spring 3.1.0 verfügbar – so muss man dies explizit in der pom.xml definieren.

Hier die Maven Dependencys für Restlet:

<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet</artifactId>
<version>2.0.11</version>
</dependency>
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet.ext.spring</artifactId>
<version>2.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm-tiger</artifactId>
<version>1.5.9</version>
</dependency>

Die Library spring-oxm-tiger beinhaltet den Jaxb2Marshaller, der zum marshalling bzw. unmarshalling von Java Objekten in XML und umgekehrt benötigt wird.

Für die Ergebnis-Darstellung wird Spring MVC verwendet. Unter Spring 3.x wurden weitere Views, wie die MarshallingView zur Darstellung von XML-Inhalten und die MappingJacksonJsonView für JSON-Inhalte, hinzugefügt.

Im folgenden Beispiel soll ein REST-Service umgesetzt werden, der die bekannten GET, POST, PUT und DELETE Operationen auf der Ressource MyBean zur Verfügung stellt. Im ersten Schritt findet der Datentransfer ausschließlich mittels XML statt, im zweiten Schritt soll zusätzlich JSON unterstützt werden. D.h. der Service kann XML und JSON verarbeiten und abhängig vom unterstützten Datenformat des Clients findet die Antwort des Servers in XML oder JSON statt.

Starten wir mit der Spring-Konfiguration. Zunächst muss die MVC Java Konfiguration aktiviert werden:

<mvc:annotation-driven />

Weiterhin wird das Component-Scanning auf Package-Ebene aktiviert. Hiermit erspart man sich, jede Bean einzeln in der Konfigurationsdatei anzugeben:

<context:component-scan base-package=“de.mz.rest“ />

Nun wird das RequestMapping auf Type- und Methoden-Ebene aktiviert:

<bean class=“org.spring…DefaultAnnotationHandlerMapping“/>
<bean class=“org.spring…AnnotationMethodHandlerAdapter“ />

Anschließend wird der Jaxb2Marshaller definiert, der fürs marshalling bzw. unmarshalling von Objekten in XML und umgekehrt zuständig ist. Unter classesToBeBound sind alle Objekte aufgelistet, die der Marshaller in irgendeiner Form umwandeln soll:

<bean id=“jaxbMarshaller“ class=“org.springframework.oxm.jaxb.Jaxb2Marshaller“>
<property name=“classesToBeBound“>
<list>
<value>de.mz.rest.data.MyBean</value>
</list>
</property>
</bean>

Für die Darstellung der vom Jaxb2Marshaller aufbereiteten XML-Inhalte ist die MarshallingView zuständig:

<bean id=“viewName“ class=“org.springframework.web.servlet.view.xml.MarshallingView“>
<constructor-arg ref=“jaxbMarshaller“ />
</bean>

Zu guter Letzt wird noch ein ViewResolver benötigt, der die Requests auf die entsprechenden Controller weiterleitet. Hier wird der BeanNameViewResolver verwendet. Über das RequestMapping im Controller selber, wird  das Mapping später aber noch mal explizit gesetzt:

<bean  class=“org.spring…BeanNameViewResolver“ id=“viewResolver“ />

Nun wird die Klasse de.mz.rest.data.MyBean erstellt, bei der es sich um die Bean handelt, die vom Jaxb2Marshaller in XML umgewandelt werden soll. Hierfür werden JAXB (Java Architecture for XML Binding) Annotationen verwendet:

@XmlRootElement
@XmlType
public class MyBean {
private String id;
private String message;
// getter and setter for id and message
}

Die Annotation @XmlRootElement definiert MyBean als ein Top Level Element. @XmlType hingegen kann dazu verwendet werden, um für die Attribute von MyBean eine bestimmte Reihenfolge zu definieren. Eigentlich ist diese Annotation laut JAXB-Spezifikation nicht zwingend nötig, allerdings benötigt der Jaxb2Marshaller diese Information. Andernfalls erachtet sich der Jaxb2Marshaller für die Klasse MyBean als nicht zuständig und beim Versuch diese Klasse in XML zu transformieren wird folgende, irreführende Fehlermeldung geworfen:

javax.servlet.ServletException: Unable to locate object to be marshalled in model

Wenden wir uns nun der REST-Ressource zu, die die gewohnte Spring MVC Annotation @Controller verwendet:

@Controller
@RequestMapping(„/myPath“)
public class MyResource extends ServerResource {

@RequestMapping(value=“/{id}“, method=RequestMethod.GET)
public ModelAndView get(@PathVariable String id)  {
return new ModelAndView(„viewName“, „object“, new MyBean());
}
}

Wie man in obigem Beispiel sieht, kommt die @RequestMapping Annotation zweimal zum Einsatz. Zum einen wird über die annotierte Klasse MyResource der Pfad definiert, unter dem die komplette REST-Ressource erreichbar ist, zum anderen werden die Methoden-Details definiert, wie Übergabe-Parameter und die jeweiligen Operationen (GET, POST, …) für die die Methode zuständig ist. Um innerhalb der Methode Zugriff auf die id aus dem Pfad zu erlangen, wird diese einfach als Übergabe-Parameter angegeben und mit der Annotation @PathVariable versehen. Nun muss dem ModelAndView-Element noch der ViewName der MarshallingView und die gewünschte MyBean übergeben werden. Die obige REST-Ressource ist über einen GET-Request auf die URI http://<server>:<port>/<webapp>/myPath/<id> aufrufbar.

Die letzten Einstellungen die noch fehlen, sind der ContextLoaderListener und das DispatcherServlet in der web.xml Datei:

<listener>
<listener-class>org.spring…ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>servletName</servlet-name>
<servlet-class>org.spring…DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

Bislang liefert die REST-Ressource nur ein einziges MyBean-Element zurück, welches über die id spezifiziert wird. Will man die vollständige Liste an MyBeans erhalten, wird ein Wrapper für eine Liste von MyBeans benötigt. Auch dieser Wrapper, die MyBeanList, ist wie gehabt mit dem JAXB @XmlRootElement zu versehen:

@XmlRootElement
@XmlType
public class MyBeanList {

private List<MyBean> myBeans;

public List<MyBean> getMyBeans() {
return myBeans;
}
public void setMyBeans(List<MyBean> myBean) {
this.myBean = myBean;
}
}

Anschließend kann die MyBean-Ressource um folgende Methode ergänzt werden:

@RequestMapping(method = RequestMethod.GET)
public ModelAndView getMyBeans() {
List<MyBean> myBeans = new ArrayList<MyBean>();
// add MyBeans to list
MyBeanList beanList = new MyBeanList();
beanList.setMyBeans(myBeans);
return new ModelAndView(„viewName“, „object“, beanList);
}

Es ist zu beachten, dass auch die MyBeanList in der Spring Konfiguration des Jaxb2Marshaller unter classesToBeBound hinzugefügt werden muss.

Diese Ressource ist mittels eines GET-Requests unter der URI http://<server>:<port>/<webapp>/myPath erreichbar.

Die GET-Requests aus den vorangegangenen Beispielen sind recht simpel zu testen, da diese Ressourcen über einen Web-Browser aufrufbar sind. Die vom Jaxb2Marshaller serialisierten Daten werden im Browser als XML dargestellt. Zusätzlich sollen aber noch die Operationen POST, PUT und DELETE umgesetzt werden. Der POST und PUT Request beinhaltet Daten, die von der REST-Ressource deserialisiert werden müssen, um das Objekt MyBean zu erhalten. Hier nun die Umsetzung der POST-Schnittstelle:

@RequestMapping(method=RequestMethod.POST)
public ModelAndView addBean(@RequestBody MyBean bean) {
// add bean
return new ModelAndView(„viewName“, „object“, bean);
}

Die Methode des RequestMapping wird als POST-Operation definiert. Die Daten selber werden als Übergabeparameter in die Methoden-Signatur aufgenommen und müssen lediglich mit der Annotation @RequestBody versehen werden. Den Rest übernimmt der Jaxb2Marshaller.

Die Ressource ist mit einem POST-Request unter der URI http://<server>:<port>/webapp/myPath erreichbar.

Zum Testen der Ressource kann das Kommandozeilen-Tool curl http://curl.haxx.se/ verwendet werden. Hier der Befehl inklusive Parameter, mit denen sich die Ressource Testen lässt:

curl -v -H „Accept: application/xml“ -H „Content-type: application/xml“ -X POST -d ‚<myBean><id>1</id><message>some text</message></myBean>‘  http://<server>:<port>/<webapp>/myPath

Hier die Erläuterung zu den einzelnen Parametern:

  • H <header>: Hiermit wird dem Request eine Header-Information hinzugefügt.
    1. Die Antwort des Servers soll XML sein, da dies vom Client akzeptiert wird.
    2. Die vom Client gesendeten Daten bestehen aus XML.
  • X <command>: Der Request wird ausgewählt, z.B. GET, POST, PUT oder DELETE
  • d <data>: Hier wird der eigentliche Inhalt des Requests definiert.
  • v: Steht für verbose. Wird verwendet um mehr Informationen zu erhalten, kann ansonsten aber auch weggelassen werden.

Wenn man lieber mit einem Grafischen Tool arbeiten möchte, kann man statt curl das Firefox Plugin REST Client verwenden.

Erweitern wir nun die REST-Ressource um eine PUT-Methode, damit eine bestehende MyBean verändert werden kann. Hierbei wird zum einen die id benötigt, die im Pfad angegeben wird und als @PathVariable in der Methoden-Signatur annotiert ist. Zum anderen wird – wie beim POST-Request – die mit @RequestBody annotierte MyBean übergeben, die die aktualisierten Daten beinhaltet:

@RequestMapping(value = „/{id}“, method=RequestMethod.PUT)
public ModelAndView updateMyBean(@PathVariable String id, @RequestBody MyBean bean) {
// update bean
return new ModelAndView(„viewName“, „object“, bean);
}

Aufrufbar ist diese Ressource unter einem PUT-Request auf http://<server>:<port>/webapp/myPath/<id>.

Für die DELETE-Operation wird nur die id der zu löschenden MyBean im Pfad übergeben. Die REST-Ressource gibt dann eine Liste der verbleibenden MyBeans zurück, die wie gehabt in dem Wrapper MyBeanList enthalten sind.

@RequestMapping(value = „/{id}“, method=RequestMethod.DELETE)
public ModelAndView removeMyBean(@PathVariable String id) {
// delete mybean and return list of remaining beans
return new ModelAndView(„todos“, „object“, remainingBeans);
}

Die Ressource wird mittels eines DELETE-Requests auf http://<server>:<port>/webapp/myPath/<id> aufgerufen.

Im letzten Teil des Artikels soll der REST-Service erweitert werden, dass er nicht nur XML sondern auch JSON unterstützt. Hierfür bietet Spring den ContentNegotiatingViewResolver an, der für die korrekte Darstellung das View Konzept verwendet. Abhängig vom gewünschten Inhalt - diese gibt der Client im Accept-Header des Requests mit an (z.B.: Accept: application/json) - wird die entsprechende View angesprochen.

Für die JSON Unterstützung wird das Jackson-Framework http://xircles.codehaus.org/projects/jackson benötigt, welches zur Zeit in der Version 1.9.5 zu downloaden ist.

Das Jackson Framework kann natürlich auch als Maven Dependency verwendet werden:

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.5</version>
</dependency>

Nun muss der ContentNegotiatingViewResolver konfiguriert werden:

<bean class=“org.spring…ContentNegotiatingViewResolver“>
<property name=“mediaTypes“>
<map>
<entry key=“json“ value=“application/json“/>
<entry key=“xml“ value=“application/xml“/>
</map>
</property>
<property name=“viewResolvers“>
<list>
<ref bean=“viewResolver“ />
</list>
</property>
<property name=“defaultViews“>
<list>
<ref bean=“viewName“/>
<bean class=“org.spring…MappingJacksonJsonView“>
<property name=“prefixJson“ value=“true“/>
</bean>
</list>
</property>
</bean>

Zunächst werden die beiden MediaTypes definiert, die unterstützt werden sollen, also XML und JSON. Als ViewResolver kann der bereits definierte BeanNameViewResolver verwendet werden. Ansonsten müssen jetzt noch die Views definiert werden. Da weiterhin XML unterstützt werden soll, erhält die Property defaultViews die bereits definierte MarshallingView übergeben. Außerdem wird hier noch die MappingJacksonJsonView definiert.

Die JSON-Unterstützung der REST-Ressource kann ebenfalls mit curl überprüft werden, indem die Header-Informationen und die Daten angepasst werden:

curl -v -H „Accept: application/json“ -H „Content-type: application/json“ -X POST -d ‚{„id“:“1″,“message“:“Some Text“}‘  http://<server>:<port>/<webapp>/myPath

Insgesamt lässt sich sagen, dass man mit Spring 3.x recht komfortable REST-Services erstellen kann. Die dafür benötigte Konfigurations-Arbeit hält sich in Grenzen und mit der Kombination aus Spring MVC- und JAXB-Annotations kommt man schnell zum Ziel. Lediglich die Dokumentation ist mangelhaft und man muss sich die Informationen recht mühsam zusammen suchen. Auch das die vom Jaxb2Marshaller zu transformierenden Klassen zwangsläufig die @XmlType Annotation benötigen, ist nur schwer nachzuvollziehen.

Testgetriebene Entwicklung mit JUnit, EasyMock und Spring

Veröffentlicht in: Java, test-driven development | 0

Die testgetriebene Entwicklung – test-driven development (TDD) – wird häufig in der agilen Softwareentwicklung eingesetzt. Hierbei werden zunächst die Testfälle konzeptioniert und erst anschließend die gewünschte Funktionalität umgesetzt. Aufgrund fehlender Umsetzung werden daher die Testfälle zunächst zwangsläufig fehlschlagen. Erst mit sukzessiver Funktions-Implementierung werden die Testfälle nach und nach erfolgreich durchlaufen.

Am einfachsten zu Testen sind Methoden, die für unterschiedliche Übergabe-Parameter entsprechende Rückgabe-Werte liefern wie z.B. statische Utility-Methoden. Um diese Methoden zu testen wird JUnit verwendet, welches unter http://www.junit.org/ in der aktuellen Version 4.10 zum Download bereit liegt. Alternativ kann man sich JUnit auch als Maven Dependency laden:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>

Das Testen einer Utility-Methode mit JUnit sieht dann folgendermaßen aus:

assertTrue(MyUtility.returnTrue(„parameter“));

Sofern die zu testende Methode Aufgaben an weitere Klassen delegiert, ist es nicht mehr möglich ausschließlich JUnit Tests zu verwenden, ohne eine vollständige Anwendungs-Umgebung zu initialisieren. Stattdessen müssen zusätzlich alle verwendeten Klassen mit initialisiert und getestet werden.

Hierbei handelt es sich dann nicht mehr um Modultests, sondern um Integrationstests, die definitiv wichtig sind und ihre Berechtigung haben, bei der testgetriebenen Entwicklung aber nicht im Vordergrund stehen. Hier soll nur das zu entwickelnde Modul getestet werden.

Daher werden bei der testgetriebenen Entwicklung Mocking-Frameworks verwendet, damit alle Klassen die in der zu testenden Methode verwendet werden, durch sogenannte Mock-Objekte ersetzt werden können. Diese Mocks bieten die Möglichkeit zu testen, ob die erwarteten Methode-Aufrufe mit den definierten Übergabe-Parametern erfolgen. Findet der Aufruf nicht statt oder ein nicht erwarteter Aufruf, so schlägt der Testfall fehl. Die Funktionalität der gemockten Objekte wird also nicht mit getestet, sondern kann in separaten Testfällen geprüft werden.

Als Mocking-Framework wird im folgenden Beispiel EasyMock verwendet, welches unter http://easymock.org/ in der aktuellen Version 3.1 verfügbar ist. Auch EasyMock ist als Maven Dependency erhältlich:

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>

Die erste Library wird zum Mocken von Interfaces benötigt, was als best practice anzusehen ist. Ist es dennoch mal nötig eine Klasse zu mocken, weil man z.B. bestehenden Code nachträglich testen möchte, kann die EasyMock-Classextension verwendet werden.

Kommen wir nun zu einem kleinen Beispiel. Es wird ein UserService getestet der einen User erhält und diesen mittels eines UserDAOs speichern soll. Hierbei wird der UserDAO gemockt und es soll überprüft werden, ob die save-Methode des DAOs auch wirklich aufgerufen wird.

Auch wenn bei der testgetriebenen Entwicklung zuerst der Testfall erstellt werden sollte, folgt zum besseren Verständnis zunächst der UserService und erst anschließend die Testfälle:

public class UserService {

private UserDAO userDAO;

public User save(User user) {
return userDAO.save(user);
}

public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}

Zuallererst wird ein Setup initialisiert, in welchem der DAO gemockt und die erwarteten Methoden-Aufrufe definiert werden. Die Annotation @Before besagt hierbei, dass das Setup vor der Ausführung jedes einzelnen Testfalls in dieser Test-Klasse ausgeführt werden soll.

@Before
public void setUp() {
name = „userName“;
user = new User(name);
userDAO = createMock(UserDAO.class);
expect(userDAO.save(user)).andReturn(user); // (1)
replay(userDAO);

userService = new UserService();
userService.setUserDAO(userDAO);
}

Wie bereits erwähnt wird der UserDAO gemockt und die Erwartung ausgesprochen, dass die save-Methode mit dem gegebenen User aufgerufen wird. Bei der expect-Methode handelt es sich um eine statische Methode von EasyMock, die verwendet wird, wenn der erwartete Methode-Aufruf einen Return-Value besitzt. Über die andReturn-Methode kann anschließend der Rückgabe-Wert definiert werden, den das Mock-Objekt zurückgeben soll.

Hätte die save-Methode keinen Rückgabe-Wert, sähe die Stelle (1) folgendermaßen aus:

userDAO.save(user);

Der expect-Aufruf würde also einfach weggelassen. Der replay-Aufruf in der darauf folgenden Zeile besagt, dass keine weiteren Aufrufe auf dem UserDAO-Mock erwartet werden und nun nur noch die Aufrufe folgen, die bei Test-Ausführung getätigt werden.

Nun muss der UserService noch den UserDAO-Mock gesetzt bekommen und wir können zum eigentlich Test kommen. Hierbei sollte man sich nicht wundern, das Setup ist meist deutlich länger als der eigentliche Test.

@Test
public void shouldSaveUser() {
User save = userService.save(user);
verify(userDAO);
assertNotNull(save);
assertEquals(„userName“, save.getName());
}

Hier wird die save-Methode des UserService aufgerufen und anschließend die verify-Methode von EasyMock aufgerufen. Damit wird überprüft ob die zuvor definierten Mock-Aufrufe auch wirklich getätigt werden oder nicht. Anschließend kann der Rückgabe-Wert wie gehabt mit JUnit überprüft werden.

Um EasyMock etwas genauer zu verstehen modifizieren wir nun die Zeile (1) der Setup-Methode:

expect(userDAO.save(new User(„userName“))).andReturn(user);

In der Setup-Methode wird dem UserDAO-Mock also ein anderer User übergeben, als dem UserService im Test, lediglich der Namen ist gleich. Da nur der Name auf Gleichheit überprüft wird, läuft der Testfall wie gehabt durch. Fügen wir nun dem Testfall noch folgende Zeile hinzu:

assertEquals(save, user);

Hierauf hin wird der Testfall fehlschlagen. Grund dafür ist, dass EasyMock die equals-Methode der User-Klasse überprüft, um zu Testen ob der Übergabe-Parameter gleich ist. Sofern die equals-Methode nicht überschrieben wurde, wird die equals-Methode von Object verwendet, die auf gleiche Referenz testet. Da hier nun zwei unterschiedliche Objekte mit lediglich den gleichen Property-Werten verwendet werden, schlägt der Testfall fehl. Sobald allerdings die equals-Methode von der User-Klasse überschrieben wird und hierbei auf Namens-Gleichheit getestet wird, ist der Test-Durchlauf wieder erfolgreich.

Dies Verhalten ist auch der Grund, warum ich auf einen weiteren wichtigen Bestandteil von EasyMock kommen möchte, das Capture.

Stellen wir uns vor, der UserService reicht das User-Objekt nicht direkt zum UserDAO durch, sondern erstellt aufgrund des Users ein UserEntity, welches vom UserDAO konsumiert wird. Die Properties des Users sind hierbei identisch mit denen des UserEntities. Die Implementierung sieht also so aus:

public User save(User user) {
UserEntity userEntity = new UserEntity(user.getName());
UserEntity entity = userDAO.save(userEntity);
User result = new User(entity.getName());
return result;
}

Sofern das UserEntity viele Properties beinhaltet, müßte für den Testfall also ein exakt gleiches UserEntity erstellt werden, damit das UserEntity im Testfall und das im UserService erzeugte von EasyMock als gleich anerkannt wird. Sofern die equals-Methode nicht überschrieben werden dürfte, würde der Testfall sogar immer fehlschlagen, da es sich zwangsläufig um zwei unterschiedliche Referenzen handeln würde. Aber auch so ein Fall lässt sich problemlos mit EasyMock abbilden.

Hierzu muss lediglich der übergebene User durch ein Capture ersetzt werden:

captureUser = new Capture<UserEntity>();
userDAO = createMock(UserDAO.class);
expect(userDAO.save(capture(captureUser))).andReturn(userEntity);
replay(userDAO);

Es wird ein Capture Objekt erzeugt, welches nicht direkt in die save-Methode des UserDAOs gegeben wird. Stattdessen wird hier die EasyMock-Methode capture aufgerufen. Nun kann im Test über das Capture auf das dem Mock übergebene Objekt zugegriffen und die Werte überprüft werden:

assertNotNull(captureUser.getValue());
UserEntity userEntity = captureUser.getValue();
assertEquals(„userName“, userEntity.getName());

Mit diesem Funktionsumfang von EasyMock lassen sich bereits die meisten Test-Szenarien abdecken. Ab und an wird man in spezial Fällen Lösungen in der EasyMock-Dokumentation suchen müssen, für die meisten Anwendungsfälle dürfte es aber genügen.

Nun noch ein kleiner Exkurs über die Modultest hinaus zu den Integrationstests. Sofern man Spring verwendet kann es sehr nützlich sein, für Testfälle einen Spring-Context hochzuziehen. Folgende Spring-Library ermöglicht dies:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.0.6.RELEASE</version>
<scope>test</scope>
</dependency>

Nun legt man den zu testenden Service an und definiert diesen in der Spring-XML Konfiguration, welche sich im Classpath befinden sollte:

<version=“1.0″ encoding=“UTF-8″?>
<beans>
<context:annotation-config/>
<bean id=“myService“ class=“MyService/>
</beans>

Nun kann man die XML-Konfiguration über die Annotation ContextConfiguration angeben und muss anschließend die Runner Klasse definieren. Da in der Spring-Konfiguration Annotations aktiviert wurden, kann nun der Service direkt in den Testfall per Autowire-Annotation injected werden:

@ContextConfiguration(locations = { „/test.xml“ })
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringContextTest {

@Autowired
private MyService myService;

@Test
public void shouldExecuteMyService() {
assertNotNull(myService);
assertTrue(myService.doWhatYouHaveTodo());
}
}

Über die testgetriebene Entwicklung wird immer wieder gesagt, dass sie zu langsam und damit zu teuer ist. Dies konnte ich bislang nicht feststellen. Sicherlich ist es zunächst mehr Aufwand testgetrieben zu Entwickeln, aber die Einarbeitung in jedes neue Framework kostet zunächst einmal Zeit und bringt es mit sich, sich bei neuen Gegebenheiten erneut mit der API und deren Dokumentation zu beschäftigen. Der Zeitaufwand der beim Erstellen der Testfälle selber benötigt wird, ist überschaubar und wird durch die direkt bemerkten Flüchtigkeitsfehler wieder wett gemacht. Es muss nicht umständlich ein Server gestartet werden, nur um dann doch wieder ein Fehlverhalten festzustellen. Es ist grundsätzlich schneller einen Testfall auszuführen, als bei einer Software manuell einen ganz bestimmten Zustand zu testen.

Verwendung einer SQLite Datenbank in einer Android App

Veröffentlicht in: Android, Java | 0

Will man in einer Android Anwendung Daten speichern, kann hierzu bequem die in das Android SDK integrierte SQLite Datenbank verwendet werden. Diese ist besonders für den mobilen Anwendungsbereich konzipiert, da sie mit gerade mal 250 KByte extrem genügsam im Bezug auf Speicherplatz ist.

Im folgenden Beispiel sollen die Einstellungen einer Anwendung in der Datenbank gespeichert werden. Hierzu wird eine SettingsActivity erstellt – nicht vergessen, die Activity im Android Manifest zu definieren –  wobei das Layout in einer XML-Datei definiert ist. Als variables Feld erhält die Activity einen Benutzer-Namen, welcher vom Benutzer editiert werden kann. Außerdem gibt es den obligatorischen Speichern-Button, damit die Änderungen vom Benutzer auch gespeichert werden können:

<EditText
android:id=“@+id/name“
android:layout_width=“fill_parent“
android:layout_height=“wrap_content“  android:background=“@android:drawable/editbox_background“
android:text=““
android:padding=“3dip“ />

<Button
android:layout_column=“1″
android:id=“@+id/save“
android:layout_width=“wrap_content“
android:layout_height=“wrap_content“
android:layout_alignParentRight=“true“
android:layout_marginLeft=“10dip“
android:text=“Speichern“ />

Um in den Activities der Anwendung direkten Zugriff auf die UI-Komponenten zu haben, wird das Dependency Injection Framework roboguice verwendet, welches zur Zeit in Version 1.2.2 unter folgendem Link zum Download bereit liegt: http://code.google.com/p/roboguice/wiki/Downloads

Zusätzlich zu roboguice wird noch die Google Library guice benötigt, die man unter folgendem Link erhält: http://code.google.com/p/google-guice/downloads/list

Hierbei ist zu beachten, dass nicht die aktuelle Version 3.0, sondern guice-2.0-no_aop.jar benötigt wird. Beide Libraries werden dem Classpath hinzugefügt. Nun wird im Android Manifest unter Application der Name roboguice.application.RoboApplication gesetzt. Dies ist nötig, da die Activities nun nicht mehr von Activity oder ListActivity direkt erben, sondern von RoboActivity bzw. RoboListActivity.

In der folgenden SettingsActivity wird über die Annotation @InjectView per Dependency Injection auf die im XML definierten UI-Elemente per Id zugegriffen:

public class SettingActivity extends RoboActivity {

private DatabaseManager databaseManager;
@InjectView(R.id.name)
private EditText nameText;
@InjectView(R.id.save)
private Button saveButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.setting);
databaseManager = new DatabaseManager();
final Settings settings = databaseManager.fetchSetting();
nameText.setText(settings.getName());
saveButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View view) {
settings.setName(nameText.getText().toString());
if (settings.getId() == null) {
databaseManager.createSettings(settings);
} else {
databaseManager.updateSettings(settings);
}
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
databaseManager.close();
}
}

In der onCreate-Methode wird ein DatabaseManager erzeugt, der sich um die Datenbank Connection kümmert, aber hierzu später noch mehr. Ebenfalls über den DatabaseManager werden die Settings bezogen, die in der UI dargestellt werden. Hierzu wird dem nameText, der Name aus den Settings zugewiesen. Nun wird noch dem Save-Button ein onClickListener hinzugefügt, der die Änderungen der UI dem Settings Objekt übergibt und dieses anschließend in der Datenbank speichert.

Wichtig ist, dass in der onDestroy Methode der Activity, die Datenbank-Connection wieder geschlossen wird – verlässt der Benutzer also die Einstellungen wird automatisch die Datenbank Verbindung zurück gesetzt.

Nun zur eigentlichen Interaktion mit der Datenbank über den DatabaseManager:

public class DatabaseManager {

private static final String DB_FILE = „/data/data/myapp.db“;
private SQLiteDatabase database;

public DatabaseManager() {
File file = new File(DB_FILE);
if (!file.exists()) {
database = SQLiteDatabase.openOrCreateDatabase(file, null);
database.execSQL(„create table Settings (id integer primary key autoincrement not null, name text);“);
} else {
database = SQLiteDatabase.openOrCreateDatabase(file, null);
}
}

public void close() {
database.close();
}

public Settings createSettings(Settings settings) {
ContentValues initialValues = createContentValues(settings);
long id = database.insert(„Settings“, null, initialValues);
settings.setId((int) id);
return settings;
}

public long updateSettings(Settings settings) {
ContentValues updateValues = createContentValues(settings);
return database.update(„Settings“, updateValues, „id=“ + settings.getId(), null);
}

public Settings fetchSetting() {
try {
Cursor cursor = database.query(„Settings“,
new String[] {
„id“, „name“
}, null, null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
Integer id = cursor.getInt(cursor.getColumnIndexOrThrow(„id“));
String name = cursor.getString(cursor.getColumnIndexOrThrow(„name“);
Settings settings = new Settings(id, name);
return settings;
} else {
return new Settings();
}
} catch (SQLiteException e) {
return new Settings();
}
}

private ContentValues createContentValues(Settings settings) {
ContentValues values = new ContentValues();
values.put(„name“, settings.getName());
return values;
}
}

Im Konstruktor, der in der onCreate-Methode der SettingsActivity aufgerufen wurde, wird zunächst überprüft, ob die Datenbank-Datei bereits angelegt wurde. Ist dies nicht der Fall, werden die Settings der Anwendung Initial gespeichert und zusätzlich zum Öffnen der Datenbank muss die Settings Tabelle mittels SQL-Statement generiert werden.

Der nächste Aufruf der SettingsActivity lädt die bereits gespeicherten Settings über die fetchSettings-Methode. Hier wird auf der Datenbank eine Query gefeuert, der die gewünschten Properties mitgegeben werden und die sonst keinerlei Einschränkung enthält, es werden also wirklich alle Settings geladen. Das Ergebnis ist über einen Cursor zugreifbar, der zunächst über die moveToFirst-Methode auf die erste Position gesetzt werden muss. Die über den Cursor zugreifbaren Properties werden in einem Settings Objekt gespeichert, bzw. falls die Datenbank noch keinen Eintrag enthält wird ein neues Settings Objekt erzeugt und der SettingsActivity geliefert.

Werden die Einstellungen vom Benutzer geändert und der Speicher-Button betätigt, wird das Settings-Objekt entweder Initial in der Datenbank gespeichert oder aktualisiert. Dies hängt davon ab, ob die Settings bereits eine eindeutige Id enthalten. Bei dem Update-Statement wird eine Where Clause benötigt, die eine Einschränkung auf die gegebene Id – also den Primär Schlüssel – enthält. Andernfalls wird das Objekt neu erzeugt und die von der Datenbank vergebene Id in dem Settings-Objekt gespeichert.

Beim Insert bzw. Update wird nicht das Settings-Objekt selber verwendet sondern das generische Objekt ContentValues, dem per put-Befehl die einzelnen Properties zugewiesen werden können.

Sobald man nun noch die Permissions im Android-Manifest richtig gesetzt hat, steht dem ersten Probelauf jetzt nichts mehr im Wege.

<uses-permission android:name= „android.permission.WRITE_EXTERNAL_STORAGE“/>
<permission-group android:name=“android.permission-group.STORAGE“/>

Insgesamt lässt sich sagen, dass der Zugriff auf die SQLite Datenbank bei Android Anwendungen recht komfortable gestaltet ist, gerade weil hier alle benötigten Libraries bereits gegeben sind. Nur Schade, dass man nicht direkt mit Entities arbeiten kann, sondern dass diese auf die Datenbank Inhalte gemappt werden müssen. Es wäre angenehmer auf die Cursor zu verzichten und besser direkt über die Entity an die gewünschten Properties zu gelangen – ebenso wie beim Mappen der Properties auf die ContentValues ein zusätzlicher Schritt benötigt wird.