9. 从 JPA 实体生成数据库
可以基于 JPA 实体创建数据库表。我们将在此演示这一过程。目的是验证由 JPA 实体生成的数据库是否确实是我们想要的。
9.1. 设置工作环境
我们将首先使用 EclipseLink JPA 实现 [1]。
![]() |
然后,我们使用客户端 [MyManager] 从 MySQL 数据库 [dbproduitscategories] 中删除表(参见第 23.5 节)。首先删除包含外键的表 [1-3]:
![]() |
然后,我们对剩下的三个表 [4-6] 重复上述操作:
![]() |
对于 [spring-jdbc-01 至 03] 项目所使用的 [dbproduits] 表,我们也采用同样的方法:
![]() | ![]() |
此外,您还必须导入两个数据库生成项目:
![]() |
- 在 [1] 中,导入 [generic-create-dbproduits] 项目,该项目位于 [<examples>/spring-database-generic/spring-jpa] [2];
![]() |
- 在 [4] 中,导入 [generic-create-dbproduitscategories] 项目,该项目位于 [<examples>/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 行:对一个编织器的依赖,该编织器将用于增强 EclipseLink 和 OpenJpa 实现中的 JPA 实体。虽然 [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 实现):
// the provider JPA
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
// Note: JPA entities and Eclipselink configuration are in the META-INF/persistence.xml file
EclipseLinkJpaVendorAdapter eclipseLinkJpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkJpaVendorAdapter.setShowSql(false);
eclipseLinkJpaVendorAdapter.setDatabase(Database.MYSQL);
eclipseLinkJpaVendorAdapter.setGenerateDdl(true);
return eclipseLinkJpaVendorAdapter;
}
第 8 行是这里的关键。它存在于所有使用的 JPA 实现中。它指定如果与 JPA 实体关联的表不存在,则必须创建它们。我们将使用此属性来生成表。
9.2.3. 存储库
![]() |
[ProductsRepository] 接口如下:
package console;
import generic.jpa.entities.dbproduitscategories.Produit;
import org.springframework.data.repository.CrudRepository;
public interface ProduitsRepository extends CrudRepository<Produit, Long> {
}
实例化此接口将触发 JPA 层的实例化。事实上,在第 7 行,该接口引用了 JPA 实体 [Product],这将强制实例化 JPA 层。我们本可以使用任何引用某个 JPA 实体的 [CrudRepository] 接口。 我们可以看到,尽管 [repository] 仅引用了 JPA 实体 [Product],但所有 JPA 实体的所有表均被生成。
9.2.4. 可执行类
![]() |
[CreateDatabase] 类如下所示:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CreateDataBase {
public static void main(String[] args) {
// simply instantiate the Spring context to create the database tables [dbproduitscategories]
// you also need at least one Spring Data Repository, otherwise nothing happens
System.out.println("Travail en cours...");
new AnnotationConfigApplicationContext(AppConfig.class).close();
System.out.println("Travail terminé...");
}
}
- 第 11 行:我们实例化 Spring 上下文并立即将其关闭。在此上下文中,存在一个引用 JPA 实体 [Product] 的 [ProductsRepository] Bean。这足以实例化 JPA 层,从而在 [productcategories] 数据库中生成表。
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'
;
让我们以生成 [PRODUCTS] 表的 SQL 脚本为例(第 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] 属性。这对应于 [@ManyToOne(fetch = FetchType.LAZY), @JoinColumn(name = ConfigJdbc.TAB_PRODUITS_CATEGORIE_ID)] 注解,以及 [Categorie.produits] 字段的 [@OneToMany(fetch = FetchType.LAZY, mappedBy = "categorie", cascade = { CascadeType.ALL }), @CascadeOnDelete] 注解;
- 第 19 行:[NAME] 列的定义对应于 [Product.name] 字段的注解 [@Column(name = ConfigJdbc.TAB_PRODUITS_NAME, unique = true, length = 30, nullable = false)];
- 第 20 行:[PRICE] 列的定义对应于 [Product.price] 字段的注解 [@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],用于测试 Hibernate 或 EclipseLink 的 JPA 实现(在此情况下,将使用 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] 对应的等效注解,该注解会在外键 [PRODUCTS.CATEGORY_ID] 上生成 SQL 属性 [ON DELETE CASCADE](第 25 行)。因此,该属性必须手动生成,因为它是测试所必需的:
![]() |
![]() |
![]() |
对于 [USERS_ROLES] 表中的两个外键,也必须进行同样的操作:
![]() |
最后,与 EclipseLink 的实现方式一样,这五个表中的 [VERSIONING] 列必须设置默认值:
![]() |
完成此操作后,请验证以下测试配置是否通过:
- [spring-jdbc-generic-04.JUnitTestDao],用于测试 JDBC 实现;
- [spring-jpa-generic-JUnitTestDao-hibernate-eclipselink],用于测试 Hibernate 或 Eclipselink 的 JPA 实现(在此情况下,将使用 Hibernate)
两项测试都必须通过。
9.2.7. 使用 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 {
// the provider 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],用于测试 OpenJPA 的 JPA 实现;
这两次运行都必须成功。
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>
<!-- configuration JPA of SGBD -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>spring.data.console.Main</start-class>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
该项目中仅有一处依赖(第 22–26 行),用于配置 JPA 层。最终,依赖关系如下:
![]() |
9.3.2. Spring 配置
![]() |
Spring 配置类如下:
package console;
import generic.jdbc.config.ConfigJdbc;
import generic.jpa.config.ConfigJpa;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@EnableJpaRepositories(basePackages = { "console" })
@Configuration
@Import({ ConfigJpa.class })
public class AppConfig {
// data source
@Bean
public DataSource dataSource() {
// data source TomcatJdbc
DataSource dataSource = new DataSource();
// configuration access JDBC
dataSource.setDriverClassName(ConfigJdbc.DRIVER_CLASSNAME);
dataSource.setUsername(ConfigJdbc.USER_DBPRODUITS);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITS);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITS);
// initially open connections
dataSource.setInitialSize(5);
// result
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] 类中的 [entityManagerFactory] Bean。在该类中,JPA 实体为 [Product, Category]。此处仅为 [Product],且其定义与配置 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">
<!-- entities JPA -->
<class>generic.jpa.entities.dbproduits.Produit</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
- 第 6 行:唯一的 JPA 实体;
- 第 4 行:[entityManagerFactory] Bean 中引用的持久化单元名称 [generic-jpa-entities-dbproduits];
9.3.3. JPA 实体 [Product]
![]() |
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 {
// fields
@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;
// manufacturers
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 and setters
...
}
这是一个现已成为标准的 JPA 定义。请注意以下几点:
- 第 12 行:我们为该实体命名为 [Product1]。默认情况下,实体的名称即为类名,在此例中即为 [Product]。然而,由于同一项目中还存在另一个名为 [Product] 的 JPA 实体,因此在执行开始前就报错了。我们通过以下方式解决了这个问题;
- 第 24 行:这里的类别仅仅是一个数字;
- 实体之间不存在关联。因此,我们面临的情况非常简单;
9.3.4. 可执行类
![]() |
[CreateDatabase] 类如下所示:
package console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CreateDataBase {
public static void main(String[] args) {
// simply instantiate the Spring context to create the database tables [dbproduits]
// you also need at least one Spring Data Repository, otherwise nothing happens
System.out.println("Travail en cours...");
new AnnotationConfigApplicationContext(AppConfig.class).close();
System.out.println("Travail terminé...");
}
}
这是我们之前见过的代码。
9.3.5. EclipseLink 生成
[PRODUCTS] 表是使用以下运行时配置创建的:
![]() | ![]() |
控制台日志如下:
现在,让我们回到 [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'
;

















































