前面已经有文章说了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;

转载自一枝花算不算浪漫

https://www.cnblogs.com/wang-meng/p/5525389.html

【转载】关于DBUtils中QueryRunner的一些解读的更多相关文章

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

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

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

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

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

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

  4. [转载]解析WINDOWS中的DLL文件---经典DLL解读

    [转载]解析WINDOWS中的DLL文件---经典DLL解读 在Windows世界中,有无数块活动的大陆,它们都有一个共同的名字——动态链接库.现在就走进这些神奇的活动大陆,找出它们隐藏已久的秘密吧! ...

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

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

  6. 转载:WinForm中播放声音的三种方法

    转载:WinForm中播放声音的三种方法 金刚 winForm 播放声音 本文是转载的文章.原文出处:http://blog.csdn.net/jijunwu/article/details/4753 ...

  7. 转载:C#中事件和委托的编译代码

    接上文转载:C#中事件的由来,这时候,我们注释掉编译错误的行,然后重新进行编译,再借助Reflactor来对 event的声明语句做一探究,看看为什么会发生这样的错误: public event Gr ...

  8. 转载:C#中事件的由来

    原文地址 http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx 感谢博主分享! 我们继续思考转 ...

  9. 【转载】 Sqlserver中通过Select Into语句快速单表备份

    在Sqlserver数据库中,备份数据的方式有很多种,可以使用整个数据库备份,也可使用导出包含数据和架构的脚本文件的方式来进行单表或多表数据的备份,其实还有一种Select Into的方式可以快速备份 ...

随机推荐

  1. java.lang.ClassFormatError: Unknown constant tag 0 in class file

    在通过文件上传之后,运行java程序,突然发现这么一个错误:java.lang.ClassFormatError: Unknown constant tag 0 in class file,通过网上查 ...

  2. [bzoj2599][IOI2011]Race_树上点分治

    Race bzoj-2599 题目大意:询问一颗树上最短的.长度为k的链,边有边权,n个节点. 注释:$1\le n \le 2\cdot 10^5$,$1\le k \le 10^6$. 想法:树上 ...

  3. POJ 3278 Catch That Cow(求助大佬)

    Catch That Cow Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 109702   Accepted: 34255 ...

  4. Java分代垃圾回收机制:年轻代/年老代/持久代(转)

    虚拟机中的共划分为三个代:年轻代(Young Generation).年老点(Old Generation)和持久代(Permanent Generation).其中持久代主要存放的是Java类的类信 ...

  5. PHP array_diff_ukey()

    定义和用法 array_diff_ukey() 返回一个数组,该数组包括了所有出现在 array1 中但是未出现在任何其它参数数组中的键名的值.注意关联关系保留不变.与 array_diff() 不同 ...

  6. find -perm命令

    http://www.2cto.com/os/201205/130125.html find -perm,根据文件的权限来查找文件,有三种形式: find -perm mode find -perm ...

  7. Yum重装走过的坑

    今天因为用yum方式安装mongo遇到报错,从而我选择卸载yum并重新安装. 我先选择了用rpm方式进行重装,从163的packages列表里面找到64位redhat6.5可以用的三个rpm包,安装过 ...

  8. oc55--ARC单个对象的内存管理

    // Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @end // Person.m #i ...

  9. javascript之模块加载方案

    前言 主要学习一下四种模块加载规范: AMD CMD CommonJS ES6 模块 历史 前端模块化开发那点历史 require.js requirejs 为全局添加了 define 函数,你只要按 ...

  10. 函数中的this的四种绑定形式

    目录 this的默认绑定 this的隐式绑定 隐式绑定下,作为对象属性的函数,对于对象来说是独立的 在一串对象属性链中,this绑定的是最内层的对象 this的显式绑定:(call和bind方法) n ...