Skip to content

9. ایجاد پایگاه‌های داده از اشیاء JPA

امکان ایجاد جداول یک پایگاه داده از روی اشیاء JPA وجود دارد. این کاری است که اکنون انجام خواهیم داد. هدف این است که تأیید کنیم پایگاه داده تولید شده از روی اشیاء JPA واقعاً همان چیزی است که می‌خواهیم.

9.1. راه‌اندازی محیط کار

ابتدا با پیاده‌سازی شامل JPA، EclipseLink و [1] کار خواهیم کرد.

سپس با استفاده از کلاینت [MyManager] جداول MySQL و [dbproduitscategories] را از پایگاه داده حذف می‌کنیم (به بخش 23.5 مراجعه کنید). ما با حذف جداول حاوی کلیدهای خارجی [1-3] شروع می‌کنیم:

سپس دوباره با سه جدول باقی‌مانده [4-6] شروع می‌کنیم:

ما همین کار را با جدول [dbproduits] که توسط پروژه‌های [spring-jdbc-01 à 03] استفاده می‌شود، انجام می‌دهیم:

علاوه بر این، ما باید دو پروژه نسل‌گیری را برای دو پایگاه داده وارد کنیم:

  • در [1]، پروژه [generic-create-dbproduits] را وارد کنید که در [<exemples>/spring-database-generic/spring-jpa] و [2] یافت می‌شود؛
  • در [4]، ما پروژه [generic-create-dbproduitscategories] را وارد می‌کنیم که در [<exemples>/spring-database-generic/spring-jpa] و [5] یافت می‌شود؛

توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژه‌های Maven را مجدداً تولید کنید؛

9.2. در حال تولید پایگاه داده [dbproduitscategories]

 

9.2.1. پیکربندی Maven

فایل [pom.xml] پروژه به شرح زیر است:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>dvp.spring.database</groupId>
    <artifactId>generic-create-dbproduitscategories</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>generic-create-dbproduitscategories</name>
    <description>création de la bases de données [dbproduitscategories] à l'aide des annotations JPA</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.3.RELEASE</version>
    </parent>

    <dependencies>
        <!-- spring-jpa-generic -->
        <dependency>
            <groupId>dvp.spring.database</groupId>
            <artifactId>spring-jpa-generic</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- Spring Weaver -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>spring.data.console.Main</start-class>
        <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>
  • خطوط ۲۲–۲۶: وابستگی به پروژه [spring-jpa-generic] که در بخش ۶.۴ مورد بحث قرار گرفته است؛
  • خطوط ۲۸–۳۲: وابستگی به یک Weaver که برای غنی‌سازی اشیاء JPA در پیاده‌سازی‌های EclipseLink و OpenJpa استفاده خواهد شد. این وابستگی در فایل [pom.xml] مورد نیاز نیست، اما JAR آن به عنوان عامل جاوا استفاده خواهد شد. قرار دادن وابستگی در فایل [pom.xml] تضمین می‌کند که JAR واقعاً در دسترس خواهد بود؛

در نهایت، وابستگی‌ها به شرح زیر است:

  

9.2.2. پیکربندی Spring

  

کلاس [AppConfig] پروژه Spring را پیکربندی می‌کند:


package console;

import generic.jpa.config.ConfigJpa;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@Import({ ConfigJpa.class })
@EnableJpaRepositories(basePackages = { "console" })
public class AppConfig {

}
  • خط ۱۰: این کلاس بی́n‌ها را از کلاس [ConfigJpa] بازیابی می‌کند. توجه داشته باشید که این کلاس با انتیت‌های JPA از پایگاه داده [dbproduitscategories] کار می‌کند (به بخش ۶.۳ مراجعه کنید);
  • خط ۱۱: مشخص شده است که باید بسته [console] برای یافتن نمونه‌های [CrudRepository] اسکن شود؛

در کلاس [ConfigJpa]، بیان زیر یافت می‌شود (بسته به پیاده‌سازی JPA مورد استفاده، متفاوت است):


    // ارائه‌دهنده JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        //توجه: اشیاء JPA و پیکربندی EclipseLink در فایل META-INF/persistence.xml قرار دارند.
        EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
        eclipseLinkJpaVendorAdapter.setShowSql(false);
        eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
        eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
        return eclipseLinkJpaVendorAdapter;
}

خط ۸ در اینجا مهم است. این خط در تمام پیاده‌سازی‌های JPA مورد استفاده وجود دارد. در آن آمده است که اگر جداول مرتبط با انتیت‌های JPA وجود نداشته باشند، باید ایجاد شوند. ما برای تولید جداول به این ویژگی تکیه خواهیم کرد.

9.2.3. انبارها

  

رابط [ProduitsRepository] به شرح زیر است:


package console;

import generic.jpa.entities.dbproduitscategories.Produit;

import org.springframework.data.repository.CrudRepository;

public interface ProduitsRepository extends CrudRepository<Produit, Long> {

}

این نمونه‌سازی این رابط است که نمونه‌سازی لایه JPA را تحریک می‌کند. در واقع، در خط ۷، این رابط به موجودیت JPA [Produit] ارجاع می‌دهد که باعث ایجاد لایه JPA می‌شود. می‌توانستیم از هر رابط [CrudRepository] که به یکی از موجودیت‌های JPA ارجاع می‌دهد، استفاده کنیم. در واقع می‌توانیم ببینیم که اگرچه [repository] تنها به موجودیت JPA [Produit] ارجاع می‌دهد، در عمل تمام جداول مربوط به تمام موجودیت‌های JPA تولید می‌شوند.

9.2.4. کلاس قابل اجرا

  

کلاس [CreateDatabase] به شرح زیر است:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CreateDataBase {

    public static void main(String[] args) {
        // به سادگی کانکست Spring را نمونه کنید تا جداول پایگاه داده [dbproduitscategories] ایجاد شوند.
        // شما همچنین به حداقل یک مخزن داده‌های Spring نیاز دارید، در غیر این صورت هیچ اتفاقی نخواهد افتاد
        System.out.println("Travail en cours...");
        new AnnotationConfigApplicationContext(AppConfig.class).close();
        System.out.println("Travail terminé...");
    }

}
  • خط ۱۱: کانکست Spring ایجاد شده و بلافاصله بسته می‌شود. در این کانکست، بین [ProduitsRepository] وجود دارد که به انتیت‌های JPA و [Produit] ارجاع می‌دهد. این برای ایجاد لایه JPA و در نتیجه تولید جداول در پایگاه داده [dbproduitscategories] کافی است.

ما در پیکربندی زیر هستیم:

 
  • لایه [JDBC] برای پایگاه داده [dbproduitscategories] از MySQL پیکربندی شده است؛
  • لایه [JPA] با EclipseLink پیاده‌سازی شده است؛
  • پایگاه داده [dbproduitscategories] هیچ جدولی ندارد؛

توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژه‌های Maven را مجدداً تولید کنید؛

از پیکربندی اجرای زیر استفاده می‌شود:

  • در [1-2]، این پیکربندی اجرا برای موفقیت آزمون به یک عامل جاوا نیاز دارد. بسته به شرایط، EclipseLink همیشه به این ایجنت نیاز ندارد، اما در این مورد اگر این ایجنت موجود نباشد، اجرا با شکست مواجه می‌شود. این ایجنت، ایجنت EclipseLink نیست، بلکه یک ایجنت Spring است. این ایجنت توسط وابستگی زیر فراهم می‌شود:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <scope>runtime</scope>
</dependency>

ذخیره شده در فایل [pom.xml] پروژه. این ایجنت در [<m2-repo>/org/springframework/spring-instrument/4.1.6.RELEASE/spring-instrument-4.1.6.RELEASE.jar] یافت می‌شود، جایی که <m2-repo> مخزن محلی Maven است؛

اجرای این دستور نتیجه زیر را تولید می‌کند:

در [3] می‌بینیم که جداول ایجاد شده‌اند. اکنون بیایید DDL (زبان تعریف دامنه) برای پایگاه داده را بررسی کنیم:

اسکریپت تولید جدول SQL را می‌توان در فایلی به نام [1] نیز ذخیره کرد.

 

اسکریپت تولیدشده SQL به شرح زیر است:


SET FOREIGN_KEY_CHECKS=0;

USE `dbproduitscategories`;

CREATE TABLE `categories` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `produits` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci DEFAULT NULL,
  `CATEGORIE_ID` BIGINT(20) NOT NULL,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRIX` DOUBLE NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  `CATEGORIE` INTEGER(11) NOT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `NOM` (`NOM`) USING BTREE,
  KEY `FK_PRODUITS_CATEGORIE_ID` (`CATEGORIE_ID`) USING BTREE,
  CONSTRAINT `FK_PRODUITS_CATEGORIE_ID` FOREIGN KEY (`CATEGORIE_ID`) REFERENCES `categories` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `roles` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `users` (
 ...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `users_roles` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

برای مثال، اسکریپت SQL را در نظر بگیرید که جدول [PRODUITS] (خطوط 15–29) را تولید می‌کند:

  • خط 16: [ID] کلید اصلی (خط 23) با ویژگی [AUTO_INCREMENT] (خط 5) است. این مطابق با حاشیه‌نویسی‌های [@Id, @GeneratedValue(strategy = GenerationType.IDENTITY), @Column(name = ConfigJdbc.TAB_JPA_ID)] برای فیلد [id] از انتیت JPA است؛
  • خط 17: تعریف ستون [DESCRIPTION] با انوتیشن [@Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100)] برای فیلد [description] از انتیت JPA مطابقت دارد؛
  • خط ۱۸: ستون [CATEGORIE_ID] یک کلید خارجی از جدول [PRODUITS] بر روی ستون [CATEGORIES.ID] (خط ۲۶) است. علاوه بر این، این کلید خارجی دارای ویژگی [ON DELETE CASCADE] است. این مطابق با حاشیه‌نویسی‌های [@ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)] برای فیلد [Produit.categorie] و حاشیه‌نویسی [@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL }), @CascadeOnDelete] برای فیلد [Categorie.produits] است؛
  • خط ۱۹: تعریف ستون [NOM] با حاشیه نویسی [@Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)] برای فیلد [Produit.nom] مطابقت دارد؛
  • خط ۲۰: تعریف ستون [PRIX] با حاشیه نویسی [@Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)] برای فیلد [Produit.prix] مطابقت دارد؛
  • خطوط ۲۴–۲۵: اسکریپت برای هر یک از ستون‌های منحصربه‌فرد جدول سه نمایه ایجاد می‌کند؛

جدول‌های تولیدشده مقدار پیش‌فرض برای فیلد VERSIONING ندارند، در حالی که کد جاوا انتظار دارد این مقدار وجود داشته باشد. اگر این مقدار پیش‌فرض وجود نداشته باشد، برخی تست‌ها شکست خواهند خورد. این ویژگی به شرح زیر اضافه می‌شود:

 
 
 

این کار برای پنج جدولی انجام می‌شود که ستون [VERSIONING] را دارند. خود مقدار پیش‌فرض اهمیتی ندارد؛ فقط باید وجود داشته باشد. سپس هر بار که ردیفی که به آن تعلق دارد تغییر می‌کند، مقدار آن ۱ واحد افزایش می‌یابد.

پس از انجام این کار، بررسی کنید که پیکربندی‌های اجرایی زیر با موفقیت اجرا شوند:

  • [spring-jdbc-generic-04.JUnitTestDao]، که پیاده‌سازی JDBC را آزمایش می‌کند؛
  • [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]، که پیاده‌سازی‌های JPA (Hibernate یا Eclipselink؛ در این مورد، EclipseLink خواهد بود) را آزمایش می‌کند

هر دو اجرا باید با موفقیت به پایان برسند.

9.2.6. ایجاد جداول با Hibernate

ما جداول Hibernate را با استفاده از محیط Eclipse زیر ایجاد می‌کنیم:

 

جدول‌ها توسط پیکربندی اجرای به نام [generic-create-dbproduitscategories-hibernate] بدون یک عامل جاوا تولید می‌شوند؛

اسکریپت SQL برای پایگاه داده تولید شده توسط Hibernate به شرح زیر است:


SET FOREIGN_KEY_CHECKS=0;

USE `dbproduitscategories`;

CREATE TABLE `categories` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `UK_7ajcg7japnxw846ru01damg8s` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `produits` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci DEFAULT NULL,
  `CATEGORIE_ID` BIGINT(20) NOT NULL,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRIX` DOUBLE NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `UK_hfvjn9lp7qoo5x79uu0ump3rf` (`NOM`) USING BTREE,
  KEY `FK_p3foj9yrqnmi7856n9s8mbpue` (`CATEGORIE_ID`) USING BTREE,
  CONSTRAINT `FK_p3foj9yrqnmi7856n9s8mbpue` FOREIGN KEY (`CATEGORIE_ID`) REFERENCES `categories` (`ID`)
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `roles` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `users` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `users_roles` (
 ...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

جدول‌های تولیدشده یکسان هستند، زیرا Hibernate نیز از anotationهای JPA استفاده کرده است. برای Hibernate، معادل annotationهای EclipseLink [@OnCascadeDelete] را که جدول‌ها را تولید می‌کردند، پیدا نکرده‌ام.ویژگی SQL [ON DELTE CASCADE] روی کلید خارجی [PRODUITS.CATEGORIE_ID] (خط 25). بنابراین این ویژگی باید به‌صورت دستی ایجاد شود، زیرا برای تست مورد نیاز است:

 

همین کار باید برای دو کلید خارجی در جدول [USERS_ROLES] نیز انجام شود:

در نهایت، همان‌طور که در پیاده‌سازی EclipseLink انجام شد، ستون‌های [VERSIONING] در پنج جدول باید دارای مقدار پیش‌فرض باشند:

 

پس از انجام این کار، بررسی کنید که پیکربندی‌های اجرایی زیر با موفقیت اجرا شوند:

  • [spring-jdbc-generic-04.JUnitTestDao]، که پیاده‌سازی JDBC را آزمایش می‌کند؛
  • [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]، که پیاده‌سازی‌های JPA را با استفاده از Hibernate یا EclipseLink (در این مورد، Hibernate خواهد بود) آزمایش می‌کند

هر دو اجرا باید با موفقیت به پایان برسند.

9.2.7. ایجاد جداول با OpenJpa

ما رویه قبلی را با پیاده‌سازی JPA OpenJpa تکرار می‌کنیم:

 

توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژه‌های Maven را مجدداً تولید کنید؛

ما کلاس [ConfigJpa] را که پروژه [mysql-config-jpa-openjpa] را پیکربندی می‌کند، به شرح زیر تغییر می‌دهیم:


package generic.jpa.config;

import generic.jdbc.config.ConfigJdbc;

import java.util.Map;

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;
    }
..
    // EntityManagerFactory
    @Bean
    public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(jpaVendorAdapter);
        factory.setPackagesToScan(ENTITIES_PACKAGES);
        Map<String, Object> mapJpaProperties = factory.getJpaPropertyMap();
        mapJpaProperties.put("openjpa.jdbc.MappingDefaults",
                "ForeignKeyDeleteAction=cascade,JoinForeignKeyDeleteAction=restrict");
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }
}
  • خطوط ۴۰–۴۱: ما یک ویژگی برای OpenJPA ایجاد می‌کنیم که مشخص می‌کند هنگام ایجاد جداول، کلیدهای خارجی چگونه تولید شوند. بدون این ویژگی، کلیدهای خارجی ایجاد نمی‌شوند. ویژگی [ForeignKeyDeleteAction=cascade] امکان تولید ویژگی [ON DELETE CASCADE] را روی این کلیدهای خارجی فراهم می‌کند؛

جدول‌ها توسط پیکربندی زمان اجرا با نام [generic-create-dbproduitscategories-openjpa] ایجاد می‌شوند که دو عامل جاوا دارد؛

Image

  • آژانس جاوا اول، آژانس Spring است که قبلاً با EclipseLink استفاده می‌شود؛
  • عامل دوم جاوا توسط OpenJpa فراهم می‌شود؛

سپس اسکریپت SQL از پایگاه دادهٔ تولیدشده به شرح زیر است:


SET FOREIGN_KEY_CHECKS=0;

USE `dbproduitscategories`;

CREATE TABLE `categories` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `U_CTGORIS_NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `produits` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci DEFAULT NULL,
  `CATEGORIE_ID` BIGINT(20) DEFAULT NULL,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRIX` DOUBLE NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `U_PRODUTS_NOM` (`NOM`) USING BTREE,
  KEY `CATEGORIE_ID` (`CATEGORIE_ID`) USING BTREE,
  CONSTRAINT `produits_ibfk_1` FOREIGN KEY (`CATEGORIE_ID`) REFERENCES `categories` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `roles` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `NAME` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `U_ROLES_NAME` (`NAME`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `users` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `LOGIN` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `NAME` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PASSWORD` VARCHAR(60) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `U_USERS_LOGIN` (`LOGIN`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

CREATE TABLE `users_roles` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `VERSIONING` BIGINT(20) DEFAULT NULL,
  `ROLE_ID` BIGINT(20) NOT NULL,
  `USER_ID` BIGINT(20) NOT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  KEY `ROLE_ID` (`ROLE_ID`) USING BTREE,
  KEY `USER_ID` (`USER_ID`) USING BTREE,
  CONSTRAINT `users_roles_ibfk_2` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`ID`) ON DELETE CASCADE,
  CONSTRAINT `users_roles_ibfk_1` FOREIGN KEY (`ROLE_ID`) REFERENCES `roles` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

این همانند مورد EclipseLink است. بنابراین همان اصلاحات را روی جداول اعمال خواهیم کرد. پس از انجام این کار، بررسی کنید که پیکربندی‌های اجرایی زیر با موفقیت اجرا شوند:

  • [spring-jdbc-generic-04.JUnitTestDao]، که پیاده‌سازی JDBC را آزمایش می‌کند؛
  • [spring-jpa-generic-JUnitTestDao-openjpa]، که پیاده‌سازی JPA و OpenJpa را آزمایش می‌کند؛

هر دو اجرا باید با موفقیت انجام شوند.

9.3. ایجاد پایگاه داده [dbproduits]

پایه [dbproduits] توسط پروژه‌های [spring-jdbc-01 à 03] استفاده می‌شود. همچنین می‌توان آن را از یک موجودیت JPA تولید کرد.

  • در [1]، پروژه‌های Eclipse. این در یک پیکربندی MySQL / EclipseLink خواهد بود. پروژهٔ تولید برای پایگاه دادهٔ [dbproduits]، [generic-create-dbproduits] است؛
  • در [2]، جدول [PRODUITS] باید تولید شود؛

توجه: کلیدهای Alt-F5 را فشار دهید و همه پروژه‌های Maven را مجدداً تولید کنید؛

9.3.1. پیکربندی Maven

پیکربندی Maven برای پروژه [generic-create-dbproduits] به شرح زیر است:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>dvp.spring.database</groupId>
    <artifactId>generic-create-dbproduits</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>generic-create-dbproduits</name>
    <description>création de la bases de données [dbproduits] à l'aide des annotations JPA</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.3.RELEASE</version>
    </parent>

    <dependencies>
        <!--پیکربندی JPA از SGBD -->
        <dependency>
            <groupId>dvp.spring.database</groupId>
            <artifactId>generic-config-jpa</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>spring.data.console.Main</start-class>
        <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>

تنها یک وابستگی وجود دارد، در خطوط 22–26، به پروژه‌ای که لایه JPA را پیکربندی می‌کند. در نهایت، وابستگی‌ها به شرح زیر است:

  

9.3.2. پیکربندی Spring

  

کلاس پیکربندی Spring به شرح زیر است:


package console;

import generic.jdbc.config.ConfigJdbc;
import generic.jpa.config.ConfigJpa;

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.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

@EnableJpaRepositories(basePackages = { "console" })
@Configuration
@Import({ ConfigJpa.class })
public class AppConfig {

    //منبع داده
    @Bean
    public DataSource dataSource() {
        //منبع داده TomcatJdbc
        DataSource dataSource = new DataSource();
        // پیکربندی دسترسی JDBC
        dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
        dataSource.setUsername(ConfigJdbc.USER_DBPRODUITS);
        dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITS);
        dataSource.setUrl(ConfigJdbc.URL_DBPRODUITS);
        //اتصالات در ابتدا باز شدند
        dataSource.setInitialSize(5);
        // نتیجه
        return dataSource;
    }

    //EntityManagerFactory
    @Bean
    public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(jpaVendorAdapter);
        factory.setPersistenceUnitName("generic-jpa-entities-dbproduits");
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

}
  • خط ۱۸: بین‌های کلاس [ConfigJpa] (بخش ۷.۳) وارد می‌شوند؛
  • خطوط 22–35: منبع داده [dataSource] مجدداً تعریف می‌شود. در [ConfigJpa]، منبع داده پایگاه داده [dbproduitscategories] است. در اینجا، این پایگاه داده [dbproduits] خواهد بود؛
  • خطوط ۳۸–۴۶: بیون [entityManagerFactory] از کلاس [ConfigJpa] بازتعریف شده است. در آن کلاس، انتیت‌های JPA، [Produit, Categorie] بودند. در اینجا، فقط [Produit] است و تعریف یکسانی با پروژه ای که لایه JPA را پیکربندی می‌کند، ندارد؛
  • خط ۴۲: برای تعریف این موجودیت جدید JPA، ما به موجودیت‌های JPA که در فایل [META-INF/persistence.xml] تعریف شده‌اند ارجاع می‌دهیم:
  

فایل [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-dbproduits" transaction-type="RESOURCE_LOCAL">
        <!-- نهادها JPA -->
        <class>generic.jpa.entities.dbproduits.Produit</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
    </persistence-unit>
</persistence>
  • خط ۶: موجودیت واحد JPA;
  • خط ۴: نام واحد پایداری [generic-jpa-entities-dbproduits]، که در بین [entityManagerFactory] ارجاع شده است؛

9.3.3. واحد JPA [Produit]

  

اِنتیتی JPA در پروژه [mysql-config-jpa-eclipselink] به شرح زیر تعریف شده است:


package generic.jpa.entities.dbproduits;

import generic.jdbc.config.ConfigJdbc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity(name="Produit1")
@Table(name = ConfigJdbc.TAB_PRODUITS)
public class Produit {

    // میدان‌ها
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = ConfigJdbc.TAB_PRODUITS_ID)
    private int id;
    @Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)
    private String nom;
    @Column(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE, nullable = false)
    private int categorie;
    @Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)
    private double prix;
    @Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100, nullable = false)
    private String description;

    // سازنده‌ها
    public Produit() {

    }

    public Produit(int id, String nom, int categorie, double prix, String description) {
        this.id = id;
        this.nom = nom;
        this.categorie = categorie;
        this.prix = prix;
        this.description = description;
    }

    // گیرنده و تنظیم‌کننده
    ...
}

این یک تعریف JPA است که اکنون به یک استاندارد تبدیل شده است. ما صرفاً به نکات زیر اشاره می‌کنیم:

  • خط ۱۲: این انتیت به نام [Produit1] نام‌گذاری شده است. به‌طور پیش‌فرض، نام یک انتیت نام کلاس است، در این مورد [Produit]. با این حال، از آنجا که یک موجودیت دیگر به نام JPA [Produit] در همان پروژه وجود داشت، خطایی قبل از شروع اجرا گزارش شد. ما آن را به شرح زیر حل کردیم؛
  • خط ۲۴: دسته در اینجا صرفاً یک عدد است؛
  • هیچ رابطه‌ای بین این اشیاء وجود ندارد. بنابراین با یک وضعیت بسیار ساده مواجه هستیم؛

9.3.4. کلاس قابل اجرا

  

کلاس [CreateDatabase] به شرح زیر است:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CreateDataBase {

    public static void main(String[] args) {
        // به سادگی کانکست Spring را نمونه سازی کنید تا جداول پایگاه داده را ایجاد کند [dbproduits]
        // شما همچنین به حداقل یک مخزن داده‌های Spring نیاز دارید، در غیر این صورت هیچ اتفاقی نخواهد افتاد
        System.out.println("Travail en cours...");
        new AnnotationConfigApplicationContext(AppConfig.class).close();
        System.out.println("Travail terminé...");
    }

}

این کدی است که قبلاً با آن مواجه شده‌ایم.

جدول [PRODUITS] با پیکربندی اجرای زیر ایجاد شده است:

لاگ‌های کنسول به شرح زیر است:

Travail en cours...
16:31:17.668 [main] INFO  o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6fadae5d: startup date [Tue Jun 02 16:31:17 CEST 2015]; root of context hierarchy
16:31:17.758 [main] INFO  o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'entityManagerFactory': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jpa.config.ConfigJpa; factoryMethodName=entityManagerFactory; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jpa.config.ConfigJpa] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=appConfig; factoryMethodName=entityManagerFactory; initMethodName=null; destroyMethodName=(inferred); defined in console.AppConfig]
16:31:17.759 [main] INFO  o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'dataSource': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=generic.jpa.config.ConfigJpa; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class generic.jpa.config.ConfigJpa] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=appConfig; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in console.AppConfig]
16:31:18.462 [main] INFO  o.s.o.j.LocalContainerEntityManagerFactoryBean - Building JPA container EntityManagerFactory for persistence unit 'generic-jpa-entities-dbproduits'
[EL Info]: server: 2015-06-02 16:31:18.624--ServerSession(817299424)--Detected server platform: org.eclipse.persistence.platform.server.NoServerPlatform.
[EL Info]: server: 2015-06-02 16:31:19.015--ServerSession(817299424)--Detected server platform: org.eclipse.persistence.platform.server.NoServerPlatform.
[EL Info]: 2015-06-02 16:31:19.021--ServerSession(817299424)--EclipseLink, version: Eclipse Persistence Services - 2.6.0.v20150309-bf26070
[EL Info]: connection: 2015-06-02 16:31:20.122--ServerSession(817299424)--/file:/D:/data/istia-1415/spring data/dvp/dvp-spring-database-04/spring-database-generic/spring-jpa/generic-create-dbproduits/target/classes_generic-jpa-entities-dbproduits login successful
16:31:20.654 [main] INFO  o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6fadae5d: startup date [Tue Jun 02 16:31:17 CEST 2015]; root of context hierarchy
[EL Info]: connection: 2015-06-02 16:31:20.656--ServerSession(817299424)--/file:/D:/data/istia-1415/spring data/dvp/dvp-spring-database-04/spring-database-generic/spring-jpa/generic-create-dbproduits/target/classes_generic-jpa-entities-dbproduits logout successful
[EL Warning]: 2015-06-02 16:31:20.657--session_manager_no_partition
Travail terminé...

اکنون، بیایید به کلاینت [MyManager] بازگردیم و نمای [1-2] را تازه کنیم:

در [3] می‌توانیم ببینیم که یک جدول ایجاد شده است. اکنون بیایید DDL (زبان تعریف دامنه) پایگاه داده را بررسی کنیم:


SET FOREIGN_KEY_CHECKS=0;

USE `dbproduits`;

CREATE TABLE `produits` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `CATEGORIE` INTEGER(11) NOT NULL,
  `DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci NOT NULL,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRIX` DOUBLE NOT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

ما در واقع جدول مورد انتظار را دریافت می‌کنیم. برای تأیید این موضوع، پیکربندی زیر را اجرا خواهیم کرد:

این باید با موفقیت انجام شود.

9.3.6. تولید Hibernate

 

توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژه‌های Maven را مجدداً تولید کنید؛

پیکربندی زمان اجرا به شرح زیر است:

اسکریپت SQL تولید شده توسط Hibernate به شرح زیر است:


USE `dbproduits`;

CREATE TABLE `produits` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `CATEGORIE` INTEGER(11) NOT NULL,
  `DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci NOT NULL,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRIX` DOUBLE NOT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `UK_hfvjn9lp7qoo5x79uu0ump3rf` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;

9.3.7. تولید OpenJpa

 

توجه: کلیدهای Alt-F5 را فشار دهید و تمام پروژه‌های Maven را مجدداً تولید کنید؛

پیکربندی زمان اجرا به شرح زیر است:

اسکریپت SQL که توسط OpenJpa تولید شده است، به شرح زیر است:


USE `dbproduits`;

CREATE TABLE `produits` (
  `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `CATEGORIE` INTEGER(11) NOT NULL,
  `DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci NOT NULL,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRIX` DOUBLE NOT NULL,
  PRIMARY KEY (`ID`) USING BTREE,
  UNIQUE KEY `U_PRODUTS_NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;