Skip to content

8. Spring Data JPA OpenJpa

8.1. مقدمة

نحن نعيد استخدام البنية السابقة، التي نقوم الآن بتنفيذها باستخدام طبقة JPA/OpenJpa.

8.2. إعداد بيئة التطوير

باستخدام STS، قم بتنزيل مشروع [myql-config-jpa-eclipselink] [1-4]:

ثم قم باستيراد مشروع [mysl-config-jpa-openjpa] [5] الموجود في المجلد [<examples>/spring-database-config/mysql/eclipse] [6]:

بمجرد الانتهاء من ذلك، قم بتحديث بيئة Maven (Alt-F5) لجميع المشاريع في [Package Explorer]:

 

ثم، للتحقق من بيئة العمل، قم بتشغيل تكوين البناء المسمى [spring-jpa-generic-JUnitTestDao-openjpa]:

تقوم هذه التهيئة بتشغيل اختبار [JUnitTestDao]. يجب أن ينجح هذا الاختبار:

 

8.3. مشروع تكوين طبقة JPA

  

الغرض من هذا المشروع هو تكوين طبقة JPA للبنية الموضحة أدناه:

8.3.1. تكوين Maven

المشروع هو مشروع Maven تم تكوينه بواسطة ملف [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>
        <!-- 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>
  • الأسطر 5–7: عنصر Maven الذي تم إنشاؤه بواسطة هذا المشروع. وهو نفس عنصر مشروع [mysql-config-jpa-hibernate]. وهذا يعني أنه لا يمكن أن يكون سوى مشروع واحد من هذين المشروعين نشطًا في أي وقت من الأوقات؛
  • الأسطر 10–14: مشروع Maven الرئيسي الذي يحدد إصدار معظم التبعيات التي يحتاجها المشروع؛
  • الأسطر 19–23: مكتبة OpenJpa؛
  • الأسطر 26-29: مكتبة Spring Data؛
  • الأسطر 32-34: مشروع تكوين طبقة JPA يستند إلى مشروع تكوين طبقة JDBC، الذي يحدد، من بين أمور أخرى، برنامج تشغيل JDBC لنظام إدارة قواعد البيانات المستخدم وتفاصيل اتصال قاعدة البيانات؛
  • الأسطر 35–40: مشروع تكوين طبقة JDBC يتضمن مكتبة [Spring JDBC]، والتي تم استبدالها هنا بمكتبة [Spring Data JPA]. لذلك، نحدد عدم تضمينها في تبعيات المشروع. ومع ذلك، إذا بقيت، فلن يتسبب ذلك في أي أخطاء؛

في النهاية، تكون التبعيات كما يلي:

  

8.3.2. تكوين الربيع

 

تقوم فئة [ConfigJpa] بتكوين مشروع Spring على النحو التالي:


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

هذا التكوين مشابه للتكوين الموصوف في القسم 6.3.2 لتطبيق Hibernate JPA. سنكتفي بتفصيل الاختلافات فقط:

  • الأسطر 23–30: يتم الآن تنفيذ حبة [jpaVendorAdapter] باستخدام OpenJpa؛

8.4. ملف [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>
  • السطر 4: وحدة الاستمرارية المرتبطة بقاعدة البيانات [dbproduitscategories]. يمكن أن تحمل أي اسم (سمة name
  • الأسطر 6-10: الكيانات الخمسة لـ JPA التي سيتم إدارتها؛
  • الأسطر 13–17: وحدة استمرارية أخرى مرتبطة بقاعدة البيانات [dbproduitscategories]. يسمح OpenJpa بوجود ملف استمرارية يحتوي على وحدات استمرارية متعددة. أما EclipseLink فلا يسمح بذلك. تذكر أنه مع Hibernate، لم نستخدم ملف استمرارية؛

8.5. طبقة الاختبار

  

الاختبارات المذكورة أعلاه مطابقة لتلك الخاصة بتنفيذات Spring JDBC و Spring JPA Hibernate و Spring JPA EclipseLink. راجع الصفحات التالية إذا لزم الأمر:

تكوينات التنفيذ التي يجب استخدامها هي كما يلي:

لكي يتم دمج كيانات JPA بواسطة OpenJpa، يجب تشغيل تطبيقات Java أو اختبارات JUnit باستخدام وكيل Java. سيقوم هذا الوكيل بدمج كيانات JPA قبل أن يتم تحميلها بواسطة JVM [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html]. لنلقِ نظرة، على سبيل المثال، على تكوين اختبار [JUnitTestDao]:

  • في علامة التبويب [Arguments] [1]، نقدم مرجع وكيل Java إلى JVM باستخدام الصيغة [2]. وكيل Java هو الأرشيف [openjpa-<version>.jar] الموجود في المجلد [<m2-repo>/org/apache/openjpa/<version>]. يتم تحديد المجلد <m2-repo> في تكوين Eclipse ([3] أدناه):

في [2] أعلاه، استخدمنا متغيرًا محددًا من قبل المستخدم [M2_REPO]:

 

قمنا بتعيين القيمة الموضحة في [3] للمتغير [M2_REPO].

فيما يلي النتائج التي تم الحصول عليها لاختباري [JUnitTestDao] و[JUnitTestPushTheLimits]:

  • في [1]، [JUnitTestPushTheLimits-EclipseLink]: 70.583 ثانية؛
  • في [2]، [JUnitTestPushTheLimits-Hibernate]: 78.945 ثانية؛
  • في [3]، [JUnitTestPushTheLimits-JDBC]: 36.09 ثانية؛
  • في [4]، [JUnitTestPushTheLimits-OpenJpa]: 80.394 ثانية؛

نتائج وحدة التحكم التي تم الحصول عليها لاختبار [JUnitTestProxies] هي كما يلي:

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]

نرى هنا أنه عند الوصول إلى حقل [Categorie.produits] لفئة من نوع PROXY وحقل [Produit.categorie] لمنتج من نوع PROXY، نحصل على مؤشر فارغ في كلتا الحالتين (السطران 7 و17).