Java 自定义dbutils下的QueryRunner

摘要:
如果我们只会使用一些工具,那是知其然,但是如果我们也能写出一个类似的工具,那就是知其所以然;一步步理解下QueryRunner,提高下java的编程思想,理解底层原理;站在框架设计者的角度看本文,主要理解QueryRunner底层的思想,设计原理,同时还可以辅助理解动态代理,希望下面的思路能帮助大家好好理解
涉及到的知识点
1.QueryRunner的使用
2.mysql数据库
3.c3p0数据库连接池及配置文件
4.可变参数(实质就是数组)
5.接口回调
源码及所有配置文件:https://download.csdn.net/download/u010452388/10398934

一 使用DBUtils下的QueryRunner

1-1 数据库表

这里写图片描述

1-2 c3p0配置文件

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day05</property>
        <property name="user">root</property>
        <property name="password">123</property>
        <!-- 如果连接池中连接不够时一次性增长多少连接 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化连接池时池子中有多少个连接 -->
        <property name="initialPoolSize">20</property>
        <!-- 池子中最小连接数 -->
        <property name="minPoolSize">10</property>
        <!-- 池子中最大连接数 -->
        <property name="maxPoolSize">40</property>

    </default-config>
    <named-config name="day06"> 
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day06</property>
        <property name="user">root</property>
        <property name="password">123</property>
    </named-config>
</c3p0-config>

1-3 文件目录

这里写图片描述

1-4 User类代码


public class User {
    private int id;
    private String username;
    private String password;
    private String email;
    public User(int id, String username, String password, String email) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
    }
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + "]";
    }
    public int getId() {
        return id;
    }
    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
    public String getEmail() {
        return email;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setEmail(String email) {
        this.email = email;
    }
}

1-5 DbutilsDemo测试代码


import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DbutilsDemo {

    public static void main(String[] args) throws SQLException {
        // 首先创建c3p0的数据库连接池(属于开源包c3p0-0.9.12.jar)
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        // 再创建QueryRunner(属于开源包commons-dbutils-1.6.jar),并传入连接池(可以用c3p0,或者dbcp的连接池);
        QueryRunner runner = new QueryRunner(dataSource);
        // 声明查询sql语句
        String sql = "select * from user where username=? and password=?";
        // 可变参数其实就是数组,要查询的顺序和数量要和sql语句一致
        Object[] params = { "zs", "123" };
        ResultSetHandler<User> rsh = new ResultSetHandler<User>() {
            @Override
            public User handle(ResultSet rs) throws SQLException {
                if (rs.next()) {
                    int id = rs.getInt("id");
                    String username = rs.getString("username");
                    String password = rs.getString("password");
                    String email = rs.getString("email");
                    User user = new User(id, username, password, email);
                    return user;
                }
                return null;
            }
        };
        // query的第三个参数是可变参数,可变参数其实就是数组
        User user = runner.query(sql, rsh, params);
        System.out.println(user);
    }

}

1-6 测试结果

红色部分不用管,是c3p0自动打印的
从结果可以看出,控制台已经打印出我们需要的信息
这里写图片描述

二 自定义MyQueryRunner

现在我们要站在框架设计者的角度去思考问题

第1步-定义一个构造函数

构造函数的参数是DataSource
可能你有疑问:
1. 为什么传DataSource,而不传c3p0的的数据库连接池?
答:我们目前是架构设计者,并不清楚调用者用的是哪种连接池,有可能是c3p0,也有可能是Apache的dbcp连接池,所以我们用DataSource接口,让调用者根据自己实际用的连接池来实现DataSource接口

import javax.sql.DataSource;

public class MyQueryRunner {
    DataSource ds;

    public MyQueryRunner(DataSource ds) {
        this.ds = ds;
    }
}

第2步-创建查询方法query

站在框架设计者的角度思考:
1.调用者调用我们query方法之后,我们需要与数据库进行连接
2.调用者查询语句,应该要传入一个要进行预编译sql语句,所以要提供一个参数给调用者传入
3.调用者传入了需要预编译的sql语句,还要传入一个预编译的参数个数,但是我们不清楚调用者到底传入几个参数,该如何处理?
(这里我们可以运用可变参数,即可以解决)
方法的返回值我们先用User(有助于大家理解),后面会改成泛型

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.dbutils.User;

public class MyQueryRunner {
    DataSource ds;

    public MyQueryRunner(DataSource ds) {
        this.ds = ds;
    }
        //sql为调用者传入的查询语句,params为设置的参数
    public User query(String sql,Object...params) throws SQLException{
        //获取连接池中的连接
        Connection conn = ds.getConnection();
        //对sql进行预编译
        PreparedStatement statement = conn.prepareStatement(sql);
        //先返回null,避免编译报错,后面代码会更改
        return null;
    }
}

第3步-判断查询语句与传入的参数个数

这里我们需要考虑一个问题:
如果调用者传入的参数个数与sql预编译语句里需要的参数不对怎么办?
所以我们要先进行判断


import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.dbutils.User;

public class MyQueryRunner {
    DataSource ds;

    public MyQueryRunner(DataSource ds) {
        this.ds = ds;
    }
        //sql为调用者传入的查询语句
    public User query(String sql,Object...params) throws SQLException{
        //获取连接池中的连接
        Connection conn = ds.getConnection();
        //对sql进行预编译
        PreparedStatement statement = conn.prepareStatement(sql);
        //获取预编译查询语句里的元数据
        ParameterMetaData metaData = statement.getParameterMetaData();
        //获取元数据的个数
        int count = metaData.getParameterCount();
        //如果元数据的个数和传入的params数据个数不一致,则抛出异常
        if(count!=params.length){
            throw new IllegalArgumentException("你个傻鸟,传进来的数据个数不对");
        }
        //到这一步,说明说明传进来的params数据个数一致了
        //循环对查询语句sql中的?进行赋值
        for (int i = 0; i < params.length; i++) {
            statement.setObject(i+1, params[i]);
        }
        //执行查询语句,并将结果集返回给resultSet
        ResultSet resultSet = statement.executeQuery();
        //先返回null,避免编译报错,后面代码会更改
        return null;
    }
}

第4步 关键点-返回值问题

根据上面最后的代码,我们要考虑几个问题
1.我们目前有什么?resultSet
2.我们需要什么?User
3.如何将resultSet 变成User对象?
站在框架设计者角度想:如果有个方法,可以将resultSet变成User,但是怎么实现我们不清楚,可以想到用抽象方法。能够将resuletSet传入,给我们返回一个User多好?
比如:public abstract User handle(ResultSet resultSet) ,看到这里,我们可以想到抽象类或者接口,我们这里不选用抽象类,因为抽象类无法实例化,我们选用接口,让调用者实现此接口,然后将其实现对象传入query方法,在方法内部调用handle方法,将resultSet变成User;这样我们就解决了刚刚1,2,3的问题了
竟然要实现了此接口,那么调用者就要重写handle方法,即我们给调用者提供resultSet,调用者返回给我们User。
下面我们添加接口MyResultSetHandle,以及对query方法添加一个MyResultSetHandle参数,让调用者传入其实现对象

就此完成MyQueryRunner完整代码,下面我们开始测试query方法,最后会给出


import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.dbutils.User;

//创建resultSet处理接口
interface MyResultSetHandle{
    //返回值为User,传入resultSet
    User handle(ResultSet resultSet) throws SQLException;
}
public class MyQueryRunner {
    DataSource ds;

    public MyQueryRunner(DataSource ds) {
        this.ds = ds;
    }
        //sql为调用者传入的查询语句
    public User query(String sql,MyResultSetHandle mrsh,Object...params) throws SQLException{
        //获取连接池中的连接
        Connection conn = ds.getConnection();
        //对sql进行预编译
        PreparedStatement statement = conn.prepareStatement(sql);
        //获取预编译查询语句里的元数据
        ParameterMetaData metaData = statement.getParameterMetaData();
        //获取元数据的个数
        int count = metaData.getParameterCount();
        //如果元数据的个数和传入的params数据个数不一致,则抛出异常
        if(count!=params.length){
            throw new IllegalArgumentException("你个傻鸟,传进来的数据个数不对");
        }
        //到这一步,说明说明传进来的params数据个数一致了
        //循环对查询语句中的?进行赋值
        for (int i = 0; i < params.length; i++) {
            statement.setObject(i+1, params[i]);
        }
        //执行查询语句,并将结果集返回给resultSet
        ResultSet resultSet = statement.executeQuery();
        //执行调用者重写的方法,实现接口回调
        User user = mrsh.handle(resultSet);
        //返回user对象
        return user;
    }
}

三-测试MyQueryRunner

3-1 测试Demo

下面MyTestDemo代码是调用自己写的MyQueryRunner实现查询功能,可以观察和1-5测试的代码一致,只是1-5测试里用到了泛型,后面我们只要在接口和方法加上泛型即可


import java.sql.ResultSet;
import java.sql.SQLException;

import com.dbutils.User;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class MyTestDemo {
    public static void main(String[] args) throws SQLException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        //调用自己编写的MyQueryRunner
        MyQueryRunner runner = new MyQueryRunner(dataSource);
        String sql="select * from user where username=? and password=?";
        Object[] params={"zs","123"};
        MyResultSetHandle mrsh=new MyResultSetHandle() {

            @Override
            public User handle(ResultSet rs) throws SQLException {
                if(rs.next()){
                    int id = rs.getInt("id");
                    String username = rs.getString("username");
                    String password = rs.getString("password");
                    String email = rs.getString("email");
                    User user = new User(id, username, password, email);
                    return user;
                }
                return null;
            }
        };
        User user = runner.query(sql, mrsh, params);
        System.out.println(user);
    }
}

3-2 测试结果

这里写图片描述

3-3 代码执行流程

这里有个注意点:
main方法中,mrsh对象创建好的时候,其重写的handle方法并没有执行,方法只有被调用的时候才会执行内部细节
这里写图片描述

3-4 添加泛型后的代码

MyQuerRunner添加泛型后代码


import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;


//创建resultSet处理接口
interface MyResultSetHandle<T> {
    // 返回值为User,传入resultSet
    T handle(ResultSet resultSet) throws SQLException;
}

public class MyQueryRunner {
    DataSource ds;

    public MyQueryRunner(DataSource ds) {
        this.ds = ds;
    }

    public <T> T query(String sql, MyResultSetHandle<T> mrsh, Object... params) throws SQLException {

        Connection conn = ds.getConnection();
        PreparedStatement statement = conn.prepareStatement(sql);
        ParameterMetaData metaData = statement.getParameterMetaData();
        int count = metaData.getParameterCount();
        if (count != params.length) {
            throw new IllegalArgumentException("你个傻鸟,传进来的数据个数不对");
        }
        for (int i = 0; i < params.length; i++) {
            statement.setObject(i + 1, params[i]);
        }
        ResultSet resultSet = statement.executeQuery();
        T t = mrsh.handle(resultSet);
        return t;
    }
}

MyTestDemo代码:


import java.sql.ResultSet;
import java.sql.SQLException;

import com.dbutils.User;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class MyTestDemo {
    public static void main(String[] args) throws SQLException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        MyQueryRunner runner = new MyQueryRunner(dataSource);
        String sql = "select * from user where username=? and password=?";
        Object[] params = { "zs", "123" };
        MyResultSetHandle<User> mrsh = new MyResultSetHandle<User>() {

            @Override
            public User handle(ResultSet rs) throws SQLException {
                if (rs.next()) {
                    int id = rs.getInt("id");
                    String username = rs.getString("username");
                    String password = rs.getString("password");
                    String email = rs.getString("email");
                    User user = new User(id, username, password, email);
                    return user;
                }
                return null;
            }
        };

        User user = runner.query(sql, mrsh, params);
        System.out.println(user);
    }
}

猜你喜欢

转载自blog.csdn.net/u010452388/article/details/80223689
今日推荐