Skip to content

7. Spring Data JPA EclipseLink

7.1. Introduction

We nemen de vorige architectuur weer op en implementeren deze nu met een JPA / EclipseLink-laag.

7.2. Het opzetten van de werkomgeving

Download met STS het project [myql-config-jpa-hibernate] [1-4]:

en importeer vervolgens het project [mysl-config-jpa-eclipselink] [5] dat zich in de map [<exemples>/spring-database-config/mysql/eclipse] [6] bevindt:

Zodra dit is gebeurd, reset je de Maven-omgeving (Alt-F5) van alle projecten in [Package Explorer]:

 

Voer vervolgens, om de werkomgeving te controleren, de uitvoerconfiguratie met de naam [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink] uit:

 

Deze configuratie voert de test [JUnitTestDao] uit. Deze test moet slagen:

 

7.3. Het configuratieproject van de laag JPA

  

Dit project heeft als doel de laag JPA van de onderstaande architectuur te configureren:

7.3.1. Maven-configuratie

Het project is een Maven-project en wordt geconfigureerd door het volgende bestand [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>
        <!-- variabele afhankelijkheden ********************************************** -->
        <!-- JPA-provider -->
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>eclipselink</artifactId>
            <version>2.6.0</version>
        </dependency>
        <!-- constante afhankelijkheden ********************************************** -->
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>
        <!-- overgenomen configuratie 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>
            <!-- [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>
            <!-- Deze plug-in zorgt voor de statische verwerking van EclipseLink -->
            <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>
                <!--De configuratie van deze plug-in wordt uitsluitend gebruikt om Eclipse m2e-instellingen op te slaan. Het heeft geen invloed op de Maven-build zelf. -->
                <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>
  • regels 5-7: het door dit project gegenereerde Maven-artefact. Dit is hetzelfde als dat van het project [mysql-config-jpa-hibernate]. Dit betekent dat slechts één van deze projecten op een bepaald moment actief kan zijn;
  • regels 10-14: het bovenliggende Maven-project dat de versie vastlegt van de meeste afhankelijkheden die het project nodig heeft;
  • regels 19-22: de bibliotheek EclipseLink;
  • regels 26-29: de Spring Data-bibliotheek;
  • regels 32-34: het configuratieproject van de laag JPA is gebaseerd op dat van de configuratielaag JDBC, dat onder andere de driver JDBC van de gebruikte SGBD en de gegevens van de te gebruiken database;
  • regels 35-40: het configuratieproject van de laag JDBC bevat de bibliotheek [Spring JDBC], die hier wordt vervangen door de bibliotheek [Spring Data JPA]. Daarom wordt aangegeven deze niet op te nemen in de afhankelijkheden van het project. Als deze toch blijft staan, leidt dit echter niet tot fouten;
  • de plug-in in de regels 58-81 implementeert de 'weaving' van de entiteiten JPA. Wat de Engelstaligen de 'weaving' noemen, is de transformatie (verrijking) van de entiteiten 'JPA' zodat ze 'Lazy Loading' ondersteunen. We hoefden Hibernate niet te configureren om dit weaving te laten plaatsvinden. Voor EclipseLink is een Maven-plugin nodig. Ik heb heel lang gezocht naar een manier om EclipseLink te dwingen het attribuut [fetch = FetchType.LAZY] van de onderstaande annotatie [@ManyToOne] te respecteren:

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

De specificatie JPA geeft aan dat het attribuut [fetch = FetchType.LAZY] van de annotatie [@ManyToOne] een 'hint' (aanbeveling) waaraan de implementatie JPA niet hoeft te voldoen. En inderdaad, EclipseLink houdt zich er standaard niet aan. Er is een speciale configuratie nodig om ervoor te zorgen dat het zich eraan houdt. Na veel vruchteloos zoeken heb ik de oplossing gevonden voor de in regel 51 genoemde URL. Wanneer men de regels 58-81 opneemt in het bestand [pom.xml], geeft Eclipse een foutmelding over het bestand. Dit is een configuratieprobleem van de plug-in [m2e], die het beheer van Maven-projecten binnen Eclipse verzorgt. Je moet de regels 83-119 toevoegen om de fout te verhelpen.

Uiteindelijk zijn de afhankelijkheden als volgt:

  

7.3.2. Spring-configuratie

 

De klasse [ConfigJpa] configureert het Spring-project:


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 {

    // de provider JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        // Opmerking: de entiteiten JPA en de EclipseLink-configuratie bevinden zich in het bestand META-INF/persistence.xml
        EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
        eclipseLinkJpaVendorAdapter.setShowSql(false);
        eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
        eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
        return eclipseLinkJpaVendorAdapter;
    }

    // gegevensbron
    @Bean
    public DataSource dataSource() {
        // gegevensbron TomcatJdbc
        DataSource dataSource = new DataSource();
        // toegangscontrole JDBC
        dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
        dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
        dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
        dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
        // aanvankelijk geopende verbindingen
        dataSource.setInitialSize(5);
        // resultaat
        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;
    }

    // Transactiemanager
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

}

Deze configuratie is vergelijkbaar met die beschreven in paragraaf 6.3.2, voor de Hibernate-implementatie JPA. We beschrijven alleen de verschillen:

  • regels 23-31: de bean [jpaVendorAdapter] wordt nu geïmplementeerd met EclipseLink;
  • regels 50-58: in de Hibernate-versie JPA stond:
factory.setPackagesToScan(ENTITIES_PACKAGES);

die werd gebruikt om aan te geven waar de entiteiten JPA moesten worden opgezocht. Hier wordt verwezen naar het bestand [persistence.xml] (opmerking op regel 25) (zie paragraaf 6.3.4) om zowel:

  • het definiëren van de entiteiten JPA;
  • EclipseLink te configureren voor de weaving van deze entiteiten;

7.4. Het bestand [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">
        <!-- entiteiten 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>
        <!-- eigenschappen die nodig zijn om [@ManyToOne] te kunnen zoeken in de modus LAZY -->
        <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>
  • regel 4: de persistentie-eenheid. Deze kan een willekeurige naam hebben (attribuut name);
  • regels 6-10: de vijf te beheren entiteiten JPA;
  • regel 11 is belangrijk. Het komt soms voor dat een project entiteiten definieert die in verschillende contexten worden gebruikt. Regel 11 zorgt ervoor dat er geen andere entiteiten zijn dan die welke in de regels 5-10 zijn gedefinieerd. Dit is belangrijk wanneer deze worden gebruikt om de tabellen van de gegevensbron te genereren. Te veel entiteiten zouden leiden tot te veel tabellen;
  • regels 13-17: configuratie van EclipseLink voor een statische weaving. Er bestaan twee soorten weaving:
    • [statique]: de entiteiten JPA worden verrijkt (woven) zodra de laag JPA wordt geïnstantieerd;
    • [dynamique]: de entiteiten JPA worden verrijkt (woven) zodra ze voor het eerst in de laag JPA terechtkomen;

7.5. De entiteiten JPA

  

De entiteiten JPA zijn de entiteiten die in paragraaf 6.3.3 worden beschreven voor de Hibernate-implementatie, met twee verschillen:

  • alle entiteiten JPA hebben de annotatie [@Cache(alwaysRefresh = true)], die de cache van EclipseLink uitschakelt. In dit document wordt geen gebruik gemaakt van de caches van de gebruikte JPA-implementaties. Die van EclipseLink lijkt standaard actief te zijn en veroorzaakte fouten in de tests.

@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
  • alle annotaties [@OneToMany] gaan vergezeld van de annotatie [@CascadeOnDelete]:

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

Deze annotatie speelt een rol bij het genereren van tabellen op basis van de entiteiten JPA. Het voegt aan de vreemde sleutels (hier PRODUITS[CATEGORIE_ID] ---> CATEGORIES[ID]) het attribuut SQL [ON DELETE CASCADE] toe, waardoor telkens wanneer een categorie uit de tabel [CATEGORIES] wordt verwijderd, de bijbehorende producten in de tabel [PRODUITS] eveneens worden verwijderd;

Opmerking: het is belangrijk op te merken dat deze annotatie zowel bij het aanmaken van de tabel, zoals we zojuist hebben gezien, als bij het gebruik ervan wordt gebruikt. EclipseLink gaat ervan uit dat het attribuut SQL [ON DELETE CASCADE] aanwezig is en gebruikt dit telkens wanneer er wordt gevraagd om een categorie te verwijderen. Als dit ontbreekt, zou dit fouten veroorzaken.

7.6. De testlaag

  

De bovenstaande tests zijn identiek aan die van de Spring-implementaties JDBC en Spring JPA Hibernate. Raadpleeg indien nodig de volgende pagina’s:

  • [JUnitTestCheckArguments]: paragraaf 4.11.1;
  • [JUnitTestDao]: paragraaf 4.11.2;
  • [JUnitTestPushTheLimits]: paragraaf 4.11.3;
  • [JUnitTestProxies]: paragraaf 6.4.5;

De verkregen resultaten zijn als volgt:

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

De test [JUnitTestProxies] levert de volgende console-uitvoer op:

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 zien we dat wanneer we het veld [Categorie.produits] van een categorie van het type PROXY en het veld [Produit.categorie] van een product van het type PROXY benaderen, de informatie in beide gevallen wordt verkregen (regels 7 en 17). Van de drie implementaties van JPA is dit de enige die dit mogelijk maakt voor entiteiten van het type PROXY.