第二十五章 JDBC 和数据库连接池

一、JDBC 概述(P821)

1. 基本介绍

(1)JDBC 为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。
(2)Java 程序员使用 JDBC,可以连接任何提供了 JDBC 驱动程序的数据库系统,从完成对数据库的各种操作。

2. 模拟 JDBC

public interface JdbcInterface {

    //连接
    public Object getConnection() ;
    //crud
    public void crud();
    //关闭连接
    public void close();
}
public class MysqlJdbcImpl implements JdbcInterface {
    @Override
    public Object getConnection() {
        System.out.println("得到 mysql 的连接");
        return null;
    }

    @Override
    public void crud() {
        System.out.println("完成 mysql 增删改查");
    }

    @Override
    public void close() {
        System.out.println("关闭 mysql 的连接");
    }
}
public class OracleJdbcImpl implements JdbcInterface {
    @Override
    public Object getConnection() {
        System.out.println("得到 oracle 的连接 升级");
        return null;
    }

    @Override
    public void crud() {
        System.out.println("完成 对 oracle 的增删改查");
    }

    @Override
    public void close() {
        System.out.println("关闭 oracle 的连接");
    }
}
public class TestJDBC {
    public static void main(String[] args) throws Exception {
        //完成对 mysql 的操作
        JdbcInterface jdbcInterface = new MysqlJdbcImpl();
        jdbcInterface.getConnection(); //通过接口来调用实现类[动态绑定]
        jdbcInterface.crud();
        jdbcInterface.close();
        //完成对 oracle 的操作
        System.out.println("==============================");
        jdbcInterface = new OracleJdbcImpl();
        jdbcInterface.getConnection(); //通过接口来调用实现类[动态绑定]
        jdbcInterface.crud();
        jdbcInterface.close();
    }
}

说明:JDBC 是 Java 提供一套用于数据库操作的接口 API,Java 程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。

3. JDBC API

JDBC API 是一系列的接口,它统一和规范了应用程序与数据库的连接、执行 SQL 语句,并到得到返回结果等各类操作,相关类和接口在 【java.sql】与【javax.sql】 包中

二、JDBC 快速入门

1. JDBC程序编写步骤

(1)注册驱动 - 加载 Driver 类
(2)获取连接-得到Connection
(3)执行增删改查 - 发送 SQL 给 mysql 执行
(4)释放资源-关闭相关连接

2. JDBC 第一个程序

import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class Jdbc01 {
    public static void main(String[] args) throws Exception {
        //前置工作: 在项目下创建一个文件夹比如 libs
        // 将 mysql.jar 拷贝到该目录下,点击 add to project ..加入到项目中
        //1. 注册驱动
        Driver driver = new Driver(); //创建 driver 对象
        //2. 得到连接
        //(1) jdbc:mysql:// 规定好表示协议,通过 jdbc 的方式连接 mysql
        //(2) localhost 主机,可以是 ip 地址
        //(3) 3306 表示 mysql 监听的端口
        //(4) hsp_db02 连接到 mysql dbms 的哪个数据库
        //(5) mysql 的连接本质就是前面学过的 socket 连接
        String url = "jdbc:mysql://localhost:3306/hsp_db02";

        //将 用户名和密码放入到 Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "hsp"); //密码

        Connection connect = driver.connect(url, properties);

        //3. 执行 sql
        //String sql = "insert into actor values(null, '刘德华', '男', '1970-11-11', '110')";
        //String sql = "update actor set name='周星驰' where id = 1";
        String sql = "delete from actor where id = 1";
        //statement 用于执行静态 SQL 语句并返回其生成的结果的对象
        Statement statement = connect.createStatement();

        int rows = statement.executeUpdate(sql); // 如果是 dml 语句,返回的就是影响行数
        System.out.println(rows > 0 ? "成功" : "失败");
        //4. 关闭连接资源
        statement.close();
        connect.close();
    }
}

三、获取数据库连接 5 种方式

1. 方式1 获取 Driver 实现类对象

public class Demo {

    public static void main(String[] args) throws Exception {
        Driver driver = new Driver();
        String url = "jdbc:mysql://localhost:3306/hsp_db02";

        Properties properties = new Properties();
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "hsp"); //密码
        Connection connect = driver.connect(url, properties);       
    }
}

2. 方式2 反射加载 Driver 类

public class JdbcConn {

    @Test
    public void connect02() throws Exception {
        //使用反射加载 Driver 类 , 动态加载,更加的灵活,减少依赖性
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.newInstance();
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        //将 用户名和密码放入到 Properties 对象
        Properties properties = new Properties();
        //说明 user 和 password 是规定好,后面的值根据实际情况写
        properties.setProperty("user", "root");// 用户
        properties.setProperty("password", "hsp"); //密码
        Connection connect = driver.connect(url, properties);
        System.out.println("方式 2=" + connect);
    }
}

3. 方式3 使用 DriverManager 替代 driver

public class JdbcConn {
    
    //方式 3 使用 DriverManager 替代 driver 进行统一管理
    @Test
    public void connect03() throws Exception {
        //使用反射加载 Driver
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
        Driver driver = (Driver) aClass.newInstance();
        //创建 url 和 user 和 password
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        String user = "root";

        String password = "hsp";
        DriverManager.registerDriver(driver);//注册 Driver 驱动
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("第三种方式=" + connection);
    }
}

4. 方式4 使用 Class.forName 自动完成注册驱动(推荐使用)

public class JdbcConn {

    //方式 4: 使用 Class.forName 自动完成注册驱动,简化代码
    //这种方式获取连接是使用的最多,推荐使用
    @Test
    public void connect04() throws Exception {
        //使用反射加载了 Driver 类
        //在加载 Driver 类时,完成注册
        /*
        源码:
            1. 静态代码块,在类加载时,会执行一次.
            2. DriverManager.registerDriver(new Driver());
            3. 因此注册 driver 的工作已经完成
        static {
            try {
                DriverManager.registerDriver(new Driver());
            } catch (SQLException var1) {
                throw new RuntimeException("Can't register driver!");
            }
        }
        */
        Class.forName("com.mysql.jdbc.Driver");
        //创建 url 和 user 和 password
        String url = "jdbc:mysql://localhost:3306/hsp_db02";
        String user = "root";
        String password = "hsp";
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("第 4 种方式~ " + connection);
    }
}

5. 方式5 增加配置文件

public class JdbcConn {

    //方式 5 , 在方式 4 的基础上改进,增加配置文件,让连接 mysql 更加灵活
    @Test
    public void connect05() throws Exception {
        //通过 Properties 对象获取配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        Class.forName(driver);//建议写上
        Connection connection = DriverManager.getConnection(url, user, password);
        System.out.println("方式 5 " + connection);
    }
}

四、ResultSet(结果集)

1. 基本介绍

(1)表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
(2)ResultSet 对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前。
(3)next 方法将光标移动到下一行,并且由于在 ResultSet 对象中没有更多行时返回 false,因此可以在 while 循环中使用循环来遍历结果集。

2. 应用实例

演示 select 语句返回 ResultSet ,并取出结果。

public class ResultSet_ {
    public static void main(String[] args) throws Exception {
        //通过 Properties 对象获取配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        //1. 注册驱动
        Class.forName(driver);//建议写上
        //2. 得到连接
        Connection connection = DriverManager.getConnection(url, user, password);

        //3. 得到 Statement
        Statement statement = connection.createStatement();
        //4. 组织 SqL
        String sql = "select id, name , sex, borndate from actor";
        //执行给定的 SQL 语句,该语句返回单个 ResultSet 对象
        /*
        +----+-----------+-----+---------------------+
        | id | name | sex | borndate |
        +----+-----------+-----+---------------------+
        | 4 | 刘德华 | 男 | 1970-12-12 00:00:00 |
        | 5 | jack | 男 | 1990-11-11 00:00:00 |
        +----+-----------+-----+---------------------+
        */
        /*
        老韩阅读 debug 代码 resultSet 对象的结构
        */
        ResultSet resultSet = statement.executeQuery(sql);
        //5. 使用 while 取出数据
        while (resultSet.next()) { // 让光标向后移动,如果没有更多行,则返回 false
            int id = resultSet.getInt(1); //获取该行的第 1 列
            //int id1 = resultSet.getInt("id"); 通过列名来获取值, 推荐
            String name = resultSet.getString(2);//获取该行的第 2 列
            String sex = resultSet.getString(3);
            Date date = resultSet.getDate(4);
            System.out.println(id + "\t" + name + "\t" + sex + "\t" + date);
        }
        //6. 关闭连接
        resultSet.close();
        statement.close();
        connection.close();
    }

}

五、Statement

1. 基本介绍

(1)Statement 对象,用于执行静态 SQL 语句并返回其生成的结果的对象。
(2)在连接建立后,需要对数据库进行访问,执行命名或是 SQL 语句,可以通过
1️⃣Statement【存在 SQL 注入】
2️⃣PreparedStatement【预处理】
3️⃣CallableStatement【存储过程】
(3)Statement 对象执行 SQL 语句,存在 SQL 注入风险。
(4)SQL注入 是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL 语句段或命令,恶意攻击数据库。
(5)要防范 SQL 注入,只要用 PreparedStatement(从 Statement 扩展而来)取
代 Statement 就可以了。

2. 应用实例

public class ResultSet_ {
    public static void main(String[] args) throws Exception {
        //让用户输入管理员名和密码(万能用户名和密码)
        System.out.print("请输入管理员的名字: ");
        String admin_name = "1\' or";
        System.out.print("请输入管理员的密码: ");
        String admin_pwd = "\'1\'=\'1 ";

        //通过 Properties 对象获取配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
//获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");

        //1. 注册驱动
        Class.forName(driver);
        //2. 得到连接
        Connection connection = DriverManager.getConnection(url, user, password);
        //3. 得到 Statement
        Statement statement = connection.createStatement();
        //4. 组织 SqL
        String sql = "select name , pwd from admin where "
                + "name ='" + admin_name
                + "' and pwd = '" + admin_pwd + "'";
        ResultSet resultSet = statement.executeQuery(sql);
        if (resultSet.next()) { //如果查询到一条记录,则说明该管理存在
            System.out.println("恭喜, 登录成功");
        } else {
            System.out.println("对不起,登录失败");
        }
        //关闭连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

六、PreparedStatement

1. 基本介绍

(1)PreparedStatement 执行的 SQL 语句中的参数用问号(?)来表示,调用
Preparedstatement 对象的 setXxx()方法来设置这些参数。setXxx()方法有
两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从1开始),第二
个是设置的 SQL 语句中的参数的值。
(2)调用 executeQuery(),返回 ResultSet 对象。
(3)调用 executeUpdate(),执行更新,包括增、删、修改。

2. 预处理好处

(1)不再使用 + 拼接 sql 语句,减少语法错误。
(2)有效的解决了 sql 注入问题!
(3)大大减少了编译次数,效率较高。

3. 应用实例

public class PreparedStatement_ {
    public static void main(String[] args) throws Exception {

        Scanner scanner = new Scanner(System.in);
        //让用户输入管理员名和密码
        System.out.print("请输入管理员的名字: "); //next(): 当接收到 空格或者 '就是表示结束
        String admin_name = scanner.nextLine(); // 老师说明,如果希望看到 SQL 注入,这里需要用 nextLine
        System.out.print("请输入管理员的密码: ");
        String admin_pwd = scanner.nextLine();

        //通过 Properties 对象获取配置文件的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //获取相关的值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        //1. 注册驱动
        Class.forName(driver);//建议写上
        //2. 得到连接
        Connection connection = DriverManager.getConnection(url, user, password);

        //3. 得到 PreparedStatement
        //3.1 组织 SqL , Sql 语句的 ? 就相当于占位符
        String sql = "select name , pwd from admin where name =? and pwd = ?";
        //3.2 preparedStatement 对象实现了 PreparedStatement 接口的实现类的对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //3.3 给 ? 赋值
        preparedStatement.setString(1, admin_name);
        preparedStatement.setString(2, admin_pwd);
        //4. 执行 select 语句使用 executeQuery
        // 如果执行的是 dml(update, insert ,delete) executeUpdate()
        // 这里执行 executeQuery ,不要在写 sql
        ResultSet resultSet = preparedStatement.executeQuery(sql);
        if (resultSet.next()) { //如果查询到一条记录,则说明该管理存在
            System.out.println("恭喜, 登录成功");
        } else {
            System.out.println("对不起,登录失败");
        }
        //关闭连接
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

七、封装 JDBCUtils

1. 说明

在 jdbc 操作中,获取连接和释放资源是经常使用到,可以将其封装 JDBC 连接的工具类 JDBCUtils。

2. 代码实现

public class JDBCUtils {

    //定义相关的属性(4 个), 因为只需要一份,因此,我们做出 static
    private static String user; //用户名
    private static String password; //密码
    private static String url; //url
    private static String driver; //驱动名

    //在 static 代码块去初始化
    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\mysql.properties"));
            //读取相关的属性值
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            driver = properties.getProperty("driver");
        } catch (IOException e) {
            //在实际开发中,我们可以这样处理
            //1. 将编译异常转成 运行异常
            //2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便. 
            throw new RuntimeException(e);
        }
    }

    //连接数据库, 返回 Connection
    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            //1. 将编译异常转成 运行异常
            //2. 调用者,可以选择捕获该异常,也可以选择默认处理该异常,比较方便. 
            throw new RuntimeException(e);
        }
    }

    //关闭相关资源
    /*
    1. ResultSet 结果集
    2. Statement 或者 PreparedStatement
    3. Connection
    4. 如果需要关闭资源,就传入对象,否则传入 null
    */
    public static void close(ResultSet set, Statement statement, Connection connection) {
        //判断是否为 null
        try {
            if (set != null) {
                set.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            //将编译异常转成运行异常抛出
            throw new RuntimeException(e);
        }
    }
}

八、事务

1. 基本介绍

(1)JDBC 程序中当一个 Connection 对象创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
(2)JDBC 程序中为了让多个 SQL 语句作为一个整体执行,需要 使用事务
(3)调用 Connection 的 setAutoCommit(false)可以取消自动提交事务。
(4)在所有的 SQL 语句都成功执行后,调用 Connection 的 commit()方法提交事务。
(5)在其中某个操作失败或出现异常时,调用 Connection 的 rollback()方法回滚事务。

2. 代码实现

public class Transaction_ {

    //事务来解决
    @Test
    public void useTransaction() {
        //操作转账的业务
        //1. 得到连接
        Connection connection = null;
        //2. 组织一个 sql
        String sql = "update account set balance = balance - 100 where id = 1";
        String sql2 = "update account set balance = balance + 100 where id = 2";
        PreparedStatement preparedStatement = null;
        //3. 创建 PreparedStatement 对象
        try {
            connection = JDBCUtils.getConnection(); // 在默认情况下,connection 是默认自动提交
            
            //将 connection 设置为不自动提交
            connection.setAutoCommit(false); //开启了事务
            
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeUpdate(); // 执行第 1 条 sql
            int i = 1 / 0; //抛出异常
            preparedStatement = connection.prepareStatement(sql2);
            preparedStatement.executeUpdate(); // 执行第 3 条 sql
            //这里提交事务
            connection.commit();
        } catch (SQLException e) {
            //这里我们可以进行回滚,即撤销执行的 SQL
            //默认回滚到事务开始的状态. System.out.println("执行发生了异常,撤销执行的 sql");
            try {
                connection.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.close(null, preparedStatement, connection);
        }
    }
}

九、批处理

1. 基本介绍

(1)当需要感批插入或者更新记录时。可以采用 Java 的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
(2)JDBC 的批量处理语句包括下面方法:
1️⃣addBatch():添加需要批量处理的 SQL 语句或参数
2️⃣executeBatch():执行批量处理语句
3️⃣clearBatch():清空批处理包的语句
(3)JDBC 连接 MySQL 时,如果要使用批处理功能,请再 url 中加参数【rewriteBatcherStatements=true】
(4)批处理往往和 PreparedStatement 一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高。

2. 应用实例

public class Batch_ {

    //使用批量方式添加数据
    @Test
    public void batch() throws Exception {
        Connection connection = JDBCUtils.getConnection();
        String sql = "insert into admin2 values(null, ?, ?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        System.out.println("开始执行");
        long start = System.currentTimeMillis();//开始时间
        for (int i = 0; i < 5000; i++) {//5000 执行
            preparedStatement.setString(1, "jack" + i);
            preparedStatement.setString(2, "666");
            //将 sql 语句加入到批处理包中
            preparedStatement.addBatch();
            //当有 1000 条记录时,在批量执行
            if ((i + 1) % 1000 == 0) {//满 1000 条 sql
                preparedStatement.executeBatch();
                //清空一把
                preparedStatement.clearBatch();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("批量方式 耗时=" + (end - start));//批量方式 耗时=108
        //关闭连接
        JDBCUtils.close(null, preparedStatement, connection);
    }
}

十、数据库连接池

1. 传统获取 Connection 问题分析

(1)传统的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证IP地址、用户名和密码。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器前溃。
(2)每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库 内存泄露,最终将导致重启数据库。
(3)传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL 崩溃。
(4)解决传统开发中的数据库连接问题,可以采用数据库 连接池 技术(connection pool)。

2. 数据库连接池基本介绍

(1)预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”取出一个,使用完毕之后再放回去。
(2)数据库连接池负责分配、管理和释放数据库连接,它允许应用程序 重复使用 一个现有的数据库连接,而不是重新建立一个。
(3)当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

3. 数据库连接池种类

(1) JDBC 的数据库连接池使用 javax.sgl.DataSource 来表示,DataSource 只是一个接口,该接口通常由第三方提供实现。
1️⃣ C3P0 数据库连接池,速度相对较慢,稳定性不错(hibernate,spring)
2️⃣DBCP 数据库连接池,速度相对 C3P0 较快,但不稳定
3️⃣Proxool 数据库连接池,有监控连接池状态的功能,稳定性较 C3P0 差一点
4️⃣BoneCP数据库连接池,速度快
5️⃣ Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3PO、Proxool 优点于一身的数据库连接池。

4. C3P0 应用实例

public class C3P0_ {

    //方式 1: 相关参数,在程序中指定 user, url , password 等
    @Test
    public void testC3P0_01() throws Exception {
        //1. 创建一个数据源对象
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();

        //2. 通过配置文件 mysql.properties 获取相关连接的信息
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\mysql.properties"));
        //读取相关的属性值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");
        //给数据源 comboPooledDataSource 设置相关的参数
        //注意:连接管理是由 comboPooledDataSource 来管理
        comboPooledDataSource.setDriverClass(driver);
        comboPooledDataSource.setJdbcUrl(url);
        comboPooledDataSource.setUser(user);
        comboPooledDataSource.setPassword(password);

        //设置初始化连接数
        comboPooledDataSource.setInitialPoolSize(10);
        //最大连接数
        comboPooledDataSource.setMaxPoolSize(50);
        //测试连接池的效率, 测试对 mysql 5000 次操作
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            //这个方法就是从 DataSource 接口实现的
            Connection connection = comboPooledDataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        //c3p0 5000 连接 mysql 耗时=391
        System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));
    }

    //方式 2: 使用配置文件模板来完成
    @Test
    public void testC3P0_02() throws SQLException {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_edu");
        //测试 5000 次连接 mysql
        long start = System.currentTimeMillis();
        System.out.println("开始执行....");
        for (int i = 0; i < 500000; i++) {
            Connection connection = comboPooledDataSource.getConnection();
            connection.close();
        }
        long end = System.currentTimeMillis();
        //c3p0 的第二种方式 耗时=413
        System.out.println("c3p0 的第二种方式(500000) 耗时=" + (end - start));//1917
    }
}

5. Druid(德鲁伊)应用实例

public class Druid_ {

    @Test
    public void testDruid() throws Exception {
        //1. 加入 Druid jar 包
        //2. 加入 配置文件 druid.properties , 将该文件拷贝项目的 src 目录
        //3. 创建 Properties 对象, 读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\druid.properties"));
        //4. 创建一个指定参数的数据库连接池, Druid 连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            Connection connection = dataSource.getConnection();
            System.out.println(connection.getClass());
            //System.out.println("连接成功!");
            connection.close();
        }
        long end = System.currentTimeMillis();
        //druid 连接池 操作 5000 耗时=412
        System.out.println("druid 连接池 操作 500000 耗时=" + (end - start));//539
    }
}

十一、Apache—DBUtils

1. 基本介绍

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

DbUtils类
(1)QueryRunner类:该类封装了 SQL 的执行,是线程安全的。可以实现增、删、改、查、批处理。
(2)使用 QueryRunner 类实现查询。
(3)ResultSetHandler接口:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。第二十五章 JDBC 和数据库连接池_第1张图片

2. 应用实例

public class DBUtils_USE {

    //使用 apache-DBUtils 工具类 + druid 完成对表的 crud 操作
    @Test
    public void testQueryMany() throws Exception { //返回结果是多行的情况
        //1. 得到 连接 (druid)
        Connection connection = JDBCUtilsByDruid.getConnection();
        //2. 使用 DBUtils 类和接口 , 先引入 DBUtils 相关的 jar , 加入到本 Project
        //3. 创建 QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //4. 就可以执行相关的方法,返回 ArrayList 结果集
        //String sql = "select * from actor where id >= ?";
        // 注意: sql 语句也可以查询部分列
        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
        //(6) 1 就是给 sql 语句中的? 赋值,可以有多个值,因为是可变参数 Object... params
        //(7) 底层得到的 resultset ,会在 query 关闭, 关闭 PreparedStatment
        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);
    }
}

十二、DAO

1. 基本说明

(1)DAO:data access object,数据访问对象
(2)这样的通用类,称为 BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作。
(3)在 BaiscDao 的基础上,实现一张表对应一个 Dao,更好的完成功能。

你可能感兴趣的:(数据库)