Skip to content

9. JPA varlıklarından veritabanlarının oluşturulması

JPA varlıklarından bir veritabanının tablolarını oluşturmak mümkündür. Şimdi bunu göstereceğiz. Bunun amacı, JPA varlıklarından oluşturulan veritabanının gerçekten istediğimiz veritabanı olup olmadığını doğrulamaktır.

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

Öncelikle JPA, EclipseLink ve [1] uygulamalarıyla çalışacağız.

Ardından, MySQL ve [dbproduitscategories] veritabanlarındaki tabloları [MyManager] istemcisi ile sileceğiz (bkz. paragraf 23.5). Öncelikle, [1-3] yabancı anahtarlarını içeren tabloları siliyoruz:

Ardından, kalan üç tabloyla yeniden başlıyoruz: [4-6]:

Aynı işlemi, [spring-jdbc-01 à 03] projeleri tarafından kullanılan [dbproduits] tablosu için de yapıyoruz:

Ayrıca, her iki veritabanını oluşturan iki projeyi de içe aktarmamız gerekiyor:

  • [1] projesine, [<exemples>/spring-database-generic/spring-jpa] ve [2] projelerinde bulunan [generic-create-dbproduits] projesini içe aktarın;
  • [4] dosyasında, [generic-create-dbproduitscategories] projesini içe aktarıyoruz; bu proje [<exemples>/spring-database-generic/spring-jpa] ve [5] dosyalarında bulunur;

Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;

9.2. [dbproduitscategories] veritabanının oluşturulması

 

9.2.1. Maven yapılandırması

Projenin [pom.xml] dosyası şu şekildedir:


<?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>
        <!-- Weaver Spring -->
        <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>
  • 22-26. satırlar: 6.4. paragrafta incelenen [spring-jpa-generic] projesine olan bağımlılık;
  • 28-32. satırlar: EclipseLink ve OpenJpa uygulamalarının JPA varlıklarını zenginleştirmek için kullanılacak bir weaver'a olan bağımlılık. Bu bağımlılık [pom.xml] dosyasında gerekli değildir, ancak ilgili jar dosyası kullanılacak Java ajanı olacaktır. Bağımlılığı [pom.xml] dosyasına eklemek, jar dosyasının kesinlikle mevcut olacağını garanti eder;

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

  

9.2.2. Spring Yapılandırması

  

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


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 {

}
  • 10. satır: Sınıf, [ConfigJpa] sınıfındaki bean'leri alır. Bu sınıfın, [dbproduitscategories] veritabanındaki JPA varlıklarıyla çalıştığını hatırlatırız (bkz. 6.3. paragraf);
  • 11. satır: [console] paketinin taranarak içindeki [CrudRepository] örneklerinin bulunması gerektiği belirtilir;

[ConfigJpa] sınıfında, aşağıdaki bean bulunur (kullanılan JPA uygulamasına göre değişiklik gösterir):


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

Burada önemli olan 8. satırdır. Bu satır, kullanılan tüm JPA uygulamalarında mevcuttur. Bu satır, JPA varlıklarıyla ilişkili tabloların mevcut olmaması durumunda bunların oluşturulması gerektiğini belirtir. Tabloları oluşturmak için bu özelliğe dayanacağız.

9.2.3. Depolar

  

[ProduitsRepository] arayüzü şu şekildedir:


package console;

import generic.jpa.entities.dbproduitscategories.Produit;

import org.springframework.data.repository.CrudRepository;

public interface ProduitsRepository extends CrudRepository<Produit, Long> {

}

Bu arayüzün örneklenmesi, JPA katmanının örneklenmesine neden olacaktır. Nitekim, 7. satırda arayüz, JPA ve [Produit] varlıklarına başvurmaktadır; bu da JPA katmanının örneklenmesini zorunlu kılacaktır. JPA varlıklarından birine referans veren herhangi bir [CrudRepository] arayüzü de kullanılabilirdi. Aslında, [repository]'in yalnızca JPA ve [Produit] varlıklarına referans vermesine rağmen, JPA varlıklarının tüm tablolarının oluşturulduğu görülmektedir.

9.2.4. Çalıştırılabilir sınıf

  

[CreateDatabase] sınıfı şu şekildedir:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CreateDataBase {

    public static void main(String[] args) {
        // [dbproduitscategories] veritabanındaki tabloları oluşturmak için Spring bağlamını başlatmak yeterlidir
        // ayrıca en az bir Spring Data Repository gerekir, aksi takdirde hiçbir şey olmaz
        System.out.println("Travail en cours...");
        new AnnotationConfigApplicationContext(AppConfig.class).close();
        System.out.println("Travail terminé...");
    }

}
  • 11. satır: Spring bağlamı oluşturulur ve hemen kapatılır. Bu bağlamda, [ProduitsRepository] varlığı, JPA ve [Produit] varlıklarına başvurur. Bu, JPA katmanını oluşturmak ve dolayısıyla [dbproduitscategories] veritabanındaki tabloları oluşturmak için yeterlidir.

Şu yapılandırmada bulunuyoruz:

 
  • [JDBC] katmanı, [dbproduitscategories] veritabanı için MySQL ile yapılandırılmıştır;
  • [JPA] katmanı, EclipseLink ile birlikte uygulanmıştır;
  • [dbproduitscategories] veritabanında tablo bulunmamaktadır;

Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;

Aşağıdaki çalıştırma yapılandırması kullanılır:

  • [1-2]'te, bu yürütme yapılandırması testin başarılı olması için bir Java ajanı gerektirir. Duruma göre, EclipseLink her zaman bu ajana ihtiyaç duymaz, ancak burada ajan yoksa yürütme başarısız olur. Bu ajan, EclipseLink ajanı değil, bir Spring ajanıdır. Aşağıdaki bağımlılık tarafından sağlanır:

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

projenin [pom.xml] dosyasında kayıtlıdır. Ajan, [<m2-repo>/org/springframework/spring-instrument/4.1.6.RELEASE/spring-instrument-4.1.6.RELEASE.jar] dosyasında bulunur; burada <m2-repo>, Maven’ın yerel deposudur;

Çalıştırma işlemi şu sonucu verir:

[3] dosyasında, tabloların oluşturulduğu görülmektedir. Şimdi veritabanının DDL (Domain Definition Language) dosyasını kontrol edelim:

Tabloları oluşturan SQL komut dosyası, [1] adında bir dosyaya da kaydedilebilir.

 

Oluşturulan SQL komut dosyası şöyledir:


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

Örneğin, SQL komut dosyasını inceleyelim; bu komut dosyası, [PRODUITS] tablosunu oluşturur (15-29. satırlar):

  • 16. satır: [ID], [AUTO_INCREMENT] özniteliği (5. satır) ile birincil anahtardır (23. satır). Bu, JPA varlığının [id] alanındaki [@Id, @GeneratedValue(strategy = GenerationType.IDENTITY), @Column(name = ConfigJdbc.TAB_JPA_ID)] açıklamalarına karşılık gelir;
  • 17. satır: [DESCRIPTION] sütununun tanımı, JPA varlığının [description] alanındaki [@Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100)] notuna karşılık gelir;
  • satır 18: [CATEGORIE_ID] sütunu, [PRODUITS] tablosundaki [CATEGORIES.ID] sütununa (satır 26) ait yabancı anahtardır. Ayrıca bu yabancı anahtarın [ON DELETE CASCADE] özniteliği vardır. Bu, [Produit.categorie] alanındaki [@ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)] açıklamalarına ve [Categorie.produits] alanındaki [@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL }), @CascadeOnDelete] açıklamasına karşılık gelir;
  • 19. satır: [NOM] sütununun tanımı, [Produit.nom] alanındaki [@Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)] açıklamasıyla eşleşir;
  • 20. satır: [PRIX] sütununun tanımı, [Produit.prix] alanındaki [@Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)] açıklamasına karşılık gelir;
  • 24-25. satırlar: komut dosyası, tablodaki her bir benzersiz sütun için üç dizin oluşturur;

Oluşturulan tabloların VERSIONING alanı için varsayılan değer bulunmazken, Java kodu böyle bir değerin olmasını bekler. Bu varsayılan değer mevcut değilse, bazı testler başarısız olur. Bu öznitelik şu şekilde eklenir:

 
 
 

[VERSIONING] sütununa sahip beş tablo için de aynısını yapıyoruz. Varsayılan değerin ne olduğu önemli değildir. Sadece mevcut olması yeterlidir. Ardından, ait olduğu satırda her değişiklik yapıldığında bu değer 1 artırılır.

Bu işlem tamamlandıktan sonra, aşağıdaki yürütme yapılandırmalarının başarılı olduğunu kontrol edin:

  • [spring-jdbc-generic-04.JUnitTestDao], JDBC uygulamasını test eder;
  • [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink], JPA Hibernate veya Eclipselink uygulamalarını test eder (burada EclipseLink olacaktır)

Her iki çalıştırma da başarılı olmalıdır.

9.2.6. Hibernate ile tabloların oluşturulması

Hibernate tablolarını aşağıdaki Eclipse ortamında oluşturuyoruz:

 

Tabloların oluşturulması, Java ajanı olmadan [generic-create-dbproduitscategories-hibernate] adlı yürütme yapılandırmasıyla gerçekleştirilir;

Hibernate tarafından oluşturulan veritabanının SQL komut dosyası şu şekildedir:


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 de JPA anotasyonlarını kullandığı için oluşturulan tablolar aynıdır. Hibernate için,SQL ve [ON DELTE CASCADE] özniteliklerini oluşturan EclipseLink ve [@OnCascadeDelete] anotasyonlarının eşdeğerini bulamadım. Bu nedenle, testler için gerekli olduğu için bu özniteliği manuel olarak oluşturmam gerekiyor:

 

Aynı işlem, [USERS_ROLES] tablosundaki iki yabancı anahtar için de yapılmalıdır:

Son olarak, EclipseLink uygulamasında yapıldığı gibi, beş tablodaki [VERSIONING] sütunlarının bir varsayılan değeri olmalıdır:

 

Bu işlem tamamlandıktan sonra, aşağıdaki yürütme yapılandırmalarının başarılı olduğunu kontrol edin:

  • [spring-jdbc-generic-04.JUnitTestDao], JDBC uygulamasını test eder;
  • [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink]: JPA Hibernate veya Eclipselink uygulamalarını test eder (burada Hibernate kullanılacaktır)

Her iki çalıştırma da başarılı olmalıdır.

9.2.7. OpenJpa ile tabloların oluşturulması

JPA OpenJpa uygulamasıyla önceki prosedürü tekrarlıyoruz:

 

Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;

[mysql-config-jpa-openjpa] projesini yapılandıran [ConfigJpa] sınıfını şu şekilde değiştiriyoruz:


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 sağlayıcısı
    @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();
    }
}
  • 40-41. satırlar: Tabloların oluşturulması sırasında yabancı anahtarların nasıl oluşturulacağını belirten OpenJPA için bir özellik oluşturuyoruz. Bu özellik olmadan yabancı anahtarlar oluşturulmaz. [ForeignKeyDeleteAction=cascade] özniteliği, bu yabancı anahtarlar üzerinde [ON DELETE CASCADE] özniteliğinin oluşturulmasını sağlar;

Tabloların oluşturulması, iki Java ajanı içeren [generic-create-dbproduitscategories-openjpa] adlı yürütme yapılandırması tarafından gerçekleştirilir;

Image

  • İlk Java ajanı, EclipseLink ile daha önce kullanılan Spring ajanıdır;
  • ikinci Java ajanı ise OpenJpa tarafından sağlanmaktadır;

Oluşturulan veritabanındaki SQL komut dosyası şu şekildedir:


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

Bu, EclipseLink ile aynı durumdur. Dolayısıyla, tablolarda da aynı düzeltmeler yapılacaktır. Bu işlem tamamlandıktan sonra, aşağıdaki yürütme yapılandırmalarının başarılı olup olmadığını kontrol edin:

  • [spring-jdbc-generic-04.JUnitTestDao], JDBC uygulamasını test eder;
  • [spring-jpa-generic-JUnitTestDao-openjpa], JPA ve OpenJpa uygulamalarını test eder;

Her iki çalıştırma da başarılı olmalıdır.

9.3. [dbproduits] veritabanının oluşturulması

[dbproduits] veritabanı, [spring-jdbc-01 à 03] projeleri tarafından kullanılır. Ayrıca, JPA varlığından da oluşturulabilir.

  • [1]'te, Eclipse projeleri. MySQL / EclipseLink yapılandırmasında olacağız. [dbproduits] veritabanını oluşturan proje [generic-create-dbproduits]'tir;
  • [2]'te, oluşturulacak [PRODUITS] tablosu;

Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;

9.3.1. Maven Yapılandırması

[generic-create-dbproduits] projesinin Maven yapılandırması şu şekildedir:


<?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>
        <!-- SGBD'in JPA yapılandırması -->
        <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>

JPA katmanını yapılandıran projede, 22-26. satırlarda tek bir bağımlılık bulunmaktadır. Sonuç olarak, bağımlılıklar şu şekildedir:

  

9.3.2. Spring yapılandırması

  

Spring yapılandırma sınıfı şöyledir:


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 {

    // 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_DBPRODUITS);
        dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITS);
        dataSource.setUrl(ConfigJdbc.URL_DBPRODUITS);
        // 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.setPersistenceUnitName("generic-jpa-entities-dbproduits");
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

}
  • 18. satır: [ConfigJpa] sınıfındaki bean'ler içe aktarılır (7.3. paragraf);
  • 22-35. satırlar: [dataSource] veri kaynağı yeniden tanımlanmaktadır. [ConfigJpa]'te veri kaynağı [dbproduitscategories] veritabanıdır. Burada ise [dbproduits] veritabanı olacaktır;
  • satır 38-46: [ConfigJpa] sınıfındaki [entityManagerFactory] bean'i yeniden tanımlanıyor. Bu sınıfta, JPA varlıklarının adı [Produit, Categorie] idi. Burada ise sadece [Produit] vardır ve bu varlık, JPA katmanını yapılandıran projedeki tanımla aynı değildir;
  • 42. satır: Bu yeni JPA varlığını tanımlamak için, [META-INF/persistence.xml] dosyasında tanımlanan JPA varlıklarına atıfta bulunulur:
  

[persistence.xml] dosyası şu şekildedir:


<?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">
        <!-- varlıklar JPA -->
        <class>generic.jpa.entities.dbproduits.Produit</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
    </persistence-unit>
</persistence>
  • 6. satır: tek varlık JPA;
  • 4. satır: [entityManagerFactory] bean'inde referans verilen [generic-jpa-entities-dbproduits] kalıcılık biriminin adı;

9.3.3. JPA varlığı [Produit]

  

JPA varlığı, [mysql-config-jpa-eclipselink] projesinde şu şekilde tanımlanmıştır:


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 {

    // alanlar
    @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;

    // oluşturucular
    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;
    }

    // alıcı ve ayarlayıcılar
    ...
}

Bu, artık klasik hale gelmiş bir JPA tanımlamasıdır. Şu noktalara dikkat çekmek yeterlidir:

  • 12. satır: Varlığa [Produit1] adı verilmiştir. Varsayılan olarak, bir varlığın adı sınıfın adıdır; buradaki sınıf adı [Produit]'tir. Ancak aynı projede JPA ve [Produit] adlı başka varlıklar da bulunduğu için, herhangi bir yürütme işlemi gerçekleşmeden önce bir hata bildirildi. Bu hatayı şu şekilde giderdik;
  • 24. satır: Kategori burada sadece bir numaradır;
  • entiteler arası herhangi bir ilişki yoktur. Dolayısıyla çok basit bir durum söz konusudur;

9.3.4. Çalıştırılabilir sınıf

  

[CreateDatabase] sınıfı şöyledir:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CreateDataBase {

    public static void main(String[] args) {
        // veritabanı tablolarını oluşturmak için Spring bağlamını başlatmak yeterlidir [dbproduits]
        // ayrıca en az bir Spring Data Repository gerekir, aksi takdirde hiçbir şey olmaz
        System.out.println("Travail en cours...");
        new AnnotationConfigApplicationContext(AppConfig.class).close();
        System.out.println("Travail terminé...");
    }

}

Bu, daha önce karşılaştığımız bir koddur.

[PRODUITS] tablosu, aşağıdaki yürütme yapılandırmasıyla oluşturulur:

Konsol günlükleri şu şekildedir:

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é...

Şimdi, [MyManager] istemcisine geri dönelim ve [1-2] ekranını yenileyelim:

[3]'te bir tablonun oluşturulduğunu görüyoruz. Şimdi veritabanının DDL'ini (Domain Definition Language) kontrol edelim:


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

Beklediğimiz tablo gerçekten de karşımıza çıkıyor. Bunu doğrulamak için şu yapılandırmayı çalıştıracağız:

Bu işlem başarılı olmalıdır.

9.3.6. Hibernate Oluşturma

 

Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;

Çalıştırma yapılandırması şu şekildedir:

Hibernate tarafından oluşturulan SQL komut dosyası şöyledir:


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'in oluşturulması

 

Not: Alt-F5 tuşlarına basın ve tüm Maven projelerini yeniden oluşturun;

Çalıştırma yapılandırması şu şekildedir:

OpenJpa tarafından oluşturulan SQL komut dosyası şöyledir:


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