7. Spring Data JPA EclipseLink
7.1. Introdução
Estamos a reutilizar a arquitetura anterior, que agora estamos a implementar com uma camada JPA/EclipseLink.
![]() |
7.2. Configurar o ambiente de desenvolvimento
Utilizando o STS, descarregue o projeto [myql-config-jpa-hibernate] [1-4]:
![]() |
depois importe o projeto [mysl-config-jpa-eclipselink] [5] localizado na pasta [<examples>/spring-database-config/mysql/eclipse] [6]:
![]() |
Depois de fazer isso, atualize o ambiente Maven (Alt-F5) para todos os projetos no [Package Explorer]:
![]() |
Em seguida, para verificar o ambiente de trabalho, execute a configuração de compilação denominada [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]:
![]() |
Esta configuração executa o teste [JUnitTestDao]. Este teste deve ser aprovado:
![]() |
7.3. O projeto de configuração da camada JPA
![]() |
O objetivo deste projeto é configurar a camada JPA da arquitetura apresentada abaixo:
![]() |
7.3.1. Configuração do Maven
O projeto é um projeto Maven configurado pelo seguinte ficheiro [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>
- linhas 5–7: o artefacto Maven gerado por este projeto. É o mesmo que o do projeto [mysql-config-jpa-hibernate]. Isto significa que apenas um destes projetos pode estar ativo em qualquer momento;
- linhas 10–14: o projeto Maven pai que especifica as versões da maioria das dependências exigidas pelo projeto;
- linhas 19–22: a biblioteca EclipseLink;
- linhas 26–29: a biblioteca Spring Data;
- linhas 32–34: o projeto de configuração da camada JPA baseia-se no projeto de configuração da camada JDBC, que define, entre outras coisas, o controlador JDBC para o SGBD que está a ser utilizado e os detalhes de ligação à base de dados;
- linhas 35–40: o projeto de configuração da camada JDBC inclui a biblioteca [Spring JDBC], que é substituída aqui pela biblioteca [Spring Data JPA]. Por isso, especificamos que não a inclua nas dependências do projeto. No entanto, se permanecer, isso não causa erros;
- O plugin nas linhas 58–81 implementa o weaving de entidades JPA. O que se conhece como weaving é a transformação (enriquecimento) de entidades JPA para que suportem o carregamento diferido. Não precisámos de configurar o Hibernate para que este weaving ocorresse. Para o EclipseLink, é necessário um plugin Maven. Passei muito tempo a tentar descobrir como forçar o EclipseLink a respeitar o atributo [fetch = FetchType.LAZY] da anotação [@ManyToOne] abaixo:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)
private Categorie categorie;
A especificação JPA indica que o atributo [fetch = FetchType.LAZY] da anotação [@ManyToOne] é uma «sugestão» que a implementação JPA não é obrigada a seguir. E, de facto, o EclipseLink não o segue por predefinição. É necessária uma configuração especial para que o faça. Após muitas pesquisas infrutíferas, encontrei a solução no URL citado na linha 51. Quando as linhas 58–81 são incluídas no ficheiro [pom.xml], o Eclipse reporta um erro no ficheiro. Trata-se de um problema de configuração com o plugin [m2e], que gere projetos Maven no Eclipse. É necessário adicionar as linhas 83–119 para resolver o erro.
No final, as dependências são as seguintes:
![]() |
7.3.2. Configuração do Spring
![]() |
A classe [ConfigJpa] configura o projeto 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;
}
}
Esta configuração é semelhante à descrita na Secção 6.3.2 para a implementação do Hibernate JPA. Apenas detalharemos as diferenças:
- linhas 23–31: o bean [jpaVendorAdapter] é agora implementado com o EclipseLink;
- linhas 50–58: na versão do Hibernate JPA, o código era:
que era usado para especificar onde procurar as entidades JPA. Aqui, recorremos ao ficheiro [persistence.xml] (comentário na linha 25) (ver secção 6.3.4) para:
- definir as entidades JPA;
- configurar o EclipseLink para integrar essas entidades;
7.4. O ficheiro [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>
- linha 4: a unidade de persistência. Pode ter qualquer nome (atributo name);
- linhas 6–10: as cinco entidades JPA a gerir;
- A linha 11 é importante. Por vezes, um projeto define entidades utilizadas em contextos diferentes. A linha 11 garante que não haverá outras entidades além das definidas nas linhas 5–10. Isto é importante quando estas entidades são utilizadas para gerar as tabelas da fonte de dados. A existência de entidades adicionais geraria tabelas adicionais;
- Linhas 13–17: configuração do EclipseLink para integração estática. Existem dois tipos de integração:
- [estática]: as entidades JPA são entrelaçadas assim que a camada JPA é instanciada;
- [dinâmico]: as entidades JPA são enriquecidas (entrelaçadas) na primeira vez que entram na camada JPA;
7.5. Entidades JPA
![]() |
As entidades JPA são as descritas na Secção 6.3.3 para a implementação do Hibernate, com duas diferenças:
- todas as entidades JPA têm a anotação [@Cache(alwaysRefresh = true)], que desativa o cache do EclipseLink. Neste documento, os caches das implementações JPA utilizadas não são utilizados. O cache do EclipseLink parece estar ativado por predefinição e causou erros nos testes.
@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
- Todas as anotações [@OneToMany] são acompanhadas pela anotação [@CascadeOnDelete]:
@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL })
@CascadeOnDelete
private List<Produit> produits;
Esta anotação desempenha um papel importante na geração de tabelas a partir de entidades JPA. Adiciona o atributo SQL [ON DELETE CASCADE] às chaves estrangeiras (aqui PRODUCTS[CATEGORY_ID] ---> CATEGORIES[ID]), o que garante que, sempre que uma categoria for eliminada da tabela [CATEGORIES], os produtos correspondentes na tabela [PRODUCTS] também sejam eliminados;
Nota: É importante referir que esta anotação é utilizada tanto na criação da tabela, como acabámos de ver, como na sua consulta. O EclipseLink assume que o atributo SQL [ON DELETE CASCADE] está presente e utiliza-o sempre que lhe é solicitado que elimine uma categoria. A sua ausência causaria erros.
7.6. A camada de teste
![]() |
![]() |
Os testes acima são idênticos aos das implementações Spring JDBC e Spring JPA Hibernate. Consulte as páginas seguintes, se necessário:
- [JUnitTestCheckArguments]: secção 4.11.1;
- [JUnitTestDao]: secção 4.11.2;
- [JUnitTestPushTheLimits]: secção 4.11.3;
- [JUnitTestProxies]: secção 6.4.5;
Os resultados obtidos são os seguintes:
![]() |
![]() |
- em [1], [JUnitTestPushTheLimits-EclipseLink]: 70,583 s
- em [2], [JUnitTestPushTheLimits-Hibernate]: 78,945 s
- em [3], [JUnitTestPushTheLimits-JDBC]: 36,09 s
O teste [JUnitTestProxies] produz a seguinte saída na consola:
Aqui vemos que, ao aceder ao campo [Category.products] de uma categoria do tipo PROXY e ao campo [Product.category] de um produto do tipo PROXY, recuperamos com sucesso a informação em ambos os casos (linhas 7 e 17). Entre as três implementações JPA, esta é a única que permite isto em entidades PROXY.















