Skip to content

8. Spring Data JPA OpenJpa

8.1. Introdução

Estamos a reutilizar a arquitetura anterior, que agora estamos a implementar com uma camada JPA/OpenJpa.

8.2. Configurar o ambiente de desenvolvimento

Utilizando o STS, descarregue o projeto [myql-config-jpa-eclipselink] [1-4]:

depois importe o projeto [mysl-config-jpa-openjpa] [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-openjpa]:

Esta configuração executa o teste [JUnitTestDao]. Este teste deve ser aprovado:

 

8.3. O projeto de configuração da camada JPA

  

O objetivo deste projeto é configurar a camada JPA da arquitetura apresentada abaixo:

8.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.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>2.3.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>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </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 principal que define a versão da maioria das dependências necessárias ao projeto;
  • linhas 19–23: a biblioteca OpenJpa;
  • 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 da 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;

No final, as dependências são as seguintes:

  

8.3.2. Configuração do Spring

 

A classe [ConfigJpa] configura o projeto Spring da seguinte forma:


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.OpenJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
 
@Configuration
@Import({ConfigJdbc.class})
public class ConfigJpa {
 
    // the provider JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
        openJpaVendorAdapter.setShowSql(false);
        openJpaVendorAdapter.setDatabase(Database.MYSQL);
        openJpaVendorAdapter.setGenerateDdl(true);
        return openJpaVendorAdapter;
    }
 
    // JPA entity packages
    public final static String[] ENTITIES_PACKAGES = { "generic.jpa.entities.dbproduitscategories" };
 
    // 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.setPackagesToScan(ENTITIES_PACKAGES);
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }
 
    // 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–30: o bean [jpaVendorAdapter] é agora implementado utilizando o OpenJpa;

8.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 />
    </persistence-unit>
    <persistence-unit name="generic-jpa-entities-dbproduits" transaction-type="RESOURCE_LOCAL">
        <!-- entities JPA -->
        <class>generic.jpa.entities.dbproduits.Produit</class>
        <exclude-unlisted-classes />
    </persistence-unit>
</persistence>
  • linha 4: a unidade de persistência ligada à base de dados [dbproduitscategories]. Pode ter qualquer nome (atributo name);
  • linhas 6–10: as cinco entidades JPA a serem geridas;
  • linhas 13–17: outra unidade de persistência ligada à base de dados [dbproduitscategories]. O OpenJpa permite um ficheiro de persistência que contenha várias unidades de persistência. O EclipseLink não permite. Recorde-se que, com o Hibernate, não utilizámos um ficheiro de persistência;

8.5. A camada de teste

  

Os testes acima são idênticos aos das implementações Spring JDBC, Spring JPA Hibernate e Spring JPA EclipseLink. Consulte as páginas seguintes, se necessário:

As configurações de execução a utilizar são as seguintes:

Para que as entidades JPA sejam integradas pelo OpenJpa, as aplicações Java ou os testes JUnit devem ser executados com um agente Java. Este agente irá integrar as entidades JPA antes de estas serem carregadas pela JVM [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html]. Vejamos, por exemplo, a configuração do teste [JUnitTestDao]:

  • No separador [Arguments] [1], fornecemos a referência do agente Java à JVM utilizando a sintaxe [2]. O agente Java é o arquivo [openjpa-<versão>.jar] encontrado na pasta [<m2-repo>/org/apache/openjpa/<versão>]. A pasta <m2-repo> é especificada na configuração do Eclipse ([3] abaixo):

Em [2] acima, utilizámos uma variável definida pelo utilizador [M2_REPO]:

 

Atribuímos o valor apresentado em [3] à variável [M2_REPO].

Os resultados obtidos para os testes [JUnitTestDao] e [JUnitTestPushTheLimits] 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;
  • em [4], [JUnitTestPushTheLimits-OpenJpa]: 80,394 s;

Os resultados da consola obtidos para o teste [JUnitTestProxies] são os seguintes:

Vidage de la base de données --------------------------------
doNothing
Vidage de la base de données --------------------------------
getShortCategoriesByName1 --------------------------------
Catégorie de type : PROXY
Catégorie :
Exception : java.lang.NullPointerException, Message : null
Vidage de la base de données --------------------------------
getLongCategoriesByName1 --------------------------------
Catégorie de type : POJO
Catégorie :
1
Vidage de la base de données --------------------------------
getShortProduitsByName1 --------------------------------
Produit de type : PROXY
Nom de la catégorie du produit :
Exception : java.lang.NullPointerException, Message : null
Vidage de la base de données --------------------------------
getLongProduitsByName1 --------------------------------
Produit de type : POJO
Nom de la catégorie du produit :
categorie[0]

Aqui vemos que, ao aceder ao campo [Categorie.produits] de uma categoria do tipo PROXY e ao campo [Produit.categorie] de um produto do tipo PROXY, temos um ponteiro nulo em ambos os casos (linhas 7 e 17).