7. Spring Data JPA EclipseLink
7.1. Introduction
Ми повертаємося до попередньої архітектури, яку тепер реалізуємо за допомогою шару JPA / EclipseLink.
![]() |
7.2. Налаштування робочого середовища
За допомогою STS завантажте проект [myql-config-jpa-hibernate] [1-4]:
![]() |
потім імпортуйте проект [mysl-config-jpa-eclipselink] [5], який знаходиться у папці [<exemples>/spring-database-config/mysql/eclipse] [6]:
![]() |
Після цього скиньте налаштування середовища Maven (Alt-F5) для всіх проектів, що містяться в [Package Explorer]:
![]() |
Потім, щоб перевірити робоче середовище, запустіть конфігурацію виконання з назвою [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]:
![]() |
Ця конфігурація запускає тест [JUnitTestDao]. Цей тест має завершитися успішно:
![]() |
7.3. Проект конфігурації рівня JPA
![]() |
Цей проект призначений для конфігурації шару JPA у наведеній нижче архітектурі:
![]() |
7.3.1. Конфігурація Maven
Проєкт є проєктом Maven і налаштовується за допомогою такого файлу [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>
<!-- змінні залежності ********************************************** -->
<!-- JPA провайдер -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.0</version>
</dependency>
<!-- постійні залежності ********************************************** -->
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- успадкована конфігурація 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>
<!-- Цей плагін забезпечує статичне вплетення 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>
<!--Конфігурація цього плагіна використовується виключно для збереження налаштувань Eclipse m2e. Вона не впливає на сам процес збірки 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>
- рядки 5–7: артефакт Maven, згенерований цим проєктом. Він такий самий, як і в проєкті [mysql-config-jpa-hibernate]. Це означає, що в будь-який момент часу активним може бути лише один із цих проєктів;
- рядки 10–14: батьківський проект Maven, який визначає версії більшості залежностей, необхідних для проекту;
- рядки 19–22: бібліотека EclipseLink;
- рядки 26–29: бібліотека Spring Data;
- рядки 32–34: проект конфігурації шару JPA базується на проекті конфігурації шару JDBC, який, серед іншого, визначає драйвер JDBC для використовуваного SGBD та координати бази даних, що має використовуватися;
- рядки 35–40: проект конфігурації шару JDBC включає бібліотеку [Spring JDBC], яка тут замінена на бібліотеку [Spring Data JPA]. Тому рекомендується не включати її до залежностей проекту. Однак якщо вона залишиться, це не спричинить помилок;
- плагін у рядках 58–81 реалізує «weaving» з сутностей JPA. Те, що англосакси називають weaving, — це трансформація (розширення) сутностей JPA для забезпечення підтримки Lazy Loading. Нам не довелося налаштовувати Hibernate, щоб забезпечити роботу цього weaving. Для EclipseLink потрібен плагін Maven. Я дуже довго шукав спосіб змусити EclipseLink дотримуватися атрибута [fetch = FetchType.LAZY] з анотації [@ManyToOne], наведеної нижче:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)
private Categorie categorie;
У специфікації JPA зазначено, що атрибут [fetch = FetchType.LAZY] анотації [@ManyToOne] є «підказкою» (підказка), якого реалізація JPA не зобов’язана дотримуватися. І справді, EclipseLink за замовчуванням його не дотримується. Щоб він його дотримувався, потрібна спеціальна конфігурація. Після багатьох марних пошуків я знайшов рішення щодо URL, згаданого в рядку 51. Коли додати рядки 58–81 до файлу [pom.xml], Eclipse повідомляє про помилку у файлі. Це проблема налаштування плагіна [m2e], який забезпечує управління проектами Maven у Eclipse. Щоб усунути помилку, потрібно додати рядки 83–119.
У підсумку залежності мають такий вигляд:
![]() |
7.3.2. Конфігурація Spring
![]() |
Клас [ConfigJpa] налаштовує проект 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 {
// провайдер JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
// Примітка: сутності JPA та конфігурація EclipseLink містяться у файлі META-INF/persistence.xml
EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkJpaVendorAdapter.setShowSql(false);
eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
return eclipseLinkJpaVendorAdapter;
}
// джерело даних
@Bean
public DataSource dataSource() {
// джерело даних TomcatJdbc
DataSource dataSource = new DataSource();
// конфігурація доступу JDBC
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
// спочатку відкриті з'єднання
dataSource.setInitialSize(5);
// результат
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;
}
// Менеджер транзакцій
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
Ця конфігурація аналогічна тій, що детально описана в розділі 6.3.2 для реалізації Hibernate JPA. Ми детально опишемо лише відмінності:
- рядки 23–31: бін [jpaVendorAdapter] тепер реалізовано за допомогою EclipseLink;
- рядки 50–58: у версії Hibernate JPA було написано:
який використовувався для визначення місця пошуку об’єктів JPA. Тут використовується файл [persistence.xml] (коментар у рядку 25) (див. розділ 6.3.4) для:
- визначення символів JPA;
- налаштування EclipseLink для weaving цих об’єктів;
7.4. Файл [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">
<!-- об’єкти 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>
<!-- Властивості, необхідні для пошуку [@ManyToOne] у режимі 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>
- рядок 4: одиниця збереження. Вона може мати будь-яку назву (атрибут name);
- рядки 6–10: п’ять об’єктів JPA, які потрібно обробити;
- рядок 11 є важливим. Іноді трапляється, що проект визначає сутності, які використовуються в різних контекстах. Рядок 11 гарантує, що не буде інших сутностей, крім тих, що визначені в рядках 5–10. Це важливо, коли вони використовуються для генерації таблиць джерела даних. Надлишкові сутності призвели б до створення зайвих таблиць;
- рядки 13–17: налаштування EclipseLink для статичного weaving. Існує два типи weaving:
- [statique]: сутності JPA доповнюються (woven) одразу після інстанціювання шару JPA;
- [dynamique]: сутності JPA збагачуються (woven) під час їхнього першого надходження до шару JPA;
7.5. Об’єкти JPA
![]() |
Елементи JPA — це ті, що описані в розділі 6.3.3 для реалізації Hibernate, з двома відмінностями:
- усі сутності JPA мають анотацію [@Cache(alwaysRefresh = true)], яка скасовує кеш EclipseLink. У цьому документі не використовуються кеші реалізацій JPA. Кеш EclipseLink, здається, активний за замовчуванням і спричиняв помилки під час тестування.
@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
- усі анотації [@OneToMany] супроводжуються анотацією [@CascadeOnDelete]:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL })
@CascadeOnDelete
private List<Produit> produits;
Ця анотація відіграє роль під час генерації таблиць на основі сутностей JPA. Вона додає до зовнішніх ключів (у даному випадку PRODUITS[CATEGORIE_ID] ---> CATEGORIES[ID]) атрибут SQL [ON DELETE CASCADE], завдяки якому щоразу, коли видаляється категорія в таблиці [CATEGORIES], відповідні товари в таблиці [PRODUITS] також видаляються;
Примітка: важливо зазначити, що ця анотація використовується як під час створення таблиці, як ми щойно бачили, так і під час її експлуатації. EclipseLink передбачає наявність атрибута SQL [ON DELETE CASCADE] і використовує його щоразу, коли отримує запит на видалення категорії. Його відсутність спричинить помилки.
7.6. Рівень тестування
![]() |
![]() |
Наведені вище тести ідентичні тестам для реалізацій Spring JDBC та Spring JPA Hibernate. За необхідності див. наступні сторінки:
- [JUnitTestCheckArguments]: розділ 4.11.1;
- [JUnitTestDao]: параграф 4.11.2;
- [JUnitTestPushTheLimits]: пункт 4.11.3;
- [JUnitTestProxies]: пункт 6.4.5;
Отримано такі результати:
![]() |
![]() |
- у [1], [JUnitTestPushTheLimits-EclipseLink]: 70,583 с
- у [2], [JUnitTestPushTheLimits-Hibernate]: 78,945 с
- у [3], [JUnitTestPushTheLimits-JDBC]: 36,09 с
Тест [JUnitTestProxies] дає такі результати на консолі:
Тут видно, що при доступі до поля [Categorie.produits] категорії типу PROXY та до поля [Produit.categorie] товару типу PROXY інформацію вдається отримати в обох випадках (рядки 7 і 17). З трьох реалізацій JPA це єдина, яка дозволяє це зробити для сутностей PROXY.















