科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道在 Spring JDBC 中操作 LOB 数据

在 Spring JDBC 中操作 LOB 数据

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在 Spring JDBC 中操作 LOB 数据

作者:niyk 来源:赛迪网技术社区 2007年11月3日

关键字: LOB JDBC Spring

  • 评论
  • 分享微博
  • 分享邮件
插入 LOB 数据 )q[)B\\H  
假设我们有一个用于保存论坛帖子的 t_post 表,拥有两个 LOB 字段,其中 post_text 是 CLOB 类型,而 post_attach 是 BLOB 类型。下面,我们来编写插入一个帖子记录的代码: g"*n% Q  
duh2L_d  
清单 3. 添加 LOB 字段数据 az<p\Oxb  
package com.baobaotao.dao.jdbc;…import java.sql.PreparedStatement;import java.sql.SQLException;import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;import org.springframework.jdbc.support.lob.LobCreator;import org.springframework.jdbc.support.lob.LobHandler;public class PostJdbcDao extends JdbcDaoSupport implements PostDao {  private LobHandler lobHandler; ① 定义 LobHandler 属性  public LobHandler getLobHandler() {    return lobHandler;  }  public void setLobHandler(LobHandler lobHandler) {    this.lobHandler = lobHandler;  }  public void addPost(final Post post) {        String sql = " INSERT INTO t_post(post_id,user_id,post_text,post_attach)"        + " VALUES(?,?,?,?)";    getJdbcTemplate().execute(sql,      new AbstractLobCreatingPreparedStatementCallback(this.lobHandler) { ②          protected void setValues(PreparedStatement ps,LobCreator lobCreator)                      throws SQLException {            ps.setInt(1, 1);              ps.setInt(2, post.getUserId());              ③ 设置 CLOB 字段            lobCreator.setClobAsString(ps, 3, post.getPostText());            ④ 设置 BLOB 字段            lobCreator.setBlobAsBytes(ps, 4, post.getPostAttach());          }        });  }…}[/pre]
C6@P6  
首先,我们在 PostJdbcDao 中引入了一个 LobHandler 属性,如 ① 所示,并通过 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法完成插入 LOB 数据的操作。我们通过匿名内部类的方式定义 LobCreatingPreparedStatementCallback 抽象类的子类,其构造函数需要一个 LobHandler 入参,如 ② 所示。在匿名类中实现了父类的抽象方法 setValues(PreparedStatement ps,LobCreator lobCreator),在该方法中通过 lobCreator 操作 LOB 对象,如 ③、④ 所示,我们分别通过字符串和二进制数组填充 BLOB 和 CLOB 的数据。您同样可以使用流的方式填充 LOB 数据,仅需要调用 lobCreator 相应的流填充方法即可。 -R[C"+N<  
我们需要调整 Spring 的配置文件以配合我们刚刚定义的 PostJdbcDao。假设底层数据库是 Oracle,可以采用以下的配置方式: ihiBg"@  
p32Y#f'  
清单 4. Oracle 数据库的 LobHandler 配置 y0TcHJ-2x  
…<bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"lazy-init="true"/><bean id="oracleLobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">  <property name="nativeJdbcExtractor" ref="nativeJdbcExtractor"/> ① 设置本地 Jdbc 对象抽取器</bean><bean id="postDao" class="com.baobaotao.dao.jdbc.PostJdbcDao">  <property name="lobHandler" ref="oracleLobHandler"/> ② 设置 LOB 处理器</bean>[/pre]
kMhG,8  
大家可能已经注意到 nativeJdbcExtractororacleLobHandler Bean 都设置为 lazy-init="true",这是因为 nativeJdbcExtractor 需要通过运行期的反射机制获取底层的 JDBC 对象,所以需要避免在 Spring 容器启动时就实例化这两个 Bean。 zqLZsLN-n  
LobHandler 需要访问本地 JDBC 对象,这一任务委托给 NativeJdbcExtractor Bean 来完成,因此我们在 ① 处为 LobHandler 注入了一个 nativeJdbcExtractor。最后,我们把 lobHandler Bean 注入到需要进行 LOB 数据访问操作的 PostJdbcDao 中,如 ② 所示。 3vIZr_# 0j  
如果底层数据库是 DB2、SQL Server、MySQL 等非 Oracle 的其它数据库,则只要简单配置一个 DefaultLobHandler 就可以了,如下所示: ;K;5tf\p  
-R|<(?s  
清单 5. 一般数据库 LobHandler 的配置 a8Q ,'W  
<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true"/><bean id="postDao" class="com.baobaotao.dao.jdbc.PostJdbcDao">  <property name="lobHandler" ref=" defaultLobHandler"/> <property name="jdbcTemplate" ref="jdbcTemplate" /></bean> [/pre]
1S.UR  
DefaultLobHandler 只是简单地代理标准 JDBC 的 PreparedStatement 和 ResultSet 对象,由于并不需要访问数据库驱动本地的 JDBC 对象,所以它不需要 NativeJdbcExtractor 的帮助。您可以通过以下的代码测试 PostJdbcDao 的 addPost() 方法: q_ \opA=4  
@),sOu=^,g  
清单 6. 测试 PostJdbcDao 的 addPost() 方法 {K{uqxa\n  
package com.baobaotao.dao.jdbc;import org.springframework.core.io.ClassPathResource;import org.springframework.test.AbstractDependencyInjectionSpringContextTests;import org.springframework.util.FileCopyUtils;import com.baobaotao.dao.PostDao;import com.baobaotao.domain.Post;public class TestPostJdbcDaoextends AbstractDependencyInjectionSpringContextTests {  private PostDao postDao;  public void setPostDao(PostDao postDao) {    this.postDao = postDao;  }  protected String[] getConfigLocations() {    return new String[]{"classpath:applicationContext.xml"};  }  public void testAddPost() throws Throwable{    Post post = new Post();    post.setPostId(1);    post.setUserId(2);    ClassPathResource res = new ClassPathResource("temp.jpg"); ① 获取图片资源    byte[] mockImg = FileCopyUtils.copyToByteArray(res.getFile());  ② 读取图片文件的数据    post.setPostAttach(mockImg);    post.setPostText("测试帖子的内容");    postDao.addPost(post);  }}[/pre]
~yc@;5v  
这里,有几个知识点需要稍微解释一下:AbstractDependencyInjectionSpringContextTests 是 Spring 专门为测试提供的类,它能够直接从 IoC 容器中装载 Bean。此外,我们使用了 ClassPathResource 加载图片资源,并通过 FileCopyUtils 读取文件的数据。ClassPathResourceFileCopyUtils 都是 Spring 提供的非常实用的工具类。 2s<>m-  
以块数据方式读取 LOB 数据 p5D*H[,  
您可以直接用数据块的方式读取 LOB 数据:用 String 读取 CLOB 字段的数据,用 byte[] 读取 BLOB 字段的数据。在 PostJdbcDao 中添加一个 getAttachs() 方法,以便获取某一用户的所有带附件的帖子: A>c`KLM  
yxd6!>y  
清单 7. 以块数据访问 LOB 数据 U(/Od(  
public List getAttachs(final int userId){  String sql = "SELECT post_id,post_attach FROM t_post “+“where user_id =? and post_attach is not null ";  return getJdbcTemplate().query(        sql,new Object[] {userId},        new RowMapper() {          public Object mapRow(ResultSet rs, int rowNum) throws SQLException {            int postId = rs.getInt(1);            ① 以二进制数组方式获取 BLOB 数据。            byte[] attach = lobHandler.getBlobAsBytes(rs, 2);            Post post = new Post();            post.setPostId(postId);            post.setPostAttach(attach);            return post;          }        });}[/pre]
+Pzuyq>L%'  
通过 JdbcTemplate 的 List query(String sql, Object[] args, RowMapper rowMapper) 接口处理行数据的映射。在 RowMapper 回调的 mapRow() 接口方法中,通过 LobHandler 以 byte[] 获取 BLOB 字段的数据。 @J L$Fi-vT  
以流数据方式读取 LOB 数据 m7tVFeh  
由于 LOB 数据可能很大(如 100M),如果直接以块的方式操作 LOB 数据,需要消耗大量的内存资源,对应用程序整体性能产生巨大的冲击。对于体积很大的 LOB 数据,我们可以使用流的方式进行访问,减少内存的占用。JdbcTemplate 为此提供了一个 Object query(String sql, Object[] args, ResultSetExtractor rse) 方法,ResultSetExtractor 接口拥有一个处理流数据的抽象类 org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor,可以通过扩展此类用流的方式操作 LOB 字段的数据。下面我们为 PostJdbcDao 添加一个以流的方式获取某个帖子附件的方法: cO$ A"  
`%67 'Gq  
清单 8. 以流方式访问 LOB 数据 "%Ct6pI2  
…public void getAttach(final int postId,final OutputStream os){ ① 用于接收 LOB 数据的输出流  String sql = "SELECT post_attach FROM t_post WHERE post_id=? ";  getJdbcTemplate().query(      sql, new Object[] {postId},      new AbstractLobStreamingResultSetExtractor() { ② 匿名内部类③ 处理未找到数据行的情况protected void handleNoRowFound() throws LobRetrievalFailureException {          System.out.println("Not Found result!");        }        ④ 以流的方式处理 LOB 字段        public void streamData(ResultSet rs) throws SQLException, IOException {          InputStream is = lobHandler.getBlobAsBinaryStream(rs, 1);          if (is != null) {            FileCopyUtils.copy(is, os);          }        }      }  );}[/pre]
:ZRUgn) i  
通过扩展 AbstractLobStreamingResultSetExtractor 抽象类,在 streamData(ResultSet rs) 方法中以流的方式读取 LOB 字段数据,如 ④ 所示。这里我们又利用到了 Spring 的工具类 FileCopyUtils 将输入流的数据拷贝到输出流中。在 getAttach() 方法中通过入参 OutputStream os 接收 LOB 的数据,如 ① 所示。您可以同时覆盖抽象类中的 handleNoRowFound() 方法,定义未找到数据行时的处理逻辑。 } Q@4@  
查看本文来源
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章