ich experimentiere zur Zeit etwas mit einer generischen Dao Bibliothek
herum, basierend auf Eberhards Artikel im JavaMagazin 11.2007.
Allerdings verwende ich nicht die XML-basierte JPA Konfiguration,
sondern konfiguriere meine Entity Beans mit Annotations. Als JPA
Implementierung setze ich die stabile Toplink Variante (2.0-58g) aus dem
Glassfish Projekt ein. Die Springversion ist die aktuelle 2.5.1 (Problem
bestand schon mit der 2.5).
Ich hab nun mehrere DAO-Klassen und dazu zugehörige Test-Klassen. Führe
ich die Testklasen, die alle von der Spring AbstractJpaTests Klasse
abgeleitet sind und jeweils einen eigenen ApplicationContext haben
(siehe beipiel-context.xml unten), einzeln aus, so laufen alle Tests
problemlos durch. Baue ich aber mein gesamtes Projekt mit Hilfe von
Maven, so laufen die Tests der ersten Klasse noch durch alle folgenden
schlagen dann aber mit dem Hinweis auf nicht-existente Entities fehl.
Die selben Entities erkennt er im ersten Durchlauf jedoch noch. Wenn ich
mir die Logfiles anschaue, dann sieht man auch, dass die Datenbank sowie
die Entity-Beans für die erste Testklasse noch initalisiert werden. Für
alle weiteren geschieht dies dann nicht mehr, auch wenn ich dies
aufgrund der verschiedenen ApplicationContext Objekte eigentlich
erwartet hätte. Ebenso werden die Entities auch nicht mehr neu initalisiert.
Kann es sein, dass die entityManagerFactory, dataSource etc. Beans für
die zweite (bzw. alle folgenden) Test-Klassen irgendwie nicht neu
erzeugt werden?
Kann ich die Test-Klassen dazu zwingen immer alles komplett neu zu
initalisieren? Die Datenbankverbindung wird laut Log (siehe Links unten)
definitiv nicht neu aufgebaut ...
Hat jemand eine Idee wie ich den Tests dieses Verhalten abgewöhnen kann?
Ich bin mittlerweile schon etwas verzweifelt ...
Viele Grüße
Steffen
----- maven build log ----
siehe http://steffen.scriptroom.net/dev/genericdao/log.txt
----- Zweite (nicht Namespace) PersonDaoTest-Klasse ----
siehe http://steffen.scriptroom.net/dev/genericdao/person2_stack.txt
----- AccountDaoTest-Klasse ----
siehe http://steffen.scriptroom.net/dev/genericdao/account_stack.txt
----- beispiel-context.xml -----
<?xml ...
<beans ...
<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:hsql" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"
/>
</property>
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
<property name="databasePlatform"
value="oracle.toplink.essentials.platform.database.HSQLPlatform"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="toplink.weaving">static</prop>
<prop key="toplink.logging.level">ALL</prop>
<prop key="toplink.ddl-generation">create-tables</prop>
<prop key="toplink.ddl-generation.output-mode">both</prop>
<prop
key="toplink.create-ddl-jdbc-file-name">generated_create.sql</prop>
<prop
key="toplink.drop-ddl-jdbc-file-name">generated_drop.sql</prop>
</props>
</property>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource"/>
</bean>
<context:annotation-config/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<dao:dao-config
dao-package-name="com.example.dao.beispiel"
entity-package-name="com.example.domain.beispiel">
<dao:dao name="beispiel" />
</dao:dao-config>
------------------------------------
Viele Grüße
Steffen
PS: Solltet diese Nachricht nun versehentlich mehrfach auf der Liste
gelandet sein, bitte ich dies zu entschuldigen. Mein E-Mail Programm
wollte nicht so wie ich ...
Gruß Steffen
Kannst Du mal eine Testklasse posten?
--
Thorsten Kamann
Software-Architect, Consultant, Coaching
Germany, NRW
thorste...@googlemail.com
http://www.thorsten-kamann.de/
callto://thorque
Fornax-Platform - Platform for developing MDSD-related Tools and components
http://www.fornax-platform.org/
siehe http://steffen.scriptroom.net/dev/genericdao/AccountDaoTest.java
alle verwendeten Entities sind definiert (Account, Paper, EmailAddress,
Passwd) und auch das kaskadierte Einfügen ist entsprechend konfiguriert.
Inzwischen habe ich alle weiteren Tests bis auf ein einfaches Anlegen
der Objekte mit anschließendem wiederauslesen deaktiviert um erstmal
diese einfachen Fälle wieder zum Laufen zu bringen.
Gruß Steffen
<properties>
<spring.version>2.5.1</spring.version>
<maven.test.jvmargs>
-javaagent:/home/[...]/spring-agent.jar
</maven.test.jvmargs>
</properties>
[...]
<build>
<plugins>
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>pertest</forkMode>
<argLine>${maven.test.jvmargs}</argLine>
</configuration>
</plugin>
</plugins>
</build>
Nun laufen zumindest meine einfachen Testfälle durch und ich werde mal
die etwas umfangreicheren Test wieder mit hinzunehmen.
Merkwürdig finde ich dann aber, dass die erste ausgeführte Testklasse
immer durchlief. Wenn es ein Problem mit dem LoadTimeWeaving ist,
sollten dann nicht alle fehlschlagen?
Gruß Steffen
Problematisch wird es nun wenn ich diese Library irgendwo einsetzen
möchte. Dann liegen meine Dao-Interfaces und Domainklassen nicht mehr im
selben Sourcetree wie die Testklasse.
z.B.:
src/ main /java/com/example/domain/Person.java
src/ main /java/com/example/dao/PersonDao.java
src/ test /java/com/example/dao/PersonDaoTest.java
Dies hat dann zur Folge, dass beim Bauen des Paketes der Test mit dem
Verweis auf die nicht bekannte Entity Person fehlschlägt.
Vielleicht dazu noch jemand eine Idee? Oder einen Erklärungsansatz?
Gruß Steffen
Am Freitag, den 11.01.2008, 17:52 +0100 schrieb Steffen Fritzsche:
> Kann es sein, dass dies ein Problem mit dem LoadTimeWeaving ist? Ich
> hatte die spring-agent.jar bisher auf Ebene von NetBeans als JVM
> Argument spezifiziert. Nun habe ich zus�tzlich Maven noch �ber folgende
> Parameter mitgeteilt diese f�r das Surefire-Plugin zu verwenden
>
> <properties>
> <spring.version>2.5.1</spring.version>
> <maven.test.jvmargs>
> -javaagent:/home/[...]/spring-agent.jar
> </maven.test.jvmargs>
> </properties>
> [...]
> <build>
> <plugins>
> [...]
> <plugin>
> <groupId>org.apache.maven.plugins</groupId>
> <artifactId>maven-surefire-plugin</artifactId>
> <configuration>
> <forkMode>pertest</forkMode>
> <argLine>${maven.test.jvmargs}</argLine>
> </configuration>
> </plugin>
> </plugins>
> </build>
>
> Nun laufen zumindest meine einfachen Testf�lle durch und ich werde mal
> die etwas umfangreicheren Test wieder mit hinzunehmen.
>
> Merkw�rdig finde ich dann aber, dass die erste ausgef�hrte Testklasse
> immer durchlief. Wenn es ein Problem mit dem LoadTimeWeaving ist,
> sollten dann nicht alle fehlschlagen?
>
> Gru� Steffen
>
>
> Am Freitag, den 11.01.2008, 17:19 +0100 schrieb Steffen Fritzsche:
> >
> > Am Freitag, den 11.01.2008, 14:47 +0100 schrieb Thorsten Kamann:
> > >
> > >
> > > Kannst Du mal eine Testklasse posten?
> > >
> > >
> > >
> >
> > siehe http://steffen.scriptroom.net/dev/genericdao/AccountDaoTest.java
> >
> > alle verwendeten Entities sind definiert (Account, Paper, EmailAddress,
> > Passwd) und auch das kaskadierte Einf�gen ist entsprechend konfiguriert.
> > Inzwischen habe ich alle weiteren Tests bis auf ein einfaches Anlegen
> > der Objekte mit anschlie�endem wiederauslesen deaktiviert um erstmal
> > diese einfachen F�lle wieder zum Laufen zu bringen.
> >
> > Gru� Steffen
> >
> >
> >
> > >
> >
>
>
> >
>
project-impl
projct-test
muss Maven natürlich erst das project-impl builden und in project-test
muss das JAR referenziert werden.
Allerdings erklärt es nicht warum der 1. Test immer lief.
Ist das Projekt in einem Status, direkt ausgeführt zu werden ohne das
viel konfiguriert werden muss (embedd-db?). Wenn Du Interesse hast würde
ich dann auf das Projekt schauen, wenn du das irgendwohin hochladen kannst.
Viele Grüsse
Thorsten
Ich verwende maven2 und das eigentliche Projekt besteht aus einem
Multi-Projekt und drei Unterprojekten. Die GenericDao Library ist
allerdings nicht Teil dieses Projektes, sondern ich baue sie einzeln und
stelle sie über das lokale Repository bereit.
Ansonsten halte ich mich innerhalb der einzelnen Maven-Projekte an die
Standardkonventionen von Maven, also die "normalen" Klassen finden sich
eben unter src/main/java und die zugehörigen Testklassen in identischen
Packages unter src/test/java.
>
> muss Maven natürlich erst das project-impl builden und in project-test
> muss das JAR referenziert werden.
> Allerdings erklärt es nicht warum der 1. Test immer lief.
>
> Ist das Projekt in einem Status, direkt ausgeführt zu werden ohne das
> viel konfiguriert werden muss (embedd-db?). Wenn Du Interesse hast würde
> ich dann auf das Projekt schauen, wenn du das irgendwohin hochladen kannst.
Die Tests sind gegen eine HSQL in Memory DB programmiert. Konfiguriert
wird in den einfachen Tests auch noch nichts.
Inzwischen konnte ich auch das Problem mit den unterschiedlichen
Packages umgehen. Wenn ich mich nicht darauf verlasse, dass die
annotierten Entity-Klassen automatisch gefunden werden, sondern diese
explizit mit Hilfe von <class>...</class> in der persistence.xml angebe,
dann funktioniert alles.
Das Problem lässt sich also auf die Suche nach annotierten Klassen
eingrenzen. Es werden bei der automatisierten Suche wohl nur die Pakete
und Klassen des Test-Sourcetree betrachtet.
Das Parsen der Annotations ist ja Aufgabe von Spring. Kann ich das noch
irgendwie beeinflussen? Ich betrachte die explizite Angabe der
Entityklassen eigentlich eher als einen Workaround - man muss diese ja
händisch mit den tatsächlichen Gegebenheiten abgleichen. Schöner wäre es
auf jeden Fall wenn er die annotierten Klassen von selbst finden und
einbinden würde.
Viele Grüße
Steffen
> Inzwischen konnte ich auch das Problem mit den unterschiedlichen
> Packages umgehen. Wenn ich mich nicht darauf verlasse, dass die
> annotierten Entity-Klassen automatisch gefunden werden, sondern diese
> explizit mit Hilfe von <class>...</class> in der persistence.xml angebe,
> dann funktioniert alles.
>
Das habe ich noch nie gemacht. Das Suchen nach den Entities macht Spring
doch selber:
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"
/>
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"
/>
> Das Problem lässt sich also auf die Suche nach annotierten Klassen
> eingrenzen. Es werden bei der automatisierten Suche wohl nur die Pakete
> und Klassen des Test-Sourcetree betrachtet.
>
Nein, es werden alles SourceTrees berücksichtigt:
src/main/java
src/test/java
Du könntest rein theroretisch mit dem Buildhelper noch weitere hinzufügen.
> Das Parsen der Annotations ist ja Aufgabe von Spring. Kann ich das noch
> irgendwie beeinflussen? Ich betrachte die explizite Angabe der
> Entityklassen eigentlich eher als einen Workaround - man muss diese ja
> händisch mit den tatsächlichen Gegebenheiten abgleichen. Schöner wäre es
> auf jeden Fall wenn er die annotierten Klassen von selbst finden und
> einbinden würde.
>
Für JPA habe ich noch die EntityManagerFactory konfiguriert. In diesem
Fall für Hibernate:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- <property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</property>-->
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform"
value="${jpa.provider.databasePlatform}" />
<property name="showSql"
value="${jpa.provider.showSql}" />
<property name="generateDdl"
value="${jpa.provider.generateDdl}" />
</bean>
</property>
</bean>
In der persistence.xml:
<persistence-unit name="spring2-jpa-basic-persistence-unit">
<properties>
<property name="hibernate.jdbc.batch_size" value="0"/>
<property name="hibernate.default_batch_fetch_size" value="5"/>
<property name="hibernate.max_fetch_depth" value="3"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
Und bei mir gehts :)
> Ich habe das gleiche Problem bei der Nutzung von TopLinkEssentials.
> Ich vermute allerdings mittlerweile, dass das an Toplink liegt, da ich
> scheinbar soeben noch einen recht miesen Bug darin entdeckt habe
> (https://www.xing.com/app/forum?op=showarticles;id=7514501).
Dieses Problem kann ich durchaus bestätigen. Mir ist an einigen Stellen
ebenfalls aufgefallen, dass Toplink unerwartet kaskadiert. Allerdings
schon seit einiger Zeit nicht mehr, was nicht umbedingt heissen muss,
dass es nicht mehr auftritt, sonderen das ich mich mittlerweile daran
gewöhnt habe ...
... es kann aber auch mit meinem kürzlich durchgeführten Update von
"toplink-essentials" von V2_build_58g auf V2UR1_build_b09d
zusammenhängen. Vielleicht lohnt ein kleiner Test.
> Mit Hibernate treten beide Probleme nicht auf. Allerdings ist hierbei
> auf die korrekte Einbindung in Maven zu achten. hibernate-
> entitymanager in der letzten aktuellen Version (3.3.1.GA) referenziert
> einen hibernate core in der Version 3.2.4.GA, die durch einen bösen
> Bug auffällt (http://opensource.atlassian.com/projects/hibernate/
> browse/HHH-2605). Man muss also von Hand eine aktueller Version
> nachkonfigurieren.
Hibernate ist für mich keine Alternative. Wir haben eine bestehende
Datenbank mit zahlreichen zusammengesetzten Primärschlüsseln auf die
auch von unzähligen Shellscripten aus zugegriffen wird. Die
Unterstützung für zusammengesetzte Primärschlüssel ist aber schlichtweg
ungenügend.
Das Problem tritt dann auf wenn man zwei Tabellen die beide
zusammengesetzte Primärschlüssel verwenden die nicht identisch sind aber
gemeinsame Teile haben über eine n:m Relation miteinander verknüpfen möchte.
z.B.
Tabelle A mit dem PK (login, term, moduleid) und
Tabelle B mit dem PK (login, term, supplementId
mit der Relationentabelle AB (login, term, moduleid, supplementid)
Funktioniert mit Toplink, funktioniert aber nicht mit Hibernate. In
einem Forenposting im Hibernateforum habe ich die Antwort gefunden, dass
Hibernate wegen der internen Struktur zusammengesetzte Primärschlüssel
nur in solchen n:m Tabellen aktzeptiert, wenn jeweils eigene Felder
verwendet werden, also
AB (loginA,termA,moduleid,loginB,termB,supplementid)
oder man solle alternativ auf künstliche Primärschlüssel zurückgreifen.
Aus oben beschriebenen Gründen für uns keine Alternative und das
Ausschlußkriterium für Hibernate. In meinen Augen deckt sich das in
keinster Weise mit dem JPA Standard bzw. der Standard schweigt sich zu
diesem Thema aus.
> Nunja, nach der ersten JPA Euphorie macht sich im Detail Ernüchterung
> breit. ;)
Hier kann ich Dir nur uneingeschränkt zustimmen. Sobald man versucht
umfangreichere Dinge mit JPA umzusetzen hagelt es Probleme und es treten
deutliche Schwächen zutage. Ich bekomme bei meiner derzeitigen Arbeit
zunehmend das Gefühl, dass bisherige Lösungen (Hibernate, Toplink etc.)
im Kern nicht verändert wurden und nur auf JPA Konformität
zurechtgebogen wurden. Der Standard schweigt sich in meinen Augen leider
über viel zu viele Dinge aus.
Viele Grüße
Steffen
--
Dipl.-WiWi Steffen Fritzsche
Universität Ulm
Institut für Angewandte Informationsverarbeitung
Helmholtzstraße 18
89069 Ulm
Telefon: +49 731 50 23575
Telefax: +49 731 50 23975
mailto:steffen....@uni-ulm.de
http://www.mathematik.uni-ulm.de/iai