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] الموجود في المجلد [<exemples>/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>
        <!-- التبعيات المتغيرة ********************************************** -->
        <!-- مزود JPA -->
        <dependency>
            <groupId>org.apache.openjpa</groupId>
            <artifactId>openjpa</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- التبعيات الثابتة ********************************************** -->
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>
        <!-- التكوين الموروث 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>
            <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 الخاص بـ SGBD المستخدم وبيانات الاتصال بقاعدة البيانات المطلوب استخدامها؛
  • الأسطر 35-40: يتضمن مشروع تكوين الطبقة JDBC المكتبة [Spring JDBC] التي تم استبدالها هنا بالمكتبة [Spring Data JPA]. لذلك يُنصح بعدم تضمينها في تبعيات المشروع. ومع ذلك، فإن بقاءها لن يتسبب في حدوث أخطاء؛

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

  

8.3.2. تكوين Spring

 

تقوم الفئة [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 {

    // المزود JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
        openJpaVendorAdapter.setShowSql(false);
        openJpaVendorAdapter.setDatabase(Database.MYSQL);
        openJpaVendorAdapter.setGenerateDdl(true);
        return openJpaVendorAdapter;
    }

    // حزم كيانات JPA
    public final static String[] ENTITIES_PACKAGES = { "generic.jpa.entities.dbproduitscategories" };

    // مصدر البيانات
    @Bean
    public DataSource dataSource() {
        // مصدر البيانات TomcatJdbc
        DataSource dataSource = new DataSource();
        // تكوين الوصول JDBC
        dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
        dataSource.setUsername(ConfigJdbc.USER_DBPRODUITSCATEGORIES);
        dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
        dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
        // الاتصالات المفتوحة في البداية
        dataSource.setInitialSize(5);
        // النتيجة
        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();
    }

    // مدير المعاملات
    @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">
        <!-- الكيانات 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">
        <!-- الكيانات JPA -->
        <class>generic.jpa.entities.dbproduits.Produit</class>
        <exclude-unlisted-classes />
    </persistence-unit>
</persistence>
  • السطر 4: وحدة الاستمرارية المرتبطة بقاعدة البيانات [dbproduitscategories]. يمكن أن تحمل أي اسم (السمة name
  • الأسطر 6-10: الكيانات الخمسة JPA المطلوب إدارتها؛
  • الأسطر 13-17: وحدة استمرارية أخرى مرتبطة بقاعدة البيانات [dbproduits]. تتيح OpenJpa وجود ملف استمرارية يحتوي على عدة وحدات استمرارية. أما EclipseLink فلا تتيح ذلك. تجدر الإشارة إلى أنه مع Hibernate، لم يتم استخدام ملف استمرارية؛

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

  

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

  • [JUnitTestCheckArguments]: الفقرة 4.11.1؛
  • [JUnitTestDao]: الفقرة 4.11.2؛
  • [JUnitTestPushTheLimits]: الفقرة 4.11.3؛
  • [JUnitTestProxies]: الفقرة 6.4.5؛

إعدادات التشغيل التي يجب استخدامها هي التالية:

لكي يتم إثراء الكيانات JPA (woven) بواسطة 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] الذي حدده المستخدم:

 

وقد أُعطيت المتغير [M2_REPO] القيمة الظاهرة في [3].

النتائج التي تم الحصول عليها لاختباري [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، يكون لدينا مؤشر null في كلتا الحالتين (السطران 7 و17).