Skip to content

8. Spring Data JPA OpenJpa

8.1. Introduction

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

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

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

ardından [<exemples>/spring-database-config/mysql/eclipse] [6] klasöründe bulunan [mysl-config-jpa-openjpa] [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-openjpa] 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:

 

8.3. JPA katman yapılandırma projesi

  

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

8.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ısı -->
        <dependency>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- sabit bağımlılıklar ********************************************** -->
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>
        <!-- JDBC'ten devralı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>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </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-23: OpenJpa 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 da bu durum hata oluşturmaz;

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

  

8.3.2. Spring Yapılandırması

 

[ConfigJpa] sınıfı, Spring projesini şu şekilde 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.OpenJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
@Import({ConfigJdbc.class})
public class ConfigJpa {

    // JPA sağlayıcısı
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
        openJpaVendorAdapter.setShowSql(false);
        openJpaVendorAdapter.setDatabase(Database.MYSQL);
        openJpaVendorAdapter.setGenerateDdl(true);
        return openJpaVendorAdapter;
    }

    // JPA varlık paketleri
    public final static String[] ENTITIES_PACKAGES = { "generic.jpa.entities.dbproduitscategories" };

    // veri kaynağı
    @Bean
    public DataSource dataSource() {
        // TomcatJdbc veri kaynağı
        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.setPackagesToScan(ENTITIES_PACKAGES);
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    // İş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-30. satırlar: [jpaVendorAdapter] bean'i artık OpenJpa ile uygulanmaktadır;

8.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 />
    </persistence-unit>
    <persistence-unit name="generic-jpa-entities-dbproduits" transaction-type="RESOURCE_LOCAL">
        <!-- varlıklar JPA -->
        <class>generic.jpa.entities.dbproduits.Produit</class>
        <exclude-unlisted-classes />
    </persistence-unit>
</persistence>
  • 4. satır: [dbproduitscategories] veritabanıyla ilişkili kalıcılık birimi. Bu birime herhangi bir isim verilebilir (name özniteliği);
  • 6-10. satırlar: yönetilecek beş JPA varlığı;
  • satır 13-17: [dbproduits] veritabanına bağlı başka bir kalıcılık birimi. OpenJpa, birden fazla kalıcılık birimi içeren bir kalıcılık dosyasına izin verir. EclipseLink ise buna izin vermez. Hibernate ile bir kalıcılık dosyası kullanılmadığını hatırlatırız;

8.5. Test katmanı

  

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

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

Kullanılacak yürütme yapılandırmaları şunlardır:

JPA varlıklarının woven ile zenginleştirilebilmesi için (OpenJpa), Java uygulamalarının veya JUnit testlerinin bir Java ajanı ile çalıştırılması gerekir. Bu ajans, JPA varlıklarını, JVM ve [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html] tarafından yüklenmeden önce zenginleştirecektir. Örneğin, [JUnitTestDao] testinin yapılandırmasına bir göz atalım:

  • [Arguments] [1] sekmesinde, [2] sözdizimine göre JVM'e Java ajanın referansı verilir. Java ajanı, [<m2-repo>/org/apache/openjpa/<version>] klasöründe bulunan [openjpa-<version>.jar] arşividir. <m2-repo> klasörü, Eclipse yapılandırmasında (aşağıdaki [3]) belirtilmiştir:

Yukarıdaki [2] dosyasında, kullanıcı tarafından tanımlanan [M2_REPO] değişkeni kullanılmıştır:

 

[M2_REPO] değişkenine, [3]'te görülen değer atanmıştır.

[JUnitTestDao] ve [JUnitTestPushTheLimits] testleri için elde edilen sonuçlar şunlardır:

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

[JUnitTestProxies] testi için elde edilen konsol sonuçları şunlardır:

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]

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) bir null işaretçisi olduğu görülmektedir.