7. Spring Data JPA EclipseLink
7.1. Introduction
Wracamy do poprzedniej architektury, którą teraz wdrażamy z warstwą JPA / EclipseLink.
![]() |
7.2. Konfiguracja środowiska pracy
Za pomocą pliku STS pobierz projekt [myql-config-jpa-hibernate] [1-4]:
![]() |
a następnie zaimportuj projekt [mysl-config-jpa-eclipselink] [5] znajdujący się w folderze [<exemples>/spring-database-config/mysql/eclipse] [6]:
![]() |
Następnie zresetuj środowisko Maven (Alt-F5) dla wszystkich projektów znajdujących się w folderze [Package Explorer]:
![]() |
Następnie, aby sprawdzić środowisko robocze, uruchom konfigurację uruchomieniową o nazwie [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]:
![]() |
Ta konfiguracja uruchamia test o nazwie [JUnitTestDao]. Test ten musi zakończyć się powodzeniem:
![]() |
7.3. Projekt konfiguracji warstwy JPA
![]() |
Zadaniem tego projektu jest skonfigurowanie warstwy JPA w poniższej architekturze:
![]() |
7.3.1. Konfiguracja Maven
Projekt jest projektem Maven i jest skonfigurowany za pomocą następującego pliku [pom.xml]:
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>configuration mysql openjpa</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- zmienne zależności ********************************************** -->
<!-- JPA dostawca -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.0</version>
</dependency>
<!-- stałe zależności ********************************************** -->
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- konfiguracja dziedziczona JDBC -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<!-- [https://flexguse.wordpress.com/2013/08/10/maven-spring-data-jpa-eclipselink-and-static-weaving/] -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
<!-- Ta wtyczka zapewnia statyczne wplatanie EclipseLink -->
<plugin>
<artifactId>staticweave-maven-plugin</artifactId>
<groupId>de.empulse.eclipselink</groupId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>weave</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<logLevel>ALL</logLevel>
<!-- <includeProjectClasspath>true</includeProjectClasspath> -->
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--Konfiguracja tej wtyczki służy wyłącznie do przechowywania ustawień Eclipse m2e. Nie ma ona żadnego wpływu na sam proces kompilacji w Maven. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
de.empulse.eclipselink
</groupId>
<artifactId>
staticweave-maven-plugin
</artifactId>
<versionRange>
[1.0.0,)
</versionRange>
<goals>
<goal>weave</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute>
<runOnIncremental>true</runOnIncremental>
</execute>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- wiersze 5–7: artefakt Maven wygenerowany przez ten projekt. Jest to ten sam artefakt, co w projekcie [mysql-config-jpa-hibernate]. Oznacza to, że w danym momencie aktywny może być tylko jeden z tych projektów;
- wiersze 10–14: nadrzędny projekt Maven, który określa wersje większości zależności niezbędnych dla projektu;
- wiersze 19–22: biblioteka EclipseLink;
- wiersze 26–29: biblioteka Spring Data;
- wiersze 32–34: projekt konfiguracyjny warstwy JPA opiera się na projekcie konfiguracyjnym warstwy JDBC, który definiuje między innymi sterownik JDBC dla używanego SGBD oraz dane bazy danych, z której należy korzystać;
- wiersze 35–40: projekt konfiguracji warstwy JDBC zawiera bibliotekę [Spring JDBC], która została tutaj zastąpiona biblioteką [Spring Data JPA]. W związku z tym zaleca się, aby nie uwzględniać jej w zależnościach projektu. Jeśli jednak pozostanie, nie spowoduje to żadnych błędów;
- wtyczka z wierszy 58–81 implementuje „weaving” z elementów JPA. To, co w języku angielskim nazywa się „weaving”, to transformacja (wzbogacenie) encji JPA w celu zapewnienia obsługi funkcji Lazy Loading. Nie musieliśmy konfigurować Hibernate, aby ten weaving zadziałał. W przypadku EclipseLink potrzebna jest wtyczka Maven. Bardzo długo szukałem sposobu, aby wymusić na EclipseLink przestrzeganie atrybutu [fetch = FetchType.LAZY] z poniższej adnotacji [@ManyToOne]:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)
private Categorie categorie;
Specyfikacja JPA wskazuje, że atrybut [fetch = FetchType.LAZY] adnotacji [@ManyToOne] jest „wskazówką” (wskazówką), której implementacja JPA nie musi przestrzegać. I rzeczywiście, EclipseLink domyślnie jej nie przestrzega. Aby ją przestrzegała, konieczna jest specjalna konfiguracja. Po wielu bezowocnych poszukiwaniach znalazłem rozwiązanie dotyczące wtyczki URL, o której mowa w wierszu 51. Gdy dodamy wiersze 58–81 do pliku [pom.xml], Eclipse zgłasza błąd dotyczący tego pliku. Jest to problem związany z konfiguracją wtyczki [m2e], która odpowiada za zarządzanie projektami Maven w środowisku Eclipse. Aby usunąć ten błąd, należy dodać linie 83–119.
Ostatecznie zależności są następujące:
![]() |
7.3.2. Konfiguracja Spring
![]() |
Klasa [ConfigJpa] konfiguruje projekt Spring:
package generic.jpa.config;
import generic.jdbc.config.ConfigJdbc;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@Import({ ConfigJdbc.class })
public class ConfigJpa {
// dostawca JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
// Uwaga: elementy JPA oraz konfiguracja EclipseLink znajdują się w pliku META-INF/persistence.xml
EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkJpaVendorAdapter.setShowSql(false);
eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
return eclipseLinkJpaVendorAdapter;
}
// źródło danych
@Bean
public DataSource dataSource() {
// źródło danych TomcatJdbc
DataSource dataSource = new DataSource();
// konfiguracja dostępu JDBC
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
// początkowo otwarte połączenia
dataSource.setInitialSize(5);
// wynik
return dataSource;
}
// EntityManagerFactory
@Bean
public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
EntityManagerFactory entityManagerFactory = factory.getObject();
return entityManagerFactory;
}
// Menedżer transakcji
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
Konfiguracja ta jest analogiczna do tej opisanej w paragrafie 6.3.2, dotyczącej implementacji Hibernate o nazwie JPA. Przedstawiamy jedynie różnice:
- wiersze 23–31: bean [jpaVendorAdapter] jest teraz zaimplementowany przy użyciu EclipseLink;
- wiersze 50–58: w wersji Hibernate o numerze JPA zapisano:
który służył do wskazania, gdzie należy szukać jednostek JPA. W tym przypadku odwołujemy się do pliku [persistence.xml] (komentarz w wierszu 25) (por. punkt 6.3.4) w celu:
- zdefiniowanie elementów JPA;
- skonfigurowania pliku EclipseLink dla pliku weaving tych jednostek;
7.4. Plik [persistence.xml]
![]() |
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="generic-jpa-entities-dbproduitscategories" transaction-type="RESOURCE_LOCAL">
<!-- podmioty JPA -->
<class>generic.jpa.entities.dbproduitscategories.Categorie</class>
<class>generic.jpa.entities.dbproduitscategories.Produit</class>
<class>generic.jpa.entities.dbproduitscategories.User</class>
<class>generic.jpa.entities.dbproduitscategories.Role</class>
<class>generic.jpa.entities.dbproduitscategories.UserRole</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<!-- Właściwości niezbędne do wyszukiwania [@ManyToOne] w trybie LAZY -->
<properties>
<property name="eclipselink.weaving" value="static" />
<property name="eclipselink.weaving.lazy" value="true" />
<property name="eclipselink.weaving.internal" value="true" />
</properties>
</persistence-unit>
</persistence>
- wiersz 4: jednostka trwałości. Może ona nosić dowolną nazwę (atrybut name);
- wiersze 6–10: pięć elementów JPA, które mają być zarządzane;
- wiersz 11 jest ważny. Czasami zdarza się, że projekt definiuje encje wykorzystywane w różnych kontekstach. Wiersz 11 gwarantuje, że nie będzie żadnych innych encji poza tymi zdefiniowanymi w wierszach 5–10. Jest to istotne, gdy encje te są wykorzystywane do generowania tabel źródła danych. Nadmiar encji spowodowałby wygenerowanie zbędnych tabel;
- wiersze 13–17: konfiguracja EclipseLink dla statycznego weaving. Istnieją dwa typy weaving:
- [statique]: encje JPA są wzbogacane (woven) natychmiast po instancjonowaniu warstwy JPA;
- [dynamique]: encje JPA są wzbogacane (woven) przy pierwszym pojawieniu się w warstwie JPA;
7.5. Entities JPA
![]() |
Entytety JPA są opisane w paragrafie 6.3.3 dla implementacji Hibernate z dwiema różnicami:
- wszystkie encje JPA posiadają adnotację [@Cache(alwaysRefresh = true)], która wyłącza buforowanie EclipseLink. W niniejszym dokumencie nie korzysta się z pamięci podręcznych stosowanych w implementacjach JPA. Pamięć podręczna EclipseLink wydaje się być domyślnie aktywna i powodowała błędy podczas testów.
@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
- wszystkie adnotacje [@OneToMany] są opatrzone adnotacją [@CascadeOnDelete]:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL })
@CascadeOnDelete
private List<Produit> produits;
Adnotacja ta odgrywa rolę podczas generowania tabel na podstawie encji JPA. Dodaje ona do kluczy obcych (w tym przypadku PRODUITS[CATEGORIE_ID] ---> CATEGORIES[ID]) atrybut SQL [ON DELETE CASCADE], co powoduje, że za każdym razem, gdy usuwa się kategorię z tabeli [CATEGORIES], odpowiednie produkty w tabeli [PRODUITS] są również usuwane;
Uwaga: należy pamiętać, że ta adnotacja jest wykorzystywana zarówno podczas tworzenia tabeli, jak to właśnie widzieliśmy, jak i podczas jej eksploatacji. Tabela EclipseLink zakłada, że atrybut SQL [ON DELETE CASCADE] rzeczywiście istnieje i wykorzystuje go za każdym razem, gdy otrzymuje polecenie usunięcia kategorii. Jego brak spowodowałby błędy.
7.6. Warstwa testowa
![]() |
![]() |
Powyższe testy są identyczne z testami dla implementacji Spring JDBC i Spring JPA Hibernate. W razie potrzeby należy zapoznać się z następującymi stronami:
- [JUnitTestCheckArguments]: punkt 4.11.1;
- [JUnitTestDao]: punkt 4.11.2;
- [JUnitTestPushTheLimits]: punkt 4.11.3;
- [JUnitTestProxies]: punkt 6.4.5;
Uzyskane wyniki są następujące:
![]() |
![]() |
- w [1], [JUnitTestPushTheLimits-EclipseLink]: 70,583 s
- w [2], [JUnitTestPushTheLimits-Hibernate]: 78,945 s
- w [3], [JUnitTestPushTheLimits-JDBC]: 36,09 s
Test [JUnitTestProxies] daje następujące wyniki na konsoli:
Widać tutaj, że podczas uzyskiwania dostępu do pola [Categorie.produits] w kategorii typu PROXY oraz do pola [Produit.categorie] w produkcie typu PROXY, udaje się uzyskać informacje w obu przypadkach (wiersze 7 i 17). Spośród trzech implementacji JPA jest to jedyna, która umożliwia to w przypadku encji typu PROXY.















