Skip to content

7. Spring Data JPA EclipseLink

7.1. Einführung

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

7.2. Einrichten der Entwicklungsumgebung

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

Importieren Sie anschließend das Projekt [mysl-config-jpa-eclipselink] [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-hibernate-eclipselink] aus:

 

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

 

7.3. Das Projekt zur Konfiguration der JPA-Schicht

  

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

7.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.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>
  • Zeilen 5–7: das von diesem Projekt generierte Maven-Artefakt. Es ist dasselbe wie das des [mysql-config-jpa-hibernate]-Projekts. 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–22: die EclipseLink-Bibliothek;
  • Zeilen 26–29: die Spring Data-Bibliothek;
  • Zeilen 32–34: Das JPA-Layer-Konfigurationsprojekt baut auf dem JDBC-Layer-Konfigurationsprojekt auf, das unter anderem den JDBC-Treiber für das verwendete DBMS und die Verbindungsdetails für die Datenbank definiert;
  • Zeilen 35–40: Das JDBC-Schicht-Konfigurationsprojekt 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. Bleibt sie jedoch erhalten, verursacht dies keine Fehler;
  • Das Plugin in den Zeilen 58–81 implementiert JPA-Entity-Weaving. Als Weaving bezeichnet man die Transformation (Anreicherung) von JPA-Entitäten, damit diese Lazy Loading unterstützen. Wir mussten Hibernate nicht konfigurieren, damit dieses Weaving stattfindet. Für EclipseLink ist ein Maven-Plugin erforderlich. Ich habe lange versucht herauszufinden, wie man EclipseLink dazu zwingen kann, das Attribut [fetch = FetchType.LAZY] der untenstehenden Annotation [@ManyToOne] zu berücksichtigen:

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)
private Categorie categorie;

Die JPA-Spezifikation besagt, dass das Attribut [fetch = FetchType.LAZY] der Annotation [@ManyToOne] ein „Hinweis“ ist, dem die JPA-Implementierung nicht zwingend folgen muss. Und tatsächlich befolgt EclipseLink diesen Hinweis standardmäßig nicht. Damit dies geschieht, ist eine spezielle Konfiguration erforderlich. Nach langem erfolglosem Suchen fand ich die Lösung unter der in Zeile 51 angegebenen URL. Wenn die Zeilen 58–81 in die [pom.xml]-Datei aufgenommen werden, meldet Eclipse einen Fehler in der Datei. Dies ist ein Konfigurationsproblem mit dem [m2e]-Plugin, das Maven-Projekte innerhalb von Eclipse verwaltet. Sie müssen die Zeilen 83–119 hinzufügen, um den Fehler zu beheben.

Letztendlich lauten die Abhängigkeiten wie folgt:

  

7.3.2. Spring-Konfiguration

 

Die Klasse [ConfigJpa] konfiguriert das Spring-Projekt:


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

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–31: Die [jpaVendorAdapter]-Bean wird nun mit EclipseLink implementiert;
  • Zeilen 50–58: In der Hibernate-JPA-Version lautete der Code:
factory.setPackagesToScan(ENTITIES_PACKAGES);

Dieser Befehl diente dazu, anzugeben, wo nach JPA-Entitäten gesucht werden sollte. Hier stützen wir uns auf die Datei [persistence.xml] (Kommentar in Zeile 25) (siehe Abschnitt 6.3.4), um sowohl:

  • die JPA-Entitäten zu definieren;
  • EclipseLink für das Einbinden dieser Entitäten zu konfigurieren;

7.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>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>
  • Zeile 4: die Persistenz-Einheit. Sie kann einen beliebigen Namen haben (name-Attribut);
  • Zeilen 6–10: die fünf zu verwaltenden JPA-Entitäten;
  • Zeile 11 ist wichtig. Manchmal definiert ein Projekt Entitäten, die in verschiedenen Kontexten verwendet werden. Zeile 11 stellt sicher, dass es keine anderen Entitäten als die in den Zeilen 5–10 definierten gibt. Dies ist wichtig, wenn diese Entitäten zur Generierung der Datenquellentabellen verwendet werden. Zusätzliche Entitäten würden zusätzliche Tabellen erzeugen;
  • Zeilen 13–17: EclipseLink-Konfiguration für statisches Weaving. Es gibt zwei Arten von Weaving:
    • [static]: JPA-Entitäten werden gewoben, sobald die JPA-Schicht instanziiert wird;
    • [dynamic]: JPA-Entitäten werden beim ersten Eintritt in die JPA-Schicht angereichert (gewoben);

7.5. JPA-Entitäten

  

JPA-Entitäten sind diejenigen, die in Abschnitt 6.3.3 für die Hibernate-Implementierung beschrieben sind, mit zwei Unterschieden:

  • Alle JPA-Entitäten tragen die Annotation [@Cache(alwaysRefresh = true)], die den EclipseLink-Cache deaktiviert. In diesem Dokument werden die Caches der verwendeten JPA-Implementierungen nicht genutzt. Der EclipseLink-Cache scheint standardmäßig aktiviert zu sein und verursachte in den Tests Fehler.

@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
  • Alle [@OneToMany]-Annotationen werden von der [@CascadeOnDelete]-Annotation begleitet:

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL })
    @CascadeOnDelete
    private List<Produit> produits;

Diese Annotation spielt eine Rolle bei der Generierung von Tabellen aus JPA-Entitäten. Sie fügt den Fremdschlüsseln (hier PRODUCTS[CATEGORY_ID] ---> CATEGORIES[ID]) das SQL-Attribut [ON DELETE CASCADE] hinzu, wodurch sichergestellt wird, dass bei jeder Löschung einer Kategorie aus der Tabelle [CATEGORIES] auch die entsprechenden Produkte in der Tabelle [PRODUCTS] gelöscht werden;

Hinweis: Es ist wichtig zu beachten, dass diese Annotation sowohl beim Erstellen der Tabelle, wie wir gerade gesehen haben, als auch bei der Abfrage verwendet wird. EclipseLink geht davon aus, dass das SQL-Attribut [ON DELETE CASCADE] vorhanden ist, und verwendet es, wann immer es aufgefordert wird, eine Kategorie zu löschen. Fehlt es, würde dies zu Fehlern führen.

7.6. Die Testschicht

  

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

Die 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

Der Test [JUnitTestProxies] erzeugt die folgende Konsolenausgabe:

Vidage de la base de données --------------------------------
doNothing
Vidage de la base de données --------------------------------
getShortCategoriesByName1 --------------------------------
Catégorie de type : PROXY
Catégorie :
1
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 :
categorie[0]
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 [Category.products] einer Kategorie vom Typ PROXY und das Feld [Product.category] eines Produkts vom Typ PROXY die Informationen in beiden Fällen erfolgreich abgerufen werden (Zeilen 7 und 17). Unter den drei JPA-Implementierungen ist dies die einzige, die dies für PROXY-Entitäten zulässt.