Java Persistence API z OpenJPA i Derby oraz TestNG z Eclipse IDE w tle
Z Jacek Laskowski - Wiki Projektanta Java EE
Postanowiłem spróbować swoich sił z TestNG. Było to w momencie, kiedy powracałem do lektury specyfikacji Java Persistence API (JPA), więc pomyślałem o połączeniu przyjemnego z pożytecznym, czyli o możliwości jednoczesnego testowania tego, co czytałem. Można również inaczej - najpierw próbować się z JPA, a później szukać wyjaśnień jej działania wertując kartki specyfikacji. Sprowokowało mnie to do zestawienia środowiska, w którym będę tworzył moje komponenty encyjne i aplikacje korzystające z nich oraz posiadał mechanizm wykonywania testów. Skoro dotykam JPA to muszę mieć również bazę danych. Wybrałem Apache Derby. Do tego dorzuciłem Apache Maven 2 jako narzędzie spinające całość (i dostarczające wielu niewykorzystanych acz wielce produktywnych funkcjonalności), z Eclipse IDE w tle, co ostatecznie przemieniło się w bardzo produktywne środowisko do poznawania JPA w niezwykle praktyczny sposób - tworząc testy jednostkowe w trakcie lektury specyfikacji.
Początkowo miała to być relacja z moich doświadczeń, ale nie długo trwało zanim zorientowałem się, że wielkość materiału przekracza ramy relacji i wymaga stworzenia artykułu, co właśnie czynię.
Oprogramowanie
Rozpoczynamy od zestawienia środowiska. Poniżej znajdują się narzędzia, które będą wykorzystywane w artykule.
- Java SE 5, np. 1.5.0_11
- Apache OpenJPA 0.9.7-incubator-SNAPSHOT jako implementacja specyfikacji JPA (aka sterownik JPA)
- Apache Derby 10.2.2.0 w roli bazy danych
- TestNG 5.1 jako narzędzie do uruchamiania testów jednostkowych (alternatywnie do JUnit 4)
- Apache Maven 2.0.5 w roli narzędzia do zarządzania projektem (w naszym przypadku jedynie do budowania aplikacji i wykonywania testów)
- Eclipse IDE 3.3M5eh jako graficzne środowisko do tworzenia oprogramowania z następującymi wtyczkami:
Instalację Apache Maven 2 oraz Eclipse IDE 3.3M5eh i jego 2 wtyczek pozostawiam jako pracę domową. Pozostałe oprogramowanie będzie automatycznie pobierane przez Apache Maven 2 w trakcie naszej pracy.
Z tak przygotowanym środowiskiem zakasujemy rękawy i podchodzimy do tematu.
Przepis na środowisko do praktycznego poznawania JPA
Utworzenie struktury projektu za pomocą Apache Maven 2
Animatorem w projekcie będzie Apache Maven 2 (dalej zwany M2) i od niego właśnie zaczniemy nasze przedsięwzięcie.
Rozpoczynamy od stworzenia projektu komendą mvn archetype:create. Polecenie wydajemy w dowolnym, wybranym przez siebie, katalogu.
mvn archetype:create -DgroupId=pl.jaceklaskowski.jpa -DartifactId=jpa -Dversion=1.0
, a następnie generujemy pliki konfiguracyjne projektu dla Eclipse IDE, który posłuży nam do tworzenia klas i innych plików aplikacyjnych.
Przechodzimy do katalogu jpa i wydajemy kolejne polecenie M2.
mvn eclipse:eclipse
Import projektu do Eclipse IDE
Uruchamiamy Eclipse IDE i importujemy projekt File > Import... > General > Existing Projects into Workspace i w pole Select root directory wpisujemy katalog projektu, np. c:/projs/jpa (zauważmy dziwne zachowanie Eclipse IDE podczas ręcznego wskazania katalogu z projektem. Po wpisaniu katalogu należy wybrać przycisk Browse..., aby katalog został przejrzany pod kątem istnienia projektów do zaimportowania. Alternatywnie możemy skorzystać od razu z przycisku Browse...). Wciśnięcie przycisku Finish otwiera projekt w Eclipse IDE.
I już na początku jesteśmy konfrontowani z problemem związanym z brakiem dostępności zmiennej M2_REPO w Eclipse.
Rozwiązujemy to za pomocą innego polecenia M2 (u mnie przestrzeń robocza Eclipse to C:/.eclipse/jpa):
mvn -Declipse.workspace=C\:/.eclipse/jpa eclipse:add-maven-repo
Po tym kroku, wykonujemy odświeżenie projektu w Eclipse (menu kontekstowe Refresh, bądź klawisz F5).
Niestety w Eclipse IDE 3.3M5eh wydanie polecenia nie rozwiązuje problemu. W takiej sytuacji nie pozostaje nam nic innego, jak zdefiniować zmienną M2_REPO ręcznie poprzez Window > Preferences... > Java > Build Path > Classpath Variables, a tam przycisk New... i w pole Name wpisujemy M2_REPO, a w Path ścieżkę do lokalnego repozytorium M2, np. C:/.m2 (najczęściej jest to jednak %USERPROFILE%/.m2/repository na MS Windows bądź ~/.m2/repository na systemach uniksopodobnych).
Zatwierdzenie zmiennej (przycisk OK) spowoduje przebudowanie projektu i rozwiązanie problemu z nieistniejącą zmienną.
Zanim przejdziemy do dalszych kroków usuńmy domyślnie tworzone przez M2, podczas tworzenia projektu, klasy - pl.jaceklaskowski.jpa.AppTest oraz pl.jaceklaskowski.jpa.App. Niepotrzebnie wprowadzają do projektu zależność od JUnit, z której i tak nie będziemy korzystać. Kasujemy klasy z poziomu Eclipse.
Wiele ciekawych informacji związanych z projektami M2 w Eclipse IDE znajduje się na stronie dokumentacji M2 - Guide to using Eclipse with Maven 2.x.
Utworzenie komponentu encyjnego Pracownik
Podczas tworzenia encji będą potrzebne adnotacje JPA (w bardzo minimalistycznym przykładzie przynajmniej @Entity). Nie są one częścią Java SE 5, więc konieczne będzie dołączenie zewnętrznej biblioteki do projektu. Tutaj po raz pierwszy uwidacznia się zaleta zastosowania wtyczki Maven 2.x Plug-in for Eclipse.
Uaktywniamy ją w projekcie poprzez menu kontekstowe Maven2 > Enable (w zależności od zasobności naszego lokalnego repozytorium M2 indeksowanie bibliotek może zająć chwilę). Dzięki włączeniu wtyczki mamy możliwość skorzystania z przeglądarki bibliotek w repozytorium M2, co wykorzystujemy do zdefiniowania zależności w projekcie od biblioteki Apache OpenJPA 0.9.7-incubating-SNAPSHOT.
Wybieram z menu kontekstowego Maven2 > Add Dependency. W polu Query wpisujemy openjpa i wybieramy 0.9.7-incubating-SNAPSHOT z kategorii org.apache.openjpa openjpa-all, jak przedstawiono na poniższym obrazku.
Dodanie zależności projektu od OpenJPA instruuje Eclipse, aby rozpoznawał adnotacje JPA.
Po dodaniu zależności, Eclipse decyduje się na mały test naszych kompetencji wyświetlając komunikat błędu Build path contains duplicate entry: C:.m2/junit/junit/3.8.1/junit-3.8.1.jar for project jpa (ścieżka do repozytorium może być różna w Twoim przypadku). Skoro i tak nie zamierzaliśmy korzystać z JUnit 3.8.1, jest to idealna okazja, aby pozbyć się tej zależności z projektu i po prostu usunąć ją (menu kontekstowe Build Path > Configure Build Path..., a następnie w zakładce Libraries usuwamy pozycję dotyczącą junit-3.8.1.jar). Problem nie zawsze ujawnia się natychmiast, więc nawet bez jego wystąpienia należy usunąć zależność (problem i tak wystąpi, ale później).
Utwórzmy encję Pracownik (klasa pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik).
Utworzona klasa encji powinna wyglądać następująco:
package pl.jaceklaskowski.jpa.query.chapter3_6;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Pracownik implements Serializable {
private static final long serialVersionUID = 5457953175779773177L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long numer;
private String imie;
private String nazwisko;
public Pracownik() {
}
public Pracownik(String imie, String nazwisko) {
this.imie = imie;
this.nazwisko = nazwisko;
}
public Long getNumer() {
return numer;
}
public void setNumer(Long numer) {
this.numer = numer;
}
public String getImie() {
return imie;
}
public void setImie(String imie) {
this.imie = imie;
}
public String getNazwisko() {
return nazwisko;
}
public void setNazwisko(String nazwisko) {
this.nazwisko = nazwisko;
}
}
Definicja jednostki utrwalania - persistence.xml
JPA opiera swoje działanie na pojęciu jednostki utrwalania (ang. persistence unit), która definiuje konfigurację środowiska, tj. zarządzane encje oraz sposób podłączenia do bazy danych. Konfiguracja jednostki utrwalania znajduje się w deskryptorze XML - persistence.xml.
Zakładamy katalog src/main/resources/META-INF, w którym umieszczamy plik persistence.xml o następującej treści:
<?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="derbyPU" transaction-type="RESOURCE_LOCAL"> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <class>pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik</class> <properties> <property name="openjpa.ConnectionDriverName" value="org.apache.derby.jdbc.EmbeddedDriver" /> <property name="openjpa.ConnectionURL" value="jdbc:derby:target/derbyDB;create=true" /> <property name="openjpa.ConnectionUserName" value="app" /> <property name="openjpa.ConnectionPassword" value="app" /> <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" /> <property name="openjpa.Log" value="DefaultLevel=WARN,SQL=TRACE" /> </properties> </persistence-unit> </persistence>
Korzystamy z możliwości zanurzania Apache Derby, co znosi obowiązek ręcznego uruchamiania bazy danych w celu właściwego uruchomienia aplikacji i jej testów (więcej informacji na ten temat znajduje się na stronach dokumentacji Apache Derby - Step 3: Embedded Derby).
Wybór katalogu src/main/resources był podyktowany sposobem w jaki M2 zarządza dodatkowymi plikami aplikacji, który jest katalogiem domyślnym dla nich w M2 i podczas budowania aplikacji jego zawartość trafia do pliku wynikowego jar.
Oczywiście poleganie na Apache Derby nakłada na nas obowiązek zdefiniowania zależności projektu od bibliotek Derby. Dodajemy je do projektu podobnie jak to miało miejsce w przypadku Apache OpenJPA wcześniej. Tym razem dodajemy biblioteki org.apache.derby.derby oraz org.apache.derby.derbyclient.
Mimo, że można zaznaczyć więcej niż jedną bibliotekę (za pomocą klawisza Control), to wyłącznie pierwsza z nich będzie brana pod uwagę (w kolejności występowania na liście), tj. w naszym przypadku będzie to org.apache.derby.derby. Należy powtórzyć krok dla zależności org.apache.derby.derbyclient.
Jak widać na załączonym obrazku, wtyczka Maven 2.x Plug-in for Eclipse podświetla na czerwono już dodane zależności w projekcie.
Od czasu do czasu warto samodzielnie sprawdzić, czy zależności faktycznie zostały dodane do pliku pom.xml, który jest sercem naszego projektu zarządzanego przez M2 i gdzie wszelkie modyfikacje za pomocą wtyczki Maven 2.x Plug-in for Eclipse trafiają. Okazuje się, że mimo usunięcia zależności junit-3.8.1 z poziomu Eclipse, nie została ona usunięta. Nie stanowi to jednak problemu dla działania M2, a jedynie było nieakceptowane przez Eclipse.
Na zakończenie sekcji zdefiniujmy katalog src/main/resources jako katalog źródłowy w Eclipse IDE.
Konfiguracja Eclipse IDE i M2 do wykonywania testów TestNG
Testowanie naszych komponentów encyjnych i działania JPA możemy wykonać na dwa sposoby:
- Utworzyć klasę z metodą public static void main(String[] args)
- Utworzyć testy jednostkowe, które będą de facto odchudzonymi odpowiednikami klasy z punktu 1.
Oba podejścia mają swoje zalety i wady, jednakże celem tego doświadczenia jest wykorzystanie TestNG, którego zadaniem jest uproszczenie tworzenia krótkich programów (testów jednostkowych), gdzie M2 jest środowiskiem wyzwalającym ich wykonanie. Wybieramy zatem punkt 2.
Zaletą takiego podejścia jest jego modularność. Zamiast pisać jeden duży program z parametrami wejściowymi (klasę ze statyczną metodą main z wieloma metodami pomocniczymi) tworzymy pojedyńcze klasy-testy jednostkowe i polegamy na infrastrukturze M2 z TestNG do ich uruchamienia. Dokładając kolejny test nie jesteśmy zmuszani do innych czynności (tak, jak miałoby to miejsce w przypadku podejścia 1, gdzie musielibyśmy dodać dodatkowy parametr wejściowy, albo podobnie, albo ostatecznie stworzyć alternatywę dla TestNG i odeszlibyśmy od faktycznie zdefiniowanego zadania jakim jest poznawanie JPA praktycznie).
Dodajemy zależność TestNG w naszym projekcie (podobnie jak dla Apache OpenJPA oraz Apache Derby).
Może pojawić się problem z niemożnością rozwiązania zależności jak pokazano poniżej:
, co wymaga modyfikacji pliku pom.xml w sekcji dotyczącej zależności TestNG na poniższą:
<dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>5.1</version> <scope>test</scope> <classifier>jdk15</classifier> </dependency>
W międzyczasie na konsoli Eclipse (widok Console) mogą pojawić się komunikaty:
01.03.07 13:12:38 CET: Local repository folder "" does not exist
, których pozbywamy się poprzez wybranie Window > Preferences... > Maven2, gdzie w polu Local Repository Folder wpisujemy katalog lokalnego repozytorium M2.
Utworzenie i uruchomienie testu TestNG
Najwyższa pora trochę poprogramować (do tej pory polegaliśmy jedynie na generatorach i ich integracji).
Tworzymy klasę testową korzystającą z TestNG. M2 zakłada, że klasy testowe znajdują się w katalogu src/test/java oraz nazwa klasy kończy się słowem Test (więcej o konfiguracji M2 z TestNG w Using TestNG).
Tworzymy klasę testową pl.jaceklaskowski.jpa.chapter3_6.PracownikTest w katalogu src/test/java (jest zaznaczony jako katalog źródłowy w Eclipse - nie mylić z src/main/java!)
package pl.jaceklaskowski.jpa.chapter3_6;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class PracownikTest {
@BeforeClass
public void setUp() {
System.out.println("Wykonuje się przed wykonaniem testów w klasie");
}
@Test
public void sprawdzPolaczenie() {
System.out.println("...właściwa metoda testowa");
}
@AfterClass
public void tearDown() {
System.out.println("Wykonuje się po wykonaniu testów w klasie");
}
}
Uruchommy ją korzystając w tym celu z kolejnej wtyczki Eclipse - TestNG Eclipse plug-in. Uruchomienie odbywa się za pomocą menu kontekstowego klasy testowej - Run As > TestNG Test.
Poprawne wykonanie przedstawia się w widoku TestNG następująco:
oraz w widoku Console pojawią się poniższe komunikaty:
[Parser] Running:
C:\projs\jpa\temp-testng-customsuite.xml
Wykonuje się przed wykonaniem testów w klasie
...właściwa metoda testowa
Wykonuje się po wykonaniu testów w klasie
PASSED: sprawdzPolaczenie
===============================================
pl.jaceklaskowski.jpa.chapter3_6.PracownikTest
Tests run: 1, Failures: 0, Skips: 0
===============================================
===============================================
jpa
Total tests run: 1, Failures: 0, Skips: 0
===============================================
Utwórzmy właściwy test weryfikujący poprawność zestawienia środowiska do testów wspomagających zrozumienie specyfikacji JPA - w tym momencie korzystamy już ze wszystkich elementów układanki. Sprowadza się to, do podmiany treści klasy pl.jaceklaskowski.jpa.chapter3_6.PracownikTest na poniższą.
package pl.jaceklaskowski.jpa.chapter3_6;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik;
public class PracownikTest {
EntityManagerFactory emf;
EntityManager em;
@BeforeClass
public void setUp() {
emf = Persistence.createEntityManagerFactory("derbyPU");
em = emf.createEntityManager();
}
@Test
public void utworzPracownika() {
Pracownik jacekLaskowski = new Pracownik("Jacek", "Laskowski");
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(jacekLaskowski);
tx.commit();
}
@Test(dependsOnMethods = { "utworzPracownika" })
public void sprawdzIloscPracownikow() {
Query query = em.createQuery("SELECT p FROM Pracownik p");
int iloscPracownikow = query.getResultList().size();
assert iloscPracownikow == 1 : "Spodziewano się 1 pracownika. Było: " + iloscPracownikow;
}
@AfterClass
public void tearDown() {
em.close();
emf.close();
}
}
W powyższym przykładzie pojawiła się ciekawa funkcjonalność testów pisanych z użyciem TestNG - atrybut dependsOnMethods w adnotacji @Test. Za pomocą atrybutu dependsOnMethods jesteśmy wskazać metody zależne, których pomyślne wykonanie pozwoli na wykonanie udekorowanej metody. W naszym przykładzie uruchomienie metody sprawdzIloscPracownikow nie ma sensu, jeśli metoda utworzPracownika nie zakończyła się pomyślnie (z takim przynajmniej założeniem pisaliśmy ten test, a dzięki dependsOnMethods TestNG pozwala nam to dodatkowo wyrazić).
Do uruchomienia powyższego testu jednakże, będzie konieczna jeszcze jedna zmiana w uruchamianiu testów w Eclipse IDE. Jeśli uruchomimy powyższy test bez niej zakończy się poniższym komunikatem błędu:
FAILED: utworzPracownika
<4|false|0.9.7-incubating-SNAPSHOT> org.apache.openjpa.persistence.ArgumentException:
Attempt to cast instance "pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik@96b5c2" to PersistenceCapable failed. Ensure that it has been enhanced.
FailedObject: pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik@96b5c2
at org.apache.openjpa.kernel.BrokerImpl.assertPersistenceCapable(BrokerImpl.java:4238)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2348)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2208)
at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:991)
at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:525)
at pl.jaceklaskowski.jpa.query.entities.PracownikTest.utworzPracownika(PracownikTest.java:30)
... Removed 21 stack frames
Wynika to ze sposobu działania OpenJPA, które wymaga, aby kod klas uczestniczących w zapisie trwałym został zmodyfikowany (ang. instrumented albo enhanced). Zgodnie z dokumentacją OpenJPA - 2. Enhancement - mamy kilka możliwości. Na potrzeby uruchomienia TestNG w Eclipse wybieramy opcję modyfikacji kodu podczas jego uruchomienia (2.3. Enhancing at Runtime), co wymaga, aby wywołanie testu było opatrzone parametrem -javaagent:<M2_REPO>/org/apache/openjpa/openjpa-all/0.9.7-incubating-SNAPSHOT/openjpa-all-0.9.7-incubating-SNAPSHOT.jar, gdzie M2_REPO to katalog repozytorium lokalnego M2 (spotkaliśmy się z nim podczas importu projektu do Eclipse).
Po tej zmianie, pierwsze uruchomienie zakończy się pomyślnie.
[Parser] Running:
C:\projs\jpa\temp-testng-customsuite.xml
1922 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 9899022> executing prepstmnt 12342678 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WITH RR [params=(int) 0]
1922 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 9899022> [0 ms] spent
1953 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 749304> executing prepstmnt 17365216 INSERT INTO OPENJPA_SEQUENCE_TABLE (ID, SEQUENCE_VALUE) VALUES (?, ?) [params=(int) 0, (int) 1]
1985 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 749304> [32 ms] spent
1985 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 19699031> executing prepstmnt 28732166 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WITH RR [params=(int) 0]
1985 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 19699031> [0 ms] spent
2032 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 19699031> executing prepstmnt 8886368 UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=(long) 51, (int) 0, (long) 1]
2032 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 19699031> [0 ms] spent
2047 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 21411547> executing prepstmnt 26174809 INSERT INTO Pracownik (numer, imie, nazwisko) VALUES (?, ?, ?) [params=(long) 1, (String) Jacek, (String) Laskowski]
2047 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 21411547> [0 ms] spent
2297 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 6751353> executing prepstmnt 18082301 SELECT t0.numer, t0.imie, t0.nazwisko FROM Pracownik t0
2297 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 6751353> [0 ms] spent
PASSED: utworzPracownika
PASSED: sprawdzIloscPracownikow
===============================================
pl.jaceklaskowski.jpa.chapter3_6.PracownikTest
Tests run: 2, Failures: 0, Skips: 0
===============================================
===============================================
jpa
Total tests run: 2, Failures: 0, Skips: 0
===============================================
I tylko pierwsze! Kolejne już niestety nie. Wynika to z istnienia bazy danych i każdorazowego tworzenia pracownika "Jacek Laskowski" jedynie z innym identyfikatorem, co zwiększa ilość pracowników w bazie, więc nasze sprawdzenie w sprawdzIloscPracownikow po kolejnym uruchomieniu nie będzie spełnione.
[Parser] Running:
C:\projs\jpa\temp-testng-customsuite.xml
1750 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 176713> executing prepstmnt 4579880 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WITH RR [params=(int) 0]
1750 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 176713> [0 ms] spent
1797 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 176713> executing prepstmnt 14648293 UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=(long) 101, (int) 0, (long) 51]
1813 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 176713> [16 ms] spent
1844 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 17605128> executing prepstmnt 17284365 INSERT INTO Pracownik (numer, imie, nazwisko) VALUES (?, ?, ?) [params=(long) 51, (String) Jacek, (String) Laskowski]
1860 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 17605128> [16 ms] spent
2125 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 25591289> executing prepstmnt 3834717 SELECT t0.numer, t0.imie, t0.nazwisko FROM Pracownik t0
2125 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 10807107, conn 25591289> [0 ms] spent
PASSED: utworzPracownika
FAILED: sprawdzIloscPracownikow
java.lang.AssertionError: Spodziewano się 1 pracownika. Było: 2
at pl.jaceklaskowski.jpa.chapter3_6.PracownikTest.sprawdzIloscPracownikow(PracownikTest.java:40)
... Removed 21 stack frames
===============================================
pl.jaceklaskowski.jpa.chapter3_6.PracownikTest
Tests run: 2, Failures: 1, Skips: 0
===============================================
===============================================
jpa
Total tests run: 2, Failures: 1, Skips: 0
===============================================
Rozwiązywanie problemów to nasza specjalność i ten obsłużymy (czytaj: naprawimy) w kolejnym kroku, kiedy testy będą uruchamiane z poziomu M2.
Konfiguracja Apache Maven 2 i Eclipse IDE do uruchomienia testów TestNG
Skoro rozpoczęliśmy korzystanie ze środowiska graficznego Eclipse IDE nie pozostaje nic innego jak skorzystanie z dalszych usprawnień i możliwości uruchamiania testów TestNG z jego poziomu.
Korzystamy z menu Run > External Tools > External Tools..., gdzie definiujemy wykonanie m2 build dla naszych testów.
W zakładce Common warto zaznaczyć opcję External Tools w Display in favorites menu.
Na zakończenie wybieramy przycisk Run.
[INFO] ---------------------------------------------------------------------------- [INFO] Building jpa-query [INFO] task-segment: [clean, test] [INFO] ---------------------------------------------------------------------------- [INFO] clean:clean [INFO] Deleting directory C:\projs\jpa\target [INFO] Deleting directory C:\projs\jpa\target\classes [INFO] Deleting directory C:\projs\jpa\target\test-classes [INFO] resources:resources [INFO] Using default encoding to copy filtered resources. [INFO] compiler:compile [INFO] Compiling 2 source files to C:\projs\jpa\target\classes [ERROR] mojo-execute : compiler:compile Diagnosis: Compilation failure FATAL ERROR: Error executing Maven for a project [ERROR] project-execute : pl.jaceklaskowski.jpa.query:jpa-query:jar:1.0 ( task-segment: [clean, test] ) Diagnosis: Compilation failure FATAL ERROR: Error executing Maven for a project org.apache.maven.BuildFailureException: Compilation failure at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:555) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:475) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:454) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:306) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:273) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:140) at org.apache.maven.embedder.MavenEmbedder.execute(MavenEmbedder.java:441) at org.apache.maven.embedder.MavenEmbedder.execute(MavenEmbedder.java:382) at org.maven.ide.eclipse.Maven2Executor.main(Maven2Executor.java:68) Caused by: org.apache.maven.plugin.CompilationFailureException: Compilation failure at org.apache.maven.plugin.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:516) at org.apache.maven.plugin.CompilerMojo.execute(CompilerMojo.java:114) at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:412) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:534) ... 8 more
Komunikat nie jest precyzyjny, ale wykonanie polecenia mvn clean test z linii poleceń upraszcza zrozumienie sytuacji i jej naprawę.
Otwieramy konsolę linii poleceń w katalogu projektu i wydajemy polecenie mvn clean test (najpierw czyścimy projekt - polecenie clean, a następnie uruchamiamy testy w czystym środowisku - polecenie test).
$ mvn clean test
[INFO] Scanning for projects...
[INFO] ----------------------------------------------------------------------------
[INFO] Building jpa
[INFO] task-segment: [clean, test]
[INFO] ----------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] Deleting directory c:\projs\jpa\target
[INFO] Deleting directory c:\projs\jpa\target\classes
[INFO] Deleting directory c:\projs\jpa\target\test-classes
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[WARNING]
Artifact junit:junit:jar:3.8.1:test retains local scope 'test' overriding broader scope 'compile'
given by a dependency. If this is not intended, modify or remove the local scope.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to c:\projs\jpa\target\classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
c:\projs\jpa\src\main\java\pl\jaceklaskowski\jpa\query\chapter3_6\Pracownik.java:[10,1] annotations are not supported in -source 1.3
(try -source 1.5 to enable annotations)
@Entity
c:\projs\jpa\src\main\java\pl\jaceklaskowski\jpa\query\chapter3_6\Pracownik.java:[10,1] annotations are not supported in -source 1.3
(try -source 1.5 to enable annotations)
@Entity
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Wed Feb 28 23:52:20 CET 2007
[INFO] Final Memory: 4M/254M
[INFO] ------------------------------------------------------------------------
Okazuje się, że domyślna konfiguracja M2 zakłada użycie Java 1.3, a my korzystamy z funkcjonalności Java 5 (adnotacje). Musimy zmodyfikować pom.xml projektu dodając konfigurację wtyczki maven-compiler-plugin tak, aby zakładała korzystanie z Java 5 (przy okazji usuwamy zależność od JUnit 3.8.1, która jest niewykorzystywana oraz sekcję url - ot, takie małe porządki - czy ktoś wspominał o ciągłej modyfikacji projektu jako uzasadnionego podejścia w celu podniesienia jakości projektu?!).
Ostatecznie plik pom.xml projektu przedstawia się następująco:
<?xml version="1.0"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jaceklaskowski.jpa.query</groupId>
<artifactId>jpa-query</artifactId>
<name>jpa-query</name>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-all</artifactId>
<version>0.9.7-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.2.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.2.2.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.1</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
Niepoprawny język.
Musisz wybrać język w następujący sposób: <source lang="html4strict">...</source>
Języki obsługiwane w podświetlaniu składni:
abap, actionscript, actionscript3, ada, apache, applescript, apt_sources, asm, asp, autoit, avisynth, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_mac, caddcl, cadlisp, cfdg, cfm, cil, cmake, cobol, cpp, cpp-qt, csharp, css, d, dcs, delphi, diff, div, dos, dot, eiffel, email, erlang, fo, fortran, freebasic, genero, gettext, glsl, gml, gnuplot, groovy, haskell, hq9plus, html4strict, idl, ini, inno, intercal, io, java, java5, javascript, kixtart, klonec, klonecpp, latex, lisp, locobasic, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, make, matlab, mirc, modula3, mpasm, mxml, mysql, nsis, oberon2, objc, ocaml, ocaml-brief, oobas, oracle11, oracle8, pascal, per, perl, php, php-brief, pic16, pixelbender, plsql, povray, powershell, progress, prolog, properties, providex, python, qbasic, rails, rebol, reg, robots, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, sql, tcl, teraterm, text, thinbasic, tsql, typoscript, vb, vbnet, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xml, xorg_conf, xpp, z80
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Po tych zmianach możemy ponownie uruchomić komendę mvn clean test z poziomu Eclipse IDE.
co jest równoznaczne z uruchomieniem polecenia z poziomu linii poleceń:
$ mvn clean test [INFO] Scanning for projects... [INFO] ---------------------------------------------------------------------------- [INFO] Building jpa-query [INFO] task-segment: [clean, test] [INFO] ---------------------------------------------------------------------------- [INFO] [clean:clean] [INFO] Deleting directory c:\projs\jpa\target [INFO] Deleting directory c:\projs\jpa\target\classes [INFO] Deleting directory c:\projs\jpa\target\test-classes [INFO] [resources:resources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:compile] [INFO] Compiling 1 source file to c:\projs\jpa\target\classes [INFO] [resources:testResources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:testCompile] [INFO] Compiling 1 source file to c:\projs\jpa\target\test-classes [INFO] [surefire:test] [INFO] Surefire report directory: c:\projs\jpa\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running pl.jaceklaskowski.jpa.chapter3_6.PracownikTest 3047 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 5063138> executing stmnt 1335930 CREATE TABLE OPENJPA_SEQUENCE_TABLE (ID SMALLINT NOT NULL, SEQUENCE_VALUE BIGINT, PRIMARY KEY (ID)) 3094 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 5063138> [47 ms] spent 3094 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 3658896> executing stmnt 9135999 CREATE TABLE Pracownik (numer BIGINT NOT NULL, imie VARCHAR(255), nazwisko VARCHAR (255), PRIMARY KEY (numer)) 3110 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 3658896> [16 ms] spent Tests run: 3, Failures: 2, Errors: 0, Skipped: 1, Time elapsed: 3.735 sec <<< FAILURE! Results : Tests run: 3, Failures: 2, Errors: 0, Skipped: 1 [INFO] ------------------------------------------------------------------------ [ERROR] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] There are test failures. [INFO] ------------------------------------------------------------------------ [INFO] For more information, run Maven with the -e switch [INFO] ------------------------------------------------------------------------ [INFO] Total time: 5 seconds [INFO] Finished at: Thu Mar 01 00:35:32 CET 2007 [INFO] Final Memory: 6M/254M [INFO] ------------------------------------------------------------------------
Testy zostały wykonane, ale nie obyło się bez błędów (BUILD FAILURE). Można jednak wierzyć, że mimo (przejściowych) problemów jest to krok naprzód (w końcu rozwiązywanie problemów to nasza specjalność!).
Jak można przeczytać w pliku wynikowym testu - target/surefire-reports/pl.jaceklaskowski.jpa.chapter3_6.PracownikTest.txt:
-------------------------------------------------------------------------------
Test set: pl.jaceklaskowski.jpa.chapter3_6.PracownikTest
-------------------------------------------------------------------------------
Tests run: 3, Failures: 2, Errors: 0, Skipped: 1, Time elapsed: 3.735 sec <<< FAILURE!
utworzPracownika(pl.jaceklaskowski.jpa.chapter3_6.PracownikTest) Time elapsed: 0.015 sec <<< FAILURE!
<4|false|0.9.7-incubating-SNAPSHOT> org.apache.openjpa.persistence.ArgumentException: Attempt to cast instance "pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik@5d855f" to Persiste
nceCapable failed. Ensure that it has been enhanced.
FailedObject: pl.jaceklaskowski.jpa.query.chapter3_6.Pracownik@5d855f
at org.apache.openjpa.kernel.BrokerImpl.assertPersistenceCapable(BrokerImpl.java:4238)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2348)
at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2208)
at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:991)
at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:525)
at pl.jaceklaskowski.jpa.chapter3_6.PracownikTest.utworzPracownika(PracownikTest.java:32)
tearDown(pl.jaceklaskowski.jpa.chapter3_6.PracownikTest) Time elapsed: 0.015 sec <<< FAILURE!
<4|false|0.9.7-incubating-SNAPSHOT> org.apache.openjpa.persistence.InvalidStateException: This operation cannot be performed while a Transaction is active.
at org.apache.openjpa.kernel.BrokerImpl.close(BrokerImpl.java:3980)
at org.apache.openjpa.kernel.DelegatingBroker.close(DelegatingBroker.java:1260)
at org.apache.openjpa.persistence.EntityManagerImpl.close(EntityManagerImpl.java:981)
at pl.jaceklaskowski.jpa.chapter3_6.PracownikTest.tearDown(PracownikTest.java:45)
błędne uruchomienie testów związane jest z brakiem modyfikacji kodu testów. Hmmm, ale to już było! Ale w innym środowisku. Wtedy błąd pojawił się podczas uruchomienia testu w Eclipse IDE korzystając z wtyczki TestNG, a teraz korzystamy już z wtyczki M2.
Zgodnie z dokumentacją projektu Apache OpenJPA - EnhancingWithMaven - możemy skorzystać z wtyczki openjpa-maven-plugin bądź maven-antrun-plugin. Wybieramy opcję pierwszą (bo była pierwsza, ale również, a może przede wszystkim dlatego, że jest dedykowana do tego zadania). Jest to alternatywny sposób na "usprawnianie" kodu przez OpenJPA niż poprzednio z agentem, który dynamicznie wczytywał klasy podczas ich ładowania i wtedy "usprawniał" ich reprezentację.
Kolejny raz modyfikujemy pom.xml projektu tak, aby ostatecznie przyjął następującą postać:
<?xml version="1.0"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jaceklaskowski.jpa.query</groupId>
<artifactId>jpa-query</artifactId>
<name>jpa-query</name>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-all</artifactId>
<version>0.9.7-incubating-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.2.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.2.2.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.1</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
Niepoprawny język.
Musisz wybrać język w następujący sposób: <source lang="html4strict">...</source>
Języki obsługiwane w podświetlaniu składni:
abap, actionscript, actionscript3, ada, apache, applescript, apt_sources, asm, asp, autoit, avisynth, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_mac, caddcl, cadlisp, cfdg, cfm, cil, cmake, cobol, cpp, cpp-qt, csharp, css, d, dcs, delphi, diff, div, dos, dot, eiffel, email, erlang, fo, fortran, freebasic, genero, gettext, glsl, gml, gnuplot, groovy, haskell, hq9plus, html4strict, idl, ini, inno, intercal, io, java, java5, javascript, kixtart, klonec, klonecpp, latex, lisp, locobasic, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, make, matlab, mirc, modula3, mpasm, mxml, mysql, nsis, oberon2, objc, ocaml, ocaml-brief, oobas, oracle11, oracle8, pascal, per, perl, php, php-brief, pic16, pixelbender, plsql, povray, powershell, progress, prolog, properties, providex, python, qbasic, rails, rebol, reg, robots, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, sql, tcl, teraterm, text, thinbasic, tsql, typoscript, vb, vbnet, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xml, xorg_conf, xpp, z80
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>openjpa-maven-plugin</artifactId>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>enhance</goal>
</goals>
<configuration>
<toolProperties>
<property>
<name>addDefaultConstructor</name>
<value>true</value>
</property>
<property>
<name>enforcePropertyRestrictions</name>
<value>true</value>
</property>
</toolProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Tak zdefiniowany projekt jest gotowy do finalnego uruchomienia.
Finalne uruchomienie
Konfiguracja środowiska z wykorzystaniem JPA z Apache OpenJPA i Apache Derby oraz Apache Maven 2 i TestNG w środowisku Eclipse IDE zapewnia nam powtarzalność wykonywania testów. Wystarczy ponowne uruchamianie testów korzystając z definicji mvn clean test jako zewnętrzne narzędzie (ang. external tool) w Eclipse IDE.
Uruchamiamy mvn clean test (jako zewnętrzne narzędzie) i biorąc głęboki wdech wciskamy klawisz ENTER oczekując poprawnego wykonania testów:
[INFO] ----------------------------------------------------------------------------
[INFO] Building jpa-query
[INFO] task-segment: [clean, test]
[INFO] ----------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] Deleting directory c:\projs\jpa\target
[INFO] Deleting directory c:\projs\jpa\target\classes
[INFO] Deleting directory c:\projs\jpa\target\test-classes
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to c:\projs\jpa\target\classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [openjpa:enhance {execution: default}]
[INFO]
[INFO] [compiler:testCompile]
[INFO] Compiling 1 source file to c:\projs\jpa\target\test-classes
[INFO] [surefire:test]
[INFO] Surefire report directory: c:\projs\jpa\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running Testy Java Persistence API rozdzial 3.6
2844 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 13359324> executing stmnt 23954271 CREATE TABLE OPENJPA_SEQUENCE_TABLE (ID SMALLINT NOT NULL, SEQUENCE_VALUE BIGINT
, PRIMARY KEY (ID))
2891 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 13359324> [47 ms] spent
2891 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 24957277> executing stmnt 6026788 CREATE TABLE Pracownik (numer BIGINT NOT NULL, imie VARCHAR(255), nazwisko VARCHA
R(255), PRIMARY KEY (numer))
2906 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 24957277> [15 ms] spent
3016 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 1130533> executing prepstmnt 23119024 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WIT
H RR [params=(int) 0]
3016 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 1130533> [0 ms] spent
3047 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 11265620> executing prepstmnt 14828347 INSERT INTO OPENJPA_SEQUENCE_TABLE (ID, SEQUENCE_VALUE) VALUES (?, ?) [param
s=(int) 0, (int) 1]
3063 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 11265620> [16 ms] spent
3063 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 12915247> executing prepstmnt 25894799 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WI
TH RR [params=(int) 0]
3063 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 12915247> [0 ms] spent
3078 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 12915247> executing prepstmnt 5427620 UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENC
E_VALUE = ? [params=(long) 51, (int) 0, (long) 1]
3078 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 12915247> [0 ms] spent
3094 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 7225781> executing prepstmnt 14301042 INSERT INTO Pracownik (numer, imie, nazwisko) VALUES (?, ?, ?) [params=(long)
1, (String) Jacek, (String) Laskowski]
3094 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 7225781> [0 ms] spent
3360 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 15886558> executing prepstmnt 5123850 SELECT t0.numer, t0.imie, t0.nazwisko FROM Pracownik t0
3360 derbyPU TRACE [main] openjpa.jdbc.SQL - <t 8029412, conn 15886558> [0 ms] spent
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.859 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8 seconds
[INFO] Finished at: Thu Mar 01 01:06:18 CET 2007
[INFO] Final Memory: 7M/254M
[INFO] ------------------------------------------------------------------------
BUILD SUCCESSFUL po 8 sekundach! Teraz możemy odetchnąć - środowisko zostało poprawnie zestawione i stanowi ciekawe urozmaicenie naszego pogłębiania znajomości JPA. Teraz jesteśmy "uzbrojeni" do podjęcia się wyzwania rozpoznawania specyfikacji Java Persistence API w praktyce. Miłej lektury!
