Montag, 11. Mai 2009

Shared persistence

Nachdem ich im Internet viel gesucht und nicht viel gefunden habe, habe ich mich dazu entschlossen, selbst einen kleinen Artikel zu folgendem Thema zu verfassen:

"shared persistence / jboss / jpa"
"multiple ejb3 modules sharing one persistence"

Die Anforderung ist relativ einfach erzählt. Man möchte:

  • eine mehrschichtige Anwendung entwickeln

  • möglichst alle Module einzeln austauschbar gestalten

  • auf einem Application Server deployen (in unserem Fall JBoss)

  • möglichst für alle Module nur einen Persistence Kontext definieren

  • die Java Persistence API (JPA) in Verbindung mit EJB3 verwenden, um keine harten Inserts und Selects mehr selbst pflegen zu müssen


Grundinstallation

Und wie sieht das ganze nun aus? Eine vollständige und funktionierende Installation von JDK wird hier vorausgesetzt. Als Datenbank setze ich hier PostgreSQL ein. Um eine andere Datenbank nutzen zu können, muß allerdings nur die persistence.xml angepasst werden. Dazu ändert man den dort eingestellten Dialect entsprechend ab. Eine funktionierende Installation eines AppServers, hier JBoss, wird ebenfalls benötigt, wobei JBoss in dem Fall ja nur entzippt werden muß. Nach der Installation des JBoss muß im server/default/lib - Verzeichnis eine JDBC-Treiberdatei, z.B. postgresql-8.3-603.jdbc3.jar, abgelegt werden.

Als nächstes legen wir eine Testdatenbank an. Dies passiert in postgreSQL über den pgAdmin III. Der Datenbank wird nun der Produktname gegeben, beispielsweise myproduct. Um nun dem JBoss Application Server zu ermöglichen, auf diese DB zugreifen zu können, muß im server/default/deploy - Verzeichnis eine sogenannte DataSource hinzugefügt werden. Diese verweist auf den gewählten Datenbankdialekt und kann wie folgt aussehen:

myproduct-ds.xml:

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

<datasources>
<local-tx-datasource>
<jndi-name>myproductds</jndi-name>
<connection-url>
jdbc:postgresql://localhost:5432/myproduct
</connection-url>
<driver-class>org.postgresql.Driver</driver-class>
<user-name>postgres</user-name>
<password>postgres</password>

<metadata>
<type-mapping>PostgreSQL</type-mapping>
</metadata>
</local-tx-datasource>

</datasources>


Wird nun der Server gestartet und die erzeugte log-Datei geprüft, sollten keine Exceptions auftreten und man sollte in der Management Console unter http://localhost:8080/jmx-console die erzeugte Datenbankverbindung vorfinden. Alternativ kann man auch prüfen, ob in der Log-Datei ein Eintrag ähnlich diesem erfolgt:

[ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=myproductdb' to JNDI name 'java:myproductdb'

Somit ist eine Datenbankverbindung verfügbar. Was man nun braucht, sind die einzelnen...

Projekte
In Eclipse ist es relativ einfach, sich folgende Projekte zu erstellen:


  • Ein Java enterprise archive Projekt (EAR), genannt myproduct

  • Ein oder mehrere EJB Projekte, als Beispiel sei genannt myproductEJB

  • Ein leeres Standard Java Project, genannt myproductPersistence



Man kann diese Projekte natürlich auch per Hand konfigurieren. Wichtig dabei sind die folgenden Punkte.

Das EAR Projekt muß eine META-INF/application.xml beinhalten. Diese solle ähnlich der folgenden aussehen...

application.xml:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/application_5.xsd"
id="Application_ID" version="5">
<display-name>myproduct</display-name>
<module>
<ejb>myproductEJB.jar</ejb>
</module>
<module>
<ejb>myproductPersistence.jar</ejb>
</module>
</application>


Der Inhalt des EJB Projektes beschränkt sich in unserem Beispiel auf eine Entity, deren Tabelle erzeugt werden soll. In der Praxis werden aber für die einzelnen, austauschbaren Module jeweils eigene EJB Projekte erzeugt und somit eigene ejbmodule.jar Dateien deployed. Die Testbean sieht wie folgt aus...

Person Entity:

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Person implements Serializable {

private static final long serialVersionUID = -3293858472L;

@Id
private long Id;
private String name;

public long getId() {
return Id;
}

public void setId(long id) {
Id = id;
}

public String getName() {
return name;
}

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

}


Wichtig hierbei sind die verwendeten Annotations @Entity und @Id. Diese sind völlig ausreichend, um die Tabelle durch Hibernate erzeugen zu lassen. Für die Vergabe der IDs sollte man natürlich von long-Variablen absehen und stattdessen besser UUID benutzen, aber das ist ein anderes Thema.

Zu guter Letzt bleibt noch das Standard Java Projekt, welches das wichtigste für dieses Beispiel ist. Dieses Projekt ist nicht zwingend ein JPA Projekt, wie es in Eclipse angeboten wird. Es reicht, darin einen Ordner META-INF mit einer persistence.xml zu erzeugen. Der Rest des Projektes, welches später als myproductPersistence.jar deployed wird und uns den Persistenzkontext liefert, kann leer bleiben. Und hier die...



persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="myproduct"
transaction-type="JTA">
<jta-data-source>
java:/myproductdb
</jta-data-source>
<jar-file>
myproductEJB.jar
</jar-file>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto"
value="update"/>
</properties>
</persistence-unit>
</persistence>


Damit sind alle nötigen Schritte vollzogen, um die Tabelle erzeugen zu lassen. Für jedes Modul wird ein weiteres EJB Projekt mit eigenen Entity Beans erzeugt. Alle diese Projekte nutzen die gemeinsame Persistenz und werden durch Hibernate automatisch erkannt, solange man die einzelnen EJB Projekte als jar-file Einträge in der persistence.xml aufführt.

Wird nun die EAR Datei erzeugt und in das server/default/deploy - Verzeichnis des JBoss gelegt, sollte eine neue Tabelle namens Person in eurem Besitz sein. Dabei muss darauf geachtet werden, daß die EAR-Datei die Jar-Dateien myproductEJB.jar und myproductPersistence.jar beinhaltet.

Hinweis: Nutzt man JBoss 4.x, muß ein vorhandener Bug noch durch einen Workaround ausgebessert werden, indem man im Tag der persistence.xml als relativen Pfad angibt: ../myproductEJB.jar!

Sollten bei euch Fehler beim Testen auftreten, könnt ihr diese gerne hier loswerden in der Kommentarfunktion.

Viel Spaß beim Probieren!

Kommentare:

  1. Netter Artikel!
    Ich habe vor kurzem auch versucht die Entity Beans über ein JAR in einem anderen EAR Projekt auch auf dem JBoss 4.4.2 zu nutzen und leider hat es da auch mit der relativen Pfad Angabe nicht funktioniert. Der relative Pfad war allerdings ein wenig verschachtelter: ../../lib/myEntities.jar
    Funktioniert hat es bei mir nur als ich das jar direkt neben die persistence.xml abgelegt habe.

    Ich musste somit meine Entity Beans alle einzeln in der persistence.xml auflisten:
    <class>de.javathreads.jpa.MyEntityCLass</class>

    Grüße =)
    Markus

    AntwortenLöschen
  2. Ja mit JBoss 5 sollte das alles etwas einfacher werden. Da hat bei mir die Angabe des jar-Files gereicht. In JBoss4 wie gesagt mußte man den Pfad mit angeben und als workaround hab ich auch zuerst class anstelle von jar-file gebraucht. Aber zu guter Letzt hat ja doch alles geklappt wie erhofft, was ich super fand.

    AntwortenLöschen
  3. Sehr netter Artikel,

    ich hab das gleich ausprobiert, musste allerdings feststellen, das aus einem exploded EAR die Erkennung des jar-Files in der persistence.xml anscheint fehlschlägt. Anscheint bedeutet, dass zum Deployment keine Exceptions auftraten, aber weder die entsprechenden Datentabellen noch später die Ablage von Entity-Instanzen funktionierte. Als gepacktes EAR (gepacktes EAR mit einzelnen gepackten JAR Dateien) funktioniert das beschriebene Vorgehen. (JBOSS 5.1.0 GA). Für die Lösung des Exploded-EAR-Problems wäre ich sehr dankbar.

    AntwortenLöschen

Hier kann jeder (auch unregistrierte) Leser seine Meinung kundtun...