5.2 JDBC的核心概念
JDBC API给Java程序提供了一种访问一个或者多个数据源的途径,在大多数情况下,数据源是关系型数据库,使用SQL语言来访问。但是,JDBC Driver也可以实现为能够访问其他类型的数据源,比如文件系统或面向对象系统。JDBC API主要的动机就是提供一种标准的API,让应用程序访问多种多样的数据源。
本节介绍JDBC API的一些核心概念,此外,也介绍JDBC程序的两种使用场景,分别是两层模型和三层模型,在不同的场景中,JDBC API的功能是不一样的。
5.2.1 建立连接
JDBC API定义了Connection接口来代表与某个数据源的一条连接。
典型情况下,JDBC应用可以使用以下两种机制来与目标数据源建立连接:
- DriverManager:这个类从JDBC API 1.0版本开始就有了,当应用程序第一次尝试去连接一个数据源时,它需要指定一个URL,DriverManager将会自动加载所有它能在CLASSPATH下找到的JDBC驱动(任何JDBC API 4.0版本前的驱动,需要手动去加载)。
- DataSource:这个接口在JDBC 2.0 Optionnal Package API中首次被引进,更推荐使用DataSource,因为它允许关于底层数据源的具体信息对于应用是透明的。需要设置DataSource对象的一些属性,这样才能让它代表某个数据源。当这个接口的getConnection方法被调用时,这个方法会返回一条与数据源建立好的连接。应用程序可以通过改变DataSource对象的属性,从而让它指向不同的数据源,无须改动应用代码;同时,DataSource接口的具体实现类也可以在不改动应用程序代码的情况下进行改变。
JDBC API对DataSource接口有两方面的扩展,目的是为了支持企业应用,这两个扩展的接口如下:
- ConnectionPoolDataSource:支持对物理连接的缓存和重用,这能提高应用的性能和可扩展性。
- XADataSource:使连接能在分布式事务中使用。
以下是JDBC客户端从DriverManager获取连接的例子:
5.2.2 执行SQL并操作结果集
一旦建立好一个连接,应用程序便可以通过这条连接调用响应的API来对底层的数据源执行查询或者更新操作,JDBC API提供了对于SQL:2003标准实现的访问,支持BLOB、CLOB、ARRAY、REF、STRUCT、XML、DISTINCT等高级数据类型。由于不同的厂商对这个标准的支持程度不同,因此JDBC API提供了DatabaseMetadata这个接口,应用程序可以使用这个接口来查看某个特性是否受到底层数据库的支持。JDBC API也定义了转义语法,允许应用程序访问一些非标准的、某个数据库厂商独有的特性。使用转义语法能够让使用JDBC API的应用程序像原生应用程序一样去访问某些特性,并且也提高了应用的可移植性。
应用可以使用Connection接口中定义的方法去指定事务的属性,并创建Statement对象、PreparedStatement对象或者CallableStatement对象。这些Statement对象用来执行SQL语句,并获取执行结果。ResultSet接口包装一次SQL查询的结果。Statement可以是批量的,应用能够在一次执行中向数据库提交多条更新语句作为一个执行单元。
JDBC API的ResultSet接口扩展了RowSet接口,提供了一个功能更全面的对表格型数据进行封装和访问的容器。一个RowSet对象是一个Java Bean组件,在底层数据源断开连接的情况下也能对数据进行操作,比如一个RowSet对象可以被序列化,然后通过网络发送出去,这对于那些不想对表格型数据进行处理的客户端来说特别有用,并且无须在连接建立的情况下进行,就减轻了驱动程序的负担。RowSet的另一个特性是,它能够包含一个定制化的reader,来对表格型数据进行访问,并非只能访问关系型数据库的数据。此外,一个RowSet对象能在与数据源断开连接的情况下对行数据进行改写,并且能够包含一个定制化的writer,把改写后的数据写回底层的数据源。
以下是一个执行SQL并操作结果集的例子:
5.2.3 两层模型
两层模型定义了客户端层和服务端层,不同层实现不同的功能,如图5-1所示。
图5-1 两层模型
客户端层包含应用程序以及一个或者多个JDBC驱动,这一层的主要职责是:
- 表现层逻辑。
- 业务逻辑。
- 对于多语句事务或者分布式事务的事务管理。
- 资源管理。
在这种模型中,应用程序直接与JDBC驱动交互,包括创建和管理物理连接、处理底层数据库的细节。应用程序可能会基于对底层数据源类型的认知去访问一些特有的、非标准的特性,以此来获得性能上的提升。
这种模型有一些缺点,说明如下:
- 将表现层和业务层逻辑与底层的功能直接混合,这会使代码变得难以维护。
- 应用程序不具有可移植性,因为应用程序会使用到底层特定数据库的一些独有的特性,对于需要与多种数据源进行连接的应用程序来说,要特别注意不同厂商的数据库实现以及不同的特性。
- 限制了可扩展性。典型地,应用程序将会一直持久地与数据库连接,直到应用程序退出,这就限制了并发访问数据库的并发数,在这种模型中,所谓的性能、可扩展性以及可用性需要JDBC驱动以及底层的数据库来共同保证。如果应用程序使用的JDBC驱动不止一种,情况就会更加复杂。
5.2.4 三层模型
三层模型引进了一个中间层来处理业务逻辑并作为基础设施,如图5-2所示。
图5-2 三层模型
这种架构对于企业应用来说,性能、可扩展性和可用性都会得到提升,各层的职责如下:
- 客户端层:仅作为表现层,只需要与中间层交互,而不需要了解中间层的基础架构以及底层数据源的功能细节。
- 中间层服务器:包含以下几个组成部分:
实现了业务逻辑,并与客户端进行交互的应用程序。如果应用程序需要与底层数据源交互,那么它只需要关注高层次的抽象和逻辑连接,而不是底层的驱动API。
为应用程序提供基础设施的应用服务器。这些基础设施包括对物理数据库连接的池化和管理、事务管理以及对不同驱动API的不同点进行屏蔽,最后一点使得我们很容易写出可移植的应用程序,应用服务器这个角色可以由Java EE服务器来承担,应用服务器主要实现提供给应用程序使用的抽象层,并负责与JDBC驱动交互。
能够与底层数据源建立连接的JDBC驱动。每个驱动根据其底层数据源的特性实现标准的JDBC API,驱动层可能会屏蔽掉SQL:2003标准与数据源支持的SQL方言之间的不同。如果底层数据源并不是一个关系型的数据库,驱动就需要实现对应的关系层逻辑,提供给应用服务器使用。
- 底层的数据源:这一层是数据所在的一层,可以包含关系型数据库、文件系统、面向对象数据库、数据仓库等任何能组织和表现数据的东西,但它们都需要提供符合JDBC API规范的驱动。
5.2.5 JDBC与Java EE平台的关系
Java EE组件(比如Java Server Pages、Servlet以及EJB组件)通常需要使用JDBC API来访问关系型的数据,当Java EE组件使用JDBC API时,它们的容器会管理事务以及数据源。这意味着Java EE组件的开发者不会直接使用JDBC API的事务管理和数据源管理的能力。