9. Generowanie baz danych na podstawie encji JPA
Możliwe jest utworzenie tabel bazy danych na podstawie encji JPA. Właśnie to teraz pokażemy. Celem jest sprawdzenie, czy baza danych wygenerowana na podstawie encji JPA jest rzeczywiście tą, której potrzebujemy.
9.1. Przygotowanie środowiska pracy
Na początku będziemy pracować z implementacją JPA, EclipseLink oraz [1].
![]() |
Następnie usuwamy tabele z bazy danych MySQL, [dbproduitscategories] za pomocą klienta [MyManager] (patrz punkt 23.5). Zaczynamy od usunięcia tabel zawierających klucze obce [1-3]:
![]() |
Następnie kontynuujemy z trzema pozostałymi tabelami [4-6]:
![]() |
To samo robimy z tabelą [dbproduits] używaną przez projekty [spring-jdbc-01 à 03]:
![]() | ![]() |
Ponadto należy zaimportować dwa projekty generujące obie bazy danych:
![]() |
- w projekcie [1] należy zaimportować projekt [generic-create-dbproduits], który znajduje się w projektach [<exemples>/spring-database-generic/spring-jpa] i [2];
![]() |
- w pliku [4] importujemy projekt [generic-create-dbproduitscategories], który znajduje się w plikach [<exemples>/spring-database-generic/spring-jpa] i [5];
Uwaga: należy nacisnąć Alt-F5 i ponownie wygenerować wszystkie projekty Maven;
9.2. Generowanie bazy danych [dbproduitscategories]
![]() |
9.2.1. Konfiguracja Maven
Plik [pom.xml] projektu ma następującą treść:
<?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>
- wiersze 22–26: zależność od projektu [spring-jpa-generic] omówionego w punkcie 6.4;
- wiersze 28–32: zależność od narzędzia typu „weaver”, które zostanie wykorzystane do wzbogacenia encji JPA o implementacje EclipseLink i OpenJpa. Ta zależność nie jest konieczna w pliku [pom.xml], ale jego plik JAR będzie wykorzystywanym agentem Java. Umieszczenie tej zależności w pliku [pom.xml] gwarantuje, że plik JAR będzie rzeczywiście dostępny;
Ostatecznie zależności są następujące:
![]() |
9.2.2. Konfiguracja Spring
![]() |
Klasa [AppConfig] konfiguruje projekt 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 {
}
- wiersz 10: klasa pobiera bean z klasy [ConfigJpa]. Przypominamy, że ta klasa współpracuje z encjami JPA z bazy danych [dbproduitscategories] (patrz punkt 6.3);
- wiersz 11: deklaruje się, że pakiet [console] ma zostać przeszukany w celu znalezienia w nim instancji [CrudRepository];
W klasie [ConfigJpa] znajduje się następująca fasola (różni się w zależności od używanej implementacji JPA):
// dostawca JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
// Uwaga: encje JPA oraz konfiguracja Eclipselink znajdują się w pliku META-INF/persistence.xml
EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkJpaVendorAdapter.setShowSql(false);
eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
return eclipseLinkJpaVendorAdapter;
}
W tym przypadku istotna jest linia 8. Występuje ona we wszystkich stosowanych implementacjach JPA. Określa ona, że jeśli tabele powiązane z encjami JPA nie istnieją, należy je utworzyć. Wykorzystamy tę właściwość do wygenerowania tabel.
9.2.3. Repozytoria
![]() |
Interfejs [ProduitsRepository] wygląda następująco:
package console;
import generic.jpa.entities.dbproduitscategories.Produit;
import org.springframework.data.repository.CrudRepository;
public interface ProduitsRepository extends CrudRepository<Produit, Long> {
}
To właśnie jego instancja spowoduje instancję warstwy JPA. W linii 7 interfejs odwołuje się bowiem do encji JPA [Produit], co wymusi instancjonowanie warstwy JPA. Można było umieścić dowolny interfejs [CrudRepository] odwołujący się do jednej z encji JPA. Widać bowiem, że chociaż [repository] odwołuje się jedynie do encji JPA i [Produit], to generowane są wszystkie tabele wszystkich encji JPA.
9.2.4. Klasa wykonywalna
![]() |
Klasa [CreateDatabase] ma następującą postać:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CreateDataBase {
public static void main(String[] args) {
// wystarczy utworzyć instancję kontekstu Spring, aby utworzyć tabele bazy danych [dbproduitscategories]
// potrzebne jest również co najmniej jedno repozytorium Spring Data, w przeciwnym razie nic się nie stanie
System.out.println("Travail en cours...");
new AnnotationConfigApplicationContext(AppConfig.class).close();
System.out.println("Travail terminé...");
}
}
- wiersz 11: instancjonuje się kontekst Spring, aby natychmiast go zamknąć. W tym kontekście znajduje się bean [ProduitsRepository], który odwołuje się do encji JPA i [Produit]. To wystarcza do utworzenia instancji warstwy JPA, a tym samym do wygenerowania tabel bazy danych [dbproduitscategories].
9.2.5. Generowanie tabel za pomocą EclipseLink
Mamy następującą konfigurację:
![]() |
- warstwa [JDBC] jest skonfigurowana dla bazy danych [dbproduitscategories] z MySQL;
- warstwa [JPA] jest zaimplementowana z warstwą EclipseLink;
- baza danych [dbproduitscategories] nie zawiera żadnych tabel;
Uwaga: należy nacisnąć Alt-F5 i ponownie wygenerować wszystkie projekty Maven;
Stosowana jest następująca konfiguracja uruchomienia:
![]() |
- w przypadku [1-2] ta konfiguracja uruchomienia wymaga agenta Java, aby test zakończył się powodzeniem. W zależności od sytuacji EclipseLink nie zawsze potrzebuje tego agenta, ale w tym przypadku wykonanie kończy się niepowodzeniem, jeśli go nie ma. Agent ten nie jest agentem EclipseLink, lecz agentem Spring. Jest on dostarczany przez zależność:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<scope>runtime</scope>
</dependency>
zapisana w pliku [pom.xml] projektu. Agent znajduje się w pliku [<m2-repo>/org/springframework/spring-instrument/4.1.6.RELEASE/spring-instrument-4.1.6.RELEASE.jar], gdzie <m2-repo> to lokalne repozytorium Maven;
Wynik wykonania jest następujący:
![]() |
W pliku [3] widać, że tabele zostały wygenerowane. Teraz sprawdźmy plik DDL (Domain Definition Language) bazy danych:
![]() | ![]() |
![]() |
Skrypt SQL służący do generowania tabel można również zapisać w pliku [1].
![]() | ![]() ![]() |
Wygenerowany skrypt SQL ma następującą postać:
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'
;
Przyjrzyjmy się na przykład skryptowi SQL, który generuje tabelę [PRODUITS] (wiersze 15–29):
- wiersz 16: [ID] jest kluczem głównym (wiersz 23) z atrybutem [AUTO_INCREMENT] (wiersz 5). Odpowiada to adnotacjom [@Id, @GeneratedValue(strategy = GenerationType.IDENTITY), @Column(name = ConfigJdbc.TAB_JPA_ID)] pola [id] w encji JPA;
- wiersz 17: definicja kolumny [DESCRIPTION] odpowiada adnotacji [@Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100)] w polu [description] encji JPA;
- wiersz 18: kolumna [CATEGORIE_ID] jest kluczem obcym tabeli [PRODUITS] w odniesieniu do kolumny [CATEGORIES.ID] (wiersz 26). Ponadto ten klucz obcy posiada atrybut [ON DELETE CASCADE]. Odpowiada to adnotacjom [@ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)] pola [Produit.categorie] oraz adnotacji [@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL }), @CascadeOnDelete] pola [Categorie.produits];
- wiersz 19: definicja kolumny [NOM] odpowiada adnotacji [@Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)] w polu [Produit.nom];
- wiersz 20: definicja kolumny [PRIX] odpowiada adnotacji [@Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)] w polu [Produit.prix];
- wiersze 24–25: skrypt tworzy trzy indeksy dla każdej z unikalnych kolumn tabeli;
Wygenerowane tabele nie mają wartości domyślnej dla pola VERSIONING, podczas gdy kod Java oczekuje, że taka wartość istnieje. Jeśli ta wartość domyślna nie jest obecna, niektóre testy nie przechodzą pomyślnie. Atrybut ten dodaje się w następujący sposób:
![]() |
![]() |
![]() |
Robimy to dla pięciu tabel, które mają kolumnę [VERSIONING]. Wartość domyślna nie ma znaczenia. Musi po prostu istnieć. Następnie jest ona zwiększana o 1 przy każdej modyfikacji wiersza, do którego należy.
Po wykonaniu tych czynności należy sprawdzić, czy następujące konfiguracje uruchomienia zakończą się powodzeniem:
- [spring-jdbc-generic-04.JUnitTestDao], która testuje implementację JDBC;
- [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink], który testuje implementacje JPA Hibernate lub Eclipselink (w tym przypadku będzie to EclipseLink)
Oba uruchomienia muszą zakończyć się powodzeniem.
9.2.6. Generowanie tabel za pomocą Hibernate
Tworzymy tabele Hibernate w następującym środowisku Eclipse:
![]() |
Generowanie tabel odbywa się za pomocą konfiguracji uruchomienia o nazwie [generic-create-dbproduitscategories-hibernate] bez agenta Java;
![]() | ![]() |
Skrypt SQL z bazy wygenerowanej przez Hibernate wygląda następująco:
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'
;
Wygenerowane tabele są takie same, ponieważ Hibernate również wykorzystał adnotacje JPA. W przypadku Hibernate nie znalazłem odpowiednika adnotacji EclipseLink [@OnCascadeDelete], która wygenerowałaatrybut SQL [ON DELTE CASCADE] na kluczu obcym [PRODUITS.CATEGORIE_ID] (wiersz 25). Należy zatem wygenerować ten atrybut ręcznie, ponieważ jest on niezbędny do przeprowadzenia testów:
![]() |
![]() |
![]() |
To samo należy zrobić z dwoma kluczami obcymi w tabeli [USERS_ROLES]:
![]() |
Na koniec, podobnie jak w przypadku implementacji EclipseLink, kolumny [VERSIONING] w pięciu tabelach muszą mieć wartość domyślną:
![]() |
Po wykonaniu tej czynności należy sprawdzić, czy następujące konfiguracje uruchomienia przebiegają pomyślnie:
- [spring-jdbc-generic-04.JUnitTestDao], która testuje implementację JDBC;
- [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink], który testuje implementacje JPA w Hibernate lub Eclipselink (w tym przypadku będzie to Hibernate)
Oba uruchomienia muszą zakończyć się powodzeniem.
9.2.7. Generowanie tabel za pomocą OpenJpa
Powtarzamy poprzednią procedurę z implementacją JPA OpenJpa:
![]() |
Uwaga: należy nacisnąć Alt-F5 i ponownie wygenerować wszystkie projekty Maven;
Modyfikujemy klasę [ConfigJpa], która konfiguruje projekt [mysql-config-jpa-openjpa], w następujący sposób:
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 {
// dostawca 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();
}
}
- wiersze 40–41: tworzymy właściwość dla klasy OpenJPA, która określa sposób generowania kluczy obcych podczas tworzenia tabel. Bez tej właściwości klucze obce nie są generowane. Atrybut [ForeignKeyDeleteAction=cascade] umożliwia wygenerowanie atrybutu [ON DELETE CASCADE] dla tych kluczy obcych;
Generowanie tabel odbywa się za pomocą konfiguracji wykonawczej o nazwie [generic-create-dbproduitscategories-openjpa], która zawiera dwóch agentów Java;

- pierwszy agent Java to agent Spring, który był już używany w przypadku EclipseLink;
- drugi agent Java jest dostarczany przez OpenJpa;
Skrypt SQL z wygenerowanej bazy wygląda zatem następująco:
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'
;
Sytuacja jest taka sama jak w przypadku EclipseLink. Należy zatem wprowadzić te same poprawki w tabelach. Po wykonaniu tej czynności należy sprawdzić, czy następujące konfiguracje uruchomienia przebiegają pomyślnie:
- [spring-jdbc-generic-04.JUnitTestDao], który testuje implementację JDBC;
- [spring-jpa-generic-JUnitTestDao-openjpa], który testuje implementację JPA i OpenJpa;
Oba uruchomienia muszą zakończyć się powodzeniem.
9.3. Generowanie bazy danych [dbproduits]
Baza [dbproduits] jest wykorzystywana przez projekty [spring-jdbc-01 à 03]. Można ją również wygenerować na podstawie encji JPA.
![]() |
- w [1], projekty Eclipse. Będziemy mieli do czynienia z konfiguracją MySQL / EclipseLink. Projektem generującym bazę [dbproduits] jest [generic-create-dbproduits];
- w [2] tabela [PRODUITS] do wygenerowania;
Uwaga: należy nacisnąć Alt-F5 i ponownie wygenerować wszystkie projekty Maven;
9.3.1. Konfiguracja Maven
Konfiguracja Maven projektu [generic-create-dbproduits] jest następująca:
<?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>
<!-- konfiguracja JPA dla 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>
W projekcie, który konfiguruje warstwę JPA, występuje tylko jedna zależność (wiersze 22–26). Ostatecznie zależności są następujące:
![]() |
9.3.2. Konfiguracja Spring
![]() |
Klasa konfiguracyjna Spring wygląda następująco:
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 {
// źródło danych
@Bean
public DataSource dataSource() {
// źródło danych TomcatJdbc
DataSource dataSource = new DataSource();
// konfiguracja dostępu JDBC
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITS);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITS);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITS);
// początkowo otwarte połączenia
dataSource.setInitialSize(5);
// wynik
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();
}
}
- wiersz 18: importujemy bean z klasy [ConfigJpa] (punkt 7.3);
- wiersze 22–35: ponownie definiuje się źródło danych [dataSource]. W klasie [ConfigJpa] źródłem danych jest baza [dbproduitscategories]. Tutaj będzie to baza [dbproduits];
- wiersze 38–46: ponownie definiuje się bean [entityManagerFactory] klasy [ConfigJpa]. W tej klasie encje JPA miały nazwę [Produit, Categorie]. Tutaj jest to tylko [Produit] i nie ma ona tej samej definicji, co w projekcie konfigurującym warstwę JPA;
- wiersz 42: aby zdefiniować tę nową jednostkę JPA, odwołujemy się do jednostek JPA zdefiniowanych w pliku [META-INF/persistence.xml]:
![]() |
Plik [persistence.xml] ma następującą treść:
<?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">
<!-- podmioty JPA -->
<class>generic.jpa.entities.dbproduits.Produit</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
- wiersz 6: jedyna jednostka JPA;
- wiersz 4: nazwa jednostki trwałości [generic-jpa-entities-dbproduits], do której odwołuje się bean [entityManagerFactory];
9.3.3. Entyteta JPA [Produit]
![]() |
Entyteta JPA jest zdefiniowana w projekcie [mysql-config-jpa-eclipselink] w następujący sposób:
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 {
// pola
@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;
// konstruktorów
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;
}
// metody pobierające i ustawiające
...
}
Jest to definicja JPA, która stała się już klasyczna. Zwróćmy jedynie uwagę na następujące kwestie:
- wiersz 12: nadano jednostce nazwę [Produit1]. Domyślnie nazwa jednostki jest zgodna z nazwą klasy, w tym przypadku [Produit]. Ponieważ jednak w tym samym projekcie istnieje inna jednostka o nazwie JPA [Produit], jeszcze przed rozpoczęciem wykonywania zgłoszono błąd. Usunęliśmy go w ten sposób;
- wiersz 24: kategoria jest tutaj zwykłym numerem;
- nie ma powiązań między jednostkami. Mamy więc do czynienia z bardzo prostą sytuacją;
9.3.4. Klasa wykonywalna
![]() |
Klasa [CreateDatabase] wygląda następująco:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CreateDataBase {
public static void main(String[] args) {
// wystarczy utworzyć instancję kontekstu Spring, aby utworzyć tabele bazy danych [dbproduits]
// potrzebne jest również co najmniej jedno repozytorium Spring Data, w przeciwnym razie nic się nie stanie
System.out.println("Travail en cours...");
new AnnotationConfigApplicationContext(AppConfig.class).close();
System.out.println("Travail terminé...");
}
}
Jest to kod, z którym już się spotkaliśmy.
9.3.5. Generowanie EclipseLink
Tabela [PRODUITS] została utworzona z następującą konfiguracją wykonania:
![]() | ![]() |
Wpisy w dzienniku konsoli są następujące:
Teraz wróćmy do klienta [MyManager] i odświeżmy widok [1-2]:
![]() |
W [3] widać, że wygenerowano tabelę. Teraz sprawdźmy DDL (Domain Definition Language) bazy danych:
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'
;
Otrzymujemy rzeczywiście oczekiwaną tabelę. Aby to sprawdzić, wykonamy następującą konfigurację:
![]() | ![]() |
Konfiguracja powinna zakończyć się powodzeniem.
9.3.6. Generowanie w Hibernate
![]() | ![]() |
Uwaga: naciśnij Alt-F5 i zregeneruj wszystkie projekty Maven;
Konfiguracja uruchomienia jest następująca:
![]() | ![]() |
Skrypt SQL wygenerowany przez Hibernate wygląda następująco:
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. Generowanie OpenJpa
![]() | ![]() |
Uwaga: należy nacisnąć klawisze Alt-F5 i ponownie wygenerować wszystkie projekty Maven;
Konfiguracja uruchomienia jest następująca:
![]() | ![]() |
Skrypt SQL wygenerowany przez OpenJpa ma następującą postać:
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'
;

















































