当前位置: 首页 > news >正文

JdbcTemplate查询数据 三种callback之间的区别

JdbcTemplate针对数据查询提供了多个重载的模板方法,你可以根据需要选用不同的模板方法。 如果你的查询很简单,仅仅是传入相应SQL或者相关参数,然后取得一个单一的结果,那么你可以选择如下一组便利的模板方法:

int queryForInt(String sql) 

int queryForInt(String sql, Object[] args) 

long queryForLong(String sql) 

long queryForLong(String sql, Object[] args) 

Object queryForObject(String sql, Class requiredType) 

Object queryForObject(String sql, Object[] args, Class requiredType) 

Map queryForMap(String sql) 

Map queryForMap(String sql, Object[] args) 

比如说,你所查询的结果就包含一列数字型的结果,或者使用了SQL函数,或者其他单列的结果,我们就可以直接通过这组便利的模板方法进行查询: 
int age = jdbcTemplate.queryForInt("select age from customer where customerId=?",new Object[]{new Integer(100)}); 
... 
long interval = jdbcTemplate.queryForLong("select count(customerId) from customer"); 
... 
String customerName = jdbcTemplate.queryForString("select username from customer where customerId=110"); 
... 
Map singleCustomer = jdbcTemplate.queryForMap("select * from customer limit 1"); 
... 
queryForMap方法与其他方法不同之处在于,它的查询结果以java.util.Map的形式返回,Map的key对应所查询表的列名,Map的value当然就是对应key所在列的值啦。 当然了,你也看到了,这组模板方法主要用于单一结果的查询,使用的时候也请确保你的SQL查询所返回的结果是单一的,否则,JdbcTemplate将抛出org.springframework.dao.IncorrectResultSizeDataAccessException异常。 
如果查询的结果将返回多行,而你又不在乎他们是否拥有较强的类型约束,那么,以下模板方法可以帮助你: 

List queryForList(String sql) 

List queryForList(String sql, Object[] args) 

queryForList方法根据传入的SQL以及相应的参数执行查询,将查询的结果以java.util.List的形式返回,返回的java.util.List中的每一个元素都是java.util.Map类型,分别对应结果集中的一行,Map的Key为每一列的列名,而Map的值就是当前行列名对应的值。 
好啦,如果这些还不足以满足你的查询需要,那么我们就更进一步,使用相应的Callback接口对查询结果的返回进行定制吧! 

用于查询的回调接口定义主要有以下三种: 

org.springframework.jdbc.core.ResultSetExtractor.  基本上属于JdbcTemplate内部使用的Callback接口,相对于下面两个Callback接口来说,ResultSetExtractor拥有更多的控制权,因为使用它,你需要自行处理ResultSet: 

public interface ResultSetExtractor 

Object extractData(ResultSet rs) throws SQLException, DataAccessException; 

在直接处理完ResultSet之后,你可以将处理后的结果以任何你想要的形式包装后返回。 
org.springframework.jdbc.core.RowCallbackHandler.  RowCallbackHandler相对于ResultSetExtractor来说,仅仅关注单行结果的处理,处理后的结果可以根据需要存放到当前RowCallbackHandler对象内或者使用JdbcTemplate的程序上下文中,当然,这个完全是看个人爱好了。 RowCallbackHandler的定义如下: 

public interface RowCallbackHandler 

void processRow(ResultSet rs) throws SQLException; 

org.springframework.jdbc.core.RowMapper.  ResultSetExtractor的精简版,功能类似于RowCallbackHandler,也只关注处理单行的结果,不过,处理后的结果会由ResultSetExtractor实现类进行组合。 RowMapper的接口定义如下: 

public interface RowMapper 

Object mapRow(ResultSet rs, int rowNum) throws SQLException; 

为了说明这三种Callback接口的使用和相互之间的区别,我们暂且设定如下场景: 
数据库表customer中存在多行信息,对该表查询后,我们需要将每一行的顾客信息都映射到域对象Customer中,并以java.util.List的形式返回所有的查询结果。 

现在,我们分别使用这三种Callback接口对customer表进行查询: 

List customerList = (List)jdbcTemplate.query("select * from customer", new ResultSetExtractor(){ 

public Object extractData(ResultSet rs) throws SQLException,DataAccessException 

List customers = new ArrayList(); 
while(rs.next()) 

Customer customer = new Customer(); 
customer.setFirstName(rs.getString(1)); 
customer.setLastName(rs.getString(2)); 
... 
customers.add(customer); 

return customers; 
}}); 
List customerList = jdbcTemplate.query("select * from customer", new RowMapper(){ 

public Object mapRow(ResultSet rs, int rowNumber) throws SQLException { 
Customer customer = new Customer(); 
customer.setFirstName(rs.getString(1)); 
customer.setLastName(rs.getString(2)); 
... 
return customer; 
}}); 
final List customerList = new ArrayList(); 

jdbcTemplate.query("select * from customer", new RowCallbackHandler(){ 

public void processRow(ResultSet rs) throws SQLException { 
Customer customer = new Customer(); 
customer.setFirstName(rs.getString(1)); 
customer.setLastName(rs.getString(2)); 
... 
customerList.add(customer); 
}}); 
如果你没有发现最大的差异在哪里,那么容我细表: 
使用三种Callback接口作为参数的query方法的返回值不同: 

以ResultSetExtractor作为方法参数的query方法返回Object型结果,要使用查询结果,我们需要对其进行强制转型; 

以RowMapper接口作为方法参数的query方法直接返回List型的结果; 

以RowCallbackHandler作为方法参数的query方法,返回值为void; 

使用ResultSetExtractor作为Callback接口处理查询结果,我们需要自己声明集合类,自己遍历ResultSet,自己根据每行数据组装Customer对象,自己将组装后的Customer对象添加到集合类中,方法最终只负责将组装完成的集合返回; 

使用RowMapper比直接使用ResultSetExtractor要方便的多,只负责处理单行结果就行,现在,我们只需要将单行的结果组装后返回就行,剩下的工作,全部都是JdbcTemplate内部的事情了。 实际上,JdbcTemplae内部会使用一个ResultSetExtractor实现类来做其余的工作,毕竟,该做的工作还得有人做不是?! 

JdbcTemplae内部使用的这个ResultSetExtractor实现类为org.springframework.jdbc.core.RowMapperResultSetExtractor, 它内部持有一个RowMapper实例的引用,当处理结果集的时候,会将单行数据的处理委派给其所持有的RowMapper实例,而其余工作它负责: 

public Object extractData(ResultSet rs) throws SQLException { 
List results = (this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList()); 
int rowNum = 0; 
while (rs.next()) { 
results.add(this.rowMapper.mapRow(rs, rowNum++)); 

return results; 

这下应该清楚为啥RowMapper为啥就处理单行结果就能完成ResultSetExtractor颇费周折的工作了吧?! 
RowCallbackHandler虽然与RowMapper同是处理单行数据,不过,除了要处理单行结果,它还得负责最终结果的组装和获取工作,在这里我们是使用当前上下文声明的List取得最终查询结果, 不过,我们也可以单独声明一个RowCallbackHandler实现类,在其中声明相应的集合类,这样,我们可以通过该RowCallbackHandler实现类取得最终查询结果: 

public class GenericRowCallbackHandler implements RowCallbackHandler { 
private List collections = new ArrayList(); 

public void processRow(ResultSet rs) throws SQLException { 
Customer customer = new Customer(); 
customer.setFirstName(rs.getString(1)); 
customer.setLastName(rs.getString(2)); 
... 
collections.add(customer); 


public List getResults() 

return collections; 



GenericRowCallbackHandler handler = new GenericRowCallbackHandler(); 
jdbcTemplate.query("select * from customer",handler()); 
List customerList = handler.getResults(); 
该使用方式是明了了,不过GenericRowCallbackHandler重用性不佳。 
RowCallbackHandler因为也是处理单行数据,所以,总得有人来做遍历ResultSet的工作,这个人其实也是一个ResultSetExtractor实现类, 它是JdbcTemplate一个内部静态类,名为RowCallbackHandlerResultSetExtractor,一看它的定义你就知道奥秘之所在了: 

private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor { 

private final RowCallbackHandler rch; 

public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) { 
this.rch = rch; 


public Object extractData(ResultSet rs) throws SQLException { 
while (rs.next()) { 
this.rch.processRow(rs); 

return null; 


总的来说,内部工作归根结底是由ResultSetExtractor做了,RowCallbackHandler和RowMapper只是为了帮助我们简化使用上的操作而已。 所以,实际使用中,RowCallbackHandler和RowMapper才是我们最常用的选择。 
对于使用JdbcTemplate进行查询,基本就这些内容了,当然,如果你非要使用基于StatementCallback之类更底层的execute方法的话,那就是你个人说了算啦。 不过,要想知道JdbcTemplate中有关查询相关模板方法的更多信息,在实际使用中参考JdbcTemplate的javadoc就可以,当然,有IDE就更便捷了。 

相关文章:

  • asp.net 伪静态 IIS设置后 直正HTML无法显示
  • 适配器模式(Adapter)
  • GBK
  • 学习ADO时总结的一些经验
  • sysconf zz
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • Linux的nm查看动态和静态库中的符号
  • 产品验证环节的失语现象
  • LDR指令的应用
  • iptables应用
  • 天啊,原来计算机就是佛,信则灵
  • 如何利用HTTP缓存提高网页性能
  • mysql体系架构
  • 运维自动化之使用Cobbler自动化安装系统与FAQ
  • 凯易讯的笔试
  • Android系统模拟器绘制实现概述
  • centos安装java运行环境jdk+tomcat
  • ES学习笔记(12)--Symbol
  • Java IO学习笔记一
  • JavaScript-Array类型
  • Java程序员幽默爆笑锦集
  • Laravel核心解读--Facades
  • PV统计优化设计
  • QQ浏览器x5内核的兼容性问题
  • vue-router 实现分析
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 给初学者:JavaScript 中数组操作注意点
  • 关于 Cirru Editor 存储格式
  • 关于字符编码你应该知道的事情
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 马上搞懂 GeoJSON
  • 使用common-codec进行md5加密
  • 算法-插入排序
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (蓝桥杯每日一题)love
  • (算法)N皇后问题
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)memcache、redis缓存
  • (转)Windows2003安全设置/维护
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .bat批处理(一):@echo off
  • .net 8 发布了,试下微软最近强推的MAUI
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .Net6 Api Swagger配置
  • .Net通用分页类(存储过程分页版,可以选择页码的显示样式,且有中英选择)
  • @GetMapping和@RequestMapping的区别
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce