Skip to content

8. Spring Data JPA OpenJpa

8.1. Einführung

Wir verwenden die bisherige Architektur wieder, die wir nun mit einer JPA/OpenJpa-Schicht implementieren.

8.2. Einrichten der Entwicklungsumgebung

Laden Sie mit STS das Projekt [myql-config-jpa-eclipselink] herunter [1-4]:

Importieren Sie anschließend das Projekt [mysl-config-jpa-openjpa] [5] aus dem Ordner [<examples>/spring-database-config/mysql/eclipse] [6]:

Sobald dies erledigt ist, aktualisieren Sie die Maven-Umgebung (Alt-F5) für alle Projekte im [Package Explorer]:

 

Führen Sie anschließend zur Überprüfung der Arbeitsumgebung die Build-Konfiguration mit dem Namen [spring-jpa-generic-JUnitTestDao-openjpa] aus:

Diese Konfiguration führt den Test [JUnitTestDao] aus. Dieser Test sollte erfolgreich sein:

 

8.3. Das Projekt zur Konfiguration der JPA-Schicht

  

Ziel dieses Projekts ist es, die JPA-Schicht der unten dargestellten Architektur zu konfigurieren:

8.3.1. Maven-Konfiguration

Das Projekt ist ein Maven-Projekt, das durch die folgende [pom.xml]-Datei konfiguriert wird:


<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>
  • Zeilen 5–7: das von diesem Projekt generierte Maven-Artefakt. Es ist dasselbe wie das des Projekts [mysql-config-jpa-hibernate]. Das bedeutet, dass zu jedem Zeitpunkt nur eines dieser Projekte aktiv sein kann;
  • Zeilen 10–14: das übergeordnete Maven-Projekt, das die Versionen der meisten vom Projekt benötigten Abhängigkeiten festlegt;
  • Zeilen 19–23: die OpenJpa-Bibliothek;
  • Zeilen 26–29: die Spring Data-Bibliothek;
  • Zeilen 32–34: Das JPA-Layer-Konfigurationsprojekt basiert auf dem JDBC-Layer-Konfigurationsprojekt, das unter anderem den JDBC-Treiber für das verwendete DBMS und die Datenbankverbindungsdaten definiert;
  • Zeilen 35–40: Das Konfigurationsprojekt der JDBC-Schicht enthält die [Spring JDBC]-Bibliothek, die hier durch die [Spring Data JPA]-Bibliothek ersetzt wird. Daher legen wir fest, dass sie nicht in die Projektabhängigkeiten aufgenommen wird. Sollte sie jedoch erhalten bleiben, verursacht dies keine Fehler;

Letztendlich sehen die Abhängigkeiten wie folgt aus:

  

8.3.2. Spring-Konfiguration

 

Die Klasse [ConfigJpa] konfiguriert das Spring-Projekt wie folgt:


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;
    }
}

Diese Konfiguration ähnelt der in Abschnitt 6.3.2 für die Hibernate-JPA-Implementierung beschriebenen. Wir werden nur auf die Unterschiede eingehen:

  • Zeilen 23–30: Die [jpaVendorAdapter]-Bean wird nun mit OpenJpa implementiert;

8.4. Die Datei [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>
  • Zeile 4: die Persistenz-Einheit, die mit der Datenbank [dbproduitscategories] verknüpft ist. Sie kann einen beliebigen Namen haben (Attribut „name“);
  • Zeilen 6–10: die fünf zu verwaltenden JPA-Entitäten;
  • Zeilen 13–17: eine weitere Persistenz-Einheit, die mit der Datenbank [dbproduitscategories] verknüpft ist. OpenJpa erlaubt eine Persistenzdatei, die mehrere Persistenz-Einheiten enthält. EclipseLink tut dies nicht. Erinnern Sie sich daran, dass wir bei Hibernate keine Persistenzdatei verwendet haben;

8.5. Die Testschicht

  

Die oben genannten Tests entsprechen denen für die Implementierungen Spring JDBC, Spring JPA Hibernate und Spring JPA EclipseLink. Lesen Sie bei Bedarf die folgenden Seiten:

Die zu verwendenden Ausführungskonfigurationen lauten wie folgt:

Damit JPA-Entitäten von OpenJpa eingebunden werden können, müssen Java-Anwendungen oder JUnit-Tests mit einem Java-Agenten gestartet werden. Dieser Agent bindet die JPA-Entitäten ein, bevor sie von der JVM geladen werden [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html]. Betrachten wir zum Beispiel die Konfiguration des [JUnitTestDao]-Tests:

  • Auf der Registerkarte [Arguments] [1] geben wir die Java-Agent-Referenz für die JVM unter Verwendung der Syntax [2] an. Der Java-Agent ist das Archiv [openjpa-<version>.jar], das sich im Ordner [<m2-repo>/org/apache/openjpa/<version>] befindet. Der Ordner <m2-repo> wird in der Eclipse-Konfiguration angegeben ([3] unten):

In [2] oben haben wir eine benutzerdefinierte Variable [M2_REPO] verwendet:

 

Wir haben der Variablen [M2_REPO] den in [3] angegebenen Wert zugewiesen.

Die für die Tests [JUnitTestDao] und [JUnitTestPushTheLimits] erzielten Ergebnisse lauten wie folgt:

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

Die für den Test [JUnitTestProxies] erhaltenen Konsolenergebnisse lauten wie folgt:

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]

Hier sehen wir, dass beim Zugriff auf das Feld [Categorie.produits] einer Kategorie vom Typ PROXY und das Feld [Produit.categorie] eines Produkts vom Typ PROXY in beiden Fällen ein Null-Zeiger vorliegt (Zeilen 7 und 17).