• QueryRunner类实战


    先上一个登录代码---判断登录是否成功

    1、c3p0-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
        <default-config>
            <property name="user">root</property>
            <property name="password">123456</property>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql:///filter</property>
        </default-config> 
    </c3p0-config> 

    2.C3P0Utils

    package com.ithiema.utils;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class C3P0Utils {
    //import com.mchange.v2.c3p0.ComboPooledDataSource;
        private static DataSource dataSource = new ComboPooledDataSource();
    
        private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    
        // 直接可以获取一个连接池
        public static DataSource getDataSource() {
            return dataSource;
        }
        
        public static Connection getConnection() throws SQLException{
            return dataSource.getConnection();
        }
    
        // 获取连接对象
        public static Connection getCurrentConnection() throws SQLException {
    
            Connection con = tl.get();
            if (con == null) {
                con = dataSource.getConnection();
                tl.set(con);
            }
            return con;
        }
    
        // 开启事务
        public static void startTransaction() throws SQLException {
            Connection con = getCurrentConnection();
            if (con != null) {
                con.setAutoCommit(false);
            }
        }
    
        // 事务回滚
        public static void rollback() throws SQLException {
            Connection con = getCurrentConnection();
            if (con != null) {
                con.rollback();
            }
        }
    
        // 提交并且 关闭资源及从ThreadLocall中释放
        public static void commitAndRelease() throws SQLException {
            Connection con = getCurrentConnection();
            if (con != null) {
                con.commit(); // 事务提交
                con.close();// 关闭资源
                tl.remove();// 从线程绑定中移除
            }
        }
    
        // 关闭资源方法
        public static void closeConnection() throws SQLException {
            Connection con = getCurrentConnection();
            if (con != null) {
                con.close();
            }
        }
    
        public static void closeStatement(Statement st) throws SQLException {
            if (st != null) {
                st.close();
            }
        }
    
        public static void closeResultSet(ResultSet rs) throws SQLException {
            if (rs != null) {
                rs.close();
            }
        }
    
    }

    3.UserDao.java

    package com.ithiema.dao;
    
    import com.ithiema.domain.User;
    import com.ithiema.utils.C3P0Utils;
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    
    import java.sql.SQLException;
    
    public class UserDao {
    
        public User login(String username, String password) throws SQLException {
            QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource());
            String sql = "select * from user where username=? and password=?";
            return runner.query(sql, new BeanHandler<User>(User.class), username,password);
        }
    }

    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

    //查询所有图书
    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);
    }

    三种:需要多个参数查询

    复制代码
    //多条件查询图书信息
    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.java

    复制代码
    //第一种情况,无参数
    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()方法的内部实现.

    复制代码
    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()方法的内部实现. 

    复制代码
    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中的. 

    复制代码
    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来进行查询的方式:

    复制代码
    @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;
    复制代码
  • 相关阅读:
    Arcgis javascript api离线开发环境搭建
    hbase基础
    Dojo 之 面向对象(转)
    Linux
    并行计算多线程常见问题
    [转]捕捉DataGridView的单元格编辑时的键事件
    'router' => array( 'routes' => array( 'album' => array( 'type' => 'segment', 'options' => arra
    [疯狂xml讲义]chap4之Schema记录
    [WinForm]DataGridView如何绑定对象的复合属性
    [C#之Ftp]RFC959笔记01
  • 原文地址:https://www.cnblogs.com/hoobey/p/7845433.html
Copyright © 2020-2023  润新知