Skip to content

7. Spring Data JPA EclipseLink

7.1. Introduction

Önceki mimariyi yeniden ele alıyoruz ve bunu şimdi bir JPA / EclipseLink katmanı ile uyguluyoruz.

7.2. Çalışma ortamının kurulması

STS ile [myql-config-jpa-hibernate] ve [1-4] projelerini indirin:

ardından [<exemples>/spring-database-config/mysql/eclipse] [6] klasöründe bulunan [mysl-config-jpa-eclipselink] [5] projesini içe aktarın:

Bu işlem tamamlandıktan sonra, [Package Explorer] içinde bulunan tüm projelerin Maven ortamını (Alt-F5) sıfırlayın:

 

Ardından, çalışma ortamını kontrol etmek için [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink] adlı yürütme yapılandırmasını çalıştırın:

 

Bu yapılandırma, [JUnitTestDao] testini çalıştırır. Bu testin başarılı olması gerekir:

 

7.3. JPA katman yapılandırma projesi

  

Bu projenin amacı, aşağıdaki mimarideki JPA katmanını yapılandırmaktır:

7.3.1. Maven Yapılandırması

Proje bir Maven projesidir ve aşağıdaki [pom.xml] dosyası ile yapılandırılmıştır:


<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>
        <!-- değişken bağımlılıklar ********************************************** -->
        <!-- JPA sağlayıcı -->
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>eclipselink</artifactId>
            <version>2.6.0</version>
        </dependency>
        <!-- sabit bağımlılıklar ********************************************** -->
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>
        <!-- JDBC miras alınan yapılandırma -->
        <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>
            <!-- Bu eklenti, EclipseLink statik dokumayı sağlar -->
            <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>
                <!--Bu eklentinin yapılandırması yalnızca Eclipse m2e ayarlarını depolamak için kullanılır. Maven derlemesinin kendisi üzerinde hiçbir etkisi yoktur. -->
                <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>
  • 5-7. satırlar: Bu proje tarafından oluşturulan Maven artefaktı. Bu, [mysql-config-jpa-hibernate] projesininkiyle aynıdır. Bu, herhangi bir anda bu projelerden yalnızca birinin aktif olabileceği anlamına gelir;
  • 10-14. satırlar: Proje için gerekli olan çoğu bağımlılığın sürümünü belirleyen üst Maven projesi;
  • satır 19-22: EclipseLink kütüphanesi;
  • 26-29. satırlar: Spring Data kütüphanesi;
  • satır 32-34: JPA katman yapılandırma projesi, diğer şeylerin yanı sıra kullanılan SGBD'in JDBC sürücüsünü ve kullanılacak veritabanının koordinatlarını tanımlar;
  • 35-40. satırlar: JDBC katmanının yapılandırma taslağı, burada [Spring Data JPA] kütüphanesi ile değiştirilen [Spring JDBC] kütüphanesini içerir. Bu nedenle, bu kütüphanenin proje bağımlılıklarına dahil edilmemesi belirtilmiştir. Ancak, kütüphane kalırsa bu durum hata oluşturmaz;
  • 58-81. satırlardaki eklenti, JPA varlıklarının 'weaving' öğesini uygular. İngilizler'in weaving olarak adlandırdığı şey, JPA varlıklarının Lazy Loading'i desteklemesi için yapılan dönüştürme (zenginleştirme) işlemidir. Bu weaving'in çalışması için Hibernate'i yapılandırmamıza gerek kalmadı. EclipseLink için ise bir Maven eklentisi gerekiyor. Aşağıdaki [@ManyToOne] anotasyonunun [fetch = FetchType.LAZY] özniteliğine uyması için EclipseLink'i nasıl zorlayabileceğimi uzun süre araştırdım:

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

JPA spesifikasyonu, [@ManyToOne] anotasyonunun [fetch = FetchType.LAZY] özniteliğinin bir 'ipucu' (öneri) olduğunu belirtir; JPA uygulamasının buna uyması zorunlu değildir. Nitekim EclipseLink varsayılan olarak buna uymaz. Buna uyması için özel bir yapılandırma gereklidir. Birçok sonuçsuz araştırmanın ardından, 51. satırda bahsedilen URL sorununa bir çözüm buldum. 58-81. satırları [pom.xml] dosyasına eklediğinizde, Eclipse bu dosyayla ilgili bir hata bildiriyor. Bu, Eclipse içinde Maven projelerinin yönetimini sağlayan [m2e] eklentisinin yapılandırma sorunudur. Hatayı gidermek için 83-119. satırları eklenmelidir.

Sonuç olarak, bağımlılıklar şu şekildedir:

  

7.3.2. Spring yapılandırması

 

[ConfigJpa] sınıfı, Spring projesini yapılandırır:


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 {

    // JPA sağlayıcısı
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        // Not: JPA varlıkları ve Eclipselink yapılandırması, META-INF/persistence.xml dosyasında bulunur
        EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
        eclipseLinkJpaVendorAdapter.setShowSql(false);
        eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
        eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
        return eclipseLinkJpaVendorAdapter;
    }

    // veri kaynağı
    @Bean
    public DataSource dataSource() {
        // veri kaynağı TomcatJdbc
        DataSource dataSource = new DataSource();
        // erişim yapılandırması JDBC
        dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
        dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
        dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
        dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
        // başlangıçta açık olan bağlantılar
        dataSource.setInitialSize(5);
        // sonuç
        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;
    }

    // İşlem yöneticisi
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

}

Bu yapılandırma, 6.3.2 numaralı paragrafta Hibernate uygulaması JPA için ayrıntılı olarak açıklanan yapılandırmaya benzerdir. Yalnızca farklılıkları detaylandırıyoruz:

  • 23-31. satırlar: [jpaVendorAdapter] bean'i artık EclipseLink ile uygulanmaktadır;
  • 50-58. satırlar: JPA Hibernate sürümünde şu yazıyordu:
factory.setPackagesToScan(ENTITIES_PACKAGES);

JPA varlıklarının nerede aranması gerektiğini belirtmek için kullanılır. Burada, hem aşağıdakiler için hem de:

  • JPA varlıklarını tanımlamak;
  • bu varlıkların weaving'i için EclipseLink'i yapılandırmak;

7.4. [persistence.xml] dosyası

  

<?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">
        <!-- varlıklar 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>
        <!-- [@ManyToOne]'in LAZY modunda aranabilmesi için gerekli özellikler -->
        <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>
  • 4. satır: kalıcılık birimi. Herhangi bir isim verilebilir (name özniteliği);
  • 6-10. satırlar: yönetilecek beş JPA varlık;
  • 11. satır önemlidir. Bazen bir proje, farklı bağlamlarda kullanılan varlıklar tanımlayabilir. 11. satır, 5-10. satırlarda tanımlananlar dışında başka varlıkların olmamasını sağlar. Bu, söz konusu varlıklar veri kaynağının tablolarını oluşturmak için kullanıldığında önemlidir. Fazla varlık olması, fazla tablo oluşturulmasına neden olur;
  • 13-17. satırlar: Statik bir weaving için EclipseLink yapılandırması. İki tür weaving vardır:
    • [statique]: JPA varlıklar, JPA katmanı örneklendiği anda zenginleştirilir (woven);
    • [dynamique]: JPA varlıklar, JPA katmanına ilk kez geldiklerinde zenginleştirilir (woven);

7.5. JPA varlıklarının

  

JPA varlıkları, Hibernate uygulaması için 6.3.3 paragrafında açıklananlarla aynıdır, ancak iki fark vardır:

  • tüm JPA varlıklarında, EclipseLink önbelleğini devre dışı bırakan [@Cache(alwaysRefresh = true)] anotasyonu bulunmaktadır. Bu belgede, kullanılan JPA uygulamalarının önbellekleri kullanılmamaktadır. EclipseLink'in önbelleği varsayılan olarak aktif görünmekte ve testlerde hatalara neden olmaktadır.

@Entity
@Table(name = ConfigJdbc.TAB_CATEGORIES)
@JsonFilter("jsonFilterCategorie")
@Cache(alwaysRefresh = true)
public class Categorie implements AbstractCoreEntity {
  • Tüm [@OneToMany] açıklamaları, [@CascadeOnDelete] açıklamasıyla birlikte yer almaktadır:

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

Bu açıklama, JPA varlıklarından tablolar oluşturulurken rol oynar. Bu, yabancı anahtarlara (burada PRODUITS[CATEGORIE_ID] ---> CATEGORIES[ID]) SQL [ON DELETE CASCADE] özniteliğini ekler; bu sayede, [CATEGORIES] tablosundaki bir kategori her silindiğinde, [PRODUITS] tablosundaki ilgili ürünlerin de silinmesini sağlar;

Not: Bu açıklamanın, az önce gördüğümüz gibi tablonun oluşturulmasında olduğu kadar, tablonun kullanımında da kullanıldığına dikkat etmek önemlidir. EclipseLink, SQL [ON DELETE CASCADE] özniteliğinin mevcut olduğunu varsayar ve bir kategorinin silinmesi istendiğinde her seferinde bu özniteliği kullanır. Bu özniteliğin olmaması hatalara neden olur.

7.6. Test katmanı

  

Yukarıdaki testler, Spring JDBC ve Spring JPA Hibernate uygulamalarındakilerle aynıdır. Gerekirse aşağıdaki sayfalara bakınız:

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

Elde edilen sonuçlar şunlardır:

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

[JUnitTestProxies] testi aşağıdaki konsol sonuçlarını vermektedir:

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]

Burada görüldüğü gibi, PROXY türündeki bir kategorinin [Categorie.produits] alanına ve PROXY türündeki bir ürünün [Produit.categorie] alanına erişildiğinde, her iki durumda da (7. ve 17. satırlar) bilgiyi elde edebiliyoruz. Üç JPA uygulaması arasında, PROXY varlıklarında bunu sağlayan tek uygulama budur.