Skip to content

8. Spring Data JPA OpenJpa

8.1. Introduction

Wracamy do poprzedniej architektury, którą teraz wdrażamy z warstwą JPA / OpenJpa.

8.2. Konfiguracja środowiska pracy

Za pomocą pliku STS pobierz projekt [myql-config-jpa-eclipselink] [1-4]:

a następnie zaimportuj projekt [mysl-config-jpa-openjpa] [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-openjpa]:

Ta konfiguracja uruchamia test [JUnitTestDao]. Test ten musi zakończyć się powodzeniem:

 

8.3. Projekt konfiguracji warstwy JPA

  

Zadaniem tego projektu jest skonfigurowanie warstwy JPA w poniższej architekturze:

8.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.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- stałe zależności ********************************************** -->
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>
        <!-- konfiguracja odziedziczona po 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>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </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–23: biblioteka OpenJpa;
  • 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;

Ostatecznie zależności są następujące:

  

8.3.2. Konfiguracja Spring

 

Klasa [ConfigJpa] konfiguruje projekt Spring w następujący sposób:


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 {

    // dostawca JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
        openJpaVendorAdapter.setShowSql(false);
        openJpaVendorAdapter.setDatabase(Database.MYSQL);
        openJpaVendorAdapter.setGenerateDdl(true);
        return openJpaVendorAdapter;
    }

    // pakiety encji JPA
    public final static String[] ENTITIES_PACKAGES = { "generic.jpa.entities.dbproduitscategories" };

    // ź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.setPackagesToScan(ENTITIES_PACKAGES);
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    // 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 dla implementacji Hibernate o nazwie JPA. Przedstawiamy jedynie różnice:

  • wiersze 23–30: bean [jpaVendorAdapter] jest teraz zaimplementowany za pomocą OpenJpa;

8.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 />
    </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>
  • wiersz 4: jednostka trwałości powiązana z bazą [dbproduitscategories]. Może ona nosić dowolną nazwę (atrybut name);
  • wiersze 6–10: pięć jednostek JPA, które mają być zarządzane;
  • wiersze 13–17: kolejna jednostka trwałości powiązana z bazą [dbproduits]. OpenJpa umożliwia posiadanie pliku trwałości zawierającego wiele jednostek trwałości. EclipseLink tego nie pozwala. Przypominamy, że w przypadku Hibernate nie korzystaliśmy z pliku trwałości;

8.5. Warstwa testowa

  

Powyższe testy są identyczne z testami dla implementacji Spring JDBC, Spring JPA z Hibernate oraz Spring JPA i EclipseLink. 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;

Należy zastosować następujące konfiguracje uruchomieniowe:

Aby encje JPA zostały wzbogacone (woven) przez OpenJpa, aplikacje Java lub testy JUnit muszą być uruchamiane za pomocą agenta Java. Agent ten wzbogaci encje JPA, zanim zostaną one załadowane przez JVM i [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html]. Przyjrzyjmy się na przykład konfiguracji testu [JUnitTestDao]:

  • w zakładce [Arguments] [1] podaje się odniesienie do agenta Java w JVM zgodnie ze składnią [2]. Agentem Java jest archiwum [openjpa-<version>.jar], które znajduje się w folderze [<m2-repo>/org/apache/openjpa/<version>]. Folder <m2-repo> jest wskazany w konfiguracji Eclipse ([3] poniżej):

W pliku [2], przedstawionym powyżej, wykorzystano zmienną [M2_REPO] zdefiniowaną przez użytkownika:

 

Zmiennej [M2_REPO] nadano wartość widoczną w [3].

Wyniki uzyskane dla testów [JUnitTestDao] i [JUnitTestPushTheLimits] są następujące:

  • w przypadku [1], [JUnitTestPushTheLimits-EclipseLink]: 70,583 s;
  • w [2], [JUnitTestPushTheLimits-Hibernate]: 78,945 s;
  • w [3], [JUnitTestPushTheLimits-JDBC]: 36,09 s;
  • w [4], [JUnitTestPushTheLimits-OpenJpa]: 80,394 s;

Wyniki konsoli uzyskane dla testu [JUnitTestProxies] są następujące:

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]

Widać tutaj, że podczas uzyskiwania dostępu do pola [Categorie.produits] w kategorii typu PROXY oraz do pola [Produit.categorie] w produkcie typu PROXY, w obu przypadkach występuje wskaźnik null (wiersze 7 i 17).