9. 基于 JPA 实体生成数据库
可以基于 JPA 实体创建数据库表。下面我们将演示这一过程。这样做的目的是验证基于 JPA 实体生成的数据库是否正是我们想要的。
9.1. 工作环境的搭建
我们将首先使用 JPA、EclipseLink 和 [1] 这三个实体进行操作。
![]() |
然后,我们使用客户端 [MyManager] 删除数据库中的表 MySQL 和 [dbproduitscategories](参见第 23.5 节)。 首先删除包含外键的表 [1-3]:
![]() |
然后,我们从剩下的三个表开始:
![]() |
对于项目 [spring-jdbc-01 à 03] 所使用的表 [dbproduits],我们也采取同样的操作:
![]() | ![]() |
此外,还需导入这两个数据库的生成项目:
![]() |
- 在 [1] 中,需导入项目 [generic-create-dbproduits],该项目位于 [<exemples>/spring-database-generic/spring-jpa] 和 [2] 中;
![]() |
- 在 [4] 中,导入项目 [generic-create-dbproduitscategories],该项目位于 [<exemples>/spring-database-generic/spring-jpa] 和 [5] 中;
注意:按 Alt-F5 并重新生成所有 Maven 项目;
9.2. 生成 [dbproduitscategories] 数据库
![]() |
9.2.1. Maven配置
该项目的 [pom.xml] 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-create-dbproduitscategories</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>generic-create-dbproduitscategories</name>
<description>création de la bases de données [dbproduitscategories] à l'aide des annotations JPA</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- spring-jpa-generic -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>spring-jpa-generic</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 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 行:对第 6.4 节中讨论的 [spring-jpa-generic] 项目的依赖;
- 第28-32行:对一个Weaver的依赖,该Weaver将用于增强JPA中的EclipseLink和OpenJpa实现。 该依赖在文件 [pom.xml] 中并非必需,但其 JAR 文件将作为所使用的 Java 代理。将该依赖添加到文件 [pom.xml] 中可确保该 JAR 文件确实可用;
最终,依赖关系如下:
![]() |
9.2.2. Spring 配置
![]() |
类 [AppConfig] 用于配置 Spring 项目:
package console;
import generic.jpa.config.ConfigJpa;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@Import({ ConfigJpa.class })
@EnableJpaRepositories(basePackages = { "console" })
public class AppConfig {
}
- 第 10 行:该类从 [ConfigJpa] 类中获取 Bean。需要说明的是,该类处理的是数据库 [dbproduitscategories] 中的实体 JPA(参见第 6.3 节);
- 第 11 行:声明需扫描 [console] 包以查找 [CrudRepository] 实例;
在类 [ConfigJpa] 中,包含以下 Bean(具体内容因所使用的 JPA 实现而异):
// 提供程序 JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
// 注:JPA 实体和 Eclipselink 配置位于文件 META-INF/persistence.xml 中
EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkJpaVendorAdapter.setShowSql(false);
eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
return eclipseLinkJpaVendorAdapter;
}
此处关键的是第 8 行。该行存在于所有使用的 JPA 实现中。它规定,如果与 JPA 实体关联的表不存在,则必须创建这些表。我们将基于此属性来生成这些表。
9.2.3. 存储库
![]() |
[ProduitsRepository] 接口如下:
package console;
import generic.jpa.entities.dbproduitscategories.Produit;
import org.springframework.data.repository.CrudRepository;
public interface ProduitsRepository extends CrudRepository<Produit, Long> {
}
正是它的实例化将触发 JPA 层的实例化。 事实上,在第7行,该接口引用了实体 JPA [Produit],这将强制实例化 JPA 层。 其实,只要将任何引用了 JPA 实体的 [CrudRepository] 接口放入其中即可。 事实上,尽管 [repository] 仅引用了 JPA 和 [Produit] 这两个实体,但生成的却是所有 JPA 实体的所有表。
9.2.4. 可执行类
![]() |
类 [CreateDatabase] 如下所示:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CreateDataBase {
public static void main(String[] args) {
// 只需实例化 Spring 上下文即可创建数据库表 [dbproduitscategories]
// 还必须至少有一个 Spring Data Repository,否则什么也不会发生
System.out.println("Travail en cours...");
new AnnotationConfigApplicationContext(AppConfig.class).close();
System.out.println("Travail terminé...");
}
}
- 第 11 行:实例化 Spring 上下文并立即关闭。在此上下文中,存在 Bean [ProduitsRepository],它引用了实体 JPA 和 [Produit]。 这足以实例化 JPA 层,从而生成 [dbproduitscategories] 数据库中的表。
9.2.5. 使用 EclipseLink 生成表
当前配置如下:
![]() |
- [JDBC] 层配置为 MySQL 的 [dbproduitscategories] 数据库;
- 层 [JPA] 通过 EclipseLink 实现;
- 数据库 [dbproduitscategories] 没有表;
注:按 Alt-F5 并重新生成所有 Maven 项目;
使用以下运行配置:
![]() |
- 在 [1-2] 中,该运行配置需要一个 Java 代理才能使测试通过。 根据具体情况,EclipseLink 并不总是需要该代理,但在此处若缺失该代理,执行将失败。该代理并非 EclipseLink 代理,而是 Spring 代理。它由以下依赖项提供:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<scope>runtime</scope>
</dependency>
该项目中的 [pom.xml] 文件中。该代理位于 [<m2-repo>/org/springframework/spring-instrument/4.1.6.RELEASE/spring-instrument-4.1.6.RELEASE.jar] 中,其中 <m2-repo> 是 Maven 的本地仓库;
执行后得到以下结果:
![]() |
在 [3] 中,可以看到表已生成。现在检查数据库的 DDL(领域定义语言):
![]() | ![]() |
![]() |
用于生成表的脚本 SQL 也可以保存为文件 [1]。
![]() | ![]() ![]() |
生成的脚本 SQL 如下:
SET FOREIGN_KEY_CHECKS=0;
USE `dbproduitscategories`;
CREATE TABLE `categories` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `produits` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci DEFAULT NULL,
`CATEGORIE_ID` BIGINT(20) NOT NULL,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PRIX` DOUBLE NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
`CATEGORIE` INTEGER(11) NOT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `NOM` (`NOM`) USING BTREE,
KEY `FK_PRODUITS_CATEGORIE_ID` (`CATEGORIE_ID`) USING BTREE,
CONSTRAINT `FK_PRODUITS_CATEGORIE_ID` FOREIGN KEY (`CATEGORIE_ID`) REFERENCES `categories` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `roles` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `users` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `users_roles` (
...
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
以脚本 SQL 为例,该脚本生成表 [PRODUITS](第 15-29 行):
- 第 16 行:[ID] 是主键(第 23 行),其属性为 [AUTO_INCREMENT](第 5 行)。 这对应于实体 JPA 中字段 [id] 的注释 [@Id, @GeneratedValue(strategy = GenerationType.IDENTITY), @Column(name = ConfigJdbc.TAB_JPA_ID)];
- 第 17 行:列 [DESCRIPTION] 的定义对应于实体 JPA 中字段 [description] 的注释 [@Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100)];
- 第 18 行:列 [CATEGORIE_ID] 是表 [PRODUITS] 中列 [CATEGORIES.ID](第 26 行)的外键。 此外,该外键具有属性 [ON DELETE CASCADE]。 这对应于字段 [Produit.categorie] 的注释 [@ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)] 以及字段 [Categorie.produits] 的注释 [@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL }), @CascadeOnDelete];
- 第 19 行:列 [NOM] 的定义对应于字段 [Produit.nom] 的注释 [@Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)];
- 第 20 行:列 [PRIX] 的定义对应于字段 [Produit.prix] 的注释 [@Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)];
- 第 24-25 行:脚本为表中的每个唯一列创建三个索引;
生成的表中,VERSIONING字段没有默认值,而Java代码却期望该字段有默认值。如果缺少该默认值,某些测试将无法通过。我们按以下方式添加该属性:
![]() |
![]() |
![]() |
对于拥有 [VERSIONING] 列的五个表,均需进行此操作。默认值本身并不重要,关键在于其必须存在。随后,每当修改其所属的行时,该值就会递增 1。
完成上述操作后,请验证以下执行配置是否通过:
- [spring-jdbc-generic-04.JUnitTestDao],用于测试 JDBC 的实现;
- [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink],用于测试 JPA 的 Hibernate 或 Eclipselink 实现(此处为 EclipseLink)
这两次执行都必须成功。
9.2.6. 使用 Hibernate 生成表
我们使用以下 Eclipse 环境创建 Hibernate 表:
![]() |
表的生成由名为 [generic-create-dbproduitscategories-hibernate] 的运行配置完成,且不使用 Java 代理;
![]() | ![]() |
Hibernate 生成的数据库脚本 SQL 如下:
SET FOREIGN_KEY_CHECKS=0;
USE `dbproduitscategories`;
CREATE TABLE `categories` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `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 也使用了注解 JPA。对于 Hibernate,我没有找到与注解 EclipseLink [@OnCascadeDelete] 对应的注解,该注解生成了SQL [ON DELTE CASCADE] 属性(第 25 行)。因此必须手动生成该属性,因为测试需要它:
![]() |
![]() |
![]() |
对于表 [USERS_ROLES] 中的两个外键,也需进行同样的操作:
![]() |
最后,与EclipseLink的实现方式相同,这五个表中的[VERSIONING]列必须设置默认值:
![]() |
完成上述操作后,请验证以下执行配置是否通过:
- [spring-jdbc-generic-04.JUnitTestDao](用于测试 JDBC 实现);
- [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink],用于测试 JPA 的 Hibernate 或 Eclipselink 实现(此处为 Hibernate)
这两次执行都必须成功。
9.2.7. 使用 OpenJpa 生成表
我们使用 JPA 和 OpenJpa 实现重新执行上述步骤:
![]() |
注意:按 Alt-F5 并重新生成所有 Maven 项目;
我们将配置项目 [mysql-config-jpa-openjpa] 的类 [ConfigJpa] 修改如下:
package generic.jpa.config;
import generic.jdbc.config.ConfigJdbc;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@Import({ ConfigJdbc.class })
public class ConfigJpa {
// 提供程序 JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
OpenJpaVendorAdapter openJpaVendorAdapter = new OpenJpaVendorAdapter();
openJpaVendorAdapter.setShowSql(false);
openJpaVendorAdapter.setDatabase(Database.MYSQL);
openJpaVendorAdapter.setGenerateDdl(true);
return openJpaVendorAdapter;
}
..
// EntityManagerFactory
@Bean
public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan(ENTITIES_PACKAGES);
Map<String, Object> mapJpaProperties = factory.getJpaPropertyMap();
mapJpaProperties.put("openjpa.jdbc.MappingDefaults",
"ForeignKeyDeleteAction=cascade,JoinForeignKeyDeleteAction=restrict");
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
}
- 第 40-41 行:为 OpenJPA 创建一个属性,用于指定生成表时外键的生成方式。若无此属性,则不会生成外键。 属性 [ForeignKeyDeleteAction=cascade] 用于在这些外键上生成属性 [ON DELETE CASCADE];
表的生成由名为 [generic-create-dbproduitscategories-openjpa] 的运行配置负责,该配置包含两个 Java 代理;

- 第一个 Java 代理是已用于 EclipseLink 的 Spring 代理;
- 第二个 Java 代理由 OpenJpa 提供;
生成的数据库中的脚本 SQL 如下:
SET FOREIGN_KEY_CHECKS=0;
USE `dbproduitscategories`;
CREATE TABLE `categories` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `U_CTGORIS_NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `produits` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci DEFAULT NULL,
`CATEGORIE_ID` BIGINT(20) DEFAULT NULL,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PRIX` DOUBLE NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `U_PRODUTS_NOM` (`NOM`) USING BTREE,
KEY `CATEGORIE_ID` (`CATEGORIE_ID`) USING BTREE,
CONSTRAINT `produits_ibfk_1` FOREIGN KEY (`CATEGORIE_ID`) REFERENCES `categories` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `roles` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`NAME` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `U_ROLES_NAME` (`NAME`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `users` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`LOGIN` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`NAME` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PASSWORD` VARCHAR(60) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `U_USERS_LOGIN` (`LOGIN`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
CREATE TABLE `users_roles` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`VERSIONING` BIGINT(20) DEFAULT NULL,
`ROLE_ID` BIGINT(20) NOT NULL,
`USER_ID` BIGINT(20) NOT NULL,
PRIMARY KEY (`ID`) USING BTREE,
KEY `ROLE_ID` (`ROLE_ID`) USING BTREE,
KEY `USER_ID` (`USER_ID`) USING BTREE,
CONSTRAINT `users_roles_ibfk_2` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`ID`) ON DELETE CASCADE,
CONSTRAINT `users_roles_ibfk_1` FOREIGN KEY (`ROLE_ID`) REFERENCES `roles` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
这与 EclipseLink 中的脚本相同。因此,我们将对表进行相同的修正。完成上述操作后,请验证以下执行配置是否通过:
- [spring-jdbc-generic-04.JUnitTestDao],用于测试 JDBC 的实现;
- [spring-jpa-generic-JUnitTestDao-openjpa],用于测试 JPA 和 OpenJpa 的实现;
这两次执行都必须成功。
9.3. 生成基础项目 [dbproduits]
[dbproduits] 数据库由 [spring-jdbc-01 à 03] 项目使用。也可以从 JPA 实体生成该数据库。
![]() |
- 在 [1] 中,Eclipse 项目将处于 MySQL / EclipseLink 的配置中。 数据库生成项目 [dbproduits] 对应的是 [generic-create-dbproduits];
- 在 [2] 中,待生成的表为 [PRODUITS];
注:按 Alt-F5 并重新生成所有 Maven 项目;
9.3.1. Maven 配置
项目 [generic-create-dbproduits] 的 Maven 配置如下:
<?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 的配置 JPA -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>spring.data.console.Main</start-class>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
该项目仅有一个依赖项(第22-26行),用于配置JPA层。最终,依赖项如下:
![]() |
9.3.2. Spring 配置
![]() |
Spring配置类如下:
package console;
import generic.jdbc.config.ConfigJdbc;
import generic.jpa.config.ConfigJpa;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@EnableJpaRepositories(basePackages = { "console" })
@Configuration
@Import({ ConfigJpa.class })
public class AppConfig {
// 数据源
@Bean
public DataSource dataSource() {
// 数据源 TomcatJdbc
DataSource dataSource = new DataSource();
// JDBC 访问配置
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITS);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITS);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITS);
// 初始打开的连接
dataSource.setInitialSize(5);
// 结果
return dataSource;
}
// EntityManagerFactory
@Bean
public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPersistenceUnitName("generic-jpa-entities-dbproduits");
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
}
- 第 18 行:导入类 [ConfigJpa](第 7.3 节)中的 Bean;
- 第 22-35 行:重新定义数据源 [dataSource]。 在 [ConfigJpa] 中,数据源是数据库 [dbproduitscategories]。此处将使用数据库 [dbproduits];
- 第38-46行:重新定义类[ConfigJpa]中的Bean [entityManagerFactory]。 在该类中,实体 JPA 原本是 [Produit, Categorie]。此处仅为 [Produit],且其定义与配置 JPA 层的项目中的定义不一致;
- 第 42 行:为了定义这个新的实体 JPA,引用了在文件 [META-INF/persistence.xml] 中定义的实体 JPA:
![]() |
文件 [persistence.xml] 内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="generic-jpa-entities-dbproduits" transaction-type="RESOURCE_LOCAL">
<!-- 实体 JPA -->
<class>generic.jpa.entities.dbproduits.Produit</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
- 第 6 行:唯一的实体 JPA;
- 第 4 行:持久化单元名称 [generic-jpa-entities-dbproduits],该单元在 Bean [entityManagerFactory] 中被引用;
9.3.3. 实体 JPA [Produit]
![]() |
实体 JPA 在项目 [mysql-config-jpa-eclipselink] 中定义如下:
package generic.jpa.entities.dbproduits;
import generic.jdbc.config.ConfigJdbc;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity(name="Produit1")
@Table(name = ConfigJdbc.TAB_PRODUITS)
public class Produit {
// 字段
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = ConfigJdbc.TAB_PRODUITS_ID)
private int id;
@Column(name = ConfigJdbc.TAB_PRODUITS_NOM, unique = true, length = 30, nullable = false)
private String nom;
@Column(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE, nullable = false)
private int categorie;
@Column(name = ConfigJdbc.TAB_PRODUITS_PRIX, nullable = false)
private double prix;
@Column(name = ConfigJdbc.TAB_PRODUITS_DESCRIPTION, length = 100, nullable = false)
private String description;
// 构造函数
public Produit() {
}
public Produit(int id, String nom, int categorie, double prix, String description) {
this.id = id;
this.nom = nom;
this.categorie = categorie;
this.prix = prix;
this.description = description;
}
// getter 和 setter
...
}
这是一个如今已成经典的 JPA 定义。只需注意以下几点:
- 第 12 行:该实体被命名为 [Produit1]。默认情况下,实体的名称即为类名,此处为 [Produit]。 但由于同一项目中还存在另一个实体 JPA [Produit],因此在执行之前就报错了。我们通过以下方式解决了这个问题;
- 第24行:此处的类别仅为一个数字;
- 实体之间不存在关联。因此情况非常简单;
9.3.4. 可执行类
![]() |
类 [CreateDatabase] 如下所示:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CreateDataBase {
public static void main(String[] args) {
// 只需实例化 Spring 上下文即可创建数据库表[dbproduits]
// 还必须至少有一个 Spring Data Repository,否则什么也不会发生
System.out.println("Travail en cours...");
new AnnotationConfigApplicationContext(AppConfig.class).close();
System.out.println("Travail terminé...");
}
}
这是我们之前已经遇到过的代码。
9.3.5. 生成 EclipseLink
表 [PRODUITS] 已根据以下执行配置创建:
![]() | ![]() |
控制台日志如下:
现在,让我们回到客户端 [MyManager],并刷新 [1-2] 的显示:
![]() |
在 [3] 中,可以看到已生成了一张表。现在检查数据库的 DDL(域定义语言):
SET FOREIGN_KEY_CHECKS=0;
USE `dbproduits`;
CREATE TABLE `produits` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`CATEGORIE` INTEGER(11) NOT NULL,
`DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci NOT NULL,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PRIX` DOUBLE NOT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `NOM` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
确实得到了预期的表。为了验证这一点,我们将执行以下配置:
![]() | ![]() |
该配置应能成功执行。
9.3.6. Hibernate 生成
![]() | ![]() |
注:按 Alt-F5 并重新生成所有 Maven 项目;
运行配置如下:
![]() | ![]() |
Hibernate生成的脚本SQL如下:
USE `dbproduits`;
CREATE TABLE `produits` (
`ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
`CATEGORIE` INTEGER(11) NOT NULL,
`DESCRIPTION` VARCHAR(100) COLLATE utf8_general_ci NOT NULL,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PRIX` DOUBLE NOT NULL,
PRIMARY KEY (`ID`) USING BTREE,
UNIQUE KEY `UK_hfvjn9lp7qoo5x79uu0ump3rf` (`NOM`) USING BTREE
) ENGINE=InnoDB
AUTO_INCREMENT=1 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
;
9.3.7. 生成 OpenJpa
![]() | ![]() |
注意:按 Alt-F5 并重新生成所有 Maven 项目;
运行配置如下:
![]() | ![]() |
由 OpenJpa 生成的脚本 SQL 如下:
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'
;

















































