|
概述
LOB 代表大对象数据,包括 BLOB 和 CLOB 两种类型,前者用于存储大块的二进制数据,如图片数据,视频数据等,而后者用于存储长文本数据,如论坛的帖子内容,产品的详细描述等。值得注意的是:在不 同的数据库中,大对象对应的字段类型是不尽相同的,如 DB2 对应 BLOB/CLOB,MySql 对应 BLOB/LONGTEXT,SqlServer 对应 IMAGE/TEXT。需要指出的是,有些数据库的大对象类型可以象简单类型一样访问,如 MySql 的 LONGTEXT 的操作方式和 VARCHAR 类型一样。在一般情况下, LOB 类型数据的访问方式不同于其它简单类型的数据,我们经常会以流的方式操作 LOB 类型的数据。此外,LOB 类型数据的访问不是线程安全的,需要为其单独分配相应的数据库资源,并在操作完成后释放资源。最后,Oracle 9i 非常有个性地采用非 JDBC 标准的 API 操作 LOB 数据。所有这些情况给编写操作 LOB 类型数据的程序带来挑战,Spring 在 org.springframework.jdbc.support.lob 包中为我们提供了相应的帮助类,以便我们轻松应对这头拦路虎。
Spring 大大降低了我们处理 LOB 数据的难度。首先,Spring 提供了 NativeJdbcExtractor 接口,您可以在不同环境里选择相应的实现类从数据源中获取本地 JDBC 对象;其次,Spring 通过 LobCreator 接口取消了不同数据厂商操作 LOB 数据的差别,并提供了创建 LobCreator 的 LobHandler 接口,您只要根据底层数据库类型选择合适的 LobHandler 进行配置即可。
本 文将详细地讲述通过 Spring JDBC 插入和访问 LOB 数据的具体过程。不管是以块的方式还是以流的方式,您都可以通过 LobCreator 和 LobHandler 方便地访问 LOB 数据。对于 ORM 框架来说,JPA 拥有自身处理 LOB 数据的配置类型,Spring 为 Hibernate 和 iBatis 分别提供了 LOB 数据类型的配置类,您仅需要使用这些类进行简单的配置就可以像普通类型一样操作 LOB 类型数据。
本地 JDBC 对象
当 您在 Web 应用服务器或 Spring 中配置数据源时,从数据源中返回的数据连接对象是本地 JDBC 对象(如 DB2Connection、OracleConnection)的代理类,这是因为数据源需要改变数据连接一些原有的行为以便对其进行控制:如调用 Connection#close() 方法时,将数据连接返回到连接池中而非将其真的关闭。
在访问 LOB 数据时,根据数据库厂商的不同,可能需要使用被代理前的本地 JDBC 对象(如 DB2Connection 或 DB2ResultSet)特有的 API。为了从数据源中获取本地 JDBC 对象, Spring 定义了 org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor 接口并提供了相应的实现类。NativeJdbcExtractor 定义了从数据源中抽取本地 JDBC 对象的若干方法:
| 方法 |
说明 |
Connection getNativeConnection(Connection con) |
获取本地 Connection 对象 |
Connection getNativeConnectionFromStatement(Statement stmt) |
获取本地 Statement 对象 |
PreparedStatement getNativePreparedStatement(PreparedStatement ps) |
获取本地 PreparedStatement 对象 |
ResultSet getNativeResultSet(ResultSet rs) |
获取本地 ResultSet 对象 |
CallableStatement getNativeCallableStatement(CallableStatement cs) |
获取本地 CallableStatement 对象 |
有些简单的数据源仅对 Connection 对象进行代理,这时可以直接使用 SimpleNativeJdbcExtractor 实现类。但有些数据源(如 Jakarta Commons DBCP)会对所有的 JDBC 对象进行代理,这时,就需要根据具体的情况选择适合的抽取器实现类了。下表列出了不同数据源本地 JDBC 对象抽取器的实现类:
| 数据源类型 |
说明 |
| WebSphere 4 及以上版本的数据源 |
org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor |
| WebLogic 6.1+ 及以上版本的数据源 |
org.springframework.jdbc.support.nativejdbc.WebLogicNativeJdbcExtractor |
| JBoss 3.2.4 及以上版本的数据源 |
org.springframework.jdbc.support.nativejdbc.JBossNativeJdbcExtractor |
| C3P0 数据源 |
org.springframework.jdbc.support.nativejdbc.C3P0NativeJdbcExtractor |
| DBCP 数据源 |
org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor |
| ObjectWeb 的 XAPool 数据源 |
org.springframework.jdbc.support.nativejdbc.XAPoolNativeJdbcExtractor |
下面的代码演示了从 DBCP 数据源中获取 DB2 的本地数据库连接 DB2Connection 的方法:
清单 1. 获取本地数据库连接
在 ① 处我们通过 DataSourceUtils 获取当前线程绑定的数据连接,为了使用线程上下文相关的事务,通过 DataSourceUtils 从数据源中获取连接是正确的做法,如果直接通过 dateSource 获取连接,则将得到一个和当前线程上下文无关的数据连接实例。
JdbcTemplate 可以在配置时注入一个本地 JDBC 对象抽取器,要使代码 清单 1 正确运行,我们必须进行如下配置:
清单 2. 为 JdbcTemplate 装配本地 JDBC 对象抽取器
在获取 DB2 的本地 Connection 实例后,我们就可以使用该对象的一些特有功能了,如使用 DB2Connection 的特殊 API 对 LOB 对象进行操作。
LobCreator
虽然 JDBC 定义了两个操作 LOB 类型的接口:java.sql.Blob 和 java.sql.Clob,但有些厂商的 JDBC 驱动程序并不支持这两个接口。为此,Spring 定义了一个独立于 java.sql.Blob/Clob 的 LobCreator 接口,以统一的方式操作各种数据库的 LOB 类型数据。因为 LobCreator 本身持有 LOB 所对应的数据库资源,所以它不是线程安全的,一个 LobCreator 只能操作一个 LOB 数据。
为了方便在 PreparedStatement 中使用 LobCreator,您可以直接使用 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法。下面对 LobCreator 接口中的方法进行简要说明:
| 方法 |
说明 |
void close() |
关闭会话,并释放 LOB 资源 |
void setBlobAsBinaryStream(PreparedStatement ps, int paramIndex, InputStream contentStream, int contentLength) |
通过流填充 BLOB 数据 |
void setBlobAsBytes(PreparedStatement ps, int paramIndex, byte[] content) |
通过二进制数据填充 BLOB 数据 |
void setClobAsAsciiStream(PreparedStatement ps, int paramIndex, InputStream asciiStream, int contentLength) |
通过 Ascii 字符流填充 CLOB 数据 |
void setClobAsCharacterStream(PreparedStatement ps, int paramIndex, Reader characterStream, int contentLength) |
通过 Unicode 字符流填充 CLOB 数据 |
void setClobAsString(PreparedStatement ps, int paramIndex, String content) |
通过字符串填充 CLOB 数据 |
LobHandler
LobHandler 接口为操作 BLOB/CLOB 提供了统一访问接口,而不管底层数据库究竟是以大对象的方式还是以一般数据类型的方式进行操作。此外,LobHandler 还充当了 LobCreator 的工厂类。
大 部分数据库厂商的 JDBC 驱动程序(如 DB2)都以 JDBC 标准的 API 操作 LOB 数据,但 Oracle 9i 及以前的 JDBC 驱动程序采用了自己的 API 操作 LOB 数据,Oracle 9i 直接使用自己的 API 操作 LOB 数据,且不允许通过 PreparedStatement 的 setAsciiStream()、setBinaryStream()、setCharacterStream() 等方法填充流数据。Spring 提供 LobHandler 接口主要是为了迁就 Oracle 特立独行的作风。所以 Oracle 必须使用 OracleLobHandler 实现类,而其它的数据库统一使用 DefaultLobHandler 就可以了。Oracle 10g 改正了 Oracle 9i 这个异化的风格,终于天下归一了,所以 Oracle 10g 也可以使用 DefaultLobHandler。 下面,我们来看一下 LobHandler 接口的几个重要方法:
| 方法 |
说明 |
InputStream getBlobAsBinaryStream(ResultSet rs, int columnIndex) |
从结果集中返回 InputStream,通过 InputStream 读取 BLOB 数据 |
byte[] getBlobAsBytes(ResultSet rs, int columnIndex) |
以二进制数据的方式获取结果集中的 BLOB 数据; |
InputStream getClobAsAsciiStream(ResultSet rs, int columnIndex) |
从结果集中返回 InputStream,通过 InputStreamn 以 Ascii 字符流方式读取 BLOB 数据 |
Reader getClobAsCharacterStream(ResultSet rs, int columnIndex) |
从结果集中获取 Unicode 字符流 Reader,并通过 Reader以Unicode 字符流方式读取 CLOB 数据 |
String getClobAsString(ResultSet rs, int columnIndex) |
从结果集中以字符串的方式获取 CLOB 数据 |
LobCreator getLobCreator() |
生成一个会话相关的 LobCreator 对象 |
在 Spring JDBC 中操作 LOB 数据
插入 LOB 数据
假设我们有一个用于保存论坛帖子的 t_post 表,拥有两个 LOB 字段,其中 post_text 是 CLOB 类型,而 post_attach 是 BLOB 类型。下面,我们来编写插入一个帖子记录的代码:
清单 3. 添加 LOB 字段数据
首先,我们在 PostJdbcDao 中引入了一个 LobHandler 属性,如 ① 所示,并通过 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法完成插入 LOB 数据的操作。我们通过匿名内部类的方式定义 LobCreatingPreparedStatementCallback 抽象类的子类,其构造函数需要一个 LobHandler 入参,如 ② 所示。在匿名类中实现了父类的抽象方法 setValues(PreparedStatement ps,LobCreator lobCreator),在该方法中通过 lobCreator 操作 LOB 对象,如 ③、④ 所示,我们分别通过字符串和二进制数组填充 BLOB 和 CLOB 的数据。您同样可以使用流的方式填充 LOB 数据,仅需要调用 lobCreator 相应的流填充方法即可。
我们需要调整 Spring 的配置文件以配合我们刚刚定义的 PostJdbcDao。假设底层数据库是 Oracle,可以采用以下的配置方式:
清单 4. Oracle 数据库的 LobHandler 配置
大家可能已经注意到 nativeJdbcExtractor 和 oracleLobHandler Bean 都设置为 lazy-init="true",这是因为 nativeJdbcExtractor 需要通过运行期的反射机制获取底层的 JDBC 对象,所以需要避免在 Spring 容器启动时就实例化这两个 Bean。
LobHandler 需要访问本地 JDBC 对象,这一任务委托给 NativeJdbcExtractor Bean 来完成,因此我们在 ① 处为 LobHandler 注入了一个 nativeJdbcExtractor。最后,我们把 lobHandler Bean 注入到需要进行 LOB 数据访问操作的 PostJdbcDao 中,如 ② 所示。
如果底层数据库是 DB2、SQL Server、MySQL 等非 Oracle 的其它数据库,则只要简单配置一个 DefaultLobHandler 就可以了,如下所示: 共2页: 上一页 1 [2] 下一页
|