1. 简介
本文件的PDF版本可在此处获取 |HERE||。
本文档中的示例可在此处查看 |HERE|。
1.1. 目录
在本文档中,我们提议研究不同的数据库部署配置。请考虑以下分层架构:
![]() |
执行流程从左向右进行:
- 首先执行[ui](用户界面)层中的某个类。该类会实例化[business]和[DAO]层。如果[ui]层是图形用户界面,则会等待用户操作。用户操作可能会触发架构中所有层(直至数据库)的方法执行。这些执行的结果将以某种形式返回给用户;
各层的作用可能如下:
- [JDBC](Java数据库连接)层是一个通用的数据库访问接口。它始终向[DAO]层提供相同的接口。若需更换数据库管理系统(DBMS),只需更换JDBC驱动程序即可。 只要您严格遵循特定规则,[DAO] 层便保持不变。然而,很难确保不同 DBMS 之间 100% 的可移植性,因为它们通常包含大量难以忽视的专有 SQL,这些专有 SQL 往往能带来性能提升。 一旦使用了专有 SQL,不同 DBMS 之间的可移植性便不复存在。此外,各 DBMS 在自动生成主键方面的策略往往不同,且各系统预留的关键字也各不相同。尽管如此,在本文档中,我们通过接受为每个 DBMS 都需要进行配置这一前提,成功地将所研究的 JDBC 架构移植到了六个不同的 DBMS 上;
- [DAO] 层提供了一个接口,用于访问所用特定数据库中的数据(需与适用于任何 DBMS 的 JDBC 接口区分开来);
- [业务]层实现应用程序的管理规则或业务规则。
- 其输入数据包括通过 [DAO] 层获取的数据库数据,以及/或由 [UI] 层传递的用户输入;
- 它生成的数据可通过 [DAO] 层保存到数据库,和/或返回给发起查询的 [UI] 层,以便向用户显示;
- [UI]层是执行用户操作并将操作结果返回给用户的层;
在上文中,[DAO]层将SQL查询发送至[JDBC]层,以便在DBMS中执行。近年来(自2006年起),该架构已演变为如下形式:
![]() |
现在,由 JPA(Java Persistence API)层向 JDBC 层发送 SQL 查询并接收结果。JPA 层向 DAO 层提交操作请求,以实现对象的持久化、修改、删除和检索。DAO 层不再直接执行 SQL 命令。这种方法具有更高的可移植性,因为 JPA 实现会处理不同数据库管理系统(DBMS)之间的差异,但其运行速度比 JDBC 技术慢。 我们将通过性能测试来验证这一点。JPA 技术将 Hibernate 框架 [http://hibernate.org/] 多年前所做的工作进行了规范化。
我们将考察采用以下两种架构之一构建的两个 [DAO] 层:
![]() |
![]() |
我们将要求 [DAO1] 和 [DAO2] 层实现相同的 [IDAO] 接口。因此,[JUnitTestsDao] 测试在两种配置下都将保持一致,从而使我们能够比较性能。其中,[DAO1] 层将使用 Spring JDBC 实现,而 [DAO2] 层将使用 Spring JPA 实现;
完成上述工作后,我们将按如下方式在 Web 上暴露 [IDAO] 接口:
![]() |
- 在[1]中,[IDAO]层通过由Spring MVC实现的Web层[2]对外暴露。实际上,对外暴露的是[IDAO]接口,我们将根据该接口是采用[DAO-JDBC]还是[DAO-JPA-JDBC]架构实现,构建两个版本的Web服务;
- 在[B]中,远程客户端使用Web服务暴露的URL,这些URL提供了对[IDAO-server]层方法的访问。我们将确保[DAO-Client]层[3]实现了[IDAO-server]接口[1]。这将使我们能够使用已经使用过两次的同一[JUnitTestsDao]测试[4];
- 在[3]中,[DAO-Client]层将使用Spring RestTemplate进行实现;
完成上述工作后,我们将对 Web 服务访问进行安全加固:
![]() |
- 在[5]中,客户端的HTTP请求会经过一个由Spring Security实现的身份验证层;
完成此步骤后,我们将把之前的架构演进为如下形式:
![]() |
- 在[3]中,客户端应用程序本身即为一个Web应用程序。它将包含一个表单[5],允许用户查询安全Web服务的URL。对安全Web服务的HTTP访问将通过一个用JavaScript实现的[DAO-client-js]层进行处理。该架构利用了所谓的跨域请求:
- Web服务[2]提供的URL格式为[http://machine1:port1/];
- 客户端 Web 应用程序 [3] 则从 URL [http://machine2:port2/] 下载。 如果 [http://machine2:port2/] 与 [http://machine1:port1/] 不相同(不同机器、不同端口),则客户端浏览器将阻止来自 [DAO-client-js] 层的 HTTP 调用。为解决此问题,Web 服务必须允许跨域请求。我们将探讨具体实现方法;
所展示的项目已在以下六种数据库管理系统上进行了测试:
- MySQL 5 社区版;
- SQL Server 2014 Express;
- PostgreSQL 9.4;
- Oracle Express 11g Release 2;
- IBM DB2 Express-C 10.5;
- Firebird 2.5.4;
针对上述每种数据库管理系统,我们开发了四种不同的 [DAO] 层:
- 一个基于 Spring JDBC 实现的层;
- 一个基于 Spring JPA 和 Hibernate JPA 提供商实现的层;
- 一个基于 Spring JPA 和 EclipseLink JPA 提供商实现的层;
- 一个基于 Spring JPA 和 OpenJPA JPA 提供商实现的层;
因此,这里呈现了一组二十四种不同的配置。我们付出了巨大努力来实现代码的模块化:
- 大部分代码仅编写一次。它基于两个 Maven 配置项目:
- 一个用于配置 JDBC 层;
- 另一个用于配置 JPA 层;
![]() |
![]() |
针对特定数据库管理系统(DBMS)的 JDBC 层 [1] 的 Maven 配置项目包括两个步骤:
- 导入 JDBC 驱动程序归档文件;
- 定义所用数据库的访问凭据,以及 [DAO1] 层将发送给 JDBC 驱动程序的各种 SQL 语句。尽管 SQL 已标准化,但我们仍遇到了可移植性问题,这主要是因为查询中包含的表名/列名在某些 DBMS 中被视为禁用关键字(例如 DB2 中的 ROLES 表,Firebird 中的 PASSWORD 列)。 此外,尽管列名通常不区分大小写,但在 PostgreSQL 中,我们遇到了与表中主键 ID 列相关的问题。该系统要求该列必须命名为小写的“id”。这些都是典型的意外可移植性问题;
用于配置特定数据库管理系统(DBMS)JPA层[2]的三个Maven项目也包含两个步骤:
- 导入 JPA 实现归档文件;
- 配置用于连接特定数据库管理系统(DBMS)的 JPA 实现。实际上,正是 JPA 层向 JDBC 层发出 SQL 命令。为了有效工作,它必须识别数据库管理系统,以便发送其能够识别的 SQL 命令。这些命令可能使用该数据库管理系统的专有 SQL 语法及其特定功能(数据类型、序列、触发器、存储过程、主键自动生成等);
这将产生二十四个 Maven 配置项目(4 种配置 × 6 种 DBMS),所有其他数据库操作项目都将基于这些项目。在上图中,由于 [DAO1] 和 [DAO2] 层提供相同的接口,因此将使用单个测试类 [JUnitTestsDao] 来测试上述两种架构的 24 种配置。一旦这些架构得到验证,便不再有其他困难:
- 用于将数据库发布到 Web 的 Maven 项目基于这两种架构。因此这里也有 24 种可能的配置;
- 用于保障 Web 服务访问安全的 Maven 项目基于前一个项目构建,同样拥有 24 种可能的配置;
- 最后,这个支持向安全 Web 服务发起跨域请求的 Maven 项目是在前一个项目的基础上开发的,同样提供了 24 种可能的配置;
本研究采用 MySQL5 数据库管理系统(DBMS)和 Hibernate JPA 实现进行。随后,我们将代码移植到 EclipseLink 和 OpenJPA JPA 实现中。最后,我们将代码移植到其他数据库(PostgreSQL、Oracle、SQL Server、DB2、Firebird)上。
本课程面向初学者。其中涉及的大部分概念均有详细讲解。无需具备数据库编程或Web编程的先备知识。但必须对SQL语言有扎实的理解,因为所使用的SQL查询语句不会进行解释。
要理解示例,您需要具备Java语言的基础知识,这些内容在任何Java入门课程中均可找到。文档[Java语言入门]的前两章就足够了。虽然这是一份较旧的文档(1998年发布,2002年修订),但其中涵盖了基础知识。 若需系统学习,可阅读让-马里·杜杜(Jean-Marie Doudoux)所著的权威专著《Java编程语言》(http://www.jmdoudoux.fr/java)。
本文绝非面面俱到,其目的仅在于提供一种可在类似场景中复用的方法论和代码。本文的撰写旨在让读者无需手边有电脑也能阅读,因此包含大量屏幕截图。
尽管本文档并未涵盖 Java 语言的所有功能或应用领域,但仍可作为该语言的学习资源。通过遵循本文档(即使并非全部内容),初学者在语言使用和 Spring 框架应用方面都将达到“高级 Java”水平。随后,他们可以借助以下书籍继续进行 Java 学习:
- [《Spring MVC与Thymeleaf实战》] [https://stahe.github.io/zh-springmvc-thymeleaf-janv-2015/],该书通过介绍Spring生态系统的“MVC Web编程”分支,进一步深入探索Spring生态;
- [《AngularJS / Spring MVC 教程》] [https://stahe.github.io/zh-spring-angular1.x-juillet-2014/],该书介绍了一种客户端/服务器 Web 架构,其中客户端采用 [AngularJS] 框架实现,服务器端则使用 [Spring MVC];
- [Java EE 入门],该教程脱离 Spring 生态系统,转向基于 JSF(Java Server Faces)和 EJB(Enterprise JavaBeans)的 Web 架构;
- [Android 平板编程入门] [https://stahe.github.io/zh-android-aout-2016/],该教程描述了一种客户端/服务器架构,其中客户端为 Android 平板,服务器则由 Spring MVC 实现的 Web 服务;
1.2. 来源
本文档主要有两个来源:
- [ ref1]: [Spring MVC 和 Thymeleaf 实例],网址为 [https://stahe.github.io/zh-springmvc-thymeleaf-janv-2015/]。 本文档采用不同的数据库,重新探讨了 [ref1] 中完成并介绍的工作。简而言之,它将相关内容从基于 Spring MVC 的 Web 编程语境中抽离出来。我决定创建一份独立的文档,是因为我发现 [ref1] 中用于在 Web 上公开数据库的代码和方法具有可复用性;
- [ ref2]: [Java Persistence in Practice],网址为 [https://stahe.github.io/zh-jpa-juin-2007/];
若想进一步了解 Spring,可参考以下资料:
- Spring 框架参考文档 [http://docs.spring.io/spring/docs/current/spring-framework-reference/pdf/spring-framework-reference.pdf];
- 大量 Spring 教程可访问网址 [http://spring.io/guides];
- 专门介绍 Spring 的 [developpez.com] 网站 [http://spring.developpez.com/];
- 教程 [http://www.tutorialspoint.com/spring/spring_tutorial.pdf];
SQL 知识不足的读者可通过 [https://stahe.github.io/zh-sql-firebird-janv-2006/] 链接中的《Firebird 数据库管理系统 SQL 入门》一书学习基础知识。
1.3. 使用的工具
以下示例已在以下环境中经过测试:
- Windows 8.1 Pro 64位系统;
- JDK 1.8(第23.1节);
- Spring Tool Suite 3.6.3 集成开发环境(section1);
- Chrome 浏览器(未使用其他浏览器);
- Chrome 扩展程序 [Advanced Rest Client](第 1 节);
- MySQL 5.6 社区版数据库管理系统(第23.4节);
- SQL Server 2014 Express(第23.9节);
- PostgreSQL 9.4(第23.7节);
- Oracle Express 11g Release 2 数据库管理系统(第23.6节);
- IBM DB2 Express-C 10.5 数据库管理系统(第23.8节);
- Firebird 2.5.4 数据库管理系统(第 23.10 节);
- 这六种数据库管理系统对应的 EMS Manager 客户端(第 23.5 节);
关于 JDK 1.8 的说明:案例研究中的一种方法使用了 Java 8 中 [java.lang] 包中的一个方法。
大多数示例都是 Maven 项目,可在 Eclipse、IntelliJ IDEA 或 NetBeans 中打开。下文中的屏幕截图来自 Spring Tool Suite IDE(Eclipse 的一个变体)。
1.4. 示例
示例已作为可下载的 ZIP 文件发布于 |此处|。
![]() |
- 在 [1] 中,示例文件夹;
- 在 [2] 中,[spring-core] 文件夹包含 Spring 学习项目;
- 在 [3] 中,[spring-database-config] 文件夹包含针对六种数据库的 JDBC 和 JPA 配置项目;
![]() |
- 在 [4] 中,是 Oracle 数据库管理系统 (DBMS) 的配置。它包含三个文件夹:
- [databases] 包含用于生成文档中使用的两个数据库的 SQL 脚本;
- [jdbc-driver] 包含 Oracle JDBC 驱动程序以及一个用于将其安装到本地 Maven 仓库的脚本;
- [eclipse] 包含 [5] 四个 Oracle 配置项目:
- [oracle-config-jdbc] 配置用于访问数据库管理系统(DBMS)的 JDBC 层;
- [oracle-config-jpa-hibernate] 配置 JPA 层,用于通过 Hibernate JPA 提供程序访问 DBMS;
- [oracle-config-jpa-eclipselink] 配置 JPA 层,用于通过 Eclipselink JPA 提供程序访问数据库;
- [oracle-config-jpa-openjpa] 配置 JPA 层,用于通过 OpenJPA JPA 提供程序访问数据库;
- 在 [6] 中,[eclipse config / launch configurations] 文件夹包含启动配置,读者可将其导入 Eclipse 并根据自身环境进行调整;
![]() |
- 在 [7] 中,[spring-database-generic] 文件夹包含适用于六种数据库管理系统(DBMS)和三种 JPA 提供商的所有通用数据库访问代码;
- 在 [8] 中,[spring-jdbc] 包含四个项目,用于演示 JDBC API 以及 Spring JDBC;
- 在 [9] 中,[spring-jpa / spring-jpa-generic] 是使用 JPA 层访问数据库的项目。[generic-create-db*] 项目是用于创建 JPA 层所用数据库的 JPA 项目;
![]() |
-
在 [10] 中,[spring-webjson] 文件夹包含将数据库暴露在 Web 上的项目;
- [spring-webjson-server-jdbc-generic] 是通过 Spring JDBC 访问数据库的 Web 服务;
- [spring-webjson-server-jpa-generic] 是通过 Spring JPA 访问数据库并将其对外暴露的 Web 服务;
- [spring-webjson-client-generic] 是允许访问前两个 Web 服务的单一客户端;
-
在 [11] 中,[spring-security] 文件夹包含通过安全访问方式在 Web 上暴露数据库的项目;
- [spring-security-server-jdbc-generic] 是通过 Spring JDBC 访问数据库并提供安全访问的 Web 服务;
- [spring-security-server-jpa-generic] 是通过 Spring JPA 访问数据库的安全 Web 服务;
- [spring-security-client-generic] 是允许访问前两个安全 Web 服务的单一客户端;
-
在 [12] 中,[spring-cors] 文件夹包含通过 Web 暴露数据库并支持安全访问的项目,这些项目允许跨域访问,例如源自浏览器 JavaScript 代码的访问;
- [spring-cors-server-jdbc-generic] 是一个支持跨域访问的安全 Web 服务,用于暴露通过 Spring JDBC 访问的数据库;
- [spring-cors-server-jpa-generic] 是一个支持跨域访问的安全 Web 服务,用于通过 Spring JPA 访问数据库;
- [spring-cors-client-generic] 是一个 Web 应用程序,用于查询前两个 Web 服务;












