7. Spring Data JPA EclipseLink
7.1. Introduzione
Stiamo riutilizzando l'architettura precedente, che ora stiamo implementando con un livello JPA/EclipseLink.
![]() |
7.2. Configurazione dell'ambiente di sviluppo
Utilizzando STS, scaricare il progetto [myql-config-jpa-hibernate] [1-4]:
![]() |
quindi importare il progetto [mysl-config-jpa-eclipselink] [5] che si trova nella cartella [<examples>/spring-database-config/mysql/eclipse] [6]:
![]() |
Una volta fatto ciò, aggiorna l'ambiente Maven (Alt-F5) per tutti i progetti in [Package Explorer]:
![]() |
Quindi, per verificare l'ambiente di lavoro, esegui la configurazione di build denominata [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]:
![]() |
Questa configurazione esegue il test [JUnitTestDao]. Il test dovrebbe avere esito positivo:
![]() |
7.3. Il progetto di configurazione del livello JPA
![]() |
Lo scopo di questo progetto è configurare il livello JPA dell'architettura illustrata di seguito:
![]() |
7.3.1. Configurazione Maven
Il progetto è un progetto Maven configurato dal seguente file [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>
<!-- dépendances variables ********************************************** -->
<!-- JPA provider -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.0</version>
</dependency>
<!-- dépendances constantes ********************************************** -->
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- configuration JDBC inherited -->
<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>
<!-- This plugin ensures the EclipseLink static weaving -->
<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>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. -->
<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>
- righe 5–7: l'artefatto Maven generato da questo progetto. È lo stesso del progetto [mysql-config-jpa-hibernate]. Ciò significa che solo uno di questi progetti può essere attivo in un dato momento;
- righe 10–14: il progetto Maven principale che specifica le versioni della maggior parte delle dipendenze richieste dal progetto;
- righe 19–22: la libreria EclipseLink;
- righe 26–29: la libreria Spring Data;
- righe 32–34: il progetto di configurazione del livello JPA si basa sul progetto di configurazione del livello JDBC, che definisce, tra le altre cose, il driver JDBC per il DBMS in uso e i dettagli di connessione al database;
- righe 35–40: il progetto di configurazione del livello JDBC include la libreria [Spring JDBC], che qui viene sostituita dalla libreria [Spring Data JPA]. Pertanto, specifichiamo di non includerla nelle dipendenze del progetto. Tuttavia, se rimane, ciò non causa alcun errore;
- Il plugin nelle righe 58–81 implementa il weaving delle entità JPA. Ciò che viene definito weaving è la trasformazione (arricchimento) delle entità JPA in modo che supportino il caricamento differito. Non è stato necessario configurare Hibernate affinché questo weaving avvenisse. Per EclipseLink è richiesto un plugin Maven. Ho passato molto tempo a cercare di capire come forzare EclipseLink a rispettare l'attributo [fetch = FetchType.LAZY] dell'annotazione [@ManyToOne] riportata di seguito:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)
private Categorie categorie;
La specifica JPA afferma che l'attributo [fetch = FetchType.LAZY] dell'annotazione [@ManyToOne] è un "suggerimento" che l'implementazione JPA non è tenuta a seguire. E infatti, EclipseLink non lo segue per impostazione predefinita. Per farlo è necessaria una configurazione speciale. Dopo molte ricerche infruttuose, ho trovato la soluzione all'URL citato alla riga 51. Quando le righe 58-81 sono incluse nel file [pom.xml], Eclipse segnala un errore sul file. Si tratta di un problema di configurazione con il plugin [m2e], che gestisce i progetti Maven all'interno di Eclipse. È necessario aggiungere le righe 83-119 per risolvere l'errore.
Alla fine, le dipendenze sono le seguenti:
![]() |
7.3.2. Configurazione di Spring
![]() |
La classe [ConfigJpa] configura il progetto 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 {
// the provider JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
// Note: JPA entities and Eclipselink configuration are in the META-INF/persistence.xml file
EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkJpaVendorAdapter.setShowSql(false);
eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
return eclipseLinkJpaVendorAdapter;
}
// data source
@Bean
public DataSource dataSource() {
// data source TomcatJdbc
DataSource dataSource = new DataSource();
// configuration access JDBC
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
// initially open connections
dataSource.setInitialSize(5);
// result
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;
}
// Transaction manager
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
Questa configurazione è simile a quella descritta nella Sezione 6.3.2 per l'implementazione JPA di Hibernate. Ci limiteremo a illustrare le differenze:
- righe 23–31: il bean [jpaVendorAdapter] è ora implementato con EclipseLink;
- righe 50–58: nella versione JPA di Hibernate, il codice era:
che veniva utilizzato per specificare dove cercare le entità JPA. Qui ci affidiamo al file [persistence.xml] (commento alla riga 25) (vedi sezione 6.3.4) per:
- definire le entità JPA;
- configurare EclipseLink per l'integrazione di queste entità;
7.4. Il file [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">
<!-- entities 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>
<!-- properties required for [@ManyToOne] to be searched in LAZY mode -->
<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>
- riga 4: l'unità di persistenza. Può avere qualsiasi nome (attributo name);
- righe 6–10: le cinque entità JPA da gestire;
- La riga 11 è importante. A volte un progetto definisce entità utilizzate in contesti diversi. La riga 11 garantisce che non ci siano entità diverse da quelle definite nelle righe 5–10. Questo è importante quando queste entità vengono utilizzate per generare le tabelle dell'origine dati. La presenza di entità extra genererebbe tabelle extra;
- Righe 13–17: configurazione di EclipseLink per il weaving statico. Esistono due tipi di weaving:
- [static]: le entità JPA vengono intrecciate non appena il livello JPA viene istanziato;
- [dinamico]: le entità JPA vengono arricchite (woven) la prima volta che entrano nel livello JPA;
7.5. Entità JPA
![]() |
Le entità JPA sono quelle descritte nella Sezione 6.3.3 per l'implementazione Hibernate, con due differenze:
- tutte le entità JPA hanno l'annotazione [@Cache(alwaysRefresh = true)], che disabilita la cache di EclipseLink. In questo documento, le cache delle implementazioni JPA utilizzate non vengono impiegate. La cache di EclipseLink sembra essere abilitata per impostazione predefinita e ha causato errori nei test.
@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
- Tutte le annotazioni [@OneToMany] sono accompagnate dall'annotazione [@CascadeOnDelete]:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL })
@CascadeOnDelete
private List<Produit> produits;
Questa annotazione entra in gioco durante la generazione delle tabelle a partire dalle entità JPA. Aggiunge l'attributo SQL [ON DELETE CASCADE] alle chiavi esterne (in questo caso PRODUCTS[CATEGORY_ID] ---> CATEGORIES[ID]), il quale garantisce che ogni volta che una categoria viene eliminata dalla tabella [CATEGORIES], vengano eliminati anche i prodotti corrispondenti nella tabella [PRODUCTS];
Nota: è importante notare che questa annotazione viene utilizzata sia durante la creazione della tabella, come abbiamo appena visto, sia durante l'interrogazione della stessa. EclipseLink presume che l'attributo SQL [ON DELETE CASCADE] sia presente e lo utilizza ogni volta che gli viene richiesto di eliminare una categoria. La sua assenza causerebbe errori.
7.6. Il livello di test
![]() |
![]() |
I test sopra riportati sono identici a quelli per le implementazioni Spring JDBC e Spring JPA Hibernate. Se necessario, consultare le pagine seguenti:
- [JUnitTestCheckArguments]: sezione 4.11.1;
- [JUnitTestDao]: sezione 4.11.2;
- [JUnitTestPushTheLimits]: sezione 4.11.3;
- [JUnitTestProxies]: sezione 6.4.5;
I risultati ottenuti sono i seguenti:
![]() |
![]() |
- in [1], [JUnitTestPushTheLimits-EclipseLink]: 70,583 s
- in [2], [JUnitTestPushTheLimits-Hibernate]: 78,945 s
- in [3], [JUnitTestPushTheLimits-JDBC]: 36,09 s
Il test [JUnitTestProxies] produce il seguente output della console:
Qui vediamo che quando si accede al campo [Category.products] di una categoria di tipo PROXY e al campo [Product.category] di un prodotto di tipo PROXY, in entrambi i casi (righe 7 e 17) si recuperano correttamente le informazioni. Tra le tre implementazioni JPA, questa è l'unica che lo consente sulle entità PROXY.















