2.2 JDBC API中的类与接口
通过前面几节的学习,我们了解了使用JDBC API操作数据源的步骤,并以一个案例介绍了如何使用JDBC API操作关系型数据库。JDBC API中的内容远不止这些,本节我们就来全面地学习JDBC API中的一些类和接口。JDBC API由java.sql和javax.sql两个包构成,接下来会分两个小节对这两个包的内容进行详细介绍。
2.2.1 java.sql包详解
java.sql包中涵盖JDBC最核心的API,下面是java.sql包中的所有接口、枚举和类:
如上面的列表所示,java.sql包中的内容不多,大致可以分为数据类型接口、枚举类、驱动相关类和接口、异常类。
除这几部分外,剩下的就是作为Java开发人员需要掌握的API,主要包括下面几个接口:
这些接口都继承了java.sql.Wrapper接口。许多JDBC驱动程序提供超越传统JDBC的扩展,为了符合JDBC API规范,驱动厂商可能会在原始类型的基础上进行包装,Wrapper接口为使用JDBC的应用程序提供访问原始类型的功能,从而使用JDBC驱动中一些非标准的特性。
java.sql.Wrapper接口提供了两个方法,具体如下:
其中,unwrap()方法用于返回未经过包装的JDBC驱动原始类型实例,我们可以通过该实例调用JDBC驱动中提供的非标准的方法。
isWrapperFor()方法用于判断当前实例是否是JDBC驱动中某一类型的包装类型。
下面是unwrap()方法和isWrapperFor()方法的一个使用案例:
如上面的代码所示,Oracle数据库驱动中提供了一些非JDBC标准的方法,如果需要使用这些非标准的方法,则可以调用Wrapper接口提供的unwrap()方法获取Oracle驱动的原始类型,然后调用原始类型提供的非标准方法就可以访问Oracle数据库特有的一些特性了。
JDBC API中的Connection、Statement、ResultSet等接口都继承自Wrapper接口,这些接口都提供了对JDBC驱动原始类型的访问能力。
Connection、Statement、ResultSet之间的关系如图2-1所示。
图2-1 java.sql包核心类之间的关系
2.2.2 javax.sql包详解
javax.sql包中的类和接口最早是由JDBC 2.0版本的可选包提供的,这个可选包最早出现在J2SE 1.2版本中,这个包中的内容不多,主要包括下面几个类和接口:
JDBC 1.0中使用DriverManager类来产生一个与数据源连接的Connection对象。相对于DriverManager,JDBC 2.0提供的DataSource接口是一个更好的连接数据源的方式。
首先,应用程序不需要像使用DriverManager一样对加载的数据库驱动程序信息进行硬编码。开发人员可以选择通过JNDI注册这个数据源对象,然后在程序中使用一个逻辑名称来引用它,JNDI会自动根据我们给出的名称找到与这个名称绑定的DataSource对象。然后我们就可以使用这个DataSource对象来建立和具体数据库的连接了。
其次,使用DataSource接口的第二个优势体现在连接池和分布式事务上。连接池通过对连接的复用,而不是每次需要操作数据源时都新建一个物理连接来显著地提高程序的效率,适用于任务繁忙、负担繁重的企业级应用。
javax.sql.DataSource与java.sql.Connection之间的关系如图2-2所示。
图2-2 DataSource与Connection之间的关系
javax.sql包下还提供了一个PooledConnection接口。PooledConnection和Connection的不同之处在于,它提供了连接池管理的句柄。一个PooledConnection表示与数据源建立的物理连接,该连接在应用程序使用完后可以回收而不用关闭它,从而减少了与数据源建立连接的次数。
应用程序开发人员一般不会直接使用PooledConnection接口,而是通过一个管理连接池的中间层基础设施使用。当应用程序调用DataSource对象的getConnection()方法时,它返回一个Connection对象。但是当我们使用数据库连接池时(例如Druid),该Connection对象实际上是到PooledConnection对象的句柄,这是一个物理连接。连接池管理器(通常为应用程序服务器)维护所有的PooledConnection 对象资源。如果在池中存在可用的PooledConnection对象,则连接池管理器返回作为到该物理连接的句柄的Connection对象。如果不存在可用的PooledConnection对象,则连接池管理器调用ConnectionPoolDataSource对象的getConnection()方法创建新的物理连接。
连接池实现模块可以调用PooledConnection对象的addConnectionEventListener()将自己注册成为一个PooledConnection对象的监听者,当数据库连接需要重用或关闭的时候会产生一个ConnectionEvent对象,它表示一个连接事件,连接池实现模块将会得到通知。
javax.sql.PooledConnection与java.sql.Connection之间的关系如图2-3所示。
图2-3 连接池调用关系
另外,javax.sql包中还包含XADataSource、XAResource和XAConnection接口,这些接口提供了分布式事务的支持,具体由JDBC驱动来实现。更多分布式事务相关细节可参考JTA(Java Transaction API)规范文档。
XAConnection接口继承了PooledConnection接口,因此它具有所有PooledConnection的特性,我们可以调用XAConnection实例的getConnection()方法获取java.sql.Connection对象,它们与java.sql.Connection之间的关系如图2-4所示。
图2-4 JDBC分布式事务支持
JTA规范文档:http://download.oracle.com/otndocs/jcp/jta-1.1-spec-oth-JSpec/?submit=Download。
javax.sql包中还提供了一个RowSet接口,该接口继承自java.sql包下的ResultSet接口。RowSet用于为数据源和应用程序在内容中建立一个映射。RowSet对象可以建立一个与数据源的连接并在其整个生命周期中维持该连接,在这种情况下,该对象被称为连接的RowSet。RowSet对象还可以建立一个与数据源的连接,从其获取数据,然后关闭它,这种RowSet被称为非连接RowSet。非连接Rowset可以在断开时更改其数据,然后将这些更改写回底层数据源,不过它必须重新建立连接才能完成此操作。
相较于java.sql.ResultSet而言,RowSet的离线操作能够有效地利用计算机越来越充足的内存减轻数据库服务器的负担。由于数据操作都是在内存中进行的,然后批量提交到数据源,因此灵活性和性能都有了很大的提高。
RowSet默认是一个可滚动、可更新、可序列化的结果集,而且它作为一个JavaBean组件,可以方便地在网络间传输,用于两端的数据同步。通俗来讲,RowSet就相当于数据库表数据在应用程序内存中的映射,我们所有的操作都可以直接与RowSet对象交互。RowSet与数据库之间的数据同步,作为开发人员不需要关注。
javax.sql.RowSet与java.sql.ResultSet之间的关系如图2-5所示。
图2-5 RowSet与ResultSet之间的关系