JDBC数据库连接池与DBUtils工具

沐尤 2024-05-13 18:36:49

目录

1.JDBC

1.1 JDBC基础

1.1.1 什么是JDBC

1.2 JDBC的常用API

1.2.1 Driver接口

1.2.2 DriverManager接口

1.2.3 Connection接口

1.2.4 Statement接口

1.2.5 PreparedStatement接口

1.2.6 ResultSet接口

1.3 实现第一个JDBC程序

1.4 PreparedStatement对象

1.4.1 PreparedStatement对象介绍

1.4.2 PreparedStatement对象的使用

1.5 ResultSet对象

1.5.1ResultSet对象介绍

1.5.2 ResultSet对象的使用

2. 数据库连接池与DBUtils工具

2.1 数据库连接池

2.1.1 什么是数据库连接池

2.1.2  DataSource接口

2.1.3  DBCP数据库连接池

2.1.4  C3P0数据库连接池

2.1.5 C3P0数据库连接池实现步骤

2.2 DBUtils工具

2.2.1  DBUtils工具介绍

2.2.2  DBUtils类

2.2.3  QueryRunner类

2.2.4  ResultSetHandler接口

2.2.5  ResultSetHandler实现类

2.2.6 使用DBUtils实现增删改查


1.JDBC

1.1 JDBC基础

1.1.1 什么是JDBC

JDBC的概念

  • JDBC是Java数据库连接(Java Database Connectivity)的缩写。
  • 它是一套用于Java应用程序与数据库进行交互的标准API。
  • 允许Java应用程序连接到关系型数据库。
  • 提供了一组用于执行SQL语句的方法完成对数据的查询更新、新增和删除操作。

JDBC的优势

  1. 数据库独立性:减少与特定数据库的耦合。
  2. 统一接口:提供标准化的数据库操作方法。
  3. 可移植性:简化在不同数据库间切换的成本。
  4. 灵活性:支持多种数据库连接和操作方式。

JDBC访问数据库的方式

JDBC在应用程序与数据库之间起到了一个桥梁作用,当应用程序使用JDBC访问特定的数据库时,需要通过不同数据库驱动与不同的数据库进行连接,连接后即可对数据库进行相应的操作。


1.2 JDBC的常用API

1.2.1 Driver接口

Driver接口

  • Driver接口是所有JDBC驱动程序必须实现的接口。
  • 它为数据库厂商提供了一种标准化的方式来开发数据库驱动程序。
  • 通过实现Driver接口,数据库厂商可以使其数据库系统能够被Java应用程序通过JDBC进行访问。
  • 必须要把所使用的数据库驱动程序或类库加载到项目的classpath中(这里指MySQL驱动JAR包)。

1.2.2 DriverManager接口

DriverManager接口

  • DriverManager类用于加载JDBC驱动并且创建与数据库的连接
  • 在DriverManager类中,定义了两个比较重要的静态方法,如下表所示。

方法名称

功能描述

registerDriver(Driver driver)

该方法用于向DriverManager中注册给定的JDBC驱动程序

getConnection(String url,String user,String pwd)

该方法用于建立和数据库的连接,并返回表示连接的Connection对象


1.2.3 Connection接口

Connection接口

  • Connection接口表示Java程序和数据库的连接,只有获得该连接对象后才能访问数据库,并操作数据表。
  • 在Connection接口中定义了一系列方法,常用方法如下表所示。

方法名称

功能描述

getMetaData()

用于返回表示数据库元数据的DatabaseMetaData对象

createStatement()

用于创建一个Statement对象并将SQL语句发送到数据库

prepareStatement(String sql)

用于创建一个PreparedStatement对象并将参数化的SQL语句发送到数据库

prepareCall(String sql)

用于创建一个CallableStatement对象来调用数据库存储过程


1.2.4 Statement接口

Statement接口

  • Statement接口用于执行静态的SQL语句,并返回一个结果对象。
  • Statement接口的对象通过Connection实例的createStatement( )方法获得。
  • 利用Statement接口把静态的SQL语句发送到数据库编译执行,然后返回数据库的处理结果。
  • Statement接口提供了执行SQL语句的3个常用方法如下表所示。

方法名称

功能描述

execute(String sql)

用于执行各种SQL语句,该方法返回一个boolean类型的值,如果为true,表示所执行的SQL语句有查询结果,可通过StatementgetResultSet()方法获得查询结果

executeUpdate(String sql)

用于执行SQL中的insertupdatedelete语句。该方法返回一个int类型的值,表示数据库中受该SQL语句影响的记录条数

executeQuery(String sql)

用于执行SQL中的select语句,该方法返回一个表示查询结果的ResultSet对象


1.2.5 PreparedStatement接口

PreparedStatement接口的出现

  1. 简化SQL语句与程序变量的结合过程,提高编程效率。
  2. 通过参数化查询防止SQL注入攻击,提升数据安全性。
  3. 支持SQL语句的预编译,减少执行时的编译开销。
  4. 提供更强大、更灵活的数据访问能力,满足复杂查询需求。

PreparedStatement接口的介绍

  • PreparedStatement是Statement的子接口,用于执行预编译的SQL语句
  • PreparedStatement接口扩展了带有参数的SQL语句的执行操作,应用该接口中的SQL语句可以使用占位符“?”代替参数。
  • 通过setter()方法为SQL语句的参数赋值。

PreparedStatement接口的常用方法

方法名称

功能描述

executeUpdate()

在此PreparedStatement对象中执行SQL语句,该语句必须是一个DML语句或者是无返回内容的SQL语句,比如 DDL 语句

executeQuery()

在此PreparedStatement对象中执行SQL查询,该方法返回的是ResultSet对象

setInt(int parameterIndex, int x)

将指定参数设置为给定的int

setFloat(int parameterIndex, float x)

将指定参数设置为给定的float

setString(int parameterIndex, String x)

将指定参数设置为给定的String

setDate(int parameterIndex, Date x)

将指定参数设置为给定的Date

addBatch()

将一组参数添加到此PreparedStatement对象的批处理命令中

setCharacterStream(int parameterIndex,  java.io.Reader reader,int length)

将指定的输入流写入数据库的文本字段

setBinaryStream(int parameterIndex, java.io.InputStream x, int length)

将二进制的输入流数据写入到二进制字段中

需要注意的是setDate()方法可以设置日期内容,但参数Date的类型是java.sql.Date,而不是java.util.Date。在通过setter()方法为SQL语句中的参数赋值时,可以通过参数与SQL类型相匹配的方法(例如,如果参数类型为Integer,那么应该使用setInt()方法),也可以通过setObject()方法设置多种类型的输入参数。通过setter()方法为SQL语句中的参数赋值,具体示例代码如下所示:

String sql = "INSERT INTO users(id,name,email) VALUES(?,?,?)";
PreparedStatement  preStmt = conn.prepareStatement(sql);
preStmt.setInt(1, 1);                         //使用参数与SQL类型相匹配的方法
preStmt.setString(2, "zhangsan");             //使用参数与SQL类型相匹配的方法
preStmt.setObject(3, "zs@sina.com");          //使用setObject()方法设置参数
preStmt.executeUpdate();

1.2.6 ResultSet接口

ResultSet接口的介绍

  • 用于保存JDBC执行查询时返回的结果集
  • 结果集封装在逻辑表格中。
  • 初始化时游标在第一行前。
  • next()方法移动游标到下一行。
  • 无数据时next()返回false。
  • 常作为while循环条件迭代数据。

ResultSet接口的常用方法

方法名称

功能描述

getString(int columnIndex)

用于获取指定字段的String类型的值,参数columnIndex代表字段的索引

getString(String columnName)

用于获取指定字段的String类型的值,参数columnName代表字段的名称

getInt(int columnIndex)

用于获取指定字段的int类型的值,参数columnIndex代表字段的索引

getInt(String columnName)

用于获取指定字段的int类型的值,参数columnName代表字段的名称

getDate(int columnIndex)

用于获取指定字段的Date类型的值,参数columnIndex代表字段的索引

getDate(String columnName)

用于获取指定字段的Date类型的值,参数columnName代表字段的名称

方法名称

功能描述

next()

将游标从当前位置向下移一行

absolute(int row)

将游标移动到此 ResultSet 对象的指定行

afterLast()

将游标移动到此 ResultSet 对象的末尾,即最后一行之后

beforeFirst()

将游标移动到此 ResultSet 对象的开头,即第一行之前

previous()

将游标移动到此 ResultSet 对象的上一行

last()

将游标移动到此 ResultSet 对象的最后一行

ResultSet接口中方法的使用

  • ResultSet接口中定义了大量的getter()方法,而采用哪种getter()方法获取数据,取决于字段的数据类型
  • 既可以通过字段的名称获取指定数据,也可以通过字段的索引获取指定的数据,字段的索引是从1开始编号的。
  • 例如,数据表的第一列字段名为id,字段类型为int,那么既可以调用getInt(1),以字段索引的方式获取该列的值,也可以调用getInt("id"),以字段名称的方式获取该列的值。

1.3 实现第一个JDBC程序

步骤一:加载并注册数据库驱动

DriverManager.registerDriver(Driver driver);
或
Class.forName("DriverName");

步骤二:通过DriverManager获取数据库连接

Connection conn = DriverManager.getConnection(String url, String user, String pwd); 

getConnection()方法有3个参数,它们分别表示连接数据库的URL地址、登录数据库的用户名和密码。以MySQL数据库为例,MySQL 5.5及之前版本,其URL地址的书写格式如下:

jdbc:mysql://hostname:port/databasename

MySQL数据库的时区设定

MySQL 5.6及之后版本的MySQL数据库的时区设定比中国时间8个小时,需要在URL地址后面指定时区,具体如下所示:

jdbc:mysql://hostname:port/databasename?serverTimezone=GMT%2B8

上面代码中:

  • jdbc:mysql:是固定的写法
  • mysql指的是MySQL数据库 
  • hostname指的是主机的名称
  • port指的是连接数据库的端口号(MySQL端口号默认为3306)
  • databasename指的是MySQL中相应数据库的名称。

步骤三:通过Connection对象获取Statement对象

通过Connection对象获取Statement对象的方式有如下三种:

  • 调用createStatement()方法:创建基本的Statement对象。
  • 调用prepareStatement()方法:创建PreparedStatement对象。
  • 调用prepareCall()方法:创建CallableStatement对象。

以创建基本的Statement对象为例,创建方式如下:

Statement stmt = conn.createStatement();

步骤四:使用Statement对象执行SQL语句

所有的Statement对象都有如下三种执行SQL语句的方法:

  • execute():可以执行任何SQL语句。
  • executeQuery():通常执行查询语句,执行后返回代表结果集的ResultSet对象。
  • executeUpdate():主要用于执行DML和DDL语句。执行DML语句,如 INSERT、UPDATE或DELETE时,返回受SQL语句影响的行数,执行DDL语句返回0。

以executeQuery()方法为例,其使用方式如下:

// 执行SQL语句,获取结果集ResultSet
ResultSet rs = stmt.executeQuery(sql);

步骤五:操作ResultSet结果集

如果执行的SQL语句是查询语句,执行结果将返回一个ResultSet对象,该对象保存了SQL语句查询的结果。程序可以通过操作该ResultSet对象获取查询结果。

// 遍历ResultSet对象,获取查询结果
while (rs.next()) {
    // 通过列名或索引获取查询结果的数据
    int id = rs.getInt("id");
    String name = rs.getString("name");
    // 其他操作...
}

步骤六:关闭连接,释放资源

每次操作数据库结束后都要关闭数据库连接,释放资源,包括ResultSetStatementConnection等资源。

// 关闭ResultSet对象
if (rs != null) {
    try {
        rs.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

// 关闭Statement对象
if (stmt != null) {
    try {
        stmt.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

// 关闭Connection对象
if (conn != null) {
    try {
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

编写JDBC程序从users表中读取数据,并将结果打印在控制台,具体步骤如下所示。

步骤一:搭建数据库环境

在MySQL中创建一个名称为jdbc的数据库,然后在该数据库中创建一个users表,再向users表中插入3条数据。

步骤二:创建项目环境,导入数据库驱动

在IDEA中创建一个名称为chapter10的Web项目,将下载好的MySQL数据库驱动文件mysql-connector-java-8.0.15-bin.jar复制到项目的lib目录中。加入驱动后的项目结构如下图所示。

 创建项目环境,导入数据库驱动

将MySQL的数据库驱动添加到项目的lib目录之后,在IDEA菜单栏单击【File】→【Project Structure】→【libraries 】,进入Project Structure窗口,如下图所示。

 在上图中,单击【+】→【Java】,进入【Select Library Files】窗口,选择项目中lib目录下的MySQL的数据库驱动JAR包,将MySQL的数据库驱动发布到项目的类路径下,如下图所示。

 在上图中,单击“OK”按钮,返回Project Structure窗口,如下图所示。

 在上图中,单击【Apply】→【OK】按钮完成数据驱动的导入。至此,MySQL的数据库驱动就成功发布到项目的类路径下了。

步骤三:编写JDBC程序

在项目chapter10的src目录下,创建一个名称为cn.itcast.jdbc.example的包,在该包中创建类Example01,该类用于读取数据库中的users表,并将读取到的数据输出到控制台。

package cn.itcast.jdbc.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Date;
public class Example01 {
    public static void main(String[] args) throws SQLException {
        Statement stmt = null;
        ResultSet rs = null;
        Connection conn = null;
        try {
            // 1. 注册数据库的驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2.通过DriverManager获取数据库连接
            String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
            String username = "root";//数据库名称
            String password = "root";//数据库密码
            conn = DriverManager.getConnection (url, username,
                    password);
            // 3.通过Connection对象获取Statement对象
            stmt = conn.createStatement();
            // 4.使用Statement执行SQL语句。
            String sql = "select * from users";
            rs = stmt.executeQuery(sql);
            // 5. 操作ResultSet结果集
            System.out.println("id | name   | password | email  | birthday");
            while (rs.next()) {
                int id = rs.getInt("id"); // 通过列名获取指定字段的值
                String name = rs.getString("name");
                String psw = rs.getString("password");
                String email = rs.getString("email");
                Date birthday = rs.getDate("birthday");
                System.out.println(id + " | " + name + " | " + psw + " | " + email
                        + " | " + birthday);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally{
            // 6.回收数据库资源
            if(rs!=null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if(stmt!=null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                stmt = null;
            }
            if(conn!=null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}

步骤四:执行程序,查看结果

控制台的效果如下

实现JDBC程序需要注意的地方注册驱动

  1. 避免DriverManager.registerDriver重复注册。
  2. 使用Class.forName()方法加载驱动类代替直接注册。
  3. MySQL 5.5及以下使用Class.forName("com.mysql.jdbc.Driver")方式加载驱动类。
  4. MySQL 5.6及以上使用com.mysql.cj.Driver方式加载驱动类。
  5. 更新驱动版本时注意修改加载类名。

实现JDBC程序需要注意的地方释放资源

  1. 数据库资源宝贵,必须及时释放资源
  2. 将资源释放操作放在finally代码块中
  3. 关闭ResultSet、Statement和Connection。

1.4 PreparedStatement对象

1.4.1 PreparedStatement对象介绍

使用Statement对象实现的弊端

  1. 编译频繁:Statement对象每次执行SQL语句都要对其进行编译,导致相同的SQL语句被重复编译。
  2. 重复工作:对于相同的SQL语句,每次执行都要重新编译,增加了数据库的负担和工作量。
  3. 访问效率下降:频繁编译相同的SQL语句会降低数据库的访问效率,因为编译过程需要消耗时间和资源。

解决Statement对象实现的弊端

Statement提供了一个子类PreparedStatementPreparedStatement对象可以对SQL语句进行预编译,预编译的信息会存储在PreparedStatement对象中。当相同的SQL语句再次执行时,程序会使用PreparedStatement对象中的数据,而不需要对SQL语句再次编译去查询数据库,这样就大大提高了数据的访问效率。


1.4.2 PreparedStatement对象的使用

步骤一:

在chapter10项目的cn.itcast.jdbc.example包中创建一个名称为Example02的类,在该类中使用PreparedStatement对象对数据库进行插入数据的操作。

package cn.itcast.jdbc.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement ;
import java.sql.SQLException;
public class Example02 {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        PreparedStatement  preStmt = null;
        try {
            // 加载数据库驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
            String username = "root";
            String password = "root";
            // 创建应用程序与数据库连接的Connection对象
            conn = DriverManager.getConnection(url, username, password);
            // 执行的SQL语句
            String sql = "INSERT INTO users(name,password,email,birthday)"
                    + "VALUES(?,?,?,?)";
            // 1.创建执行SQL语句的PreparedStatement对象
            preStmt = conn.prepareStatement(sql);
            // 2.为SQL语句中的参数赋值
            preStmt.setString(1, "zl");
            preStmt.setString(2, "123456");
            preStmt.setString(3, "zl@sina.com");
            preStmt.setString(4, "1989-12-23");
            // 3.执行SQL
            preStmt.executeUpdate();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {    // 释放资源
            if (preStmt != null) {
                try {
                    preStmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                preStmt = null;
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}

Example02.java运行成功后,会在users表中插入一条数据。进入MySQL数据库,使用SELECT语句查看users表,查询结果如下图所示:


1.5 ResultSet对象

1.5.1ResultSet对象介绍

解决Statement对象实现的弊端

  1. ResultSet主要用于存储结果集。
  2. 可以通过next()方法由前向后逐个获取结果集中的数据。
  3. 如果想获取结果集中任意位置的数据,则需要在创建Statement对象时,设置两个ResultSet定义的常量,设置方式如下:
 Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENITIVE,ResultSet.CONCUR_READ_ONLY);
 ResultSet rs = st.excuteQuery(sql);

在上述方式中,常量“Result.TYPE_SCROLL_INSENITIVE”表示结果集可滚动,常量“ResultSet.CONCUR_READ_ONLY”表示以只读形式打开结果集。


1.5.2 ResultSet对象的使用

步骤一:

在chapter10项目的cn.itcast.jdbc.example包中创建一个名称为Example03的类,在该类中使用ResultSet对象取出指定数据的信息。

package cn.itcast.jdbc.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Example03 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
            String username = "root";
            String password = "root";
            //1.获取Connection对象
            conn = DriverManager.getConnection(url, username, password);
            String sql = "select * from users";
            //2.创建Statement对象并设置常量
            stmt =conn.createStatement(
                    ResultSet.TYPE_SCROLL_INSENSITIVE,
                    ResultSet.CONCUR_READ_ONLY);
            //3.执行SQL并将获取的数据信息存放在ResultSet中
            ResultSet rs = stmt.executeQuery(sql);
            //4.取出ResultSet中指定数据的信息
            System.out.print("第2条数据的name值为:");
            rs.absolute(2);        //将指针定位到结果集中第2行数据
            System.out.println(rs.getString("name"));
            System.out.print("第1条数据的name值为:");
            rs.beforeFirst();      //将指针定位到结果集中第1行数据之前
            rs.next();              //将指针向后滚动
            System.out.println(rs.getString("name"));
            System.out.print("第4条数据的name值为:");
            rs.afterLast();        //将指针定位到结果集中最后一条数据之后
            rs.previous();         //将指针向前滚动
            System.out.println(rs.getString("name"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally { // 释放资源
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                stmt = null;
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}

步骤二:

运行结果如下图所示。


思维导图


2. 数据库连接池与DBUtils工具

2.1 数据库连接池

2.1.1 什么是数据库连接池

JDBC连接数据库的缺陷

 

  1. 连接开销大:每次创建和断开Connection对象连接都消耗大量时间和资源。
  2. 并发性能低:在高并发下,频繁创建连接java.sql.Connection对象影响访问效率。
  3. 资源管理复杂:需要手动管理连接的生命周期,易出错。
  4. 性能瓶颈:对于大量请求,连接管理可能成为性能瓶颈,甚至导致数据库崩溃

数据库连接池技术

  • 避免频繁创建连接,提高性能。
  • 分配、管理、释放数据库连接。
  • 允许应用重复使用现有连接。
  • 预先在“缓冲池”中存放连接。
  • 需要时从“缓冲池”取出、使用并放回连接。

连接池连接数据库的原理

连接池连接数据库的原理图的解释

  1. 初始化时,预先创建数据库连接放入连接池。
  2. 应用向连接池申请连接,避免直接创建。
  3. 若有空闲连接,直接返回给应用;否则新建连接。
  4. 使用完连接后,连接池回收并复用,减少资源消耗。
  5. 减少创建和断开数据库连接的次数,提高数据库的访问效率

2.1.2  DataSource接口

DataSource接口的方法

为了获取数据库连接对象(Connection),JDBC提供了javax.sql.DataSource接口javax.sql.DataSource接口负责与数据库建立连接,并定义了返回值为Connection对象的方法,具体如下。

  • Connection getConnection() 。
  • Connection getConnection(String username, String password)。

注意上述两个重载的方法,都能用来获取Connection对象。不同的是,第一个方法是通过无参的方式建立与数据库的连接,第二个方法是通过传入登录信息的方式建立与数据库的连接。

DataSource接口的实现类

  • 实现类:如BasicDataSource(Apache Commons DBCP)。
  • 数据源:实现了javax.sql.DataSource接口的类。
  • 每创建一个数据库连接,这个数据库连接信息都会存储到数据源中。

数据源的解释

  • 数据源:管理数据库连接池的资源。
  • 数据库连接池:类似管道,存储和管理数据库连接。
  • 终端用户:通过连接池获取数据,如管道流水。
  • 开源实现:DBCPC3P0是常用连接池实现。

2.1.3  DBCP数据库连接池

DBCP数据库连接池

  • DBCP即数据库连接池(DataBase Connection Pool),是Apache组织下的开源连接池实现,也是Tomcat服务器使用的连接池组件。
  • 使用DBCP数据库连接池时,需要在应用程序中导入commons-dbcp2.jar和commons-pool2.jar两个JAR包。

commons-dbcp2.jar

  • commons-dbcp2.jar包是DBCP数据库连接池的实现包。
  • 包含所有操作数据库连接信息和数据库连接池初始化信息的方法。
  • 实现了DataSource接口的getConnection()方法

commons-pool2.jar

  • commons-pool2.jar包是commons-dbcp2.jar包的依赖包。
  • commons-dbcp2.jar包中的方法提供了支持
  • 缺少该依赖包,commons-dbcp2.jar包中的很多方法就没有办法实现。

commons-pool2.jar包含的核心类

  • 包含两个核心类:BasicDataSourceFactoryBasicDataSource。
  • 两个核心类都包含获取DBCP数据库连接池对象的方法。

BasicDataSource类的常用方法

方法名称

功能描述

void setDriverClassName(String driverClassName)

设置连接数据库的驱动名称

void setUrl(String url)

设置连接数据库的路径

void setUsername(String username)

设置数据库的登录账号

void setPassword(String password)

设置数据库的登录密码

void setInitialSize(int initialSize)

设置数据库连接池初始化的连接数目

void setMinIdle(int minIdle)

设置数据库连接池最小闲置连接数目

Connection getConnection()

从连接池中获取一个数据库连接

  • setDriverClassName()setUrl()setUsername()setPassword()等方法都是设置数据库连接信息的方法;
  • setInitialSize()、setMinIdle()等方法都是设置数据库连接池初始化值的方法;
  • getConnection()方法可以从DBCP数据库连接池中获取一个数据库连接。

BasicDataSourceFactory工厂类

  1. BasicDataSourceFactory:创建BasicDataSource对象的工厂类
  2. createDataSource():读取配置创建数据源。
  3. 配置文件:存储数据库连接信息。
  4. 简化代码:信息集中配置,代码更清晰。

创建数据源对象通过BasicDataSource类直接创建数据源对象

在使用BasicDataSource类创建数据源对象时,需要手动给数据源对象设置属性值,然后获取数据库连接对象。


2.1.4  C3P0数据库连接池

C3P0数据库连接池

  • 流行的开源数据库连接池。
  • 实现DataSource数据源接口,支持JDBC2和JDBC3。
  • 易于扩展,性能优越。
  • HibernateSpring支持。
  • 核心类ComboPooledDataSource,提供数据源方法。

ComboPooledDataSource类的常用方法

方法名称

功能描述

void setDriverClass()

设置连接数据库的驱动名称

void setJdbcUrl()

设置连接数据库的路径

void setUser()

设置数据库的登录账号

void setPassword()

设置数据库的登录密码

void setMaxPoolSize()

设置数据库连接池最大连接数目

void setMinPoolSize()

设置数据库连接池最小连接数目

void setInitialPoolSize()

设置数据库连接池初始化的连接数目

Connection getConnection()

从数据库连接池中获取一个连接

使用C3P0数据库连接池

  1. 创建数据源对象,使用ComboPooledDataSource
  2. 无参构造ComboPooledDataSource()或带配置名构造ComboPooledDataSource(String configName)

2.1.5 C3P0数据库连接池实现步骤

1. 通过ComboPooledDataSource()构造方法创建数据源对象

  • 调用ComboPooledDataSource()构造方法创建数据源对象
  • 需要手动给数据源对象设置属性值,然后获取数据库连接对象。

步骤一:chapter11中导入jar包并编写代码

  • 在项目chapter11中导入JAR包c3p0-0.9.2.1.jar和mchange-commons-java-0.2.3.4.jar。
  • 在cn.itcast.chapter11.example包下创建一个Example03类,Example03类采用C3P0数据库连接池。
  • 使用C3P0数据库连接池对象获取Connection对象。
package cn.itcast.chapter11.example;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Example03 {
    public static DataSource ds = null;
    // 初始化C3P0数据库连接池
    static {
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        // 设置连接数据库需要的配置信息
        try {
            cpds.setDriverClass("com.mysql.cj.jdbc.Driver");
            cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8");
                    cpds.setUser("root");
            cpds.setPassword("123456");
            //设置连接池的参数
            cpds.setInitialPoolSize(5);
            cpds.setMaxPoolSize(15);
            ds = cpds;
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    public static void main(String[] args) throws SQLException {
        // 获取数据库连接对象
        System.out.println(ds.getConnection());
    }
}

步骤二:运行chapter11项目

2. 通过ComboPooledDataSource(String configName)构造方法创建数据源对象

  • 调用ComboPooledDataSource(String configName)构造方法可以读取c3p0-config.xml配置文件
  • 根据配置文件中的配置信息创建数据源对象,然后获取数据库连接对象。

步骤一:chapter11项目下创建配置文件

在chapter11项目的src根目录下创建一个c3p0-config.xml文件,用于设置数据库的连接信息和数据源的初始化信息。

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">
            jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8
        </property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <property name="checkoutTimeout">30000</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    </default-config>
    <named-config name="itcast">
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">
            jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8
        </property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">15</property>
    </named-config>
</c3p0-config>

步骤二:chapter11项目下创建Example04类并编写代码

  • 在cn.itcast.chapter11.example包下创建一个Example04类。
  • 使用C3P0数据库连接池从配置文件中获取Connection对象
package cn.itcast.chapter11.example;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Example04 {
    public static DataSource ds = null;
    // 初始化C3P0数据库连接池
    static {
        // 使用c3p0-config.xml配置文件中的named-config节点中name属性的值
        ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");
        ds = cpds;
    }
    public static void main(String[] args) throws SQLException {
        System.out.println(ds.getConnection());
    }
}

步骤二:运行Example04

需要注意的是,在使用ComboPooledDataSource(String configName)方法创建数据源对象时必须遵循以下两点:

(1)配置文件名称必须为c3p0-config.xml或者c3p0.properties,并且位于该项目的src根目录下。

(2)当传入的configName值为空或者不存在时,使用默认配置信息创建数据源。


2.2 DBUtils工具

2.2.1  DBUtils工具介绍

DBUtils工具的作用

  • 写数据,DBUtils可以通过编写SQL语句对数据表进行增、删、改操作。
  • 读数据,DBUtils工具可以将从数据表中读取的数据结果集转换成Java常用类集合,以方便对结果进行处理。
  • 优化性能,在使用DBUtils工具的基础上,程序可以使用数据源、JNDI、数据库连接池等技术减少代码冗余。

DBUtils的核心类库的三个核心API

上图可知,DBUtils核心类库主要包括DBUtilsQueryRunnerResultSetHandler接口DBUtils工具主要通过这三个核心API进行JDBC的所有操作。


2.2.2  DBUtils

DBUtils类的作用

DBUtils类主要提供了加载JDBC驱动、关闭资源等方法DBUtils类中的方法一般为静态方法,可以直接使用类名进行调用。

DBUtils类的常用方法

方法名称

功能描述

void close(Connection conn)

当连接不为NULL时,关闭连接

void close(Statement stat)

当声明不为NULL时,关闭声明

void close(ResultSet rs)

当结果集不为NULL时,关闭结果集

void closeQuietly(Connection conn)

当连接不为NULL时,关闭连接,并隐藏一些在程序中抛出的SQL异常

void closeQuietly(Statement stat)

当声明不为NULL时,关闭声明,并隐藏一些在程序中抛出的SQL异常

void closeQuietly(ResultSet rs)

当结果集不为NULL时,关闭结果集,并隐藏一些在程序中抛出的SQL异常

void commitAndCloseQuietly(

Connection conn)

提交连接后关闭连接,并隐藏一些在程序中抛出的SQL异常

Boolean loadDriver(String driveClassName)

装载并注册JDBC驱动程序,如果成功就返回true


2.2.3  QueryRunner

QueryRunner类的作用

  • QueryRunner类简化了执行SQL语句的代码,它与ResultSetHandler配合就能完成大部分的数据库操作,大大减少了编码量。
  • QueryRunner类提供了带有一个参数的构造方法,该方法以javax.sql.DataSource的实例对象作为参数传递到QueryRunner的构造方法中来获取Connection对象。针对不同的数据库操作,QueryRunner类提供不同的方法。

QueryRunner类的常用方法

方法名称

功能描述

Object query(Connection conn,String sql,ResultSetHandler rsh,Object[] params)

执行查询操作,传入的Connection对象不能为空

Object query (String sql, ResultSetHandler rsh,Object[] params)

执行查询操作

Object query (Connection conn,String sql, ResultSetHandler rsh)

执行一个不需要置换参数的查询操作

int update(Connection conn, String sql, ResultSetHandler rsh)

执行一个更新(插入、删除、更新)操作

int update(Connection conn, String sql)

执行一个不需要置换参数的更新操作

int batch(Connection conn,String sql, Object[] []params)

批量添加、修改、删除

int batch(String sql, Object[][] params)

批量添加、修改、删除


2.2.4  ResultSetHandler接口

ResultSetHandler接口中的常见实现类

ResultSetHandler接口用于处理ResultSet结果集,它可以将结果集中的数据转换为不同的形式。根据结果集中不同的数据类型,ResultSetHandler提供了几种常见的实现类。

  • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,并存放到List里。
  • ColumnListHandler:将某列属性的值封装到List集合中。
  • ScalarHandler:将结果集中某一条记录的某一列数据存储成Object对象。

ResultSetHandler接口中的方法handle (java.sql.ResultSet rs)

ResultSetHandler接口还提供了一个单独的方法handle (java.sql.ResultSet rs),如果上述实现类没有提供想要的功能,可以自定义一个实现ResultSetHandler接口的类,然后通过重写handle()方法,实现结果集的处理。


2.2.5  ResultSetHandler实现类

BeanHandlerBeanListHandler

  • BeanHandler和BeanListHandler实现类是将结果集中的数据封装到对应的JavaBean中。
  • 在封装时,表中数据的字段和JavaBean的属性是相互对应的,一条数据记录被封装进一个对应的JavaBean对象中。
  • BeanHandler和BeanListHandler的对比如下表所示。

类名称

相同点

不同点

BeanHandler

都要先将结果集封装进JavaBean

封装单条数据,把结果集的第一条数据的字段放入一个JavaBean

BeanListHandler

封装多条数据,把每条数据的字段值各放入一个JavaBean中,再把所有JavaBean都放入List集合中

ColumnListHandlerScalarHandler

  • ColumnListHandler和ScalarHandler类可以对指定的列数据进行封装。
  • 在封装时,查询指定列数据,然后将获得的列数据封装到容器中。
  • ColumnListHandler和ScalarHandler的对比如下表所示。

类名称

相同点

不同点

ColumnListHandler

都是对指定列的查询结果集进行封装

封装指定列的所有数据,将他们放入一个List集合中

ScalarHandler

封装单条列数据,也可以封装类似countavgmaxminsum等聚合函数的执行结果

注意ColumnListHandler可以对指定列的所有数据进行封装,ScalarHandler主要针对单行单列的数据进行封装。


2.2.6 使用DBUtils实现增删改查

使用DBUtils实现增删改查的思路

由于每次操作数据库时,都需要加载数据库驱动建立数据库连接以及关闭数据库连接,为了避免代码的重复书写,需要建立一个专门用于操作数据库的工具类高效地进行用户信息的增删改查。以下是点分简述的过程:

  1. 创建用户实体类(User类)
  2. 创建数据库连接工具类
  3. 创建增删改查操作的DAO类
  4. 编写具体的增删改查逻辑

步骤一:搭建开发环境

在jdbc数据库中创建的user表作为数据表,使用DBUtils工具对user表进行增删改查操作。

步骤二:创建JavaBean

在项目chapter11的目录下,创建一个名为cn.itcast.jdbc.javabean的包,创建User.java,使用User.java作为JavaBean。

package cn.itcast.chapter11.example;
public class User {
    private int id;
    private String name;
    private String password;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

步骤三:创建C3p0Utils工具类

在项目chapter11的src目录下,创建一个名为cn.itcast.jdbc.utils的包,然后在该包下创建C3p0Utils类,用于创建数据源。

package cn.itcast.jdbc.utils;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3p0Utils {
    private static DataSource ds;
    static {
        ds = new ComboPooledDataSource();
    }
    public static DataSource getDataSource() {
        return ds;
    }
}

步骤四:创建InsertDao类,完成插入操作

在项目chapter11的src目录下,创建一个名为cn.itcast.jdbc.dao的包,然后在该包下创建一个InsertDao类,实现对user表插入数据的操作。

package cn.itcast.jdbc.dao;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import cn.itcast.jdbc.javabean.User;
import cn.itcast.jdbc.utils.C3p0Utils;
public class InsertDao {
    public static void main(String[] args)throws SQLException{
        // 创建QueryRunner对象
        QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "insert into user (name,password) values ('hello1',123456)";
        int num = runner.update(sql);
        if (num > 0){
            System.out.println("添加成功!");
        }else{
            System.out.println("添加失败!");
        }
    }
}

运行结果

 MySQL数据库发送查询语句:

步骤五:创建UpdateDao类,完成修改操作

在cn.itcast.jdbc.dao包下创建一个UpdateDao类,实现对user表数据的修改操作。

package cn.itcast.jdbc.dao;
import cn.itcast.jdbc.javabean.User;
import cn.itcast.jdbc.utils.C3p0Utils;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.SQLException;
public class UpdateDao {
    public static void main(String[] args)throws SQLException {
        // 创建QueryRunner对象
        QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
        // 写SQL语句
        String sql = "update user set name='hello2',password=111111 where name='hello1'";
        // 调用方法
        int num = runner.update(sql);
        if (num > 0){
            System.out.println("修改成功!");
        }else{
            System.out.println("修改失败!");
        }
    }
}

运行结果:

向MySQL数据库发送查询语句:

步骤六:创建DeleteDao类,完成删除操作

在cn.itcast.jdbc.dao包下创建一个DeleteDao类,实现对user表数据的删除操作。DeleteDao类的实现如下所示。

package cn.itcast.jdbc.dao;
import cn.itcast.jdbc.utils.C3p0Utils;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.SQLException;
public class DeleteDao {
    public static void main(String[] args)throws SQLException {
        // 创建QueryRunner对象
        QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
        // 写SQL语句
        String sql = "delete from user where name='hello2'";
        // 调用方法
        int num = runner.update(sql);
        if (num > 0){
            System.out.println("删除成功!");
        }else{
            System.out.println("删除失败!");
        }
    }
}

运行结果:

向MySQL数据库发送查询语句:

步骤七:创建QueryDao类,完成查询操作

1. 在cn.itcast.jdbc.dao包下创建一个QueryDao类,实现对user表中单条数据的查询操作:

package cn.itcast.jdbc.dao;
import cn.itcast.jdbc.javabean.User;
import cn.itcast.jdbc.utils.C3p0Utils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;

public class QueryDao {
    public static void main(String[] args)throws SQLException {
        // 创建QueryRunner对象
        QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
        // 写SQL语句
        String sql = "select * from user where id=2";
        // 调用方法
        User user = (User) runner.query(sql,new BeanHandler(User.class));
        System.out.println(user.getId()+","+user.getName()+","+user.getPassword());
    }
}

运行结果:

2. 查询user表中的全部数据:

package cn.itcast.jdbc.dao;
import cn.itcast.jdbc.javabean.User;
import cn.itcast.jdbc.utils.C3p0Utils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

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

public class QueryDao {
    public static void main(String[] args)throws SQLException {
        // 创建QueryRunner对象
        QueryRunner runner = new QueryRunner(C3p0Utils.getDataSource());
       /* // 写SQL语句
        String sql = "select * from user where id=2";
        // 调用方法
        User user = (User) runner.query(sql,new BeanHandler(User.class));
        System.out.println(user.getId()+","+user.getName()+","+user.getPassword());*/
        // 写SQL语句
        String sql = "select * from user";
        // 调用方法
        List<User> list = (List) runner.query(sql,new BeanListHandler(User.class));
        for(User user : list){
            System.out.println(user.getId()+","+user.getName()+","+user.getPassword());
        }

    }
}

运行结果:

需要注意的是:

 

  1. 使用BeanHandler处理单行数据查询结果。
  2. 使用BeanListHandler处理多行数据查询结果。
  3. 错误使用会导致程序报错。
  4. 注意查询方法与处理类的匹配。

思维导图

...全文
158 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

490

社区成员

发帖
与我相关
我的任务
社区描述
闽江学院IT领域专业的学生社区
社区管理员
  • c_university_1157
  • 枫_0329
  • 傅宣
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧