Hibernate 4.x mit Spring 3.x

Veröffentlicht in: Hibernate, Java, spring | 0

Der nachfolgende Artikel beschreibt die Verwendung des ORM – Object-Relational Mapping – Frameworks Hibernate 4 inklusive der Hibernate Integration von Spring 3, wobei letztere auch die Vorgängerversion Hibernate 3 unterstützt. Als Persistenz-Schicht wird die In-Memory Datenbank HSQLDB verwendet. Es entfällt also die Installation einer Datenbank, da für den Einsatz der HSQLDB lediglich die entsprechende Library eingebunden werden muss.

In diesem Beispiel sollen Benutzer- und Adressdaten persistiert werden, wobei einem Benutzer-Eintrag beliebig viele Adress-Informationen zugewiesen werden können. Es gibt also ein 1..n Mapping zwischen Benutzer und Adressen. Weiterhin wird ein DAO – Data Access Object – implementiert, welches die CRUD – Create/Read/Update/Delete – Datenbank Operationen für die Haupt-Entität (Benutzer) zur Verfügung stellt.

Im ersten Schritt wird ein Maven-Projekt erstellt, in dessen pom.xml die folgenden Dependencys ergänzt werden:

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.7.Final</version>
</dependency>

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>

<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>

<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>

Zuerst werden die Entitäten erstellt, welche die Tabellen der relationalen Datenbank als Java-Klassen repräsentieren. Da der Benutzer Kenntnis über die zu ihm gehörenden Adressen benötigt, umgekehrt aber keine Verknüpfung notwendig ist, kennt die Adress-Entität lediglich sich selber. Ein guter Grund mit dieser zu beginnen:

@Entity
public class Address {

@Id
@GeneratedValue
private Long id;
private String street;
private String city;

// getter setter methods

}

Die Klasse Address wird mit der Annotation Entity versehen, um sie als solche zu kennzeichnen. Weiterhin erhält sie neben den benötigten Eigenschaften (Straße, Stadt, …) einen Identifier. Die entsprechende Annotation definiert diesen als Primär-Schlüssel, während GeneratedValue dafür sorgt, dass sein Wert automatisch von der Datenbank vergeben werden soll.

Nun kann auch die Klasse User mit Zugriff auf die Adress-Datensätze erzeugt werden:

@Entity
public class User {

@Id
@GeneratedValue
private Long id;
private String forname;
private String lastname;

@OneToMany
@Cascade(value=CascadeType.ALL)
private List<Address> addresses;

// getter setter methods

}

Da ein User eine beliebige Anzahl an Adressen erhalten kann, wird hier als Mapping die OneToMany-Annotation verwendet. Weiterhin wird der CascadeType ALL gewählt, der unter anderem die CascadeTypen SAVE_UPDATE und DELETE beinhaltet. Hiermit wird erreicht, dass beim Speichern des Users gleichzeitig seine Adress-Daten persistiert werden. Wäre der CascadeTyp SAVE_UPDATE nicht gewählt, müssten Adress- und Benutzerdaten separat gespeichert werden, wobei ein Benutzer nur auf Adressen verweisen darf, die bereits in der Datenbank existieren. Der CascadeType DELETE sorgt dafür, dass beim Löschen eines Benutzers auch seine Adressen aus der Datenbank entfernt werden.

Wie man an obigem Beispiel erkennt, gibt es Attribute die sich in den Entitäten wiederholen. In diesem Beispiel ist es nur der Identifier, der sowohl beim User als auch bei der Adresse vorkommt. Aber auch das Erstellungs- und letztes Änderungsdatum einer Entität wären Kandidaten, die zu Code-Wiederholung führen könnten. Doppelte Attribute können in eine gemeinsame Oberklasse ausgelagert werden, sofern die Oberklasse mit MappedSuperclass annotiert wird:

@MappedSuperclass
public abstract class AbstractEntity {

@Id
@GeneratedValue
private Long id;

// getter setter methods
}

Nachdem die Entitäten erstellt wurden, soll nun der UserDAO erzeugt werden, der sukzessive um die CRUD-Operationen erweitert wird. Hierfür benötigt er lediglich die SessionFactory. Wer bislang das HibernateTemplate verwendete, wird feststellen, dass dies unter Hibernate 4 nicht mehr unterstützt wird. Das ist nicht weiter verwunderlich, da dessen Verwendung bereits seit Hibernate 3.0.1 obsolet ist. Stattdessen kann bei gültigem Transaktions-Management über die SessionFactory auf die aktuelle Session zugegriffen und auf dieser Datenbank-Operationen ausgeführt werden. Auf die Konfiguration des Transaktions-Management wird später noch eingegangen. Aber zurück zum UserDAO, der zunächst nur aus folgendem Rumpf besteht:

public class UserDAO {

private SessionFactory sessionFactory;

// methods for crud operations
// and a setter method for the sessionFactory
}

Wenden wir uns nun der Spring Konfiguration zu, die in der Datei src/main/resources/applicationContext.xml erstellt wird. Zunächst muss die Verbindung zur Datenbank in einer Datasource konfiguriert werden. Es werden Details zum Datenbanktreiber, URL zur Datenbank, Benutzername und Passwort benötigt.

<bean id=“dataSource“ class=“org.apache.commons.dbcp.BasicDataSource“ destroy-method=“close“>
<property name=“driverClassName“ value=“org.hsqldb.jdbcDriver“/>
<property name=“url“ value=“jdbc:hsqldb:mem:myDB“/>
<property name=“username“ value=“sa“/>
<property name=“password“ value=““/>
</bean>

Bei Verwendung einer HSQLDB kommt der Treiber org.hsqldb.jdbcDriver zum Einsatz. Die URL besteht aus drei Teilen, wobei der erste für alle HSQLDBs gleich ist und jdbc:hsqldb: lauten muss. Der Prefix mem: signalisiert, dass es sich um eine In-Memory Datenbank handelt, während der letzte Teil den Namen der zu verwendeten Datenbank – myDB – bestimmt.

Will man später von einer In-Memory Datenhaltung auf eine dauerhafte Persistierung wechseln, kann die Datasource ersetzt und hier die Verbindungsdaten an die neue Datenbank angepasst werden.

Nun soll die SessionFactory definiert werden, die der UserDAO für die Datenbank-Operationen benötigt:

<bean id=“sessionFactory“ class=“org.springframework.orm.hibernate4.LocalSessionFactoryBean“>
<property name=“dataSource“ ref=“dataSource“/>
<property name=“packagesToScan“>
<list>
<value>de.mz.entity</value>
</list>
</property>
<property name=“hibernateProperties“>
<props>
<prop key=“hibernate.dialect“>org.hibernate.dialect.HSQLDialect</prop>
<prop key=“hibernate.hbm2ddl.auto“>create</prop>
<prop key=“hibernate.show_sql“>true</prop>
</props>
</property>
</bean>

Die SessionFactory braucht Zugriff auf die bereits definierte Datasource und verwaltet alle verfügbaren Entitäten. Hierzu muss entweder eine Liste aller annotierten Klassen – User und Address – über das Property annotatedClasses angegeben werden oder es wird, wie in diesem Beispiel, eine Liste von zu scannenden Packages übergeben. Anschließend können noch Hibernate-Eigenschaften konfiguriert werden, wobei die Angabe des Datenbank-Dialekts zwingend nötig ist. Bislang hat nur die Datasource Informationen über die zu konnektierende Datenbank. Aber auch Hibernate muss wissen, um welche Datenbank es sich handelt, damit die entsprechende Kommunikation gewählt werden kann. Für eine HSQLDB wird der Dialekt org.hibernate.dialect.HSQLDialect verwendet. Die weiteren Einstellungen können die Entwicklung erheblich erleichtern. So sorgt die Property hibernate.hbm2ddl.auto mit dem Wert create dafür, dass Hibernate die Datenbank-Struktur mit Tabellen, Primär- und Fremdschlüsseln automatisch anlegt. Erhält hibernate.show_sql den Wert true, wird jedes von Hibernate ausgeführte SQL-Statement geloggt und kann so nachvollzogen werden. Dies ist zwar für eine Live-Umgebung tödlich, kann aber beim Aufsetzten eines neuen Projektes durchaus hilfreich sein.

Wie bereits erwähnt, wird ein Transaktion-Management benötigt, damit die SessionFactory den UserDAO mit einer gültigen Session versorgen kann. Die Transaktionen sollen dabei über Annotationen definierbar sein. Zunächst wird der HibernateTransactionManager konfiguriert, der Zugriff auf die SessionFactory benötigt:

<bean id=“transactionManager“ class=“org.springframework.orm.hibernate4.HibernateTransactionManager“>
<property name=“sessionFactory“ ref=“sessionFactory“ />
</bean>

Um die Steuerung von Transaktionen mit der Annotation Transactional zu ermöglichen, muss dieses Verhalten aktiviert werden:

<tx:annotation-driven/>

Sofern der Name des TransactionManagers nicht transactionManager lauten würde, müsste hier noch der entsprechende Name angegeben werden:

<tx:annotation-driven transaction-manager=“transactionManager“/>

Zu guter Letzt muss der UserDAO in der Spring-Konfiguration aufgenommen werden und Zugriff auf die SessionFactory erhalten:

<bean id=“userDAO“ class=“de.mz.dao.UserDAO“>
<property name=“sessionFactory“ ref=“sessionFactory“ />
</bean>

Nun wird der UserDAO um die erste CRUD-Operationen erweitert, wobei mit dem Speichern eines Benutzers begonnen wird:

@Transactional
public User save(User user) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(user);
return user;
}

Die save-Methode wird mit der Annotation Transactional versehen. Beim Aufruf der Methode wird nun eine Transaktion erzeugt, die erst beim Verlassen wieder geschlossen wird, womit dann die Datenbank-Änderungen wirksam werden. Träte zuvor eine Exception auf, so fände keine der Datenbank-Operationen der save-Methode statt. Weiterhin sorgt die Transaktion dafür, dass die SessionFactory eine gültige Session liefert, über die der Benutzer gespeichert werden kann.

Durch die Verwendung der saveOrUpdate-Methode kann die gleiche Methode zum Anlegen und Aktualisieren von Benutzerdaten verwendet werden. Besitzt der User keine Id, vergibt Hibernate den Primär-Schlüssel und führt das SQL-Statement Insert aus, andernfalls wird ein Update ausgeführt.

Anschließend wird zum Testen der Anwendung eine Klasse mit ausführbarer main-Methode erstellt, die den Spring-Context hochfährt und den UserDAO aus selbigem lädt:

ApplicationContext context = new ClassPathXmlApplicationContext(„applicationContext.xml“);
UserDAO userDAO = (UserDAO) context.getBean(„userDAO“);

Nun wird ein Benutzter mitsamt zugehöriger Adresse erstellt und über den UserDAO gespeichert:

List<Address> addresses = new ArrayList<Address>();
Address address = new Address();
address.setCity(„Cologne“);
addresses.add(address);

User user = new User();
user.setForname(„Max“);
user.setLastname(„Mustermann“);
user.setAddresses(addresses);

userDAO.save(user);

Dank der Hibernate-Einstellung show_sql lässt sich in den Log-Meldungen gut nachvollziehen, was für SQL-Statements Hibernate durchführt. Zuerst werden die Datenbank-Tabellen erstellt:

Hibernate: create table Address (id bigint generated by default as identity (start with 1), city varchar(255), street varchar(255), primary key (id))
Hibernate: create table User (id bigint generated by default as identity (start with 1), forname varchar(255), lastname varchar(255), primary key (id))
Hibernate: create table User_Address (User_id bigint not null, addresses_id bigint not null, unique (addresses_id))

Die Tabellen Address und User entsprechen unseren Java-Entitäten, während die Tabelle User_Address als Mapping-Tabelle zwischen Benutzer und Adressen fungiert. Anschließend werden der Mapping-Tabelle per constraints Fremdschlüssel auf die Haupt-Entitäten hinzugefügt:

Hibernate: alter table User_Address add constraint FK838B1F807A26EF9A foreign key (User_id) references User
Hibernate: alter table User_Address add constraint FK838B1F805ABA690C foreign key (addresses_id) references Address

Als nächstes werden nun die Benutzerdaten gespeichert. Es lässt sich gut nachvollziehen wie aufgrund des CascadeTypes SAVE_UPDATE der Benutzer und die dazugehörigen Adressen gespeichert werden. Natürlich darf auch der Eintrag in der Mapping-Tabelle nicht fehlen:

Hibernate: insert into User (id, forname, lastname) values (default, ?, ?)
Hibernate: insert into Address (id, city, street) values (default, ?, ?)
Hibernate: insert into User_Address (User_id, addresses_id) values (?, ?)

Die ersten zwei CRUD-Operationen wurden umgesetzt, als nächstes sollen Benutzer-Daten aus der Datenbank ausgelesen werden. Hierzu wird der UserDAO um die find-Methode erweitert:

@Transactional
public List<User> find() {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(User.class);
List<User> users = criteria.list();
return users;
}

Hibernate bietet verschiedene Möglichkeiten, um Datenbank-Abfragen umzusetzen, so kann auf einer Session ein HQL- (Hibernate Query Language) oder SQL-Statement ausgeführt oder wie in diesem Beispiel eine Criteria erstellt werden. Criterias bieten die Möglichkeit Objekt-Orientierte-Abfragen formulieren zu können. Wie auf einer Criteria Einschränkungen definiert werden können, wird im Folgenden noch erläutert. Zunächst soll nur eine Liste aller Benutzer zurückgegeben werden.

Bei der Verarbeitung der Ergebnisliste ist zu beachten, dass Hibernate verschiedene FetchMode zum Umgang mit verknüpften Entitäten bietet. Per Default ist dies Lazy-Loading, wobei verknüpfte Entitäten nur geladen werden, sofern auf sie zugegriffen wird. In diesem Beispiel werden die Adress-Datensätze nicht geladen, da innerhalb der Transaktion kein Zugriff stattfindet. Nach Abschluss der Transaktion – also beim Verlassen der find-Methode – kann nicht mehr auf sie zugegriffen werden, ohne dass eine LazyLoadingException geworfen wird:

org.hibernate.LazyInitializationException: could not initialize proxy – no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:430)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:121)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266)

Wenn die Transaktion im verarbeitenden Service gestartet würde – nicht wie hier im UserDAO – wäre der Zugriff auf die entsprechenden Adress-Daten möglich. Hierbei wird der Vorteil des Lazy-Loadings ausgenutzt, es werden nur Daten geladen, die auch tatsächlich benötigt werden. Bei verteilten Systemen ist dies natürlich nicht unbedingt so möglich. Benötigt ein Client Adress-Informationen, die im Server geladen werden, so ist hier die Session bereits geschlossen. Daher kann für Entity-Verknüpfungen, die zwangsläufig mit der Haupt-Entität geladen werden sollen, der FetchMode Eager-Loading verwendet werden. Hier werden beim Laden des Benutzers direkt alle Adressen mitgeladen. Dies wird in der Annotation OneToMany definiert, die zusätzlich das Attribut fetch erhält. Die Property addresses der Klasse User besitzt nun folgende Annotationen:

@OneToMany(fetch=FetchType.EAGER)
@Cascade(value=CascadeType.ALL)
private List<Address> addresses;

Für große Daten-Mengen, die nicht immer benötigt werden, bietet sich dieses Verfahren natürlich nicht an. Hier kann das DTO – Data Transfair Object – Pattern zum Einsatz kommen. Hierbei wird nicht die User-Entity selber zum Client geschickt, sondern ein UserDTO, welches aber die gleichen Attribute besitzt. Bei der Anforderung der Daten werden die mitzuladenden Abhängigkeiten der Serverseite mitgeteilt, so dass die Antwort alle benötigten Daten in Form von DTOs beinhaltet. In unserem Beispiel würden beim Server die Benutzerdaten mitsamt der Adress-Datensätze angefragt werden. Sowohl die User– als auch die Adress-Daten werden in DTOs umgewandelt, wobei das UserDTO die Adressen in Form einer Liste von AddressDTOs besitzt. Da die Konvertierung innerhalb einer gültigen Session stattfindet, kommt es bei diesem Verfahren zu keiner LazyLoadingException und es werden nur die Daten geladen, die der Client auch tatsächlich benötigt.

Der Nachteil dieses Verfahrens liegt auf der Hand, bei jeder Client-Server-Kommunikation müssen DTOs in Entitäten und anschließend wieder zurück in DTOs konvertiert werden. Außerdem gibt es für jede Entität eine entsprechendes DTO, was zu dupliziertem Code führt. Der Vorteil der DTOs ist die Kapselung, da die Datenschicht vor dem Client-System versteckt wird.

Aber zurück zum Beispiel. Die find-Methode soll nicht mehr alle Benutzer liefern, sondern nur solche, deren Nachname Mustermann lautet. Hierfür wird auf der Criteria eine entsprechende Einschränkung definiert:

Criteria criteria = sessionFactory.getCurrentSession().createCriteria(User.class);
criteria.add(Restrictions.eq(„lastname“, „Mustermann“));
List<User> users = criteria.list();

Der Criteria wird hierzu eine Restriction hinzugefügt, deren erster Parameter den Namen der Property erhält, der zweite den zu prüfenden Wert. Die Restriction besitzt verschiedene Prüfungs-Methoden. Hier wurde auf Gleichheit getestet – equals – aber auch die anderen aus der SQL-Syntax bekannten Prüf-Operationen wie like, between usw. stehen zur Verfügung. Anschließend wird die Criteria wie gehabt ausgeführt und man erhält eine passende Liste von Benutzern.

Es sind aber nicht nur Suchkriterien auf dem Benutzer möglich, es können auch Einschränkungen auf der Adresse festgelegt werden. So interessieren im Folgenden nur Benutzer, die aus Köln kommen:

Criteria criteria = sessionFactory.getCurrentSession().createCriteria(User.class);
Criteria addressCiteria = criteria.createCriteria(„addresses“);
addressCiteria.add(Restrictions.eq(„city“, „Cologne“));
List<User> users = criteria.list();

Auf der bereits erstellen UserCriteria, wird eine weitere Criteria erzeugt. Der übergebene String enthält hierbei den Namen der Property aus der User-Entität, die auf die entsprechende Tabelle verweisst – in diesem Beispiel also addresses. Über die neue Criteria können nun Suchkritieren auf der Adressen definiert werden. Die list-Methode wird allerdings weiterhin auf der UserCriteria ausgeführt, da als Ergebnis-Menge eine Liste von Benutzern und keine Adressen erwartet werden.

Soweit ein kleiner Einblick in die Welt der Criterias. Bereits einen vorangegangenen Artikel – http://martinzimmermann1979.wordpress.com/2012/03/20/verwendung-einer-having-bedingung-bei-hibernate-criterias/ – widmete ich diesem Thema, wobei hier die Having-Bedingung im Vordergrund stand.

Aber zurück zum Thema, noch ist der UserDAO nicht vollständig, da die Lösch-Funktion fehlt:

@Transactional
public void delete(User user) {
sessionFactory.getCurrentSession().delete(user);
}

Diese ist denkbar einfach zu realisieren. Es muss lediglich auf der aktuellen Session die delete-Methode aufgerufen werden, die als Parameter die entsprechende Entität erwartet. Es mag merkwürdig erscheinen, dass zunächst die ganze Entität geladen werden muss, nur um sie anschließend zu löschen. Alternativ könnte eine HQL-Query zum Löschen des Benutzers verwendet werden, aber auch hier wird der Benutzer zuerst in den Hibernate internen Cache geladen, bevor er gelöscht wird. Die einzige Möglichkeit die obige Lösch-Funktion performanter zu gestalten, wäre das Laden und Löschen des Benutzers in derselben Session.

Der Artikel ist für den Einstieg in Hibernate und für das initiale Aufsetzten eines Hibernate Projektes mit Spring Integration gedacht. Es wurde der Einsatz der CRUD-Operationen und ein einfaches Mapping zwischen zwei Enitäten erläutert. Wirklich knifflig wird es erst anschließend, wenn es an das Mapping von Entitäten geht. Welche Entitäten kennen sich untereinander, wie werden sie miteinander verknüpft, welchen CascadeType und welchen FetchMode erhält welche Verbindung. Diese Fragen müssen für jedes Projekt und jede Anwendung einzeln Entschieden werden.

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

In 10 Minuten ein Spring Web MVC Projekt erstellen

Veröffentlicht in: Java, spring | 0

Bereits einen vorangegangenen Artikel widmete ich dem Thema REST Services mit Spring 3 (http://martinzimmermann1979.wordpress.com/2012/02/29/rest-services-mit-spring-3) und auch der folgende Beitrag beschäftigt sich mit Spring Web MVC. Diesmal wird allerdings eine Web-Seite realisiert und als Views JSPs (Java Server Pages) verwendet.

Innerhalb von maximal 10 Minuten soll ein vollständiges Spring Web MVC Projekt erstellt werden, mit dem sich eine Benutzerliste darstellen und über ein Formular weitere Benutzer hinzufügen lassen. Zum Einsatz kommt die aktuelle Spring Version 3.1.0. Da ich als Entwicklungsumgebung Eclipse verwende, werden zusätzlich ein paar Eclipse spezifische Details erläutert, grundsätzlich kann das Beispiel aber aus jeder IDE heraus nachvollzogen werden.

Zunächst wird in Eclipse ein Maven Projekt erstellt. Sofern das Eclipse WTP-Plugin (Web Tool Platform – http://www.eclipse.org/webtools/) installiert ist, kann man anschließend über die Projekt Einstellungen, die Project Facets konfigurieren. Hier wird nun das Häkchen bei Dynamic Web Module gesetzt.

Natürlich kann man auch andersherum vorgehen und zuerst ein Web Projekt erstellen und es anschließend zu einem Maven Projekt konvertieren.

Nun werden die drei Spring Libraries Core, Spring-Web und Spring-WebMVC als Maven Dependencys in die pom.xml eingetragen:

<properties>
<spring.version>3.1.0.RELEASE</spring.version>
</properties>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

Um mit Maven ein Web-Archive (WAR-Datei) erstellen zu können, muss das maven-war-plugin in die Maven-Konfiguration eingetragen und das Packaging von jar auf war umgestellt werden:

<packaging>war</packaging>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</build>

Sobald das Maven-Packaging geändert wird, erscheint in der Problem View von Eclipse folgender Fehler:

Project configuration is not up-to-date with pom.xml. Run Maven->Update Project Configuration or use Quick Fix.

Wie in der Meldung beschrieben, muss lediglich die Projekt-Konfiguration aktualisiert werden, um das Problem zu beheben.

Wer bereits mit WTP gearbeitet hat, ist gewohnt, dass das Web-Verzeichnis unter WebContent zu finden ist. Beim Aktivieren des Facets Dynamic Web Module wurde dieses Verzeichnis irreführender weise angelegt. Da unser Beispiel-Projekt allerdings mit Maven gebaut wird, ist dessen Standardverhalten entscheidend. Daher können die Verzeichnisse WebContent/WEB-INF getrost gelöscht und stattdessen src/main/webapp/WEB-INF angelegt werden. Hier wird vom maven-war-plugin die web.xml Datei erwartet – ist diese nicht vorhanden bricht der Build-Prozess mit einer Fehlermeldung ab.

In der web.xml Datei wird nun das DispatcherServlet definiert, welches alle eingehenden Requests annimmt und an die verantwortlichen Controller delegiert. Das DispatcherServlet ist Bestandteil von Springs IoC-Container und ermöglicht die Nutzung aller von Spring zur Verfügung gestellten Features:

<web-app>
<servlet>
<servlet-name>myWebApp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>myWebApp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

Der Spring-Dispatcher scannt die unter Spring-Context stehenden Klassen nach Controller Annotationen und erkennt für enthaltene Methoden, ob ein RequestMapping angegeben wurde. Unter dem hier angegebenen Pfad ist die Ressource dann später erreichbar, da das DispatcherServlet entsprechende Requests an die Controller-Methoden delegiert.

Wenden wir uns nun dem UserController zu, der im Package de.mz.server erstellt und mit @Controller annotiert wird. Für die Darstellung der Benutzerliste ist die Methode getUsers verantwortlich, die ein RequestMapping unter dem Pfad users erhält. Da in der web.xml als url-pattern der Root-Pfad angegeben wurde, ist die Ressource unter http://<ip-addresse>:<port>/de.mz.server/users erreichbar. Als Rückgabewert dient der Methode getUsers ein ModelAndView-Objekt. Wie der Name schon sagt, beinhaltet das Objekt die anzuzeigende View und als Model die Benutzerliste:

@Controller
public class UserController {

@RequestMapping(„/users“)
public ModelAndView getUsers() {
List<User> users = loadUsers();
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName(„users“);
modelAndView.addObject(„users“, users);
return modelAndView;
}

// add loadUsers-method
}

Widmen wir uns nun der Spring-Konfiguration, die im WEB-INF-Verzeichnis platziert wird. Der Name der Datei beginnt mit dem in der web.xml definierten Servlet-Namen ergänzt um „-servlet.xml“ – also myWebApp-servlet.xml. Damit der UserController von Spring generiert wird, wird das Component-Scanning für das Package de.mz.server aktiviert:

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

Im Controller wurde dem ModelAndView-Objekt der View Name users gesetzt. Um den Namen auf eine View zu mappen, muss ein ViewResolver definiert werden. Abhängig von der View stellt Spring hier verschiedene Strategien zur Verfügung. In diesem Beispiel wird der UrlBasedViewResolver verwendet, der ohne explizites Mapping auskommt, da die Namen der Views automatisch auf die View-Ressourcen gemappt werden:

<bean id=“viewResolver“ class=“org.springframework.web.servlet.view.UrlBasedViewResolver“>
<property name=“viewClass“ value=“org.springframework.web.servlet.view.JstlView“/>
<property name=“prefix“ value=“/WEB-INF/jsp/“/>
<property name=“suffix“ value=“.jsp“/>
</bean>

Als viewClass wird die JstlView gewählt, es werden also JSPs unterstützt, welche die JSP Standard Tag Library verwenden können. Per Präfix wird definiert, dass sich die JSP-Seiten in dem Unterverzeichnis jsp befinden und das Suffix gibt an, dass Views die gleichnamige Datei-Endung besitzen. Die im UserController referenzierte View users befindet sich also unter WEB-INF/jsp/users.jsp und hat folgenden Inhalt:

<%@taglib uri=“http://java.sun.com/jsp/jstl/core“ prefix=“c“%>
<html>
<head><title>Users</title></head>
<body>

<h3>Users</h3>
<table>
<tr>
<th>Vorname</th>
<th>Nachname</th>
</tr>

<c:forEach var=“user“ items=“${users}“>
<tr>
<td>${user.forename}</td>
<td>${user.lastname}</td>
</c:forEach>
</table>

</body>
</html>

Ohne die explizite Angabe der Tag-Library in der ersten Zeile, können die c-Tags nicht verwendet werden. Mit Hilfe des forEach-Befehls wird über die Liste der Benutzer iterieren. Dem items-Attribut wird die Liste der Benutzer zugewiesen, indem auf das im ModelAndView-Objekt hinterlegte Model verwiesen wird. Im var-Feld wird eine temporäre Variable definiert, über die während der Schleifendurchläufe auf die Benutzer zugegriffen werden kann. Vor- und Nachnamen der Benutzer können so in separate Spalten der Tabelle geschrieben werden. Natürlich müssen hierfür die Eigenschaften forename und lastname im Model vorhanden und mittels getter-Methoden zugreifbar sein:

public class User {

private String forename;
private String lastname;

// getter and setter

}

Sofern die Web-Applikation in einem Tomcat-Server laufen soll, muss die JSTL-Library als Maven Dependency eingebunden werden, da diese nicht im Tomcat integriert ist:

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

Im Anschluss kann die Web-Applikation mit Maven gebaut werden. Dies geschieht über den Kommandozeilen-Aufruf mvn package, oder aus der Eclipse IDE heraus. Egal welchen Weg man wählt, nach erfolgreichem Build-Prozess befindet sich die frisch erzeugte WAR-Datei im Target-Verzeichnis und kann deployed werden.

Im zweiten Schritt sollen weitere Benutzer über ein Formular hinzufügbar sein. Hierzu wird der UserController um die Methode addUser erweitert. Über das RequestMapping wird der Pfad auf user und POST als Request-Art gesetzt. Als Übergabe-Parameter kann direkt das Model – also die User-Klasse – verwendet werden, da sich Request-Parameter bequem mit Objekt-Eigenschaften verknüpfen lassen. Hierzu reicht es aus, den Übergabe-Parameter mit der Annotation ModelAttribute zu versehen. Der hier vergebene Name userForm muss später auch im HTML-Formular verwendet werden. Nach erfolgreichem Speichern des Benutzers, soll anschließend ein redirect auf die users-Ressource stattfinden, damit die vervollständigte Benutzerliste betrachtet werden kann. Daher ist der Rückgabe-Wert diesmal ein String und kein ModelAndView-Objekt:

@RequestMapping(value=“/user“, method = RequestMethod.POST)
public String addUser(@ModelAttribute(„userForm“) User user) {
// save user
return „redirect:/users“;
}

Widmen wir uns dem HTML-Formular. Die Input-Tags im HTML-Formular müssen die gleichen Namen verwenden, wie die entsprechenden Eigenschaften in der User-Klasse und es müssen die dazugehörigen setter-Methoden existieren. Das Form-Attribut modelAttribute erhält denselben Namen, wie die gleichnamige Annotation im Controller, um das Mapping zwischen Input-Feldern und Eigenschaften der User-Klasse zu ermöglichen. Als action wird der Pfad zur addUser-Methode und als method POST angegeben, was den RequestMapping-Einstellungen des Controllers entspricht:

<form action=“user“ modelAttribute=“userForm“ method=“POST“>
<p>Vorname:<br><input name=“forename“ type=“text“></p>
<p>Nachname:<br><input name=“lastname“ type=“text“></p>
<p><input type=“submit“ value=“Absenden“></p>
</form>

Nach einem Redeployment können wie in der Zielsetzung beschrieben, nun weitere Benutzer hinzugefügt werden.

Innerhalb kürzester Zeit wurde mit Spring 3.x eine Web-Applikation erstellt, die aus Controller und View zur Administrierung von Benutzern besteht. Hierfür wurde das DispatcherServlet in die web.xml-Datei eingetragen und für die Auswahl der richtigen View ein ViewResolver verwendet, der den View-Namen auf eine JSP-Datei mappt. Alle weiteren Einstellungen ließen sich bequem per Annotation direkt im Controller vornehmen.

Verwendung einer HAVING-Bedingung bei Hibernate Criterias

Veröffentlicht in: Hibernate, Java | 0

Ist bei HQL – Hibernate Query Language – die Benutzung von Having-Bedingungen
erlaubt, stehen diese bei Criteria-Queries leider nicht zur Verfügung.
Hier kann man sich aber mit einer Unter-Abfrage behelfen.

Kommen wir zu einem konkreten Beispiel, welches im Bereich Benutzerberechtigung
angeordnet ist. Es werden die Datenbank-Tabellen User und UserRole benötigt.
Jedem Benutzer kann hierbei genau eine Rolle, z.B. Administrator, Gast oder Ähnliches, zugewiesen werden.

Umgekehrt steht jede definierte Rolle keinem, einem oder mehreren Benutzern zur Verfügung.
Aus Sicht des Benutzers handelt es sich also um eine ManyToOne-Verbindung.

Hier nun die User-Entity:

@Entity
public class User {
@Id
private Long id
private String name;
@ManyToOne(optional = true, fetch = FetchType.EAGER)
@JoinColumn(name = „roleId“)
private UserRole role;

// … getter and setter …
}

Die Definition des Mappings besagt, dass die Tabelle User eine Spalte roleId enthält, welche die Id einer UserRole enthalten kann. Hiermit wird dem Benutzer also die jeweilige UserRole zugewiesen.
Aus Java Sicht hat man Zugriff auf die komplette UserRole-Entity, wobei der FetchMode EAGER dafür sorgt, dass die UserRole – sofern vorhanden – beim Laden des Users mit geladen wird.

Die UserRole sieht recht unspektakulär aus:

@Entity
public class UserRole {
@Id
private Long id;
private String role;

// … getter and setter
}

Nun möchte man alle UserRoles erhalten, die mehr als einem Benutzer zugewiesen sind.
Das SQL-Statement hierzu sähe dann so aus:

SELECT roleId FROM User GROUP BY roleId Having COUNT(*) > 1;

Da Criteria-Queries keine HAVING-Bedingungen unterstützten, muss die Query umformuliert werden:

SELECT * FROM User outer WHERE 1 <
(SELECT COUNT(*) FROM User inner WHERE inner.roleId = outer.roleId);

Das SQL-Statement wird also in zwei Abfragen aufgeteilt. Der innere Teil ist dafür zuständig, die Anzahl an Benutzern mit der entsprechenden Rolle zu liefern. In der äußeren Query wird überprüft, ob die Anzahl größer als Eins ist, womit das Ergebnis der beiden Abfragen dasselbe ist.

Diese abgewandelte Abfrage kann nun auch mit Criterias abgebildet werden:

DetachedCriteria innerQuery = DetachedCriteria.forClass(User.class, „inner“);
innerQuery.setProjection(Projections.rowCount());
innerQuery.add(Restrictions.eqProperty(„role.id“, „outer.role.id“));

DetachedCriteria criteria = DetachedCriteria.forClass(User.class, „outer“);
criteria.add(Subqueries.lt(1L, innerQuery));

List<User> list = hibernateTemplate.findByCriteria(criteria);

Zunächst wird die innere Abfrage als DetachedCriteria erstellt, wobei ein alias „inner“ verwendet wird, um die innere und äußere Query unterscheiden zu können. Anschließend wird eine Projektion auf die Anzahl der Spalten definiert, da in der inneren Abfrage nur die Anzahl der Rollen entscheidend ist. Mittels einer Restrictions wird auf Gleichheit der roleId von äußerer und innerer Query geprüft. Der Alias „inner“ kann hierbei weggelassen werden, da die Restriction schließlich auf der inneren Abfrage ausgeführt wird. Um die roleId der äußeren Query zu verwenden, muss explizit das Alias „outer“ angegeben werden.

Wie im SQL-Statement auch, überprüft nun die äußere Abfrage, ob Eins kleiner ist, als die in der inneren Abfrage erhaltene Anzahl. Trifft diese Bedingung zu, so erscheint der entsprechende User-Eintrag in Ergebnis.

Auch wenn Hibernate Criterias die HAVING-Bedingung nicht direkt unterstützen, können entsprechende Queries durch die Verwendung von Unter-Abfragen so umformuliert werden, dass nicht zwangsläufig HQL eingesetzt werden muss. Auch der Übersichtlichkeit tut die objektorientierte Schreibweise keinen Abbruch.