marionette是什么意思ionette在线翻译读音-高考资料


2023年4月3日发(作者:2016年元宵节)

Spring让L古代诗歌大全100首 OB数据操作变得简单易行

陈雄华(quickselect@),技术总监,宝宝淘网络科技有限公司

简介:本文讲解了在Spring中处理LOB数据的原理和方法,对于Spring

JDBC以及Spring所集成的第三方ORM框架(包括JPA、Hibernate和

iBatis)如何处理LOB数据进行了阐述。

标记本文!

发布日期:2007年7月30日

级别:中级

访问情况512次浏览

建议:0(添加评论)

平均分(共0个评分)

概述

LOB代表大对象数据,包括BLOB和四时田园杂兴其二十五翻译 CLOB两种类型,前者用于存储大块的二进

制数据,如图片数据,视频数据等,而后者用于存储长文本数据,如论坛的帖子

内容,产品的详细描述等。值得注意的是:在不同的数据库中,大对象对应的字

段类型是不尽相同的,如DB2对应BLOB/CLOB,MySql对应BLOB/LONGTEXT,

SqlServer对应IMAGE/TEXT。需要指出的是,有些数据库的大对象类型可以象

简单类型一样访问,如MySql的LONGTEXT的操作方式和VARCHAR类型一样。

在一般情况下,LOB类型数据的访问方式不同于其它简单类型的数据,我们经

常会以流的方式操作LOB类型的数据。此外,LOB类型数据的访问不是线程安

全的,需要为其单独分配相应的数据库资源,并在操作完成后释放资源。最后,

Oracle9i非常有个性地采用非JDBC标准的API操作LOB数据。所有这些情

况给编写操作LOB类型数据的程序带来挑战,Spring在

包中为我们提供了相应的帮助类,以

便我们轻松应对这头拦路虎。

Spring大大降低了我们处理LOB数据的难度。首先,Spring提供了

NativeJdbcExtractor接口,您可以在不同环境里选择相应的实现类从数据源中

获取本地JDBC对象;其次,Spring通过LobCreator接口取消了不同数据厂

商操作LOB数据的差别,并提供了创建LobCreator的LobHandler接口,您

只要根据底层数据库类型选择合适的LobHandler进行配置即可。

本文将详细地讲述通过SpringJDBC插入和访问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定义了

JdbcExtractor接口

并提供了相应的实现类。NativeJdbcExtractor定义了从数据源中抽取本地

JDBC对象的若干方法:

方法说明

ConnectiongetNativeConnection(Connectioncon)

获取本地Connection

对象

Connection

getNativeConnectionFromStatement(Statementstmt)

获取本地Statement

对象

PreparedStatement

getNativePreparedStatement(PreparedStatementps)

获取本地

PreparedStatement对

ResultSetgetNativeResultSet(ResultSetrs)

获取本地ResultSet

对象

CallableStatement

getNativeCallableStatement(CallableStatementcs)

获取本地

CallableStatement对

有些简单的数据源仅对Connection对象进行代理,这时可以直接使用

SimpleNativeJdbcExtractor实现类。但有些数据源(如JakartaCommonsDBCP)

会对所有的JDBC对象进行代理,这时,就需要根据具体的情况选择适合的抽取

器实现类了。下表列出了不同数据源本地JDBC对象抽取器的实现类:

数据源

类型

说明

ereNativeJd

re4及

以上版

本的数

据源

bcExtractor

WebLogi

c6.1+

及以上

版本的

数据源

icNativeJdb

cExtractor

JBoss

3.2.4

及以上

版本的

数据源

ativeJdbcEx

tractor

C3P0数

据源

jdbc.C3P0NativeJdbcExt

ractor

DBCP数

据源

sDbcpNative

JdbcExtractor

ObjectW

eb的

XAPool

数据源

NativeJdbcE

xtractor

下面的代码演示了从DBCP数据源中获取DB2的本地数据库连接

DB2Connection的方法:

清单1.获取本地数据库连接

;

tion;

2Connection;

oSupport;

urceUtils;

publicclassPostJdbcDaoextendsJdbcDaoSupportimplementsPostDao{

publicvoidgetNativeConn(){

try{

Connectionconn=

nection(getJdbcTemplate()

.getDataSource());①使用DataSourceUtils从模板类中获取连

②使用模板类的本地JDBC抽取器获取本地的Connection

conn=

getJdbcTemplate().getNativeJdbcExtractor().getNativeConnection(conn);

DB2Connectiondb2conn=(DB2Connection)conn;③这时可以强制进

行类型转换了

}catch(Exceptione){

tackTrace();

}

}

}

在①处我们通过DataSourceUtils获取当前线程绑定的数据连接,为了使用

线程上下文相关的事务,通过DataSourceUtils从数据源中获取连接是正确的

做法,如果直接通过dateSource获取连接,则将得到一个和当前线程上下文无

关的悠然见南山的见字赏析 数据连接实例。

JdbcTemplate可以在配置时注入一个本地JDBC对象抽取器,要使代码清单1

正确运行,我们必须进行如下配置:

清单2.为JdbcTemplate装配本地JDBC对象人闲桂花落夜静春山空是什么意思 抽取器

destroy-method=\"close\">

value=\"${ClassName}\"/>

①定义DBCP数据源的JDBC本地对象抽取器

class=\"sDbcpNativeJ

dbcExtractor\"

lazy-init=\"true\"/>

class=\"mplate\">

②设置抽取器

在获取DB2的本地Connection实例后,我们就可以使用该对象的一些特有功

能了,如使用DB2Connection的特殊API对LOB对象进行操作。

LobCreator

虽然JDBC定义了两个操作LOB类型的接口:和

,但有些厂商的JDBC驱动程序并不支持这两个接口。为此,

Spring定义了一个独立于/Clob的LobCreator接口,以统一

的方式操作各种数据库的LOB类型数据。因为LobCreator本身持有LOB所对

应的数据库资源,所以它不是线程安全的,一个LobCreator只能操作一个LOB

数据。

为了方便在PreparedStatement中使用LobCreator,您可以直接使用

JdbcTemplate#execute(String

sql,AbstractLobCreatingPreparedStatementCallbacklcpsc)方法。下面对

LobCreator接口中的方法进行简要说明:

方法说明

voidclose()

关闭会话,并

释放LOB资

voidsetBlobAsBinaryStream(PreparedStatementps,int

paramIndex,InputStreamcontentStream,intcontentLength)

通过流填充

BLOB数据

voidsetBlobAsBytes(PreparedStatementps,intparamIndex,

byte[]content)

通过二进制数

据填充BLOB

数据

voidsetClobAsAsciiStream(PreparedStatementps,int

paramIndex,InputStreamasciiStream,intcontentLength)

通过Ascii

字符流填充

CLOB数据

voidsetClobAsCharacterStream(PreparedStatementps,int

paramIndex,ReadercharacterStream,intcontentLength)

通过Unicode

字符流填充

CLOB数据

voidsetClobAsString(PreparedStatementps,intparamIndex,

Stringcontent)

通过字符串填

充CLOB数据

LobHandler

LobHandler接口为操作BLOB/CLOB提供了统一访问接口,而不管底层数据库究

竟是以大对象的方式还是以一般数据类型的方式进行操作。此外,LobHandler还

充当了LobCreator的工厂类。

大部分数据库厂商的JDBC驱动程序(如DB2)都以JDBC标准的API操作LOB

数据,但Oracle9i及以前的JDBC驱动程序采用了自己的API操作LOB数

据,Oracle9i直接使用自己的API操作LOB数据,且不允许通过

PreparedStatement的拼音龘靐齾龗鱻爩麤灪爨癵籱麣纞虋讟钃鸜麷 的setAsciiStream()、setBinaryStream()、

setCharacterStream()等方法填充流数据。Spring提供LobHandler接口主要

是为了迁就Oracle特立独行的作风。所以Oracle必须使用

OracleLobHandler实现类,而其它的数据库统一使用DefaultLobHandler就可

以了。Oracle10g改正了Oracle9i这个异化的风格,终于天下归一了,所以

Oracle10g也可以使用DefaultLobHandler。下面,我们来看一下LobHandler

接口的几个重要方法:

方法说明

InputStream

getBlobAsBinaryStream(ResultSetrs,int

columnIndex)

从结果集中返回InputStream,

通过InputStream读取BLOB

数据

byte[]getBlobAsBytes(ResultSetrs,int

columnIndex)

以二进制数据的方式获取结果集

中的BLOB数据;

InputStream

getClobAsAsciiStream(ResultSetrs,int

columnIndex)

从结果集中返回InputStream,

通过InputStreamn以Ascii

字符流方式读取BLOB数据

Reader

getClobAsCharacterStream(ResultSetrs,

intcolumnIndex)

从结果集中获取Unicode字符

流Reader,并通过Reader以

Unicode字符流方式读取CLOB

数据

StringgetClobAsString(ResultSetrs,int

columnIndex)

从结果集中以字符串的方式获取

CLOB数据

LobCreatorgetLobCreator()

生成一个会话相关的

LobCreator对象

回页首

在SpringJDBC中操作LOB数据

插入LOB数据

假设我们有一个用于保存论坛帖子的t_post表,拥有两个LOB字段,其中

post_text是CLOB类型,而post_attach是BLOB类型。下面,我们来编写

插入一个帖子记录的代码:

清单3.添加LOB字段数据

;

edStatement;

eption;

import

ctLobCreatingPreparedStat

ementCallback;

ator;

dler;

publicclassPostJdbcDaoextendsJdbcDaoSupportimplementsPostDao{

privateLobHandlerlobHandler;①定义LobHandler属性

publicLobHandlergetLobHandler(){

returnlobHandler;

}

publicvoidsetLobHandler(山重水复疑无路的寓意 LobHandlerlobHandler){

dler=lobHandler;

}

publicvoidaddPost(finalPostpost){

Stringsql=\"INSERTINTO

t_post(post_id,user_id,post_text,post_attach)\"

+\"VALUES(?,?,?,?)\";

getJdbcTemplate().execute(sql,

new

AbstractLobCreatingPreparedStatementCallback(dler){②

protectedvoidsetValues(PreparedStatementps,LobCreator

lobCreator)

throwsSQLException{

(1,1);

(2,rId());

③设置CLOB字段

bAsString(ps,3,tText());

④设置BLOB字段

bAsBytes(ps,4,tAttach());

}

});

}

}

首先,我们在PostJdbcDao中引入了一个LobHandler属性,如①所示,并

通过JdbcTemplate#execute(String

sql,AbstractLobCreatingPr朗诵配乐纯音乐mp3下载 eparedStatementCallbacklcpsc)方法完成插入

LOB数据的操作。我们通过匿名内部类的方式定义

LobCreatingPreparedStatementCallback抽象类的子类,其构造函数需要一个

LobHandler入参,如②所示。在匿名类中实现了父类的抽象方法

setValues(PreparedStatementps,LobCreatorlobCreator),在该方法中通过

lobCreator操作LOB对象,如③、④所示,我们分别通过字符绛绡缕薄冰肌莹 串和二进制数

组填充BLOB和CLOB的数据。您同样可以使用流的方式填充LOB数据,仅需

要调用lobCreator相应的流填充方法即可。

我们需要调整Spring的配置文件以配合我们刚刚定义的PostJdbcDao。假设底

层数据库是Oracle,可以采用以下的配置方式:

清单数据库的LobHandler配置

class=\"sDbcpNativeJ

dbcExtractor\"

lazy-init=\"true\"/>

class=\"LobHandler\"

lazy-init=\"true\">

设置本地Jdbc对象抽取器

②设置LOB处

理器

大家可能已经注意到nativeJdbcExtractor和oracleLobHandlerBean都设

置为lazy-init=\"true\",这是因为nativeJdbcExtractor需要通过运行期的反

射机制获取底层的JDBC对象,所以需要避免在Spring容器启动时就实例化这

两个Bean。

LobHandler需要访问本地JDBC对象,这一任务委托给NativeJdbcExtractor

Bean来完成,因此我们在①处为LobHandler注入了一个

nativeJdbcExtractor。最后,我们把lobHandlerBean注入到需要进行LOB数

据访问操作的PostJdbcDao中,如②所示。

如果底层数据库是DB2、SQLServer、MySQL等非Oracle的其它数据库,则只

要简单配置一个DefaultLobHandler就可以了,如下所示:

清单5.一般数据库LobHandler的配置

class=\"tLobHandler\"

lazy-init=\"true\"/>

DefaultLobHandler只是简单地代理标准JDBC的PreparedStatement和

ResultSet对象,由于并不需要访问数据库驱动本地的JDBC对象,所以它不需

要NativeJdbcExtractor的帮助。您可以通过以下的代码测试PostJdbcDao的

addPost()方法:

清单6.测试PostJdbcDao的addPost()方法

;

athResource;

import

ctDependencyInjectionSpringContextTest

s;

pyUtils;

o;

;

publicclassTestPostJdbcDaoextends

AbstractDependencyInjectionSpringContextTests{

privatePostDaopostDao;

publicvoidsetPostDao(PostDaopostDao){

o=postDao;

}

protectedString[]getConfigLocations(){

returnnewString[]{\"classpath:\"};

}

publicvoidtestAddPost()throwsThrowable{

Postpost=newPost();

tId(1);

rId(2);

ClassPathResourceres=newClassPathResource(\"\");①获取

图片资源

byte[]mockImg=ByteArray(e());②

读取图片文件的数据

tAttach(mockImg);

tText(\"测试帖子的内容\");

t(post);

}

}

这里,有几个知识点需要稍微解释一下:

AbstractDependencyInjectionSpringContextTests是Spring专门为测试提

供的类,它能够直接从IoC容器中装载Bean。此外,我们使用了

ClassPathResource加载图片资源,并通过FileCopyUtils读取文件的数据。

ClassPathResource和FileCopyUtils都是Spring提供的非常实用的工具

类。

以块数据方式读取LOB数据

您可以直接用数据块的方式读取LOB数据:用String读取CLOB字段的数据,

用byte[]读取BLOB字段的数据。在PostJdbcDao中添加一个getAttachs()

方法,以便获取某一用户的所有带附件的帖子:

清单7.以块数据访问LOB数据

publicListgetAttachs(finalintuserId){

Stringsql=\"SELECTpost_id,post_attachFROMt_post“+

“whereuser_id=?andpost_attachisnotnull\";

returngetJdbcTemplate().query(

sql,newObject[]{userId},

newRowMapper(){

publicObjectmapRow(ResultSetrs,introwNum)throws

SQLException{

intpostId=(1);

①以二进制数组方式获取BLOB数据。

byte[]attach=bAsBytes(rs,2);

Postpost=newPost();

tId(postId);

tAttach(attach);

returnpost;

}

});

}

通过JdbcTemplate的Listquery(Stringsql,Object[]args,RowMapper

rowMapper)接口处理行数据的映射。在RowMapper回调的mapRow()接口方法

中,通过LobHandler以byte[]获取BLOB字段的数据。

以流数据方式读取LOB数据

由于LOB数据可能很大(如100M),如果直接以块的方式操作LOB数据,需

要消耗大量的内存资源,对应用程序整体性能产生巨大的冲击。对于体积很大的

LOB数据,我们可以使用流的方式进行访问,减少内存的占用。JdbcTemplate为

此提供了一个Objectquery(Stringsql,Object[]args,ResultSetExtractor

rse)方法,ResultSetExtractor接口拥有一个处理流数据的抽象类

ctLobStreamingResultSetEx

tractor,可以通过扩展此类用流的方式操作LOB字段的数据。下面我们为

PostJdbcDao添加一个以流的方式获取某个帖子附件的方法:

清单8.以流方式访问LOB数据

publicvoidgetAttach(finalintpostId,finalOutputStreamos){①用于

接收LOB数据的输出流

Stringsql=\"SELECTpost_attachFROMt_postWHEREpost_id=?\";

getJdbcTemplate().query(

sql,newObject[过年对联 ]{postId},

newAbstractLobStreamingResultSetExtractor(){②匿名内部类

③处理未找到数据行的情况

protectedvoidhandleNoRowFound()throwsLobRetrievalFailureException{

n(\"NotFoundresult!\");

}

④以流的方式处理LOB字段

publicvoidstreamData(ResultSetrs)throwsSQLException,

IOException{

InputStreamis=bAsBinaryStream(rs,1);

if(is!=null){

(is,os);

}

}

}

);

}

通过扩展AbstractLobStreamingResultSetExtractor抽象类,在

streamData(ResultSetrs)方法中以流的方式读取LOB字段数据,如④所

示。这里我们又利用到了Spring的工具类FileCopyUtils将输入流的数据拷

贝到输出流中。在getAttach()方法中通过入参OutputStreamos接收LOB

的数据,如①所示。您可以同时覆盖抽象类中的handleNoRowFound()方法,

定义未找到数据行时的处理逻辑。

回页首

在JPA中操作LOB数据

在JPA中LOB类型的持久化更加简单,仅需要通过特殊的LOB注释

(Annotation)就可以达到目的。我们对Post中的LOB属性类型进行注释:

清单9.注释LOB类型属性

;

;

;

;

@Entity(name=\"T_POST\")

publicclassPostimplementsSerializable{

@Lob①-1表示该属性是LOB类型的字段

@Basic(fetch=)①-2不采用延迟加载机制

@Column(name=\"POST_TEXT\",columnDefinition=\"LONGTEXTNOTNULL\")

①-3对应字段类型

privateStringpostText;

@Lob②-1表示该属性是LOB类型的字段

@Basic(fetch=)②-2采用延迟加载机制

@Column(name=\"POST_ATTACH\",columnDefinition=\"BLOB\")②-3对应字

段类型

privatebyte[]postAttach;

postText属性对应T_POST表的POST_TEXT字段,该字段的类型是LONTTEXT,

并且非空。JPA通过@Lob将属性标注为LOB类型,如①-1和②-1所示。

通过@Basic指定LOB类型数据的获取策略,表示非延迟加

载,而表示延迟加载,如①-2和②-2所示。通过@Column

的columnDefinition属性指定数据表对应的LOB字段类型,如①-3和②-3

所示。

关于JPA注释的更多信息,请阅读参考资源中的相关技术文章。

回页首

在Hibernate中操作LOB数据

提示

使用SpringJDBC时,我们除了可以按byte[]、String类型处理LOB数据外,

还可以使用流的方式操作LOB数据,当LOB数据体积较大时,流操作是唯一可

行的方式。可惜,Spring并未提供以流方式操作LOB数据的UserType(记得

Spring开发组成员认为在实现上存在难度)。不过,替

Spring完成了这件难事,读者可以通过这里了解到这个满足要求的

BlobInputStream类型。

Hibernate为处理特殊数据类型字段定义了一个接口:

pe。Spring在

t包中为BLOB和CLOB类型提

供了几个UserType的实现类。因此,我们可以在Hibernate的映射文件中直

接使用这两个实现类轻松处理LOB类型的数据。

BlobByteArrayType:将BLOB数据映射为byte[]类型的属性;

BlobStringType:将BLOB数据映射为String类型的属性;

BlobSerializableType:将BLOB数据映射为Serializable类型的属

性;

ClobStringType:将CLOB数据映射为String类型的属性;

下面我们使用Spring的UserType为Post配置Hibernate的映射文件,如

清单10所示:

清单数据映射配置

<?xmlversion=\"1.0\"encoding=\"UTF-8\"?>

DTD//EN\"

\"/\">

type=\"ringType\"/>①

对应CLOB字段

type=\"teArrayType\"/>

②BLOB字段

class=\"\"/>

postText为String类型的属性,对应数据库的CLOB类型,而postAttach为

byte[]类型的属性,对应数据库的BLOB类型。分别使用Spring所提供的相

应UserType实现类进行配置,如①和②处所示。

在配置好映射文件后,还需要在Spring配置文件中定义LOB数据处理器,让

SessionFactory拥有处理LOB数据的能力:

清单11.将LobHandler注入到SessionFactory中

class=\"tLobHandler\"

lazy-init=\"true\"/>

class=\"essionFactoryBean\">

①设置LOB处理器

在一般的数据库(如DB2)中,仅需要简单地使用

HibernateTemplate#save(Objectentity)等方法就可以正确的保存LOB数据

了。如果是Oracle9i数据库,还需要配置一个本地JDBC抽取器,并使用特

定的LobHandler实现类,如清单4所示。

使用LobHandler操作LOB数据时,需要在事务环境下才能工作,所以必须事

先配置事务管理器,否则会抛出异常。

回页首

在iBatis中操作LOB数据

iBatis为处理不同类型的数据定义了一个统一的接口:

ndler。这个接口类似于Hibernate的

UserType。iBatis本身拥有该接口的众多实现类,如LongTypeHandler、

DateTypeHandler等,但没有为LOB类型提供对应的实现类。Spring在

t包中为我们提供了几个处理LOB

类型的TypeHandler实现类:

BlobByteArrayTypeHandler:将BLOB数据映射为byte[]类型;

BlobSerializableTypeHandler:将BLOB数据映射为Serializable类

型的对象;

ClobStringTypeHandler:将CLOB数据映射为String类型;

当结果集中包括LOB数据时,需要在结果集映射配置项中指定对应的Handler

类,下面我们采用Spring所提供的实现类对Post结果集的映射进行配置。

清单12.对LOB数据进行映射

<?xmlversion=\"1.0\"encoding=\"UTF-8\"?>

\"/dtd/\">

型数据

typeHandler=\"ringTypeHan

dler\"/>

BLOB类型数据

typeHandler=\"teArrayType

Handler\"/>

SELECTpost_id,user_id,post_text,post_attach,post_time

FROMt_postWHEREpost_id=#postId

#

INSERTINTOt_post(user_id,post_text,post_attach,post_time)

VALUES(#userId#,

#postText,handler=ringTy

peHandler#,③

#postAttach,handler=teAr

rayTypeHandler#,④

#postTime#)

提示

为每一个LOB类型字段分别指定处理器并不是一个好主意,iBatis允许在

配置文件中通过标签统一定义特殊类型

数据的处理器,如:

callback=\"ringTypeHandle

r\"/>

当iBatis引擎从结果集中读取或更改LOB类型数据时,都需要指定处理器。

我们在①和②处为读取LOB类型的数据指定处理器,相似的,在③和④

处为插入LOB类型的数据也指定处理器。

此外,我们还必须为SqlClientMap提供一个LobHandler:

清七年级下册语文电子书 单13.将LobHandler注入到SqlClientMap中

class=\"tLobHandler\"

lazy-init=\"true\"/>

class=\"ClientFactoryBean\">

①设置

LobHandler

value=\"classpath:com/baobaotao/dao/ibatis/\"

/>

处理LOB数据时,Spring要求在事务环境下工作,所以还必须配置一个事务管

理器。iBatis的事务管理器和SpringJDBC事务管理器相同,此处不再赘述。

回页首

小结

本文就Spring中如何操作LOB数据进行较为全面的讲解,您仅需简单地配置

LobHandler就可以直接在程序中象一般数据一样操作LOB数据了。对于ORM

框架来说,Spring为它们分别提供了支持类,您仅要使用相应的支持类进行配

置就可以了。因此您会发现在传统JDBC程序操作LOB头疼的问题将变得轻松

了许多。

更多推荐

extractor是什么意思ractor在线翻译读音