跳转至

JDBC和连接池04-2

10.数据库连接池

10.5Apache-DBUtils

10.5.1resultSet问题

先分析一个问题

在之前的程序中,执行sql语句后返回的结果集存在如下问题:

  1. 关闭connection后,resultSet结果集无法使用

如果要使用结果集,就不能关闭连接,不能关闭连接,就会反过来影响别的程序去连接数据库,就会对多并发程序造成很大的影响

  1. resultSet不利于数据的管理

如果其它的方法或者程序想要使用结果集,也需要一直保持连接,影响其他程序对数据库的连接

  1. 使用返回信息也不方便

image-20221016194216492

解决方法:

定义一个类,该类的属性和表的字段是对应关系/映射关系,即用类的属性和表的字段(列)关联起来 我们把这种类叫做JavaBean,或者POJO,Domain。

一个Actor对象就对应一条actor表的记录,将Actor对象放入到ArrayList集合中(将结果集的记录封装到ArrayList中)

image-20221017215610887

10.5.2土方法完成封装

Actor类(JavaBean):

package li.jdbc.datasource;

import java.util.Date;

/**
 * Actor对象和actor表的记录对应
 */
public class Actor {//JavaBean/POJO/Domain
    private Integer id;
    private String name;
    private String sex;
    private Date borndate;
    private String phone;

    public Actor() {//一定要给一个无参构造器[反射需要]
    }

    public Actor(Integer id, String name, String sex, Date borndate, String phone) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.borndate = borndate;
        this.phone = phone;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBorndate() {
        return borndate;
    }

    public void setBorndate(Date borndate) {
        this.borndate = borndate;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "\nActor{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", borndate=" + borndate +
                ", phone='" + phone + '\'' +
                '}';
    }
}

测试程序:

package li.jdbc.datasource;

import org.junit.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;

public class JDBCUtilsByDruid_Use {
    //使用土方法尝试解决ResultSet问题==封装=>ArrayList
    @Test
    public void testSelectToArrayList() {//也可以设置返回值
        System.out.println("使用druid方式完成");
        //1.得到连接
        Connection connection = null;
        //2.组织一个sql语句
        String sql = "Select * from actor where id >=?";
        //3.创建PreparedStatement对象
        PreparedStatement preparedStatement = null;
        ResultSet set = null;
        ArrayList<Actor> list = new ArrayList<>();//创建ArrayList对象,存放actor对象
        try {
            connection = JDBCUtilsByDruid.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, 1);//给?号赋值
            //执行sql,得到结果集
            set = preparedStatement.executeQuery();
            //遍历该结果集
            while (set.next()) {
                int id = set.getInt("id");
                String name = set.getString("name");
                String sex = set.getString("sex");
                Date borndate = set.getDate("borndate");
                String phone = set.getString("phone");
                //把得到的当前 resultSet的一条记录,封装到一个Actor对象中,并放入arraylist集合
                list.add(new Actor(id,name,sex,borndate,phone));
            }

            System.out.println("list集合数据="+list);
            //or
            for (Actor actor:list) {
                System.out.println("id="+actor.getId()+"\t"+"name="+actor.getName());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源(不是真的关闭连接,而是将Connection对象放回连接池中)
            JDBCUtilsByDruid.close(set, preparedStatement, connection);
        }
        //因为ArrayList 和 connection 没有任何关联,所以该集合可以复用
        //return list;
    }
}

image-20221016202536169

10.5.3Apache-DBUtils

  • 基本介绍

commons-dbutils是Apache组织提供的一个开源 JDBC工具类库,它是对 JDBC的封装,使用dbutils能极大简化 JDBC编码的工作量。

  • DbUtils类

  • QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理

  • 使用QueryRunner类实现查询
  • ResultSetHandler接口:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式
方法 解释
ArrayHandler 将结果集中的第一行数据转成对象数组
ArrayListHandler 把结果集中的每一行数据都转成一个数组,再存放到List中
BeanHandler 将结果集中的第一行数据封装到一个对应的JavaBean实例中
BeanListHandler 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,再存放到List中
ScalarHandler 可以返回指定列的一个值或返回一个统计函数的值,比如count(1)
ColumnListHandler 将结果集中某一列的数据存放到List中
KeyedHandler(name) 将结果集中的每行数据都封装到Map中,再把这些map再存放到一个map里,其key为指定的key
MapHandler 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
MapListHandler 将结果集中的每一行数据都封装到一个Map里,然后再存放到List

DBUtils的jar包下载可以去官网下载

应用实例

使用DBUtils+数据库连接池(德鲁伊)方式,完成对表actor的crud操作

image-20221016205404120

首先将DBUtils的jar包添加到项目的libs文件夹下面,右键选择add as library

image-20221016210631987

Actor类详见10.5.2

DBUtils_USE:

package li.jdbc.datasource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class DBUtils_USE {

    //使用Apache-DBUtils工具类 + Druid 完成对表的crud操作
    @Test
    public void testQueryMany() throws SQLException {//返回结果是多行多列的情况
        //1.得到连接(Druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2.使用DBUtils类和接口(先引入相关的jar,加入到本地的project)
        //3.创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //4.然后就可以执行相关的方法,返回ArrayList结果集
        //String sql = "Select * from actor where id >=?";
        //注意 :sql语句也可以查询部分的列,没有查询的属性就在actor对象中置空
        String sql = "Select id,name from actor where id >=?";
        /**
         * (1) query方法就是执行sql语句,得到resultSet--封装到-->Arraylist集合中
         * (2) 然后返回集合
         * (3) connection就是连接
         * (4) sql:执行的sql语句
         * (5) new BeanListHandler<>(Actor.class): 将resultSet->Actor对象->封装到ArrayList
         *             底层使用反射机制,去获取 Actor的属性,然后进行封装
         * (6) 1 就是给sql语句中的?赋值,可以有多个值,因为是可变参数
         * (7) 底层得到的resultSet,会在query关闭,同时也会关闭PreparedStatement对象
         */
        List<Actor> list =
                queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
        System.out.println("输出集合的信息:");
        for (Actor actor : list) {
            System.out.print(actor);
        }

        //释放资源
        JDBCUtilsByDruid.close(null, null, connection);
    }
}

image-20221016213812415

10.5.4ApDBUtils源码分析

在上述10.5.3代码中,在List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);语句旁打上断点,点击debug,点击step into

image-20221016214206359

光标跳转到如下方法:

public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh,
        Object... params) throws SQLException {

    PreparedStatement stmt = null;//定义PreparedStatement对象
    ResultSet rs = null;//接收返回的resultSet
    T result = null;//返回ArrayList

    try {
        stmt = this.prepareStatement(conn, sql);//创建PreparedStatement
        this.fillStatement(stmt, params);//对SQL语句进行?赋值
        rs = this.wrap(stmt.executeQuery());//执行SQL,返回resultSet
        result = rsh.handle(rs);//将返回的resultSet-->封装到ArrayList中[使用反射,对传入的class对象进行处理]

    } catch (SQLException e) {
        this.rethrow(e, sql, params);

    } finally {
        try {
            close(rs);//关闭resultSet
        } finally {
            close(stmt);//关闭preparedStatement
        }
    }

    return result;//返回ArrayList
}

10.5.5ApDBUtils查询

使用DBUtils+数据库连接池(德鲁伊)方式,完成对表actor的查询操作

1.多行多列
package li.jdbc.datasource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class DBUtils_USE {

    //返回结果是多行多列的情况
    @Test
    public void testQueryMany() throws SQLException {
        //得到连接(Druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //然后就可以执行相关的方法,返回ArrayList结果集
        //sql语句也可以查询部分的列,没有查询的属性就在actor对象中置空
        String sql = "Select id,name from actor where id >=?";
        List<Actor> list =
                queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
        System.out.println("输出集合的信息:");
        for (Actor actor : list) {
            System.out.print(actor);
        }
        //释放资源
        JDBCUtilsByDruid.close(null, null, connection);
    }
}

image-20221017161939620

2.单行多列
package li.jdbc.datasource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import org.junit.Test;

import java.sql.Connection;

public class DBUtils_USE {

    //演示DBUtils+druid完成-返回的结果是单行记录(单个对象)的情况-单行多列
    @Test
    public void testQuerySingle() throws Exception {
        //1.得到连接
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2.创建QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();
        //3.执行相关方法,返回单个对象
        String sql = "Select * from actor where id =?";
        //因为我们返回的是单行记录,对应单个对象,
        // 因此使用的 Handler是BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中
        Actor actor =
                queryRunner.query(connection, sql, new BeanHandler<>(Actor.class), 2);
        System.out.println(actor);
        //释放资源
        JDBCUtilsByDruid.close(null, null, connection);
    }
}

image-20221017162030552

3.单行单列
package li.jdbc.datasource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;

import java.sql.Connection;

public class DBUtils_USE {

    //演示DBUtils+druid完成-查询结果是单行单列的情况-返回的就是Object
    @Test
    public void testScalar() throws Exception {//Scalar 单一值
        //获取连接
        Connection connection = JDBCUtilsByDruid.getConnection();
        //创建QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();
        //执行相关方法,返回单行单列
        String sql = "Select name from actor where id =?";
        //因为返回的是一个对象,因此使用的 Handler是 ScalarHandler
        Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 1);
        System.out.println(obj);
        //释放资源
        JDBCUtilsByDruid.close(null,null,connection);
    }
}

image-20221017162108628

10.5.6ApDBUtilsDML

使用DBUtils+数据库连接池(德鲁伊)方式,完成对表actor的DML(update,insert,delete)操作

package li.jdbc.datasource;

import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

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


public class DBUtils_USE {

    //演示DBUtils+druid完成 dml操作
    @Test
    public void testDML() throws SQLException {
        //获取连接
        Connection connection = JDBCUtilsByDruid.getConnection();
        //创建QueryRunner对象
        QueryRunner queryRunner = new QueryRunner();
        //这里组织sql完成update,insert,delete
        //String sql = "update actor set name =? where id =?";
        //String sql = "insert into actor values (null,?,?,?,?)";
        String sql = "delete from actor where id =?";
        /**
         * 1.执行dml的操作是queryRunner.update()
         * 2.返回的值是受影响的行数,如果返回的是0,代表sql执行失败 或者 执行成功但是表没受影响
         */
        //int affectedRow = queryRunner.update(connection, sql,"黎明","女","1999-10-09","123");
        int affectedRow = queryRunner.update(connection, sql,1000);
        System.out.println(affectedRow > 0 ? "执行成功" : "执行没有影响到表");
        //释放资源
        JDBCUtilsByDruid.close(null, null, connection);
    }
}

10.6表和JavaBean的类型映射关系

image-20221017165527148