8. Spring Data JPA OpenJpa
8.1. 简介
我们将复用之前的架构,并在此基础上实现 JPA/OpenJpa 层。
8.2. 配置开发环境
使用 STS 下载 [myql-config-jpa-eclipselink] 项目 [1-4]:
然后导入位于 [<examples>/spring-database-config/mysql/eclipse] 文件夹 [6] 中的 [mysl-config-jpa-openjpa] 项目 [5]:
完成此操作后,请为 [Package Explorer] 中的所有项目刷新 Maven 环境(Alt-F5):
然后,为了验证运行环境,请运行名为 [spring-jpa-generic-JUnitTestDao-openjpa] 的构建配置:
此配置将运行 [JUnitTestDao] 测试。该测试应通过:
8.3. JPA 层配置项目
本项目的目的是配置下图所示架构的 JPA 层:
8.3.1. Maven 配置
该项目是一个由以下 [pom.xml] 文件配置的 Maven 项目:
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>configuration mysql openjpa</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
</parent>
<dependencies>
<!-- dépendances variables ********************************************** -->
<!-- JPA provider -->
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa</artifactId>
<version>2.3.0</version>
</dependency>
<!-- dépendances constantes ********************************************** -->
<!-- Spring Data -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- configuration JDBC inherited -->
<dependency>
<groupId>dvp.spring.database</groupId>
<artifactId>generic-config-jdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<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>
- 第 5–7 行:该项目生成的 Maven 工件。它与 [mysql-config-jpa-hibernate] 项目的工件相同。这意味着在任何给定时间,这两个项目中只能有一个处于活动状态;
- 第 10–14 行:作为父级 Maven 项目,用于设置该项目所需的大部分依赖项的版本;
- 第 19–23 行:OpenJpa 库;
- 第 26–29 行:Spring Data 库;
- 第 32–34 行:JPA 层配置项目基于 JDBC 层配置项目,后者定义了所用 DBMS 的 JDBC 驱动程序以及数据库连接详细信息等内容;
- 第 35–40 行:JDBC 层配置项目包含 [Spring JDBC] 库,此处已被 [Spring Data JPA] 库取代。因此,我们指定不将其包含在项目依赖项中。不过,即使保留该库,也不会引发任何错误;
最终,依赖项如下:
8.3.2. Spring 配置
[ConfigJpa] 类对 Spring 项目进行如下配置:
package generic.jpa.config;
import generic.jdbc.config.ConfigJdbc;
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;
}
// JPA entity packages
public final static String[] ENTITIES_PACKAGES = { "generic.jpa.entities.dbproduitscategories" };
// 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_DBPRODUITSCATEGORIES);
dataSource.setPassword(ConfigJdbc.PASSWD_DBPRODUITSCATEGORIES);
dataSource.setUrl(ConfigJdbc.URL_DBPRODUITSCATEGORIES);
// 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.setPackagesToScan(ENTITIES_PACKAGES);
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
// Transaction manager
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
此配置与第6.3.2节中描述的Hibernate JPA实现配置类似。我们仅详细说明其中的差异:
- 第 23–30 行:[jpaVendorAdapter] Bean 现采用 OpenJpa 实现;
8.4. [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-dbproduitscategories" transaction-type="RESOURCE_LOCAL">
<!-- entities JPA -->
<class>generic.jpa.entities.dbproduitscategories.Categorie</class>
<class>generic.jpa.entities.dbproduitscategories.Produit</class>
<class>generic.jpa.entities.dbproduitscategories.User</class>
<class>generic.jpa.entities.dbproduitscategories.Role</class>
<class>generic.jpa.entities.dbproduitscategories.UserRole</class>
<exclude-unlisted-classes />
</persistence-unit>
<persistence-unit name="generic-jpa-entities-dbproduits" transaction-type="RESOURCE_LOCAL">
<!-- entities JPA -->
<class>generic.jpa.entities.dbproduits.Produit</class>
<exclude-unlisted-classes />
</persistence-unit>
</persistence>
- 第 4 行:与 [dbproduitscategories] 数据库关联的持久化单元。其名称可以任意设定(name 属性);
- 第 6–10 行:待管理的五个 JPA 实体;
- 第 13–17 行:另一个与 [dbproduitscategories] 数据库关联的持久化单元。OpenJpa 允许一个持久化文件包含多个持久化单元,而 EclipseLink 不支持。回想一下,在使用 Hibernate 时,我们并未使用持久化文件;
8.5. 测试层
上述测试与 Spring JDBC、Spring JPA Hibernate 以及 Spring JPA EclipseLink 实现的测试完全相同。如有需要,请参阅以下页面:
要使用的执行配置如下:
要让 OpenJpa 对 JPA 实体进行编织,必须通过 Java 代理启动 Java 应用程序或 JUnit 测试。该代理将在 JVM 加载 JPA 实体之前对其进行编织 [http://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html]。 让我们以 [JUnitTestDao] 测试的配置为例:
- 在 [Arguments] 选项卡 [1] 中,我们使用语法 [2] 向 JVM 提供 Java 代理的引用。该 Java 代理即位于 [<m2-repo>/org/apache/openjpa/<version>] 文件夹中的 [openjpa-<version>.jar] 归档文件。<m2-repo> 文件夹在 Eclipse 配置中指定(如下 [3] 所示):
在上文的 [2] 中,我们使用了用户定义的变量 [M2_REPO]:
我们将[3]中显示的值赋给了[M2_REPO]变量。
针对 [JUnitTestDao] 和 [JUnitTestPushTheLimits] 测试获得的结果如下:
- 在 [1] 中,[JUnitTestPushTheLimits-EclipseLink]:70.583 秒;
- 在 [2] 中,[JUnitTestPushTheLimits-Hibernate]:78.945 秒;
- 在 [3] 中,[JUnitTestPushTheLimits-JDBC]:36.09 秒;
- 在 [4] 中,[JUnitTestPushTheLimits-OpenJpa]:80.394 秒;
[JUnitTestProxies] 测试获得的控制台结果如下:
| Vidage de la base de données --------------------------------
doNothing
Vidage de la base de données --------------------------------
getShortCategoriesByName1 --------------------------------
Catégorie de type : PROXY
Catégorie :
Exception : java.lang.NullPointerException, Message : null
Vidage de la base de données --------------------------------
getLongCategoriesByName1 --------------------------------
Catégorie de type : POJO
Catégorie :
1
Vidage de la base de données --------------------------------
getShortProduitsByName1 --------------------------------
Produit de type : PROXY
Nom de la catégorie du produit :
Exception : java.lang.NullPointerException, Message : null
Vidage de la base de données --------------------------------
getLongProduitsByName1 --------------------------------
Produit de type : POJO
Nom de la catégorie du produit :
categorie[0]
|
这里我们可以看到,当访问 PROXY 类型类别的 [Categorie.produits] 字段以及 PROXY 类型产品的 [Produit.categorie] 字段时,这两种情况都出现了空指针(第 7 行和第 17 行)。