返回介绍

5.1 示例工程与搭建过程

发布于 2025-04-22 19:57:18 字数 7212 浏览 0 评论 0 收藏

长期以来,在 Java 程序中,我们都使用字符串来定义数据库查询,正如前文所述这种方式易于出错。列或表的名字可能会发生变化,我们可能会添加一列或更改已有列的类型。在 Java IDE 中,我们已经习惯于为 Java 类做类似的重构,而 IDE 会指导我们找到所有需要修改的引用,包括在注释或配置文件中的引用。对于包含复杂 SQL 查询的表达式字符串来说,并没有这样的功能支持。为了避免这种问题,我们提供了类型安全查询的替代方案,也就是 Querydsl。很多的数据访问技术都能很好地集成 Querydsl,第 3 章中已提供了一些背景知识。在这一部分,我们将会关注 Querydsl SQL 模块以及它如何与 Spring 的 JdbcTemplate 进行集成,每个 Spring 开发人员对于 JdbcTemplate 应该都很熟悉。

不过,在看新的 JDBC 支持功能之前,需要讨论一些通用的关注点,如数据库的配置以及工程构建系统的搭建。

5.1.1 HyperSQL 数据库

在本章的 Querydsl 示例中,使用 HyperSQL 数据库的 2.2.8 版本( http://hsqldb.org/ )。HyperSQL 有一个很好的特性,就是能够以服务器的模式以及内存的模式运行数据库。对于集成测试来说,基于内存的方式是很好的选择,因为数据库的启动和停止可以通过应用程序的配置来进行控制,这是通过 Spring 的 Embedded DatabaseBuilder 来实现的,如果使用 spring-jdbc XML 命名空间的话,那就是<jdbc:embedded-database>标签。构建脚本会自动地下载依赖并启动内存数据库。要使用独立的服务器模式的话,需要下载分发包并将其解压到系统的目录之中。这些完成之后,我们可以切换到已解压的分发包的 hsqldb 目录下,并使用以下命令来启动数据库:

c0501

运行这个命令会启动服务器,它会产生一些输出日志以及服务器已经启动的提示信息,也会告知我们使用 Ctrl-C 会停止服务器。现在,可以打开另一个命令窗口并切换到相同的 hsqldb 目录下,我们可以启动数据库客户端,这样的话就可以与数据库交互了(如创建表以及运行查询等)。对于 Windows 来说,我们只需要执行 bin 目录下的 runManagerSwing.bat 批处理文件;对于 OS X 或 Linux,我们需要运行以下的命令:

c0502

这会产生如图 5-1 所示的登录对话框。我们将 Type 修改为“HSQL Database Engine Server”,并将“test”作为数据库的名字添加到 URL 上,这样 URL 就会是“jdbc:hsqldb:hsql://localhost/test”。默认的用户是“sa”,密码为空。一旦连接成功,就会看到一个激活的 GUI 数据库客户端。

0501

图 5-1 HSQLDB 客户端登录对话框

5.1.2 Querydsl 的 SQL 模块

Querydsl 的 SQL 模块为 Java 开发人员提供了一种类型安全的可选方案来与关系型数据库交互。Querydsl 会基于数据库表的元数据生成查询类型,而不是在 Java 程序中编写 SQL 查询并将其嵌入到字符串中。我们可以使用这些生成的类型来编写查询并对数据库执行 CURD 操作,从而避免以字符串的形式提供列名或表名。

相对于其他的 Querydsl 模块,SQL 模块生成查询类型的方式稍微有所不同。它不是依赖于注解,SQL 模块依赖于实际的数据库表和可用的 JDBC 元数据来生成查询类型。这意味着在生成查询类之前,要把表创建好并且需要访问一个可用的数据库。有鉴于此,我们建议将其作为一个单独的构建步骤来运行并将生成的类作为工程的一部分存储到源码控制系统之中。只有当修改了表结构并且尚没有签入代码的时候,才需要重新运行这个步骤。我们期望持续集成系统也运行这个步骤,因为 Java 类型和数据表的任何不匹配都能在构建的时候发现。

稍后会看一下我们需要生成的查询类型,但首先需要理解它们包含了什么以及如何使用它们。它们包含了 Querydsl 用来生成查询的信息,也包含了构造查询、执行更新、插入和删除以及将数据映射到领域对象的信息。让我们快速看一个表的例子,这个表包含了地址信息。表 address 有 3 个 VARCHAR 类型的列:street、city 以及 country。示例 5-1 展示了创建这个表的 SQL 语句。

示例 5-1 创建 address 表

c0503

示例 5-2 展示了基于 address 表所生成的查询类型。它有多个构造函数、针对每列的 Querydsl 路径表达式、创建主键和外键类型的方法并提供了 QAddress 类型实例的静态域。

示例 5-2 生成的查询类型 - QAddress

在 Java 代码中,可以通过以下的方式创建引用:

055555

这样就可以更容易地使用 qAddress 来引用表和列,而不用再借助于字符串常量了。

在示例 5-3 中,查找所有 city 为“London”的 address,并获取其 street、city 以及 country 字段。

示例 5-3 使用生成的查询类

c0505

首先,创建对查询类型的引用以及对应于所使用数据库的 SQLTemplates 实例,在我们的场景下也就是 HSQLDBTemplates。SQLTemplates 封装了不同数据库之间的差异,类似于 Hibernate 中的 Dialect。接下来,使用 JDBC javax.sql. Connection 和 SQLTemplates 作为参数构造了一个 SQLQuery。使用 from 方法来指明要查询的表,这需要传递进来查询的类型。然后,通过 where 方法来提供 where 子句或断言,在这里我们使用了 qAddress 引用来指明 city 等于“London”。

执行这个 SQLQuery,使用了 list 方法,它会返回结果的一个 List。我们同时使用 QBean 提供了映射的实现,它以领域类型以及包含了 street、city 以及 country 列的投射作为参数。

我们得到的结果是 Address 的 List,其中 Address 已经通过 QBean 进行了填充。QBean 类似于 Spring 的 BeanPropertyRowMapper,它需要领域类型遵循 JavaBean 的规则。作为替代方案,也可以使用 MappingProjection,它类似于 Spring 中的 RowMapper,对于结果如何映射到领域对象上你会有更多的控制权。

基于这个简单的例子,我们总结一下 SQL 查询所用到的 Querydsl 组件。

  • SQLQueryImpl 类,它持有目标表,可能还会有断言或 where 子句相关的其他表,如果我们查询多表的话,可能还会有连接表达式。
  • 断言(Predicate),通常会是 BooleanExpression 的形式,允许我们对结果声明过滤器。
  • 映射或结果提取器(results extractor),通常是 QBean 或 MappingProjection 的形式,其参数是一个或多个表达式所形成的投射。

到此为止,我们还没有与任何的 Spring 特性进行集成,不过本章后面的内容将会涉及这种集成。第一个例子的目的就在于介绍 Querydsl SQL 模块的基础知识。

5.1.3 构建系统集成

本章中 Querydsl 部分的代码位于 GitHub 示例工程( https://github.com/SpringSource/ spring-data-book )的 jdbc 模块之中。

在工程中真正使用 Querydsl 之前,需要配置构建系统,这样才能生成查询类型。Querydsl 提供了 Maven 和 Ant 的集成,其文档位于 Querydsl 参考文档( http://www.querydsl.com/ static/querydsl/latest/reference/html/ )的“Querying SQL”一章。

在 Maven 的 pom.xml 文件中添加了插件配置,如示例 5-4 所示。

示例 5-4 搭建用于代码生成的 Maven 插件

c0506

需要使用如下的 Maven 命令明确执行这个插件:

c0507

通过指明执行目标,可以将这个插件设置为 generate-sources 生命周期的一部分来执行。在示例工程中,就是这样做的,同时也使用了预先定义的 HSQL 数据库,这样就能避免在构建示例工程时强制启动数据库。不过,在真实的工作场景中,需要有一个可修改的模式的数据库并且要重新运行 Querydsl 的代码生成过程。

5.1.4 数据库模式

既然构建已经配置完成,那就可以生成查询类了,但是先看看这一部分所使用的数据库模式。我们已经看到了 address 表,现在添加了 customer 表,这个表与 address 表有一对多的关系。为 HSQLDB 数据库所定义的模式如示例 5-5 所示。

示例 5-5 schema.sql

c0508

customer 和 address 这两个表通过由 address 到 customer 的外键连接了起来。同时,我们为 address 表的 email_address 列上定义了唯一索引。

这样,我们的领域模型实现就如图 5-2 所示。

0502

图 5-2 使用 Querydsl SQL 的领域模型实现

5.1.5 示例工程的领域实现

我们已经看到了数据库的模式,接下来将会看到对应的 Java 领域类,这些类会在示例中用到。我们需要 Customer 类和 Address 类来持有数据库表中的数据。这两个类都扩展自 AbstractEntity,这个类除了有 equals(...) 和 hashCode() 方法以外,还有 id 的 setter 和 getter 方法,在这里 id 字段是 Long 类型的:

c0509

Customer 类有名字和电子邮件信息以及地址的集合。这个实现是一个传统的 JavaBean,包含了所有属性的 setter 和 getter 方法:

c0510-1

c0510-2

电子邮件地址在数据库中存储为 VARCHAR 列,但是在 Java 类中我们使用了 EmailAddress 值对象类型,它会使用正则表达式对电子邮件地址进行校验。这个类与我们在其他章节看到的是相同的:

c0511

最后一个领域类是 Address 类,同样也是一个传统的 JavaBean,包含了地址属性的 setter 和 getter 方法。除了无参的构造器,我们还有一个接收所有地址属性的构造器:

c0512-1

c0512-2

前面介绍的 3 个类构成了我们的领域模型,它们位于 JDBC 示例工程的 com.oreilly. springdata.jdbc.domain 包中。现在,我们看一下 CustomerRepository 的接口定义:

c0513

我们有几个查找方法以及用于保存和删除的方法。这其中并没有任何的存储方法来保存和删除 Address 对象,因为它们始终属于 Customer 实例。当 Customer 实例保存的时候,我们必须要持久化它所提供的 address 信息。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。