Beispiel: Einfache Container Managed Entity Bean
Inhalt:
Der Weg des Assistenten: Project Facet "Java Persistence API"
Der Weg für Harte: Anlegen der Entity Bean ohne JPA
Der Weg für Harte: persistence.xml
Der Weg des Assistenten: Anlegen der Entity Bean mit JPA
Anlegen der Session Bean
Application Client
Datenbank
Ausführen des Clients
Logging der SQL-Parameter
Ohne Annotations
Manueller Primary Key
Troubleshooting
Beispiel für eine Container Managed Entity Bean, auf die per Applicationclient zugegriffen wird.
Hier gibt es das Projekt zum Download (dies ist ein EAR-Export, die
Importanleitung findet man im Stateless-Beispiel): KuchenSimple.ear
Falls mit der Project Facet "Java Persistence API" gearbeitet werden soll, muss diese nach jedem Import dem Projekt neu zugefügt werden
(identische Schritte wie beim Projekt-Erstellen)
Aufbau des Beispieles
a) Entity Bean-Klasse
b) Zugriff auf die Entity-Bean erfolgt über eine Stateless Session Bean.
c) Ein Application Client greift auf die Session Bean zu.
Das Beispiel besteht aus einem "EAR Application Project" mit dem Namen "KuchenSimple" sowie einem EJB-Projekt und
einem Anwendungsclientprojekt.
Der Weg des Assistenten: Project Facet "Java Persistence API"
Der folgende Abschnitt ist optional, man kann seine Entity Beans sowie den nötigen Deployment Deskriptor "persistence.xml" auch händisch erzeugen,
und der Aufwand dürfte sogar geringer sein als beim Klicken über Assistenten ;-).
Schritt 1: Rechtsklick auf das EJB-Projekt, in den "Properties" den Punkt "Projekt Facets" auswählen.
Den Haken bei "Java Persistence" setzen, die Version bleibt auf "1.0".
Anschließend auf "Further Configuration available" klicken.
Hier bleiben fast alle Einstellungen auf ihren Default-Werten (da wir keinen Zugriff auf eine vorhandene Datenbank brauchen, und uns
darauf verlassen, dass der JBoss eine JPA-Implementation mitbringt). Wichtig ist nur, dass der Haken "Create orm.xml" entfernt wird.
Diese Datei wird erst im Beispiel "Ohne Annotations" wichtig sein.
Jetzt wird im Projekt eine Datei "META-INF\persistence.xml" erzeugt, und es taucht im "Project Explorer" ein Punkt "JPA Content" auf.
Die Datei "persistence.xml" öffnen wir (per Doppelklick, oder Rechtsklick => "Open With" => "Persistence XML Editor"), und ändern
auf dem Karteireiter "General" den Name auf "kuchenPersistenceUnit". Der einzige Grund hierfür ist allerdings, dass ich dies im Vorjahresbeispiel
genauso gehalten hatte ;-).
Auf dem Karteireiter "Connection" ändern wir den "Transaction Type" auf "JTA" und können danach beim "JTA Data Source Name" den Wert java:/DefaultDS
eintragen (Erklärung im Abschnitt Der Weg für Harte: persistence.xml).
Auf dem Karteireiter "Properties" werden die Properties "hibernate.hbm2ddl.auto" mit dem Wert "create-drop" und "hibernate.show_sql" mit dem Wert "true"
angelegt (Erklärung im Abschnitt Der Weg für Harte: persistence.xml).
Auf dem Karteireiter "Source" können wir uns das Ergebnis anschauen:
<?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="kuchenPersistenceUnit" transaction-type="JTA">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="true"></property>
</properties>
</persistence-unit>
</persistence>
Anmerkung:
In meinen händisch erzeugten "persistence.xml" fehlt das Attribut "transaction-type". Zitat hierzu aus der
Hibernate-Doku:
"transaction-type (attribute): Transaction type used. Either JTA or RESOURCE_LOCAL (default to JTA in a JavaEE environment and to
RESOURCE_LOCAL in a JavaSE environment). When a jta-datasource is used, the default is JTA, if non-jta-datasource is used, RESOURCE_LOCAL is used."
Wir können hier also gemäß Doku den Defaultwert belassen, nur Eclipse scheint das anders zu sehen.
Das Hinzufügen der "Java Persistence"-Facet führt zu einer Warnung "No connection specified for project. No database-specific validation will be performed.".
Der Warnung zu folgen und eine Connection anzulegen, ist nicht sinnvoll, denn wir definieren durch unsere EJBs schließlich die Struktur
der Datenbank, nicht umgekehrt. Ein Vergleich der Entity-Beans und ihrer Felder mit einer Datenbankstruktur wäre also nicht sinnvoll.
Außerdem hindert uns die Hypersonic-Datenbank des JBoss effektiv daran, sie zu konfigurieren, da bei laufendem JBoss kein Zugriff eines zweiten Clients (also
z.B. Eclipse) auf die Datenbank möglich ist.
Deshalb empfehle ich, die JPA-Validierung abzuschalten. Dies geht in den Preferences in der Rubrik "Validation": die Haken in der Zeile "JPA Validator" entfernen.
Der Weg für Harte: Anlegen der Entity Bean ohne JPA
Wir fügen eine Klasse "KuchenSimpleBean" zu. Der Namenszusatz "Simple" kommt daher dass es noch weitere Beispiele
kommen in denen Kuchen-Entity-Beans enthalten sind. Deshalb muss in jedem Beispiel ein eindeutiger Name für das Objekt "Kuchen"
vergeben sein damit es keine Konflikte beim JNDI-Namen oder bei Tabellennamen gibt.
Es wird eine neue Klasse "KuchenSimpleBean" im Package "de.fhw.komponentenarchitekturen.knauf.kuchen" angelegt. Wichtig ist dass
diese Klasse das Interface "java.io.Serializable" implementiert !
Die Bean-Klasse bekommt die Annotation "@javax.persistence.Entity". Da wir die EJB-QL-Strings zum Finden der Instanzen
nicht hartcodiert in der Session-Bean haben wollen deklarieren wir bei der Bean-Klasse eine "@javax.persistence.NamedQuery".
@NamedQuery (name="findAllKuchen", query="select o from KuchenSimpleBean o")
@Entity()
public class KuchenSimple implements Serializable
{
Anmerkung 1:
Wenn mehr als eine Query nötig sind muss man diese in eine Annotation "@NamedQueries" packen:
@NamedQueries({
@NamedQuery (name="findAllKuchen", query="select o from KuchenSimpleBean o"),
@NamedQuery (name="findByName", query="select o from KuchenSimpleBean o where o.name like ?1")
})
Anmerkung 2:
In EJB3 RC8 hätte die Query noch kürzer dargestellt werden können (so steht es auch im EJB3-Buch von Monson-Haefel/Bill Burke):
@NamedQuery (name="findAllKuchen", query="from KuchenSimpleBean")
In EJB3 RC9 (ist in JBoss 4.2 enthalten) führt dies allerdings zu einer Fehlermeldung und ist wohl
FALSCH.
Zwei Felder sowie die zugehörigen Getter und Setter werden zugefügt:
private Integer intId;
private String strName;
@Column()
@Id ()
@GeneratedValue ()
public Integer getId()
{
return this.intId;
}
public void setId(Integer int_Id)
{
this.intId = int_Id;
}
@Column()
public String getName()
{
return this.strName;
}
public void setName(String str_Name)
{
this.strName = str_Name;
}
@Override
public String toString()
{
return this.strName;
}
Die Properties werden mit der Annotation @javax.persistence.Column
als persistente Bean-Felder markiert.
Die Property "ID" wird mit der Annotation @javax.persistence.Id
als Primary-Key-Feld markiert. Der Wert soll vom Container
automatisch generiert werden (@javax.persistence.GeneratedValue
). Das Verfahren der Generierung bleibt dem Server überlassen,
beim JBoss und der Hypersonic-Datenbank wird daraus eine Auto-ID-Spalte (sprich: beim Insert wird die ID erzeugt).
Die Methode toString
wird überladen und gibt den Namen des Kuchens zurück. Das hat den Vorteil, dass wir die Kuchen im Client
als Objekt in eine Listbox packen und auch wieder auslesen können.
Der Weg für Harte: persistence.xml
Für Entity Beans muss eine Persistence Unit deklariert werden. Dies geschieht über eine Datei "persistence.xml" im Unterverzeichnis
"META-INF" des EJB-Projekts.
Sie hat diesen Inhalt:
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="1.0">
<persistence-unit name="kuchenPersistenceUnit">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<!-- Setzen dieser Property aktiviert das automatische Tabellen-Generieren und Löschen beim Deploy! -->
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<!-- SQL-Logging einschalten: -->
<property name="hibernate.show_sql" value="true"></property>
</properties>
</persistence-unit>
</persistence>
Es wird eine "persistence-unit" namens "kuchenPersistenceUnit" deklariert. Sie ist verbunden mit einer JDBC-Datenquelle des
Servers die im JNDI unter dem Namen "java:/DefaultDS" abgelegt ist und auf die JBoss-interne Hypersonic-Datenbank zeigt.
Die Property "hibernate.hbm2ddl.auto" ist JBoss-spezifisch und legt fest dass Datenbanktabelle beim Deploy einer Bean erzeugt
und beim Undeploy wieder gelöscht werden sollen. Ohne diesen Parameter müssten wir die Datenbanktabellen von Hand anlegen.
Die Property "hibernate.show_sql" gibt an dass SQL-Befehle ins Server-Log geschrieben werden sollen, und als netter
Nebeneffekt auch auf die Server-Console in Eclipse. Damit haben wir eine gute Diagnosemöglichkeit falls Datenbankzugriffe
Probleme machen.
Der Weg des Assistenten: Anlegen der Entity Bean mit JPA
Im "Project Explorer" das EJB-Projekt wählen, Rechtsklick, "New" => "Other" wählen. In der Rubrik "JPA" den Punkt "Entity" wählen.
Name ("KuchenSimpleBean") und Package ("de.fhw.komponentenarchitekturen.knauf.kuchen") werden angegeben.
Im nächsten Dialog werden zwei Felder "id" (vom Typ java.lang.Integer
, nicht in der Auswahlliste) und "name" (vom Typ "string") )
angelegt.
Am Ende sollte der Dialog so aussehen:
-Das Feld "id" ist als Primary Key markiert.
-Der "Access Type" wird von "Field-based" (Annotations werden an die Feldvariablen gesetzt) auf "Property-based" geändert (Zugriff und Annotations nur über getter/setter).
Hier der Dialog für das Hinzufügen des Felds "id":
Die so erzeugte Entity taucht jetzt auch im "Project Explorer" auf:
Nachbearbeitung: Es muss eine Named Query zugefügt werden:
@NamedQuery (name="findAllKuchen", query="select o from KuchenSimpleBean o")
@Entity()
public class KuchenSimple implements Serializable
{
Die Property "id" muss außerdem mit der Annotation javax.persistence.GeneratedValue
versehen werden:
@Id
@GeneratedValue()
public Integer getId()
{
return this.id;
}
Anschließend wird die toString
überladen (siehe Abschnitt Der Weg für Harte: Anlegen der Entity Bean ohne JPA).
Anlegen der Session Bean
Da der Entity-Manager für den Zugriff auf die Entity-Bean nicht in einem Application Client verwendet werden kann, müssen wir
alle Zugriffe auf die Bean kapseln. Dazu verwenden wir eine Session Bean "KuchenWorkerBean" mit einem Remote Interface "KuchenWorkerRemote".
Der Entity-Manager für den Zugriff auf die Entity Bean wird als vom Container "injected" Variable deklariert:
@PersistenceContext(unitName="kuchenPersistenceUnit")
private EntityManager entityManager = null;
Die Implementierung von "saveKuchen" sieht so aus:
public void saveKuchen (KuchenSimpleBean kuchen)
{
this.entityManager.merge(kuchen);
}
Wichtig ist dass hier "merge" und nicht "persist" genommen wird, siehe Abschnitt Troubleshooting.
"deleteKuchen":
public void deleteKuchen(KuchenSimpleBean kuchen)
{
kuchen = this.entityManager.find (KuchenSimpleBean.class, kuchen.getId() );
this.entityManager.remove(kuchen);
}
Wichtig ist dass das zu löschende Objekt eventuell "detached" ist und deshalb vorher unter Container-Verwaltung gestellt werden muss, siehe Abschnitt Troubleshooting.
"getKuchen":
Diese Methode verwendet die NamedQuery die wir in der Entity-Bean deklariert haben:
public List<KuchenSimpleBean> getKuchen()
{
Query query = this.entityManager.createNamedQuery("findAllKuchen");
List<KuchenSimpleBean> listKuchen = query.getResultList();
return listKuchen;
}
Anmerkung: wir hätten die Query hier auch direkt erzeugen können.
Query query = this.entityManager.createQuery("select o from KuchenSimpleBean o");
Dadurch hätten wir allerdings eine Abhängigkeit vom Namen "KuchenSimpleBean" gebaut die weit entfernt von der eigentlichen
Klasse ist!
Anmerkung: die Zuweisung der ResultList an "List<KuchenSimpleBean>" führt
zu einer Compilerwarnung (
Type safety: The expression of type List needs unchecked conversion to conform to List<KuchenSimpleBean>
).
Dies könnten wir umgehen indem wir vor die entsprechende Zeile folgende Annotation eintragen:
@SuppressWarnings("unchecked")
List<KuchenSimpleBean> listKuchen = query.getResultList();
Application Client
Zuallerest einmal werfen die die Klasse "Main" im Default-Package weg.
Der Client muss die EJB-Jars referenzieren (siehe dazu Anleitung in den vorherigen Beispielen).
Für die GUI empfehle ich den Eclipse-Plugin "Jigloo" erzeugt. Da er nicht Open Source ist, ist er nicht in meinem Eclipse-Paket enthalten.
Man findet ihn auf http://www.cloudgarden.com/jigloo/ (für nicht-kommerzielle Verwendung frei).
Die Installation ist einfach: Jigloo ins Eclipse-Verzeichnis entpacken. Danach die Dialoge mit dem Designer öffenen per Rechtsklick => "Open With" =>
"Form Editor".
Folgende GUI-Elemente sind vorhanden:
-de.fhw.komponentenarchitekturen.knauf.kuchen.FrameKuchen
ist das Hauptfenster der Anwendung. Er besteht aus einer JList auf einem JScrollPane
und drei Buttons "Neu", "Bearbeiten" und "Löschen".
-Ein JDialog KuchenDialog
zum Bearbeiten eines Kuchens (Textfeld für den Namen und OK/Abbrechen-Buttons)
Die Main-Methode soll in einer Klasse FrameKuchen
liegen, die gleichzeitig das Hauptfenster der Anwendung ist.
Die SessionBean wird per Injecton in den FrameKuchen
gepackt:
public class FrameKuchen extends JFrame
{
@EJB
private static KuchenWorkerRemote kuchenWorker;
Trotzdem sind zwei Deployment-Deskriptoren nötig:
Die Datei "application-client.xml" sieht so aus:
<?xml version="1.0" encoding="UTF-8"?>
<application-client version="5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application-client_5.xsd">
<display-name>KuchenSimpleClient</display-name>
</application-client>
Außerdem benötigen wir den JBoss-spezifischen Deployment-Deskriptor "jboss-client.xml":
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-client PUBLIC
"-//JBoss//DTD Application Client 5.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss-client_5_0.dtd">
<jboss-client>
<jndi-name>KuchenSimpleClient</jndi-name>
</jboss-client>
Jetzt noch die Main class in "MANIFEST.MF" eintragen:
Manifest-Version: 1.0
Class-Path: KuchenSimpleEJB.jar
Main-Class: de.fhw.komponentenarchitekturen.knauf.kuchen.FrameKuchen
Jetzt können wir die Bean auf den Server stellen. Dieser Prozess erzeugt die Datenbanktabelle. Im
Server-Log finden sich unter anderem diese Einträge, an der wir erkennen können wie die Tabelle erzeugt
wird und mit welchem Statements ein Einfügen oder Löschen erfolgt (diese werden scheinbar beim Deploy vorbereitet):
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Static SQL for entity: de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Version select: select id from KuchenSimpleBean where id =?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Snapshot select: select kuchensimp_.id, kuchensimp_.name as name10_ from KuchenSimpleBean kuchensimp_ where kuchensimp_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Insert 0: insert into KuchenSimpleBean (name, id) values (?, ?)
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Update 0: update KuchenSimpleBean set name=? where id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Delete 0: delete from KuchenSimpleBean where id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.persister.entity.AbstractEntityPersister] (RMI TCP Connection(23)-127.0.0.1) Identity insert: insert into KuchenSimpleBean (id, name) values (null, ?)
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for action ACTION_MERGE on entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
2008-11-05 20:15:12,578 DEBUG [org.hibernate.loader.entity.EntityLoader] (RMI TCP Connection(23)-127.0.0.1) Static select for action ACTION_REFRESH on entity de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean: select kuchensimp0_.id as id10_0_, kuchensimp0_.name as name10_0_ from KuchenSimpleBean kuchensimp0_ where kuchensimp0_.id=?
...
2008-11-05 20:15:12,593 INFO [org.hibernate.tool.hbm2ddl.SchemaExport] (RMI TCP Connection(23)-127.0.0.1) Running hbm2ddl schema export
2008-11-05 20:15:12,593 DEBUG [org.hibernate.tool.hbm2ddl.SchemaExport] (RMI TCP Connection(23)-127.0.0.1) import file not found: /import.sql
2008-11-05 20:15:12,593 INFO [org.hibernate.tool.hbm2ddl.SchemaExport] (RMI TCP Connection(23)-127.0.0.1) exporting generated schema to database
2008-11-05 20:15:12,593 DEBUG [org.hibernate.tool.hbm2ddl.SchemaExport] (RMI TCP Connection(23)-127.0.0.1) drop table KuchenSimpleBean if exists
2008-11-05 20:15:12,593 DEBUG [org.hibernate.tool.hbm2ddl.SchemaExport] (RMI TCP Connection(23)-127.0.0.1) create table KuchenSimpleBean (id integer generated by default as identity (start with 1), name varchar(255), primary key (id))
2008-11-05 20:15:12,593 INFO [org.hibernate.tool.hbm2ddl.SchemaExport] (RMI TCP Connection(23)-127.0.0.1) schema export complete
Datenbank-Adminstrations-Tool
Die Hypersonic-Datenbank bringt ein rudimentäres Administrationstool mit. Dieses erreichen wir, indem
wir bei laufendem Server auf die JMX-Konsole gehen (http://localhost:8080/jmx-console)
und in der Kategorie "jboss" die MBean "database=localDB,service=Hypersonic" auswählen.
Ab zur Funktion "startDatabaseManager" und auf "Invoke" klicken.
Es erscheint nur eine Seite mit einer schnöden Erfolgsmeldung, aber nach kurzer Zeit sollte sich eine Java-Anwendung, der
"HSQL Database Manager" öffnen. Hier sehen wir auf der linken Seite die Tabelle unserer Bean. Im Fenster rechts oben
können wir SQL-Statements abfeuern, im Beispiel ist das ein Select auf die "KuchenSimpleBean"-Tabelle
Ausführen des Clients
Im Menü "Run" den Punkt "Open Run Dialog..." wählen.
In der Auswahl "Configurations" unter "Java Application" eine neue Konfiguration erstellen.
Die Einstellungen sollten so aussehen:
MainClass:
org.jboss.client.AppClientMain
Program Arguments:
-jbossclient de.fhw.komponentenarchitekturen.knauf.kuchen.FrameKuchen -launchers org.jboss.ejb3.client.ClientLauncher -j2ee.clientName KuchenSimpleClient
VM Arguments:
-Djava.naming.factory.initial=org.jnp.interfaces.NamingContextFactory -Djava.naming.provider.url=jnp://localhost:1099
Logging der SQL-Parameter
Leider aktiviert die Property "hibernate.show_sql" in persistence.xml nur das Logging der SQL-Statements. Die Parameter der dabei
benutzten Prepared Statements werden leider nicht ausgegeben. Wir erhalten nur solche Ausgaben:
insert into KuchenSimpleBean (id, name) values (null, ?)
Es gibt allerdings eine Lösung: mittels Log4J können wir die Ausgabe der Parameter konfigurieren.
Dies geht so: (Quelle: http://www.javalobby.org/java/forums/t44119.html und
http://www.hibernate.org/hib_docs/reference/en/html/configuration-logging.html).
In der Datei "\server\default\conf\jboss-log4j.xml" wird folgendes eingetragen (ACHTUNG: Datei als UTF-8 speichern !):
- Im Bereich "appender" wird ein neuer Appender namens "CONSOLE.HIBERNATE_PARAMETERS" angehängt.
<appender name="CONSOLE.HIBERNATE_PARAMETERS" class="org.apache.log4j.ConsoleAppender">
<errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
<param name="Target" value="System.out"/>
<param name="Threshold" value="TRACE"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
</layout>
</appender>
Es wird ein neuer Appender definiert, der auf die Konsole (System.out) loggt. Einziger Unterschied zum vorhandenen Konsolen-Appender: sein
"Threshold" also das Loglevel, ab dem Meldungen durchgelassen wurde, wurde auf die maximale Detailstufe "TRACE" erhöht.
Anmerkung: es würde eigentlich reichen, den Standard-Konsolenappender auf "TRACE" umzustellen, allerdings würden wir dann in JBoss-Meldungen ertrinken
- Im Bereich "Limit categorie" wird eine neue Kategorie definiert. Diese referenziert unseren Appender (dadurch werden NUR Logausgaben
der Kategorie "org.hibernate.type" an diesen Appender gegeben, und zwar mit TRACE-Level und höher).
<category name="org.hibernate.type">
<priority value="TRACE"/>
<appender-ref ref="CONSOLE.HIBERNATE_PARAMETERS"/>
</category>
Das Ergebnis eines Inserts sieht jetzt so aus:
21:37:51,593 INFO [STDOUT] Hibernate: insert into KuchenBean (id, name) values (null, ?)
21:37:51,593 TRACE [StringType] binding 'Mohnkuchen' to parameter: 1
Nachteil: jetzt werden auch bei einem Select alle zurückgelieferten Spalten ausgegeben:
21:37:51,593 INFO [STDOUT] Hibernate: select kuchenbean0_.id as id0_, kuchenbean0_.name as name0_ from KuchenBean kuchenbean0_
21:37:51,593 TRACE [IntegerType] returning '2' as column: id0_
21:37:51,593 TRACE [StringType] returning 'Käsekuchen' as column: name0_
21:37:51,593 TRACE [IntegerType] returning '3' as column: id0_
21:37:51,593 TRACE [StringType] returning 'Mohnkuchen' as column: name0_
Hier gibt es eine modifizierte jboss-log4j.xml zum Download.
Ohne Annotations
Die Deklaration ohne Annotations erfordert eine ganze Reihe Arbeit.
ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<display-name>KuchenSimpleEJB</display-name>
<enterprise-beans>
<session>
<description>
<![CDATA[Stateless Session Bean für den Zugriff auf die Entitiy Bean
* "Kuchen". Enthält Methoden zum Speichern und Löschen eines einzelnen
* Kuchens sowie zum Holen einer Liste aller Kuchen.]]>
</description>
<display-name>KuchenWorkerBean</display-name>
<ejb-name>KuchenWorkerBean</ejb-name>
<remote>de.fhw.swtvertiefung.knauf.kuchen.KuchenWorker</remote>
<ejb-class>de.fhw.swtvertiefung.knauf.kuchen.KuchenWorkerBean</ejb-class>
<session-type>Stateless</session-type>
<!--EntityManager-Injection -->
<persistence-context-ref>
<persistence-context-ref-name>KuchenPersistenceUnitRef</persistence-context-ref-name>
<persistence-unit-name>kuchenPersistenceUnit</persistence-unit-name>
<injection-target>
<injection-target-class>
de.fhw.swtvertiefung.knauf.kuchen.KuchenWorkerBean
</injection-target-class>
<injection-target-name>entityManager</injection-target-name>
</injection-target>
</persistence-context-ref>
</session>
<entity>
<description>
<![CDATA[Entity Bean für einen einzelnen Kuchen.]]>
</description>
<display-name>KuchenSimpleBean</display-name>
<ejb-name>KuchenSimpleBean</ejb-name>
<ejb-class>de.fhw.swtvertiefung.knauf.kuchen.KuchenSimpleBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>false</reentrant>
</entity>
</enterprise-beans>
</ejb-jar>
Neu in diesem Beispiel:
- Injection des Persistence Context in die Session Bean (wobei das Pflichtelement "persistence-context-ref-name"
für uns keine Bedeutung hat, das ist nur relevant wenn der Persistence Context per JNDI-Lookup geholt werden muss).
- Bei der EntityBean sind die oben markierten Angaben Pflicht. "persistence-type" gibt an ob der Container oder die Bean
selbst sich um die Datenbankzugriffe kümmert. Bei einer EntityBean gemäß EJB3-Spezifikation ist hier natürlich "Container"
die einzig sinnvolle Angabe. Die "prim-key-class" gibt den Datentyp des Primary Keys an, hier "java.lang.Integer".
"reentrant" gibt an ob man ausgehend von einer Methode der Bean zu einer anderen Bean und wieder zurück laufen kann. Für
unsere Bedürfnisse können wir hier immer "true" nehmen.
Anmerkung:
In "ejb-jar.xml" dürfen KEINE EJB3.0-Entity-Beans (durch das Element "entity") eingetragen sein, wie ich das noch im Vorjahresbeispiel für JBoss 4.2 gemacht hatte!
Dies führt zu folgender merkwürdiger Fehlermeldung:
21:54:06,312 ERROR [AbstractKernelController] Error installing to Real: name=vfszip:/C:/temp/jboss-5.0.0.CR2/server/default/deploy/KuchenSimpleNoAnnotation.ear state=PreReal mode=Manual requiredState=Real
org.jboss.deployers.spi.DeploymentException: java.lang.NullPointerException: name cannot be null
at org.jboss.wsf.container.jboss50.deployer.WebServiceDeployerEJB.internalDeploy(WebServiceDeployerEJB.java:103)
at org.jboss.deployers.spi.deployer.helpers.AbstractRealDeployer.deploy(AbstractRealDeployer.java:50)
at org.jboss.deployers.plugins.deployers.DeployerWrapper.deploy(DeployerWrapper.java:169)
at org.jboss.deployers.plugins.deployers.DeployersImpl.doDeploy(DeployersImpl.java:1285)
at org.jboss.deployers.plugins.deployers.DeployersImpl.doInstallParentFirst(DeployersImpl.java:1003)
at org.jboss.deployers.plugins.deployers.DeployersImpl.doInstallParentFirst(DeployersImpl.java:1056)
at org.jboss.deployers.plugins.deployers.DeployersImpl.install(DeployersImpl.java:944)
at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:348)
at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:1598)
at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:934)
at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:1062)
at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:984)
at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:822)
at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:553)
at org.jboss.deployers.plugins.deployers.DeployersImpl.process(DeployersImpl.java:627)
at org.jboss.deployers.plugins.main.MainDeployerImpl.process(MainDeployerImpl.java:541)
at org.jboss.system.server.profileservice.hotdeploy.HDScanner.scan(HDScanner.java:290)
at org.jboss.system.server.profileservice.hotdeploy.HDScanner.run(HDScanner.java:221)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(Unknown Source)
at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException: name cannot be null
at javax.management.ObjectName.construct(Unknown Source)
at javax.management.ObjectName.(Unknown Source)
at org.jboss.wsf.container.jboss50.deployer.WebServiceDeployerEJB.internalDeploy(WebServiceDeployerEJB.java:99)
... 26 more
Neu in diesem Beispiel ist ein weiterer Deployment-Deskriptor: "orm.xml". Hier wird das gesamte Mapping von Bean auf Datenbank
erledigt.
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<named-query name="findAllKuchen">
<query>select o from KuchenSimpleBean o</query>
</named-query>
<entity class="de.fhw.swtvertiefung.knauf.kuchen.KuchenSimpleBean" access="PROPERTY"
metadata-complete="true">
<attributes>
<id name="id">
<generated-value/>
</id>
<basic name="name">
</basic>
</attributes>
</entity>
</entity-mappings>
Das Element "entity" hat keine in der Schema-Definition angegebenen Pflicht-Unterelemente, trotzdem sind einige nötig damit unser Beispiel funktioniert.
Das Attribut "access" gibt an ob die Fehler per set-Property vom Container befüllt werden sollen, oder ob er sie direkt
in die (privaten) Membervariablen schreiben soll.
"metadata-complete" war zumindest in JBoss 4.2 sehr wichtig: dort gab es beim Deploy seltsame Fehlermeldungen, wenn es fehlte. Im JBoss 5.0 scheint es nicht mehr nötig zu sein,
ich belasse es trotzdem.
Innerhalb der Entity werden die Properties definiert. Für die ID-Spalte geben wir an dass der Wert automatisch generiert werden soll.
Anmerkung:
Eine erweiterte Version, in der z.B. Tabellen- und Spaltennamen angegeben werden, sieht so aus:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<named-query name="findAllKuchen">
<query>select o from KuchenSimpleBean o</query>
</named-query>
<entity class="de.fhw.swtvertiefung.knauf.kuchen.KuchenSimpleBean" access="PROPERTY"
metadata-complete="true">
<table name="KUCHENSIMPLEBEAN"></table>
<attributes>
<id name="id">
<column name="ID" />
<generated-value/>
</id>
<basic name="name">
<column name="NAME" />
</basic>
</attributes>
</entity>
</entity-mappings>
Wir geben hier den Tabellennamen an ("KUCHENSIMPLEBEAN"), außerdem die Attribute und die zugehörigen Datenbankspalten.
Für die ID-Spalte geben wir an dass der Wert automatisch generiert werden soll.
Die modifizierte Version des Projekts gibt es hier: KuchenSimpleNoAnnotation.ear.
ACHTUNG: Dieses Projekt kann nicht neben dem obigen KuchenSimple-Beispiel existieren !
Manueller Primary Key
Jetzt wollen wir den Primary Key manuell generieren. Das Verfahren hier ist einfach, aber seeehr inperformant:
bei jedem Neuanlegen eines Datensatzes wird eine Query "select max(id) from KuchenSimpleBean" ausgeführt.
Das Ergebnis dieser Query + 1 wird als ID für den neuen Datensatz verwendet.
Im Code ist folgendes zu ändern:
In KuchenSimpleBean.getId wird die Annotation "@GeneratedValue" entfernt:
@Column()
@Id()
public Integer getId()
{
return this.intId;
}
KuchenSimpleBean erhält eine weitere @NamedQuery
-Annotation (die wir jetzt in eine Annotation
@NamedQueries
verpacken müssen:
@NamedQueries( {
@NamedQuery(name = "findAllKuchen", query = "select o from KuchenSimpleBean o"),
@NamedQuery(name = "getMaxKuchenId", query = "select max (o.id) from KuchenSimpleBean o")
})
public class KuchenSimpleBean implements Serializable
{
...
KuchenWorkerBean.saveKuchen sieht so aus:
public void saveKuchen (KuchenSimpleBean kuchen)
{
if (kuchen.getId() == null)
{
Query query = this.entityManager.createNamedQuery("getMaxKuchenId");
Integer intMaxId = (Integer) query.getSingleResult();
//Beim ersten Aufruf kommt hier NULL zurück weil keine ID da ist.
if (intMaxId == null)
{
//Wir starten mit der ID "1"
kuchen.setId(1);
}
else
{
//MaxID + 1 verwenden:
kuchen.setId(intMaxId + 1);
}
}
this.entityManager.merge(kuchen);
}
Neu ist der fette Code: wenn der übergebene Kuchen keine ID enthält, dann nehmen wir an dass er neu ist.
In diesem Fall führen wir unsere Query aus. Sie liefert beim ersten Aufruf NULL zurück, in diesem Fall
setzen wir die ID auf "1". Finden wir eine ID erhöhen wir diese um 1.
Kleiner Kurs in Java: Vor Java5 hätten die Aufrufe zum ID-Setzen so aussehen müssen:
kuchen.setId( new Integer (intMaxId.intValue() + 1));
Neu in Java 5 kam "Auto-Boxing" hinzu, das es erlaubt einen Basisdatentyp bei Bedarf in die entsprechende Klasse
zu konvertieren und zurück (hier: int
und java.lang.Integer
).
Die modifizierte Version des Projekts gibt es hier: KuchenSimpleManuellerPK.ear.
ACHTUNG: Dieses Projekt kann nicht neben dem obigen KuchenSimple-Beispiel existieren !
Troubleshooting
Hier werden ein paar Fehler beschrieben in die ich beim Programmieren gelaufen bin ;-)
-
Beim Speichern eines bereits in der Datenbank vorhandenen Kuchens gibt es folgende Fehlermeldung:
javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean
at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:69)
at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)
at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:197)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:62)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:78)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:47)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:225)
at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:106)
at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:828)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:681)
at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:358)
at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:412)
at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:239)
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:567)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:192)
at org.jboss.ejb3.entity.TransactionScopedEntityManager.persist(TransactionScopedEntityManager.java:175)
at de.fhw.swtvertiefung.knauf.kuchen.KuchenWorkerBean.saveKuchen(KuchenWorkerBean.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
Lösung: die zu speichernde, bereits in der Datenbank vorhandene Entity wurde in der KuchenWorkerBean nicht mehr "entityManager.merge()"
gespeichert sondern mittels "entityManager.persist()". Dies klappt aber NUR bei neuen Objekten, nicht bei bereits vorhandenen Daten !
-
Beim Löschen einer Entity kommt es zu folgender Fehlermeldung:
javax.ejb.EJBException: java.lang.IllegalArgumentException: Removing a detached instance de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean#1
at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:69)
at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)
at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:197)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:62)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:78)
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.invoke(Ejb3AuthenticationInterceptor.java:131)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:47)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:263)
at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:106)
at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:828)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:681)
at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:358)
at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:412)
at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:239)
Caused by: java.lang.IllegalArgumentException: Removing a detached instance de.fhw.komponentenarchitekturen.knauf.kuchen.KuchenSimpleBean#1
at org.hibernate.ejb.event.EJB3DeleteEventListener.performDetachedEntityDeletionCheck(EJB3DeleteEventListener.java:47)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:75)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:49)
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:766)
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:744)
at org.hibernate.ejb.AbstractEntityManagerImpl.remove(AbstractEntityManagerImpl.java:245)
at org.jboss.ejb3.entity.TransactionScopedEntityManager.remove(TransactionScopedEntityManager.java:187)
at de.fhw.swtvertiefung.knauf.kuchen.KuchenWorkerBean.deleteKuchen(KuchenWorkerBean.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
Lösung:die Entities vorher müssen "attached" werden, d.h. in eine vom Container verwaltete Entity umgewandelt
werden:
kuchen = this.entityManager.find (KuchenSimpleBean.class, kuchen.getId() );
this.entityManager.remove(kuchen);
Vor dem Aufruf von "remove" müßte eigentlich noch eine Prüfung erfolgen ob der Datensatz in der Datenbank gefunden wurde.
- Beim Aufrufen einer NamedQuery kommt diese Fehlermeldung:
javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.MappingException: Named query not known: findAllKuchen
at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:69)
at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)
at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:197)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:76)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:62)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:78)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:47)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:225)
at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:106)
at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:828)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:681)
at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:358)
at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:398)
at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:239)
Caused by: javax.persistence.PersistenceException: org.hibernate.MappingException: Named query not known: findAllKuchen
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:567)
at org.hibernate.ejb.AbstractEntityManagerImpl.createNamedQuery(AbstractEntityManagerImpl.java:90)
at org.jboss.ejb3.entity.TransactionScopedEntityManager.createNamedQuery(TransactionScopedEntityManager.java:134)
at de.fhw.swtvertiefung.knauf.kuchen.KuchenWorkerBean.getKuchen(KuchenWorkerBean.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Die NamedQuery ist aber scheinbar korrekt deklariert.
Lösungsansatz: Ist die NamedQuery auf der Entity-Klasse deklariert deren Instanzen sie finden soll? Bei mir trat
dieser Fehler auf als ich die NamedQuery in der SessionBean deklarierte die den EntityManager verwendete.
Stand 11.05.2009
Historie:
05.11.2008: Erstellt aus Vorjahresbeispiel, angepaßt an Eclipse 3.4 und JBoss 5, JPA-Facet beschrieben.
07.11.2008: kleine Aufräumarbeiten in Webseite.
12.11.2008: Code aufgeräumt, "orm.xml" enthält keine <table>- und <column>-Elemente mehr.
21.11.2008: Füllen der JList
optimiert, kleine Korrekturen auf Webseite
08.03.2009: Client-Start: VM Argument Context.URL_PKG_PREFIXES
auf "org.jboss.naming.client" geändert
11.05.2009: Client-Start mit Injection: VM-Argument "Context.URL_PKG_PREFIXES" entfernt, weil unnötig.