[JavaWeb]關於DBUtils中QueryRunner的一些解讀(轉)

QueryRunnerjava

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);
sql

第一種: 不須要params
數據庫

//查詢全部圖書 public List<Book> selectAllBooks() throws SQLException { QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); return qr.query("select * from books", new BeanListHandler<Book>(Book.class)); }

 

第二種: 須要一個參數查詢
數組

//根據id查詢指定的書 public Book selectBookById(String id) throws SQLException { QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); return qr.query("select * from books where id=?", new BeanHandler(Book.class),id); }

三種:須要多個參數查詢
app

複製代碼
//多條件查詢圖書信息 public List<Book> findBookByManyCondition(String id, String category, String name, String minprice, String maxprice) throws SQLException { StringBuilder sql = new StringBuilder("select * from books where 1=1"); List list = new ArrayList(); if(!"".equals(id)){ sql.append(" and id like ?"); list.add("%"+id+"%"); } if(!"".equals(category)){ sql.append(" and category=?"); list.add(category); } if(!"".equals(name)){ sql.append(" and name like ?"); list.add("%"+name+"%"); } if(!"".equals(minprice)){ sql.append(" and price > ?"); list.add(minprice); } if(!"".equals(maxprice)){ sql.append(" and price < ?"); list.add(maxprice); } QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); return qr.query(sql.toString(),new BeanListHandler<Book>(Book.class),list.toArray()); }
複製代碼


那麼咱們來看下源碼的實現: 
(1)QueryRunner.javaless

複製代碼
//第一種狀況,無參數 public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException { Connection conn = this.prepareConnection(); return this.query(conn, true, sql, rsh, (Object[]) null); } //第二種和第三種使用同一方法: 須要參數 public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { Connection conn = this.prepareConnection(); return this.query(conn, true, sql, rsh, params); }
複製代碼

解讀: 這裏先是獲取connection, 利用this.preparaConnection() 獲取. 而後調用query()方法去執行查詢語句. 接下來看源碼是如何獲取到當前傳輸過來的connection以及query()方法的內部實現.ui

複製代碼
protected Connection prepareConnection() throws SQLException { if (this.getDataSource() == null) { throw new SQLException("QueryRunner requires a DataSource to be " + "invoked in this way, or a Connection should be passed in"); } return this.getDataSource().getConnection(); }
複製代碼

這裏很簡單, 由於咱們用的C3P0數據庫鏈接池獲取的DataSource, 因此這裏直就能夠過去到當前的Connection.接下來就看下query()方法的內部實現. this

複製代碼
private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { if (conn == null) { throw new SQLException("Null connection"); } if (sql == null) { if (closeConn) { close(conn); } throw new SQLException("Null SQL statement"); } if (rsh == null) { if (closeConn) { close(conn); } throw new SQLException("Null ResultSetHandler"); } PreparedStatement stmt = null; ResultSet rs = null; T result = null; try { stmt = this.prepareStatement(conn, sql); this.fillStatement(stmt, params); rs = this.wrap(stmt.executeQuery()); result = rsh.handle(rs); } catch (SQLException e) { this.rethrow(e, sql, params); } finally { try { close(rs); } finally { close(stmt); if (closeConn) { close(conn); } } } return result; }
複製代碼

解讀: 在這裏能夠看出, 不管是否有傳遞參數params, 都調用的是同一個query方法, 接着來看this.fillStatement(stmt, params);是如何將參數賦予preparedStatement中的. spa

複製代碼
public void fillStatement(PreparedStatement stmt, Object... params) throws SQLException { // check the parameter count, if we can ParameterMetaData pmd = null; if (!pmdKnownBroken) { pmd = stmt.getParameterMetaData(); int stmtCount = pmd.getParameterCount(); int paramsCount = params == null ? 0 : params.length; if (stmtCount != paramsCount) { throw new SQLException("Wrong number of parameters: expected " + stmtCount + ", was given " + paramsCount); } } // nothing to do here if (params == null) { return; } for (int i = 0; i < params.length; i++) { if (params[i] != null) { stmt.setObject(i + 1, params[i]); } else { // VARCHAR works with many drivers regardless // of the actual column type. Oddly, NULL and // OTHER don't work with Oracle's drivers. int sqlType = Types.VARCHAR; if (!pmdKnownBroken) { try { sqlType = pmd.getParameterType(i + 1); } catch (SQLException e) { pmdKnownBroken = true; } } stmt.setNull(i + 1, sqlType); } } }
複製代碼

這個方法就是核心所在.
第一種狀況: 當params爲null的時候, 直接return而後執行sql語句.
第二種第三種狀況: 當params不爲null時, 循環遍歷傳入的params, 而後將params賦值到preparedStatement中, 而後填充佔位符進行sql查詢. 這裏咱們也來回顧下直接使用preparedStatement來進行查詢的方式:code

複製代碼
@Test
public void update(){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try{ conn = JdbcUtils.getConnection(); String sql = "update users set name=?,email=? where id=?"; st = conn.prepareStatement(sql); st.setString(1, "gacl"); st.setString(2, "gacl@sina.com"); st.setInt(3, 2); int num = st.executeUpdate(); if(num>0){ System.out.println("更新成功!!"); } }catch (Exception e) { e.printStackTrace(); }finally{ JdbcUtils.release(conn, st, rs); } } @Test public void find(){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try{ conn = JdbcUtils.getConnection(); String sql = "select * from users where id=?"; st = conn.prepareStatement(sql); st.setInt(1, 1); rs = st.executeQuery(); if(rs.next()){ System.out.println(rs.getString("name")); } }catch (Exception e) { }finally{ JdbcUtils.release(conn, st, rs); } }
複製代碼


2, Update語句
查看update語句:

複製代碼
//修改圖書 public void updateBook(Book book) throws SQLException { QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); qr.update( "UPDATE books SET NAME=? ,price=?,bnum=?,category=?,description=? WHERE id=?", book.getName(), book.getPrice(), book.getBnum(), book.getCategory(), book.getDescription(), book.getId()) }
複製代碼

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

複製代碼
public int update(String sql, Object... params) throws SQLException { Connection conn = this.prepareConnection(); return this.update(conn, true, sql, params); } private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException { if (conn == null) { throw new SQLException("Null connection"); } if (sql == null) { if (closeConn) { close(conn); } throw new SQLException("Null SQL statement"); } PreparedStatement stmt = null; int rows = 0; try { stmt = this.prepareStatement(conn, sql); this.fillStatement(stmt, params); rows = stmt.executeUpdate(); } catch (SQLException e) { this.rethrow(e, sql, params); } finally { close(stmt); if (closeConn) { close(conn); } } return rows; }
複製代碼

到了參數賦值的時候又調用了上面的fillStatement方法, 這裏就再也不闡述了. 


3, Batch語句
這裏直接看batch方法的實例, 而後結合源碼的實現.

複製代碼
//批量刪除 public void delBooks(String[] ids) throws SQLException { QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); Object[][] params = new Object[ids.length][];//高維肯定執行sql語句的次數,低維是給?賦值 for (int i = 0; i < params.length; i++) { params[i] = new Object[]{ids[i]};//給「?」賦值  } qr.batch("delete from books where id=?", params); }
複製代碼

而後看QueryRunner中的batch()方法:

複製代碼
public int[] batch(String sql, Object[][] params) throws SQLException { Connection conn = this.prepareConnection(); return this.batch(conn, true, sql, params); } private int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException { if (conn == null) { throw new SQLException("Null connection"); } if (sql == null) { if (closeConn) { close(conn); } throw new SQLException("Null SQL statement"); } if (params == null) { if (closeConn) { close(conn); } throw new SQLException("Null parameters. If parameters aren't need, pass an empty array."); } PreparedStatement stmt = null; int[] rows = null; try { stmt = this.prepareStatement(conn, sql); for (int i = 0; i < params.length; i++) { this.fillStatement(stmt, params[i]); stmt.addBatch(); } rows = stmt.executeBatch(); } catch (SQLException e) { this.rethrow(e, sql, (Object[])params); } finally { close(stmt); if (closeConn) { close(conn); } } return rows; }
複製代碼

解讀: 由於params是一個二維數組, 因此往preparedStatement中賦值的時候使用了for循環, 而後經過preparedstatement.addBatch() 進行批量添加, 而後執行executeBatch()進行操做.

複製代碼
   /** * Adds a set of parameters to this <code>PreparedStatement</code> * object's batch of commands. * * @exception SQLException if a database access error occurs or * this method is called on a closed <code>PreparedStatement</code> * @see Statement#addBatch * @since 1.2 */ void addBatch() throws SQLException;
相關文章
相關標籤/搜索