前言:
[本文属于原创分享文章, 转载请注明出处, 谢谢.]
前面已经有文章说了DBUtils的一些特性, 这里再来详细说下QueryRunner的一些内部实现, 写的有错误的地方还恳请大家指出.

QueryRunner类

QueryRunner中提供对sql语句操作的API
它主要有三个方法
  query() 用于执行select
  update() 用于执行insert/update/delete
  batch() 批处理

1,Query语句
先来看下query的两种形式, 我们这里主要讲第一个方法, 因为我们用C3P0来统一管理connection.(QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()))
query(sql,ResultSetHandler,Object...params);
query(conn,sql,ResultSetHandler,Object...params);

第一种: 不需要params

  1. //查询所有图书
  2. public List<Book> selectAllBooks() throws SQLException {
  3. QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
  4. return qr.query("select * from books", new BeanListHandler<Book>(Book.class));
  5. }

第二种: 需要一个参数查询

  1. //根据id查询指定的书
  2. public Book selectBookById(String id) throws SQLException {
  3. QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
  4. return qr.query("select * from books where id=?", new BeanHandler(Book.class),id);
  5. }

三种:需要多个参数查询

  1. //多条件查询图书信息
  2. public List<Book> findBookByManyCondition(String id, String category,
  3. String name, String minprice, String maxprice) throws SQLException {
  4. StringBuilder sql = new StringBuilder("select * from books where 1=1");
  5. List list = new ArrayList();
  6. if(!"".equals(id)){
  7. sql.append(" and id like ?");
  8. list.add("%"+id+"%");
  9. }
  10. if(!"".equals(category)){
  11. sql.append(" and category=?");
  12. list.add(category);
  13. }
  14. if(!"".equals(name)){
  15. sql.append(" and name like ?");
  16. list.add("%"+name+"%");
  17. }
  18. if(!"".equals(minprice)){
  19. sql.append(" and price > ?");
  20. list.add(minprice);
  21. }
  22. if(!"".equals(maxprice)){
  23. sql.append(" and price < ?");
  24. list.add(maxprice);
  25. }
  26.  
  27. QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
  28. return qr.query(sql.toString(),new BeanListHandler<Book>(Book.class),list.toArray());
  29. }

那么我们来看下源码的实现: 
(1)QueryRunner.java

  1. //第一种情况,无参数
  2. public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
  3. Connection conn = this.prepareConnection();
  4.  
  5. return this.query(conn, true, sql, rsh, (Object[]) null);
  6. }
  7.  
  8. //第二种和第三种使用同一方法: 需要参数
  9. public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
  10. Connection conn = this.prepareConnection();
  11.  
  12. return this.query(conn, true, sql, rsh, params);
  13. }

解读: 这里先是获取connection, 利用this.preparaConnection() 获取. 然后调用query()方法去执行查询语句. 接下来看源码是如何获取到当前传输过来的connection以及query()方法的内部实现.

  1. protected Connection prepareConnection() throws SQLException {
  2. if (this.getDataSource() == null) {
  3. throw new SQLException("QueryRunner requires a DataSource to be " +
  4. "invoked in this way, or a Connection should be passed in");
  5. }
  6. return this.getDataSource().getConnection();
  7. }

这里很简单, 因为我们用的C3P0数据库连接池获取的DataSource, 所以这里直就可以过去到当前的Connection.接下来就看下query()方法的内部实现.

  1. private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
  2. throws SQLException {
  3. if (conn == null) {
  4. throw new SQLException("Null connection");
  5. }
  6.  
  7. if (sql == null) {
  8. if (closeConn) {
  9. close(conn);
  10. }
  11. throw new SQLException("Null SQL statement");
  12. }
  13.  
  14. if (rsh == null) {
  15. if (closeConn) {
  16. close(conn);
  17. }
  18. throw new SQLException("Null ResultSetHandler");
  19. }
  20.  
  21. PreparedStatement stmt = null;
  22. ResultSet rs = null;
  23. T result = null;
  24.  
  25. try {
  26. stmt = this.prepareStatement(conn, sql);
  27. this.fillStatement(stmt, params);
  28. rs = this.wrap(stmt.executeQuery());
  29. result = rsh.handle(rs);
  30.  
  31. } catch (SQLException e) {
  32. this.rethrow(e, sql, params);
  33.  
  34. } finally {
  35. try {
  36. close(rs);
  37. } finally {
  38. close(stmt);
  39. if (closeConn) {
  40. close(conn);
  41. }
  42. }
  43. }
  44.  
  45. return result;
  46. }

解读: 在这里可以看出, 无论是否有传递参数params, 都调用的是同一个query方法, 接着来看this.fillStatement(stmt, params);是如何将参数赋予preparedStatement中的.

  1. public void fillStatement(PreparedStatement stmt, Object... params) throws SQLException {
  2.  
  3. // check the parameter count, if we can
  4. ParameterMetaData pmd = null;
  5. if (!pmdKnownBroken) {
  6. pmd = stmt.getParameterMetaData();
  7. int stmtCount = pmd.getParameterCount();
  8. int paramsCount = params == null ? 0 : params.length;
  9.  
  10. if (stmtCount != paramsCount) {
  11. throw new SQLException("Wrong number of parameters: expected "
  12. + stmtCount + ", was given " + paramsCount);
  13. }
  14. }
  15.  
  16. // nothing to do here
  17. if (params == null) {
  18. return;
  19. }
  20.  
  21. for (int i = 0; i < params.length; i++) {
  22. if (params[i] != null) {
  23. stmt.setObject(i + 1, params[i]);
  24. } else {
  25. // VARCHAR works with many drivers regardless
  26. // of the actual column type. Oddly, NULL and
  27. // OTHER don't work with Oracle's drivers.
  28. int sqlType = Types.VARCHAR;
  29. if (!pmdKnownBroken) {
  30. try {
  31. sqlType = pmd.getParameterType(i + 1);
  32. } catch (SQLException e) {
  33. pmdKnownBroken = true;
  34. }
  35. }
  36. stmt.setNull(i + 1, sqlType);
  37. }
  38. }
  39. }

这个方法就是核心所在.
第一种情况: 当params为null的时候, 直接return然后执行sql语句.
第二种第三种情况: 当params不为null时, 循环遍历传入的params, 然后将params赋值到preparedStatement中, 然后填充占位符进行sql查询. 这里我们也来回顾下直接使用preparedStatement来进行查询的方式:

  1. @Test
  2. public void update(){
  3. Connection conn = null;
  4. PreparedStatement st = null;
  5. ResultSet rs = null;
  6. try{
  7. conn = JdbcUtils.getConnection();
  8. String sql = "update users set name=?,email=? where id=?";
  9. st = conn.prepareStatement(sql);
  10. st.setString(1, "gacl");
  11. st.setString(2, "gacl@sina.com");
  12. st.setInt(3, 2);
  13. int num = st.executeUpdate();
  14. if(num>0){
  15. System.out.println("更新成功!!");
  16. }
  17. }catch (Exception e) {
  18. e.printStackTrace();
  19.  
  20. }finally{
  21. JdbcUtils.release(conn, st, rs);
  22. }
  23. }
  24.  
  25. @Test
  26. public void find(){
  27. Connection conn = null;
  28. PreparedStatement st = null;
  29. ResultSet rs = null;
  30. try{
  31. conn = JdbcUtils.getConnection();
  32. String sql = "select * from users where id=?";
  33. st = conn.prepareStatement(sql);
  34. st.setInt(1, 1);
  35. rs = st.executeQuery();
  36. if(rs.next()){
  37. System.out.println(rs.getString("name"));
  38. }
  39. }catch (Exception e) {
  40.  
  41. }finally{
  42. JdbcUtils.release(conn, st, rs);
  43. }
  44. }

2, Update语句
查看update语句:

  1. //修改图书
  2. public void updateBook(Book book) throws SQLException {
  3. QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
  4. qr.update(
  5. "UPDATE books SET NAME=? ,price=?,bnum=?,category=?,description=? WHERE id=?",
  6. book.getName(), book.getPrice(), book.getBnum(),
  7. book.getCategory(), book.getDescription(), book.getId())
  8. }

接着是QueryRunner.java中的update 方法:

  1. public int update(String sql, Object... params) throws SQLException {
  2. Connection conn = this.prepareConnection();
  3.  
  4. return this.update(conn, true, sql, params);
  5. }
  6.  
  7. private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
  8. if (conn == null) {
  9. throw new SQLException("Null connection");
  10. }
  11.  
  12. if (sql == null) {
  13. if (closeConn) {
  14. close(conn);
  15. }
  16. throw new SQLException("Null SQL statement");
  17. }
  18.  
  19. PreparedStatement stmt = null;
  20. int rows = 0;
  21.  
  22. try {
  23. stmt = this.prepareStatement(conn, sql);
  24. this.fillStatement(stmt, params);
  25. rows = stmt.executeUpdate();
  26.  
  27. } catch (SQLException e) {
  28. this.rethrow(e, sql, params);
  29.  
  30. } finally {
  31. close(stmt);
  32. if (closeConn) {
  33. close(conn);
  34. }
  35. }
  36.  
  37. return rows;
  38. }

到了参数赋值的时候又调用了上面的fillStatement方法, 这里就不再阐述了.

3, Batch语句
这里直接看batch方法的实例, 然后结合源码的实现.

  1. //批量删除
  2. public void delBooks(String[] ids) throws SQLException {
  3. QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
  4. Object[][] params = new Object[ids.length][];//高维确定执行sql语句的次数,低维是给?赋值
  5. for (int i = 0; i < params.length; i++) {
  6. params[i] = new Object[]{ids[i]};//给“?”赋值
  7. }
  8. qr.batch("delete from books where id=?", params);
  9. }

然后看QueryRunner中的batch()方法:

  1. public int[] batch(String sql, Object[][] params) throws SQLException {
  2. Connection conn = this.prepareConnection();
  3.  
  4. return this.batch(conn, true, sql, params);
  5. }
  6.  
  7. private int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException {
  8. if (conn == null) {
  9. throw new SQLException("Null connection");
  10. }
  11.  
  12. if (sql == null) {
  13. if (closeConn) {
  14. close(conn);
  15. }
  16. throw new SQLException("Null SQL statement");
  17. }
  18.  
  19. if (params == null) {
  20. if (closeConn) {
  21. close(conn);
  22. }
  23. throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
  24. }
  25.  
  26. PreparedStatement stmt = null;
  27. int[] rows = null;
  28. try {
  29. stmt = this.prepareStatement(conn, sql);
  30.  
  31. for (int i = 0; i < params.length; i++) {
  32. this.fillStatement(stmt, params[i]);
  33. stmt.addBatch();
  34. }
  35. rows = stmt.executeBatch();
  36.  
  37. } catch (SQLException e) {
  38. this.rethrow(e, sql, (Object[])params);
  39. } finally {
  40. close(stmt);
  41. if (closeConn) {
  42. close(conn);
  43. }
  44. }
  45.  
  46. return rows;
  47. }

解读: 因为params是一个二维数组, 所以往preparedStatement中赋值的时候使用了for循环, 然后通过preparedstatement.addBatch() 进行批量添加, 然后执行executeBatch()进行操作.

  1.    /**
  2. * Adds a set of parameters to this <code>PreparedStatement</code>
  3. * object's batch of commands.
  4. *
  5. * @exception SQLException if a database access error occurs or
  6. * this method is called on a closed <code>PreparedStatement</code>
  7. * @see Statement#addBatch
  8. * @since 1.2
  9. */
  10. void addBatch() throws SQLException;

一看时间这么晚了, QueryRunner暂时就这么多了, 关于QueryRunner的用法自己挖掘的还不够透彻, 写在这里当做记录和交流. 2016/05/24.

[JavaWeb]关于DBUtils中QueryRunner的一些解读.的更多相关文章

  1. [JavaWeb]关于DBUtils中QueryRunner的一些解读(转)

    QueryRunner类 QueryRunner中提供对sql语句操作的API它主要有三个方法 query() 用于执行select update() 用于执行insert/update/delete ...

  2. 【转载】关于DBUtils中QueryRunner的一些解读

    前面已经有文章说了DBUtils的一些特性, 这里再来详细说下QueryRunner的一些内部实现, 写的有错误的地方还恳请大家指出. QueryRunner类 QueryRunner中提供对sql语 ...

  3. 关于dbutils中QueryRunner看批量删除语句batch

    //批量删除 public void delBooks(String[] ids) throws SQLException { QueryRunner qr = new QueryRunner(C3P ...

  4. 【JavaWeb】DbUtils入门之QueryRunner

    DbUtils简介 根据官网的介绍,DbUtils是一种 JDBC Utility Component (翻译过来大概就是:JDBC实用部件),故名思意,和数据库操作有关 官网上的简介也称之为 JDB ...

  5. JavaWeb之DBUtils

    一.什么是DBUtils及作用 DBUtils是apache公司写的.DBUtils是java编程中的数据库操作实用工具,小巧简单实用. DBUtils封装了对JDBC的操作,简化了JDBC操作.可以 ...

  6. JavaWeb之DButils整理

    一.DBUtils介绍  apache 什么是dbutils,它的作用 DBUtils是java编程中的数据库操作实用工具,小巧简单实用. 用前导包!!!DBUtils包!!! 二.DBUtils的三 ...

  7. java—在dbutils中处理事务与不确定条件的查询(46)

    在dbutils中处理事务        事务是指用户的一次操作.这一次操作有可能是一个表,也有可能是多个表,也有可能是对一个表的多次操作. 只要是: 1:对数据数据库进行多次操作. 2:多个表,还是 ...

  8. BiLSTM-CRF模型中CRF层的解读

    转自: https://createmomo.github.io/ BiLSTM-CRF模型中CRF层的解读: 文章链接: 标题:CRF Layer on the Top of BiLSTM - 1  ...

  9. 在jdbc基础上进阶一小步的C3p0 连接池(DBCP 不能读xml配置文件,已淘汰) 和DBUtils 中两个主要类QueryRunner和ResultSetHandler的使用

    首先看C3p0这个连接池,最大优势可以自动读取默认的配置文件 <?xml version="1.0" encoding="UTF-8"?> < ...

随机推荐

  1. java时区问题的一个坑

    事情是这样的,前台传过去一个日期字符串,就像2016/12/15 00:00,2016/12/15 23:59类似的格式,但每次从日志平台查日志查询的时间范围都不对,而是提前了一天. 原因是在java ...

  2. MySQL入门手册

    本文内容摘自MySQL5.6官方文档,主要选取了在实践过程中所用到的部分文字解释,力求只摘录重点,快速学会使用MySQL,本文所贴代码地方就是我亲自练习过的代码,凡本文没有练习过的代码都没有贴在此处, ...

  3. Postman-CI集成Jenkins

    Postman-简单使用 Postman-进阶使用 Postman-CI集成Jenkins Newman 官方说明:Postman's command-line companion lets you ...

  4. BZOJ2683 简单题(CDQ分治)

    传送门 之前听别人说CDQ分治不难学,今天才知道果真如此.之前一直为自己想不到CDQ的方法二很不爽,今天终于是想出来了一道了,太弱-- cdq分治主要就是把整段区间分成两半,然后用左区间的值去更新右区 ...

  5. Jetty源码分析(一)

    一.目的 1.了解jetty组成架构: 2.学习jetty启动过程: 3.学习请求访问过程: 4.学习jetty内各模块作用,学习各模块内部代码: 二.jetty版本 本文所学习的jetty版本为:9 ...

  6. 解决hadoop启动后datanode无法启动问题

    hadoop部署完成后datanode无法启动问题解决 1.检查是否有遗留的hadoop进程还在运行,如果有的话,先stop-all.sh或kill杀掉: 2.在master节点上,删除/tmp/ha ...

  7. android 编译代码注意事项

    1 安装openjdk1.7 sudo add-apt-repository ppa:openjdk-r/ppa sudo apt-apt update sudo apt-get install op ...

  8. IO-02. 整数四则运算(10)

    本题要求编写程序,计算2个正整数的和.差.积.商并输出.题目保证输入和输出全部在整型范围内. 输入格式: 输入在一行中给出2个正整数A和B. 输出格式: 在4行中按照格式“A 运算符 B = 结果”顺 ...

  9. LINUX下常用SHELL指令

    Linux Shell常用shell命令 一.文件.目录操作命令 1.ls命令 功能:显示文件和目录的信息 ls 以默认方式显示当前目录文件列表 ls -a 显示所有文件包括隐藏文件 ls -l 显示 ...

  10. JavaScript 学习笔记(一)

    1.javascript中,值包括原始值和对象,原始值包括布尔值.数字.字符串.null和undefined,其他的值为对象. 原始值的特点:(1)按值进行比较:3===3> true; 'ab ...