2.1 JDBC API简介
JDBC(Java Database Connectivity)是Java语言中提供的访问关系型数据的接口。在Java编写的应用中,使用JDBC API可以执行SQL语句、检索SQL执行结果以及将数据更改写回到底层数据源。JDBC API也可以用于分布式、异构的环境中与多个数据源交互。
JDBC API基于X/Open SQL CLI,是ODBC的基础。JDBC提供了一种自然的、易于使用的Java语言与数据库交互的接口。自1997年1月Java语言引入JDBC规范后,JDBC API被广泛接受,并且广大数据库厂商开始提供JDBC驱动的实现。
JDBC API为Java程序提供了访问一个或多个数据源的方法。在大多数情况下,数据源是关系型数据库,它的数据是通过SQL语句来访问的。当然,使用JDBC访问其他数据源(例如文件系统或者面向对象系统等)也是有可能的,只要该数据源提供JDBC规范驱动程序即可。
使用JDBC操作数据源大致需要以下几个步骤:
(1)与数据源建立连接。
(2)执行SQL语句。
(3)检索SQL执行结果。
(4)关闭连接。
后面的章节中会详细地介绍每个步骤以及需要使用到的JDBC接口和实现类。
2.1.1 建立数据源连接
JDBC API中定义了Connection接口,用来表示与底层数据源的连接。JDBC应用程序可以使用以下两种方式获取Connection对象。
(1)DriverManager:这是一个在JDBC 1.0规范中就已经存在、完全由JDBC API实现的驱动管理类。当应用程序第一次尝试通过URL连接数据源时,DriverManager会自动加载CLASSPATH下所有的JDBC驱动。DriverManager类提供了一系列重载的getConnection()方法,用来获取Connection对象,例如:
(2)DataSource:这个接口是在JDBC 2.0规范可选包中引入的API。它比DriverManager更受欢迎,因为它提供了更多底层数据源相关的细节,而且对应用来说,不需要关注JDBC驱动的实现。一个DataSource对象的属性被设置后,它就代表一个特定的数据源。当DataSource实例的getConnection()方法被调用后,DataSource实例就会返回一个与数据源建立连接的Connection对象。在应用程序中修改DataSource对象的属性后,就可以通过DataSource对象获取指向不同数据源的Connection对象。同样,数据源的具体实现修改后,不需要修改应用程序代码。
需要注意的是,JDBC API中只提供了DataSource接口,没有提供DataSource的具体实现,DataSource具体的实现由JDBC驱动程序提供。另外,目前一些主流的数据库连接池(例如DBCP、C3P0、Druid等)也提供了DataSource接口的具体实现。
MyBatis框架中提供了DataSource接口的实现。下面是一个使用MyBatis的DataSource实例获取Connection对象的案例:
完整代码可参考随书源码mybatis-chapter02项目的com.blog4java.jdbc.Example02案例。
另外,MyBatis框架还提供了DataSource的工厂,即DataSourceFactory。我们可以使用工厂模式创建DataSource实例,例如:
完整代码可参考随书源码mybatis-chapter02项目的com.blog4java.jdbc.Example03案例。
JDBC API中定义了两个DataSource接口比较重要的扩展,用于支撑企业级应用。这两个接口分别为:
- ConnectionPoolDataSource 支持缓存和复用Connection对象,这样能够在很大程度上提升应用性能和伸缩性。
- XADataSource 该实例返回的Connection对象能够支持分布式事务。
注意
JDBC 4.0之前的版本,创建Connection对象之前,应用程序需要显式地加载驱动类,具体代码如下:
2.1.2 执行SQL语句
通过2.1.1节的学习,我们了解到Connection是JDBC对数据源连接的抽象,一旦建立了连接,使用JDBC API的应用程序就可以对目标数据源执行查询和更新操作。JDBC API提供了访问SQL:2003规范中常用的实现特性,因为不同的JDBC厂商对这些特性的支持程度各不相同,所以JDBC API中提供了一个DatabaseMetadata接口,应用程序可以使用DatabaseMetadata的实例来确定目前使用的数据源是否支持某一特性。JDBC API中还定义了转意语法,让我们使用JDBC应用程序能够访问JDBC厂商提供的非标准的特性。
获取到JDBC中的Connection对象之后,我们可以通过Connection对象设置事务属性,并且可以通过Connection接口中提供的方法创建Statement、PreparedStatement或者CallableStatement对象。
Statement接口可以理解为JDBC API中提供的SQL语句的执行器,我们可以调用Statement接口中定义的executeQuery()方法执行查询操作,调用executeUpdate()方法执行更新操作,另外还可以调用executeBatch()方法执行批量处理。当我们不知道SQL语句的类型时,例如编写一个通用的方法,既可以执行查询语句,又可以执行更新语句,此时可以调用execute()方法进行统一的操作,然后通过execute()方法的返回值来判断SQL语句类型。最后可以通过Statement接口提供的getResultSet()方法来获取查询结果集,或者通过getUpdateCount()方法来获取更新操作影响的行数。
下面是一个通过Statement执行查询操作的案例:
2.1.3 处理SQL执行结果
SQL语句执行完毕后,通常我们需要获取执行的结果,例如执行一条SELECT语句后,我们需要获取查询的结果,执行UPDATE或者INSERT语句后,我们需要通过影响的记录数来确定是否更新成功。JDBC API中提供了ResultSet接口,该接口的实现类封装SQL查询的结果,我们可以对ResultSet对象进行遍历,然后通过ResultSet提供的一系列getXXX()方法(例如getString)获取查询结果集。
2.1.4 使用JDBC操作数据库
前面介绍了使用JDBC操作数据库的几个关键步骤,本节我们以一个具体的案例来介绍JDBC API的使用,案例代码如下:
完整代码读者可参考随书源码mybatis-chapter02项目的com.blog4java.jdbc.Example01类。如上面的代码所示,我们首先通过JDBC API中提供的DriverManager类获取一个表示数据库连接的Connection对象,然后调用Connection对象的createStatement()方法获取用于执行SQL语句的Statement对象。Statement对象是SQL语句的执行器,有了Statement对象后,我们就可以调用Statement对象的executeQuery()方法执行一个SQL查询操作了。该方法会返回一个ResultSet对象,ResultSet对象代表查询操作的结果集,我们可以调用ResultSet对象的getMetaData()方法获取结果集元数据信息。该方法返回一个ResultSetMetaData对象,我们可以通过ResultSetMetaData对象获取结果集中所有的字段名称、字段数量、字段数据类型等信息。在上面的案例代码中,我们通过ResultSetMetaData对象获取结果集所有字段名称,然后对结果集进行遍历,在控制台中打印所有查询结果。
运行上面的代码,会查询出user表中的所有记录并输出到控制台,控制台输出结果如下:
到此为止,我们使用JDBC API完成了一个完整的数据库查询功能。JDBC API的使用比较简单,遵循上面几个特定的步骤即可。2.2节我们继续学习JDBC API中的一些类和接口。