Dynamiczny dostęp do wielu baz danych w JPA
Z Jacek Laskowski - Wiki Projektanta Java EE
Kolejny raz zostałem zainspirowany przez użytkownika grupy pl.comp.lang.java. Tym razem, Michał Chmielarz zadał pytanie o Dostęp do wielu baz danych przez JPA w ramach EJB 3.0, które sprowokowało mnie do dokładniejszego spojrzenia we wnętrze JPA. Już wczoraj próbowałem się z tematem z użyciem Raven i Buildr, ale nie miałem wiele szczęścia z nimi i ostatecznie skorzystałem ze sprawdzonego Mavena 2. Zresztą nie tak dawno zestawiłem środowisko do pracy z JPA z wykorzystaniem Maven (Testowanie złączeń FETCH JOIN w JPA), więc miałem w zasadzie wszystko gotowe.
Zacznę od pobieżnego wprowadzenia do teorii JPA w kontekście dynamicznego zarządzania wieloma bazami danych, po którym nastąpi demonstracja kompletnej aplikacji w działaniu z wykorzystaniem zaprezentowanej teorii.
Kompletny projekt aplikacji jpa-multidb dostępny jest do pobrania jako jpa-multidb.zip.
Spis treści |
(Pobieżne) wprowadzenie do JPA
Dostęp do bazy danych w JPA jest realizowany przez interfejs javax.persistence.EntityManager, za pomocą którego możliwa jest współpraca z kontekstem trwałym. W środowisku serwera aplikacji dostęp do zarządcy encji (EntityManager) następuje poprzez adnotację @PersistenceContext (brak elementu unitName w adnotacji wskazuje na dostęp do domyślnego zarządcy encji, co w praktyce wymaga, aby był on wyłącznie jeden).
public class Ziarno implements ZiarnoIntf {
@PersistenceContext
EntityManager em;
}
Konfiguracja zarządcy encji następuje poprzez plik persistence.xml (w katalogu META-INF). Poniżej znajduje się przykładowy plik persistence.xml, który definiuje jednostkę trwałą bazując na wartościach domyślnych.
<persistence> <persistence-unit name="NazwaKontekstuTrwalego"> </persistence-unit> </persistence>
Plik persistence.xml definiuje jednostkę trwałą o nazwie NazwaKontekstuTrwalego z domyślnym źródłem danych (który z kolei wskazuje na pewną, relacyjną bazę danych). Zależność między jednostką trwałą a kontekstem trwałym jest podobna do zależności typu i egzemplarza (instancji) w progamowaniu obiektowym. Pierwszy jest wzorcem drugiego. Pojęcie kontekstu trwałego istnieje wyłącznie podczas działania aplikacji i zmienia się dynamicznie w zależności od wywoływanych metod klasy EntityManager. Wszystkie klasy oznaczone adnotacją @Entity oraz pliku META-INF/orm.xml znajdujące się w katalogu, w którym znajduje się katalog META-INF z plikiem persistence.xml, będą traktowane jako encje zarządzane w ramach tego kontekstu. Istnieje mechanizm za pomocą, którego jawnie wskazujemy klasy zarządzane - encje - w ramach jednostki trwałej. Tym mechanizmem jest element <class> w pliku persistence.xml.
<persistence>
<persistence-unit name="NazwaKontekstuTrwalego">
<class>pl.jaceklaskowski.jpa.Osoba</class>
</persistence-unit>
</persistence>
W pliku persistence.xml możemy definiować więcej jednostek trwałych przez kolejne elementy persistence-unit, np.
<persistence> <persistence-unit name="NazwaKontekstuTrwalego" /> <persistence-unit name="InnyKontekstTrwaly" /> </persistence>
Klasy zarządzane przez obie jednostki trwałe (dalej zwane PU - ang. persistence unit) są identyczne, ale podczas działania aplikacji korzystającej z nich, ich stan i zarządzane egzemplarze encji mogą (i zazwyczaj będą) różne.
JPA dostarcza mechanizm utworzenia fabryki zarządców encji - javax.persistence.EntityManagerFactory - za pomocą adnotacji @PersistenceUnit. W ten sposób programista bierze na siebie odpowiedzialność za zarządzanie zarządcami encji utworzonymi przez fabrykę zarządców encji. W naszym przypadku będzie to nieuniknione.
public class Ziarno implements ZiarnoIntf {
@PersistenceUnit(unitName="InnyKontekstTrwaly")
EntityManagerFactory emf;
}
Mając do dyspozycji fabrykę zarządców encji możemy tworzyć egzemplarze zarządcy encji. Fabryka tworzy wiele egzamplarzy zarządców dla pojedyńczej definicji jednostki trwałej w pliku persistence.xml (w naszym przypadku będzie to InnyKontekstTrwaly).
I tutaj kończy się pomoc serwera aplikacji zgodnego ze specyfikacją Korporacyjnej Javy 5 (Java EE 5). W zasadzie to wykorzystanie adnotacji @PersistenceUnit dyskwalifikuje przynajmniej jedną z dostępnych usług serwera - zarządzanie zarządcami encji i w przypadku stanowego ziarna sesyjnego wyklucza stosowanie zarządców o rozszerzonym zasięgu (PersistenceContextType.EXTENDED).
Siłą JPA jest możliwość uruchomienia w środowisku poza serwerem aplikacji Java EE 5, np. w ramach samodzielnej aplikacji. W takiej sytuacji rolę serwera aplikacji przejmuje na siebie sama aplikacja i obowiązek utworzenia fabryki, zarządcy trwałego spoczywa na jej barkach. Jak? Przyjrzyjmy się poniższemu wycinkowi aplikacji.
public static void main(String args[]) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("InnyKontekstTrwaly");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new Osoba());
em.getTransaction().commit();
}
Zacznijmy od pierwszej linii, w której następuje utworzenie egzemplarza EntityManagerFactory za pomocą klasy javax.persistence.Persistence - jedynej klasy należącej do pakietu javax.persistence (!) Klasa Persistence jest drzwiami wejściowymi do świata JPA - tak dla serwera aplikacji jak i samodzielnej aplikacji (nota bene serwer aplikacji to właśnie samodzielna aplikacja i zapamiętanie tego oczywistego acz często zapominanego faktu bardzo pomaga w zrozumieniu zawiłości specyfikacji Korporacyjnej Javy, a w przypadku JPA zwłaszcza). Klasa Persistence udostępnia dwie metody Persistence.createEntityManagerFactory(...), które umożliwiają utworzenie fabryki dla danej jednostki trwałej z możliwymi parametrami konfiguracyjnymi przekazanymi w formie egzemplarza typu java.util.Map. I właśnie zrozumienie znaczenia tej struktury danych jest częścią rozwiązania naszej zagadki.
Za pomocą egzemplarza Map przekazujemy parametry konfiguracyjne dla dostawcy JPA, który konfigurowany jest zazwyczaj wyłącznie za pomocą pliku persistence.xml. Przypominając sobie fakt, że JPA jest częścią specyfikacji EJB3 i jednym z elementów środowiska serwera aplikacyjnego Java EE 5, możemy w ten sposób uzmysłowić sobie w jaki sposób serwer komunikuje się z dostawcą JPA, którym może być Apache OpenJPA, Hibernate EntityManager, czy TopLink Essentials (i wymieniłem wyłącznie tych najbardziej popularnych, a istnieją inni). W zasadzie nie ma to znaczenia, jaki dostawca JPA jest wykorzystywany - za pomocą klasy Persistence i wartości konfiguracyjnych przekazywanych przez Map możemy skonfigurować dowolnego dostawcę w sposób niezależny od jego faktycznej implementacji.
Pozostałe linie w powyższym wycinku zakładam, że są zrozumiałe.
Przyjrzyjmy się wymaganiom stawianym przez Michała:
- Dostępu do wielu baz danych poprzez JPA - wymaganie mamy obsłużone przez odpowiednią zawartość persistence.xml, w którym zdefiniujemy wiele jednostek trwałych.
- Ma to się odbywać w ramach ziaren EJB 3.0 - wymaganie jest do obsłużenia w dowolnej części naszej aplikacji, czy to w ramach ziarna EJB3, czy po prostu samodzielnej aplikacji.
- Nie ma z góry przypisanej liczby baz danych, do których się podłączamy (liczba ta zmienia się w trakcie działania programu) - pamiętając, że mamy możliwość manipulacji parametrami konfiguracyjnymi dla jednostki trwałej za pomocą Map możemy z łatwością wpływać na bazę danych, z jakiej jednostka będzie korzystała
- Każda z baz danych ma tą samą strukturę (mapowane są te same klasy encji) - do realizacji tego wymagania zastosujemy mechanizm <class>, chociaż tak na prawdę nie jest on potrzebny (OpenJPA realizuje trwałość encji przy pomocy modyfikacji klas - instrumentacji - bądź właśnie skorzystaniu z elementu <class>, więc w przypadku OpenJPA skorzystamy z niego jawnie).
- Chciałbym umieścić to wszystko w ramach ziarna stanowego (Stateful EJB 3.0) - patrz wymaganie nr 2 wyżej.
Aplikacja demonstracyjna JPA
Po krótkim wprowadzeniu do wnętrza JPA i zapoznaniu się z wymaganiami przejdźmy do wykorzystania wiedzy w praktyce.
Utworzenie projektu - jpa-multidb
Rozpoczniemy od stworzenia projektu aplikacji JPA przy pomocy Apache Maven 2. Mamy do dyspozycji wykonanie odpowiedniego polecenia z linii poleceń bądź bezpośrednio z poziomu zintegrowanego narzędzia programisty (IDE). Zaczniemy z linii poleceń i wesprzemy się później IDE.
jlaskowski@dev /cygdrive/c $ mvn archetype:create -DgroupId=pl.jaceklaskowski.jpa -DartifactId=jpa-multidb -Dversion=1.0 [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'archetype'. [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Default Project [INFO] task-segment: [archetype:create] (aggregator-style) [INFO] ------------------------------------------------------------------------ ... [INFO] Archetype created in dir: c:\jpa-multidb [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------
Utworzenie pliku konfiguracyjnego JPA - persistence.xml
Po imporcie projektu do wybranego środowiska programisty tworzymy plik persistence.xml (w przypadku projektów zarządzanych przez maven2 domyślnie będzie to katalog src/main/resources/META-INF).
<?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="pu-z-persistence.xml" transaction-type="RESOURCE_LOCAL">
<class>pl.jaceklaskowski.jpa.Osoba</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(SchemaAction='add,deleteTableContents')" />
<property name="openjpa.Log" value="DefaultLevel=WARN,SQL=TRACE" />
</properties>
</persistence-unit>
</persistence>
Plik persistence.xml definiuje pojedyńczą jednostkę trwałą o nazwie pu-z-persistence.xml z transakcjami zarządzanymi lokalnie - RESOURCE_LOCAL. Jawnie wskazujemy na encję Osoba jako klasę zarządzaną w ramach tej jednostki. Na koniec, w sekcji properties, definiujemy właściwości wspólne (później stanie się jasne, co rozumiem przez pojęcie właściwości wspólne). Właściwości są specyficzne dla OpenJPA, z którego będziemy korzystać w naszym przykładzie jako dostawcę JPA, jednakże możemy zdefiniować i inne właściwości konfiguracyjne. Specyfikacja JPA gwarantuje, że właściwości nieznane dla danego dostawcy JPA będą ignorowane.
Encja Osoba
Przejdźmy do utworzenia encji Osoba.
package pl.jaceklaskowski.jpa;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Osoba implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
public void setId(int id) {
this.id = id;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
@Override
public int hashCode() {
int hash = 0;
hash += getId();
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Osoba)) {
return false;
}
Osoba other = (Osoba) object;
if (this.getId() != other.getId()) {
return false;
}
return true;
}
@Override
public String toString() {
return "pl.jaceklaskowski.jpa.Osoba[id=" + getId() + "]";
}
}
Klasa encji Osoba sama się opisuje, więc nie ma co się rozpisywać na jej temat. Plik Osoba.java należy umieścić w katalogu src/main/java/pl/jaceklaskowski/jpa.
Klasa testowa - JpaMultiDbTest
Na zakończenie należałoby zaprezentować aplikację korzystającą z JPA, która jednocześnie spełnia nasze wymagania.
package pl.jaceklaskowski.jpa;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import junit.framework.TestCase;
public class JpaMultiDbTest extends TestCase {
public void testJPA() {
// Właściwości dodatkowe dla różnych konfiguracji bazy danych
// http://openjpa.apache.org/docs/latest/manual/ref_guide_conf_openjpa.html
Map<String, String> props = new HashMap<String, String>();
{
// konfiguracja JPA dla HSQL
props.put("openjpa.Id", "hsqlPU");
props.put("openjpa.ConnectionURL", "jdbc:hsqldb:target/db-hypersonic");
props.put("openjpa.ConnectionDriverName", "org.hsqldb.jdbcDriver");
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu-z-persistence.xml", props);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new Osoba());
em.getTransaction().commit();
}
{
// konfiguracja JPA dla Derby
props.put("openjpa.ConnectionURL", "jdbc:derby:target/derbyDB;create=true");
props.put("openjpa.ConnectionDriverName", "org.apache.derby.jdbc.EmbeddedDriver");
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu-z-persistence.xml", props);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new Osoba());
em.getTransaction().commit();
}
}
}
Tak, jest to klasa testowa z wykorzystaniem JUnit. Wykorzystałem możliwość uruchamiania testów jednostkowych z poziomu Maven, aby nie angażować się w tworzenie wyrafinowanego środowiska uruchomieniowego dla samodzielnej aplikacji. Za pomocą pojedyńczego polecenia mvn test mogę w prosty sposób ją uruchomić.
Przeanalizujmy budowę metody testJPA(), która jest metodą główną w tej klasie. Podczas uruchomienia aplikacji (klasy testowej) tworzymy mapę właściwości dodatkowych. Właśnie tutaj pojawia się wyjaśnienie różnicy między właściwościami wspólnymi, a dodatkowymi. Wspólne właściwości umieściłem w pliku persistence.xml dla prostoty ich zarządzania, podczas gdy dynamicznymi zmiennymi konfiguracyjnymi zarządzam z poziomu aplikacji. Istotną kwestią jest działanie mapy właściwości, które nadpisują właściwości wspólne, zdefiniowane w persistence.xml. W ten sposób definiując pojedyńczą jednostkę trwałą traktuję ją jako wzorzec dla innych, dynamicznie definiowanych. Zarządzaniem zarządcami encji zajmuje się aplikacja, więc pozostaje umieścić egzemplarze EntityManager do osobnego egzemplarza Map, który należałoby przekazywać do metod, które z kontekstów trwałych chciałyby korzystać (tutaj uwidacznia się zaleta stosowania serwera aplikacji, który zazwyczaj robi to za nas, co w naszej sytuacji nie jest możliwe do wykorzystania).
Plik JpaMultiDbTest.java zapisujemy w katalogu src/test/java/pl/jaceklaskowski/jpa.
Konfiguracja projektu - pom.xml
Pozostaje zaprezentować plik konfiguracyjny projektu zarządzanego przez maven2 - pom.xml.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jaceklaskowski.jpa</groupId>
<artifactId>jpa-multidb</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>jpa-multidb</name>
<url>http://www.jaceklaskowski.pl/wiki/Dynamiczny_dost%C4%99p_do_wielu_baz_danych_w_JPA</url>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.3.2.1</version>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
</dependency>
</dependencies>
</project>
Uruchomienie aplikacji
Pora na uruchomienie aplikacji za pomocą mvn clean test weryfikując prawdziwość naszego dotychczasowego rozumowania.
jlaskowski@dev /cygdrive/c/jpa-multidb $ mvn clean test [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] Building jpa-multidb [INFO] task-segment: [clean, test] [INFO] ------------------------------------------------------------------------ [INFO] [clean:clean] [INFO] Deleting directory c:\jpa-multidb\target [INFO] [resources:resources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:compile] [INFO] Compiling 1 source file to c:\jpa-multidb\target\classes [INFO] [resources:testResources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:testCompile] [INFO] Compiling 1 source file to c:\jpa-multidb\target\test-classes [INFO] [surefire:test] [INFO] Surefire report directory: c:\jpa-multidb\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running pl.jaceklaskowski.jpa.JpaMultiDbTest 1203 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 21307627> executing prepstmnt 3969559 SELECT SEQUENCE_SCHEMA, SEQUENCE_NAME FROM INFORMATION_SCHEMA.SYSTEM_SEQUENCES 1203 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 21307627> [0 ms] spent 1203 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 13121485> executing stmnt 9030750 CREATE TABLE OPENJPA_SEQUENCE_TABLE (ID TINYINT NOT NULL, SEQUENCE_VALUE BIGINT, PRIMA RY KEY (ID)) 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 13121485> [15 ms] spent 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 31365828> executing stmnt 3916915 CREATE TABLE Osoba (id INTEGER NOT NULL, PRIMARY KEY (id)) 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 31365828> [0 ms] spent 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 3686501> executing stmnt 4047035 DELETE FROM OPENJPA_SEQUENCE_TABLE 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 3686501> [0 ms] spent 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 3686501> executing stmnt 17547166 DELETE FROM Osoba 1218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 3686501> [0 ms] spent 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 16625677> executing prepstmnt 3981922 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? [params=(int) 0] 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 16625677> [0 ms] spent 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 5670411> executing prepstmnt 20000831 INSERT INTO OPENJPA_SEQUENCE_TABLE (ID, SEQUENCE_VALUE) VALUES (?, ?) [params=(int ) 0, (int) 1] 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 5670411> [0 ms] spent 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 11608737> executing prepstmnt 8018457 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? [params=(int) 0] 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 11608737> [0 ms] spent 1562 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 11608737> executing prepstmnt 22584918 UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VA LUE = ? [params=(long) 51, (int) 0, (long) 1] 1578 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 11608737> [16 ms] spent 1593 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 28708894> executing prepstmnt 20391510 INSERT INTO Osoba (id) VALUES (?) [params=(int) 1] 1593 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 28708894> [0 ms] spent 8687 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 10594949> executing stmnt 25782860 CREATE TABLE OPENJPA_SEQUENCE_TABLE (ID SMALLINT NOT NULL, SEQUENCE_VALUE BIGINT, PRI MARY KEY (ID)) 8906 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 10594949> [219 ms] spent 8906 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 13218198> executing stmnt 7962652 CREATE TABLE Osoba (id INTEGER NOT NULL, PRIMARY KEY (id)) 9062 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 13218198> [156 ms] spent 9078 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 8066625> executing stmnt 20194629 DELETE FROM OPENJPA_SEQUENCE_TABLE 9109 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 8066625> [31 ms] spent 9125 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 8066625> executing stmnt 15886558 DELETE FROM Osoba 9125 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 8066625> [0 ms] spent 9203 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 23053938> executing prepstmnt 14980779 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WITH RR [params=(int) 0] 9203 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 23053938> [0 ms] spent 9218 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 19351067> executing prepstmnt 26188661 INSERT INTO OPENJPA_SEQUENCE_TABLE (ID, SEQUENCE_VALUE) VALUES (?, ?) [params=(in t) 0, (int) 1] 9234 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 19351067> [16 ms] spent 9234 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 14615126> executing prepstmnt 21209219 SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE WITH RR [params=(int) 0] 9234 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 14615126> [0 ms] spent 9250 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 14615126> executing prepstmnt 6722010 UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VAL UE = ? [params=(long) 51, (int) 0, (long) 1] 9265 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 14615126> [15 ms] spent 9265 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 14625088> executing prepstmnt 16134229 INSERT INTO Osoba (id) VALUES (?) [params=(int) 1] 9265 hsqlPU TRACE [main] openjpa.jdbc.SQL - <t 10175206, conn 14625088> [0 ms] spent Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.485 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------
BUILD SUCCESSFUL to gwarancja pomyślnego wykonania naszej aplikacji i tym samym potwierdzenie słuszności naszego rozumowania. Kolejna kwestia rozwiązana.
