Skip to content

9. Geração de bancos de dados a partir das entidades JPA

É possível criar as tabelas de um banco de dados a partir das entidades JPA. É isso que mostraremos agora. O objetivo é verificar se o banco de dados gerado a partir das entidades JPA é realmente o que se deseja.

9.1. Configuração do ambiente de trabalho

Vamos trabalhar, em primeiro lugar, com uma implementação JPA, EclipseLink e [1].

Em seguida, excluímos as tabelas do banco de dados MySQL e [dbproduitscategories] com o cliente [MyManager] (ver parágrafo 23.5). Começamos excluindo as tabelas que contêm as chaves estrangeiras [1-3]:

Em seguida, recomeçamos com as três tabelas restantes [4-6]:

Fazemos o mesmo com a tabela [dbproduits] utilizada pelos projetos [spring-jdbc-01 à 03]:

Além disso, é necessário importar os dois projetos de geração das duas bases de dados:

  • no [1], importa-se o projeto [generic-create-dbproduits], que se encontra no [<exemples>/spring-database-generic/spring-jpa] e no [2];
  • no [4], importa-se o projeto [generic-create-dbproduitscategories], que se encontra em [<exemples>/spring-database-generic/spring-jpa] e [5];

Observação: pressione Alt-F5 e gere novamente todos os projetos Maven;

9.2. Geração da base [dbproduitscategories]

 

9.2.1. Configuração do Maven

O arquivo [pom.xml] do projeto é o seguinte:


<?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>
  • linhas 22-26: a dependência do projeto [spring-jpa-generic], abordado no parágrafo 6.4;
  • linhas 28-32: a dependência de um weaver que será utilizado para enriquecer as entidades JPA das implementações EclipseLink e OpenJpa. Essa dependência não é necessária no arquivo [pom.xml], mas seu JAR será o agente Java utilizado. Colocar a dependência no arquivo [pom.xml] garante que o JAR estará disponível;

No final, as dependências são as seguintes:

  

9.2.2. Configuração do Spring

  

A classe [AppConfig] configura o projeto 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 {

}
  • linha 10: a classe recupera os beans da classe [ConfigJpa]. Vale lembrar que essa classe trabalha com as entidades JPA do banco de dados [dbproduitscategories] (ver parágrafo 6.3);
  • linha 11: declara-se que o pacote [console] deve ser verificado para localizar instâncias de [CrudRepository];

Na classe [ConfigJpa], encontra-se o seguinte bean (varia de acordo com a implementação JPA utilizada):


    // o provedor JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        // Observação: as entidades JPA e a configuração do EclipseLink estão no arquivo META-INF/persistence.xml
        EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
        eclipseLinkJpaVendorAdapter.setShowSql(false);
        eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
        eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
        return eclipseLinkJpaVendorAdapter;
}

A linha 8 é importante aqui. Ela está presente em todas as implementações JPA utilizadas. Ela indica que, se as tabelas associadas às entidades JPA não existirem, elas devem ser criadas. Vamos nos basear nessa propriedade para gerar as tabelas.

9.2.3. Os repositórios

  

A interface [ProduitsRepository] é a seguinte:


package console;

import generic.jpa.entities.dbproduitscategories.Produit;

import org.springframework.data.repository.CrudRepository;

public interface ProduitsRepository extends CrudRepository<Produit, Long> {

}

É a instânciação dela que provocará a instânciação da camada JPA. De fato, na linha 7, a interface faz referência à entidade JPA [Produit], o que forçará a instanciação da camada JPA. Poderíamos ter colocado qualquer interface [CrudRepository] que fizesse referência a uma das entidades JPA. De fato, observa-se que, embora o [repository] faça referência apenas às entidades JPA e [Produit], são geradas todas as tabelas de todas as entidades JPA.

9.2.4. A classe executável

  

A classe [CreateDatabase] é a seguinte:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CreateDataBase {

    public static void main(String[] args) {
        // basta instanciar o contexto Spring para criar as tabelas do banco de dados [dbproduitscategories]
        // também é necessário pelo menos um Spring Data Repository; caso contrário, nada acontece
        System.out.println("Travail en cours...");
        new AnnotationConfigApplicationContext(AppConfig.class).close();
        System.out.println("Travail terminé...");
    }

}
  • linha 11: instanciamos o contexto Spring para fechá-lo imediatamente. Nesse contexto, há o bean [ProduitsRepository] que faz referência às entidades JPA e [Produit]. Isso é suficiente para instanciar a camada JPA e, portanto, gerar as tabelas do banco de dados [dbproduitscategories].

Estamos na seguinte configuração:

 
  • a camada [JDBC] está configurada para o banco de dados [dbproduitscategories] de MySQL;
  • a camada [JPA] está implementada com EclipseLink;
  • o banco de dados [dbproduitscategories] não possui tabelas;

Observação: pressione Alt-F5 e gere novamente todos os projetos Maven;

Utiliza-se a seguinte configuração de execução:

  • no [1-2], essa configuração de execução requer um agente Java para que o teste seja bem-sucedido. Dependendo do caso, o EclipseLink nem sempre precisa desse agente, mas, neste caso, a execução falha se ele não estiver presente. Esse agente não é um agente EclipseLink, mas um agente Spring. Ele é fornecido pela dependência:

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

registrada no arquivo [pom.xml] do projeto. O agente é encontrado em [<m2-repo>/org/springframework/spring-instrument/4.1.6.RELEASE/spring-instrument-4.1.6.RELEASE.jar], onde <m2-repo> é o repositório local do Maven;

A execução produz o seguinte resultado:

No arquivo [3], vemos que as tabelas foram geradas. Agora, vamos verificar o arquivo DDL (Domain Definition Language) do banco de dados:

O script SQL para geração das tabelas também pode ser salvo em um arquivo [1].

 

O script SQL gerado é o seguinte:


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

Vejamos, por exemplo, o script SQL, que gera a tabela [PRODUITS] (linhas 15-29):

  • linha 16: [ID] é a chave primária (linha 23) com o atributo [AUTO_INCREMENT] (linha 5). Isso corresponde às anotações [@Id, @GeneratedValue(strategy = GenerationType.IDENTITY), @Column(name = ConfigJdbc.TAB_JPA_ID)] do campo [id] da entidade JPA;
  • linha 17: a definição da coluna [DESCRIPTION] corresponde à anotação [@Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100)] do campo [description] da entidade JPA;
  • linha 18: a coluna [CATEGORIE_ID] é uma chave estrangeira da tabela [PRODUITS] na coluna [CATEGORIES.ID] (linha 26). Além disso, essa chave estrangeira possui o atributo [ON DELETE CASCADE]. Isso corresponde às anotações [@ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)] do campo [Produit.categorie] e à anotação [@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL }), @CascadeOnDelete] do campo [Categorie.produits];
  • linha 19: a definição da coluna [NOM] corresponde à anotação [@Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)] do campo [Produit.nom];
  • linha 20: a definição da coluna [PRIX] corresponde à anotação [@Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)] do campo [Produit.prix];
  • linhas 24-25: o script cria três índices para cada uma das colunas únicas da tabela;

As tabelas geradas não possuem um valor padrão para o campo VERSIONING, enquanto o código Java espera que haja um. Se esse valor padrão não estiver presente, alguns testes não serão aprovados. Adiciona-se esse atributo da seguinte maneira:

 
 
 

Isso é feito para as cinco tabelas que possuem a coluna [VERSIONING]. O valor padrão não importa. Basta que ele exista. Em seguida, ele é incrementado em 1 a cada modificação da linha à qual pertence.

Feito isso, verifique se as seguintes configurações de execução são bem-sucedidas:

  • [spring-jdbc-generic-04.JUnitTestDao], que testa a implementação JDBC;
  • [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink], que testa as implementações JPA Hibernate ou Eclipselink (neste caso, será EclipseLink)

Ambas as execuções devem ser bem-sucedidas.

9.2.6. Geração das tabelas com o Hibernate

Criamos as tabelas do Hibernate com o seguinte ambiente do Eclipse:

 

A geração das tabelas é feita pela configuração de execução denominada [generic-create-dbproduitscategories-hibernate], sem agente Java;

O script SQL do banco de dados gerado pelo Hibernate é o seguinte:


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

As tabelas geradas são as mesmas, já que o Hibernate também utilizou as anotações JPA. Para o Hibernate, não encontrei o equivalente à anotação EclipseLink [@OnCascadeDelete], que gerou oatributo SQL [ON DELTE CASCADE] na chave estrangeira [PRODUITS.CATEGORIE_ID] (linha 25). Portanto, é preciso gerar esse atributo manualmente, pois ele é necessário para os testes:

 

É preciso fazer o mesmo com as duas chaves estrangeiras da tabela [USERS_ROLES]:

Por fim, assim como foi feito na implementação EclipseLink, as colunas [VERSIONING] das cinco tabelas devem ter um valor padrão:

 

Feito isso, verifique se as seguintes configurações de execução são bem-sucedidas:

  • [spring-jdbc-generic-04.JUnitTestDao], que testa a implementação JDBC;
  • [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink], que testa as implementações JPA do Hibernate ou do Eclipselink (neste caso, será o Hibernate)

Ambas as execuções devem ser bem-sucedidas.

9.2.7. Geração das tabelas com OpenJpa

Repetimos o procedimento anterior com uma implementação JPA OpenJpa:

 

Observação: pressione Alt-F5 e gere novamente todos os projetos Maven;

Modificamos a classe [ConfigJpa], que configura o projeto [mysql-config-jpa-openjpa], da seguinte maneira:


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 {

    // o provedor 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();
    }
}
  • linhas 40-41: criamos uma propriedade para OpenJPA que indica como gerar as chaves estrangeiras durante a geração das tabelas. Sem essa propriedade, as chaves estrangeiras não são geradas. O atributo [ForeignKeyDeleteAction=cascade] permite gerar o atributo [ON DELETE CASCADE] nessas chaves estrangeiras;

A geração das tabelas é feita pela configuração de execução denominada [generic-create-dbproduitscategories-openjpa], que possui dois agentes Java;

Image

  • o primeiro agente Java é o agente Spring já utilizado com EclipseLink;
  • o segundo agente Java é fornecido por OpenJpa;

O script SQL da base gerada é, então, o seguinte:


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

É o mesmo que no caso do EclipseLink. Portanto, faremos as mesmas correções nas tabelas. Feito isso, verifique se as seguintes configurações de execução são bem-sucedidas:

  • [spring-jdbc-generic-04.JUnitTestDao], que testa a implementação JDBC;
  • [spring-jpa-generic-JUnitTestDao-openjpa], que testa as implementações JPA e OpenJpa;

Ambas as execuções devem ser bem-sucedidas.

9.3. Geração da base de dados [dbproduits]

A base [dbproduits] é utilizada pelos projetos [spring-jdbc-01 à 03]. Também é possível gerá-la a partir de uma entidade JPA.

  • no [1], os projetos Eclipse. Estaremos em uma configuração MySQL / EclipseLink. O projeto de geração da base de dados [dbproduits] é [generic-create-dbproduits];
  • no [2], a tabela [PRODUITS] a ser gerada;

Observação: pressione Alt-F5 e gere novamente todos os projetos Maven;

9.3.1. Configuração do Maven

A configuração do Maven para o projeto [generic-create-dbproduits] é a seguinte:


<?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>
        <!-- configuração JPA do 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>

Há apenas uma dependência, nas linhas 22 a 26, no projeto que configura a camada JPA. No final, as dependências são as seguintes:

  

9.3.2. A configuração do Spring

  

A classe de configuração do Spring é a seguinte:


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 {

    // fonte de dados
    @Bean
    public DataSource dataSource() {
        // fonte de dados TomcatJdbc
        DataSource dataSource = new DataSource();
        // configuração de acesso JDBC
        dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
        dataSource.setUsername(ConfigJdbc.USER_DBPRODUITS);
        dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITS);
        dataSource.setUrl(ConfigJdbc.URL_DBPRODUITS);
        // conexões abertas inicialmente
        dataSource.setInitialSize(5);
        // resultado
        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();
    }

}
  • linha 18: importam-se os beans da classe [ConfigJpa] (parágrafo 7.3);
  • linhas 22-35: redefine-se a fonte de dados [dataSource]. Na classe [ConfigJpa], a fonte de dados é o banco de dados [dbproduitscategories]. Aqui, será o banco de dados [dbproduits];
  • linhas 38-46: redefine-se o bean [entityManagerFactory] da classe [ConfigJpa]. Nessa classe, as entidades JPA eram [Produit, Categorie]. Aqui, é apenas [Produit] e ela não tem a mesma definição que no projeto que configura a camada JPA;
  • linha 42: para definir essa nova entidade JPA, faz-se referência às entidades JPA definidas no arquivo [META-INF/persistence.xml]:
  

O arquivo [persistence.xml] é o seguinte:


<?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">
        <!-- entidades JPA -->
        <class>generic.jpa.entities.dbproduits.Produit</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
    </persistence-unit>
</persistence>
  • linha 6: a única entidade JPA;
  • linha 4: o nome da unidade de persistência [generic-jpa-entities-dbproduits], que é referenciada no bean [entityManagerFactory];

9.3.3. A entidade JPA [Produit]

  

A entidade JPA está definida no projeto [mysql-config-jpa-eclipselink] da seguinte forma:


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 {

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

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

    // getters e setters
    ...
}

Trata-se de uma definição JPA que já se tornou clássica. Basta observar os seguintes pontos:

  • linha 12: foi atribuído o nome [Produit1] à entidade. Por padrão, o nome de uma entidade é o nome da classe, neste caso, [Produit]. No entanto, como há outra entidade JPA [Produit] no mesmo projeto, um erro foi sinalizado antes mesmo de qualquer execução. Resolvemos o problema desta forma;
  • linha 24: a categoria, neste caso, é apenas um número;
  • não há relações entre entidades. Portanto, temos uma situação muito simples;

9.3.4. A classe executável

  

A classe [CreateDatabase] é a seguinte:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class CreateDataBase {

    public static void main(String[] args) {
        // basta instanciar o contexto Spring para criar as tabelas do banco de dados [dbproduits]
        // também é necessário pelo menos um Spring Data Repository; caso contrário, nada acontece
        System.out.println("Travail en cours...");
        new AnnotationConfigApplicationContext(AppConfig.class).close();
        System.out.println("Travail terminé...");
    }

}

Esse é um código que já encontramos anteriormente.

A tabela [PRODUITS] é criada com a seguinte configuração de execução:

Os registros do console são os seguintes:

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

Agora, voltemos ao cliente [MyManager] e atualizemos a exibição [1-2]:

Em [3], vemos que uma tabela foi gerada. Agora, vamos verificar a DDL (Domain Definition Language) do banco de dados:


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

De fato, obtemos a tabela esperada. Para verificar isso, executaremos a seguinte configuração:

A execução deve ser bem-sucedida.

9.3.6. Geração do Hibernate

 

Observação: pressione Alt-F5 e gere novamente todos os projetos Maven;

A configuração de execução é a seguinte:

O script SQL gerado pelo Hibernate é o seguinte:


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. Geração de OpenJpa

 

Observação: pressione Alt+F5 e gere novamente todos os projetos Maven;

A configuração de execução é a seguinte:

O script SQL gerado pelo OpenJpa é o seguinte:


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