第25章 JDBC和数据库连接池

韩顺平_循序渐进学Java零基础_第25章 JDBC和数据库连接池(P821 - P857)

第25章 JDBC和数据库连接池

821. JDBC原理示意图

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

  • Java 设计者定义了操作数据库的接口规范,由各自数据库厂商具体实现。Java 程序员只需要面向这套接口编程即可。

JDBC原理示意图

822. JDBC模拟实现

823. JDBC快速入门

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

  • JDBC 程序编写步骤:

    1. 注册驱动:加载 Driver 类
    2. 获取连接:获得 Connection 对象
    3. 执行 SQL:获得 Statement 对象
    4. 释放资源
  • 数据库连接方式1:

package p823;

import com.mysql.jdbc.Driver;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 静态加载方式连接到数据库
 * @author Spring-_-Bear
 * @version 2021-11-08 20:56
 */
public class Jdbc01 {
    
    
    public static void main(String[] args) throws SQLException {
    
    
        // 连接到的数据库:jdbc:mysql://主机IP地址:端口/db_name
        String url = "jdbc:mysql://localhost:3306/temp";
        // 设置用户名与密码
        Properties properties = new Properties();
        properties.setProperty("user", "springbear");
        properties.setProperty("password","123");
        
        // 加载驱动: new com.mysql.jdbc.Driver()
        Driver driver = new Driver();
        // 获得连接
        Connection connect = driver.connect(url, properties);

        // SQL 语句
        String insert = "INSERT INTO actor VALUES (NULL,'张三','男','1970-01-01','10086');";

        // 得到一个执行静态 SQL 语句并返回其生成的结果的对象
        Statement statement = connect.createStatement();
        // 返回受影响的行数
        int rows = statement.executeUpdate(insert);

        System.out.println("返回受影响的行数 = " + rows);

        statement.close();
        connect.close();
    }
}

824. 数据库连接方式2

/**
 * 方式1属于静态加载,灵活性差,依赖性强,考虑方式二使用反射机制 
 */
public void connect02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
    
    
    // 连接到的数据库:jdbc:mysql://主机IP地址:端口/db_name
    String url = "jdbc:mysql://localhost:3306/temp";
    // 设置用户名与密码
    Properties properties = new Properties();
    properties.setProperty("user", "springbear");
    properties.setProperty("password","123");

    // 加载类信息
    Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
    // 获得类实例
    Driver driver = (Driver)aClass.newInstance();
    // 获得连接
    Connection connect = driver.connect(url, properties);
}

825. 数据库连接方式3

/**
 * 使用 DriverManager 替代 Driver,进行统一管理
 */
public void connect03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
    
    
    // 加载类信息
    Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
    // 获得类实例
    Driver driver = (Driver)aClass.newInstance();

    String url = "jdbc:mysql://localhost:3306/temp";
    String user = "springbear";
    String pwd = "123";

    // 注册驱动
    DriverManager.registerDriver(driver);
    // 获得连接
    Connection connection = DriverManager.getConnection(url, user, pwd);
}

826. 数据库连接方式4

/**
 * 使用 Class.forName 自动完成注册驱动
 */
public void connect04() throws ClassNotFoundException, SQLException {
    
    
    // 加载类信息,在加载 Driver 的过程中自动完成注册
    Class.forName("com.mysql.jdbc.Driver");
    /*
     * static {
     *      try {
     *          DriverManager.registerDriver(new Driver());
     *      } catch (SQLException var1) {
     *          throw new RuntimeException("Can't register driver!");
     *      }
     * }
     */
    
    String url = "jdbc:mysql://localhost:3306/temp";
    String user = "springbear";
    String pwd = "123";

    // 获得连接
    Connection connection = DriverManager.getConnection(url, user, pwd);
}

827. 数据库连接方式5

  • mysql-connector-java-5.1.37-bin.jar 驱动文件 5.1.6 之后无需 Class.forName(“com.mysql.jdbc.Driver”) 也可以直接获得连接。原因:从 jdk5 以后使用了 jdbc4,不再需要显式调用 Class.forName() 注册驱动,而是自动调用驱动 jar 包下的 META-INF\services\java.sql.Driver 文本中的类名称去注册
/**
 * 在方式4的基础上使用配置文件进行连接,更加灵活
 */
public void connect05() throws IOException, ClassNotFoundException, SQLException {
    
    
    // 从配置文件中读取信息
    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);
}

828. ResultSet底层

  • ResultSet:表示从数据库读取到的数据表的结果集。ResultSet 对象保持一个光标指向当前的数据行。最初,光标位于第一行之前。其有一个 next 方法将光标移动到下一行,并且由于在 ResultSet 对象中没有更多行时返回 false,因此常用 while 循环来遍历结果集
  • com.mysql.jdbc.JDBC42 ResultSet 类下有一个 RowData(接口) 类型的字段,rowData 中有一个 ArrayList 类型的集合 rows,rows 中又有 byte[] 类型的 internalRowData,数据真正存储的位置

829. SQL注入

  • Statement 对象,用于执行静态 SQL 语句并返回其生成结果的对象
  • 在建立连接之后,想要对数据库进行操作,一般有以下三种方式:
    1. Statement:存在 SQL 注入
    2. PreparedStatement:预处理
    3. CallableStatement:用于执行数据库存储过程
  • SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入的数据中注入非法的 SQL 语句段或命令,恶意攻击数据库
package p823;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

/**
 * @author Spring-_-Bear
 * @version 2021-11-09 09:48
 */
public class SqlInjection {
    
    
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
    
    
        // 获取用户想要查询的用户名和密码
        // Input userName = 1' or
        // Input pwd = or '1' = '1
        Scanner scanner = new Scanner(System.in);
        System.out.print("Input the name that you want to query:");
        String userName = scanner.nextLine();
        System.out.print("Input the password that you want to query:");
        String pwd = scanner.nextLine();

        // 加载配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("config\\temp.properties"));

        // 加载驱动类信息,自动注册驱动
        Class.forName(properties.getProperty("driver"));
        // 获得连接
        Connection connection = DriverManager.getConnection(properties.getProperty("url"), properties);
        Statement statement = connection.createStatement();

        String select = "SELECT * FROM admin WHERE name='" + userName + "' AND pwd= '" + pwd + "'";
        ResultSet resultSet = statement.executeQuery(select);
        while (resultSet.next()) {
    
    
            userName = resultSet.getString(1);
            pwd = resultSet.getString(2);
            System.out.println(userName + "\t" + pwd);
        }

        resultSet.close();
        statement.close();
        connection.close();
    }
}

830. Statement

831. 预处理查询

  • PreparedStatement 执行的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数。setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引,从 1 开始,第二个参数是设置 SQL 语句中的参数的值
  • 预处理的好处:
    1. 不再使用 + 拼接 SQL 语句,减少语法错误
    2. 有效解决了 SQL 注入问题
    3. 大大减少了编译次数,提高了效率
String select = "SELECT * FROM admin WHERE name = ? AND pwd= ?";
// SQL 语句预处理
PreparedStatement preparedStatement = connection.prepareStatement(select);
preparedStatement.setString(1, userName);
preparedStatement.setString(2, pwd);

// 执行 SQL 语句,得到结果集
ResultSet resultSet = preparedStatement.executeQuery();

832. 预处理DML

833. JDBC API

接口名 方法名 功能
Connection createStatement() 创建执行静态 SQL 语句的对象
createPreparedStatement(sql) 获得 SQL 语句预处理对象
Statement executeUpdate(sql) 执行 DML 语句,返回受影响行数
executeQuery(sql) 执行 DQL 语句,返回结果集
execute(sql) 执行任意 SQL 语句,返回布尔值
PreparedStatement executeUpdate(sql) 执行 DML 语句,返回受影响行数
executeQuery(sql) 执行 DQL 语句,返回结果集
execute(sql) 执行任意 SQL 语句,返回布尔值
setXxx(index,value) 设置 SQL 语句中的值
setObject(index,value) 设置 SQL 语句中的值
ResultSet next() 向下移动一行,到表尾返回 false
previous() 向上移动一行,到表头返回 false
getXxx(index || colLabel) 获得指定列的值
getObject(index || colLabel) 获得指定列的值

834. JDBC Utils开发

package utils;


import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
 * 数据库连接、资源关闭工具类
 *
 * @author Spring-_-Bear
 * @version 2021-11-09 11:40
 */
public class JdbcUtils {
    
    
    private static String driver;
    private static String url;
    private static String user;
    private static String password;
    private static String path = "config\\temp.properties";

    /**
     * 读取文件信息,初始化字段
     */
    static {
    
    
        Properties properties = new Properties();
        try {
    
    
            properties.load(new FileInputStream(path));
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
        } catch (IOException e) {
    
    
            // 实际开发中,会将此类编译异常转换成运行异常,由调用方自行处理,较为方便
            throw new RuntimeException(e);
        }
    }

    /**
     * 获得对数据库的连接
     *
     * @return 数据库连接对象
     */
    public static Connection getConnection() {
    
    
        try {
    
    
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
    
    
            // 实际开发中,会将此类编译异常转换成运行异常,由调用方自行处理,较为方便
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭对应资源
     *
     * @param resultSet  none
     * @param statement  none
     * @param connection none
     */
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
    
    
        try {
    
    
            if (resultSet != null) {
    
    
                resultSet.close();
            }
            if (statement != null) {
    
    
                statement.close();
            }
            if (connection != null) {
    
    
                connection.close();
            }
        } catch (SQLException e) {
    
    
            // 实际开发中,会将此类编译异常转换成运行异常,由调用方自行处理,较为方便
            throw new RuntimeException(e);
        }
    }
}

835. JDBC Utils DML

836. JDBC Utils 查询

837. 事务介绍

  • JDBC 程序中当一个 Connectioon 对象被创建时,默认情况下是自动提交事务:每次执行一条 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚
  • 可以调用 Connection 接口的 setAutoCommit(false) 方法取消自动提交事务
  • 在所有的 SQL 语句都执行成功后,调用 commit() 方法提交事务;在其中某个操作失败或出现异常时,调用 rollback() 方法回滚事务

838. 事务处理

public void transaction() {
    
    
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    String sub = "UPDATE account SET balance = balance - 100 WHERE id = 1";
    String add = "UPDATE account SET balance = balance + 100 WHERE id = 2";

    try {
    
    
        // 获得连接
        connection = JdbcUtils.getConnection();
        // 关闭自动提交即开启事务
        connection.setAutoCommit(false);
        // 执行 SQL
        preparedStatement = connection.prepareStatement(add);
        preparedStatement.executeUpdate();
        int temp = 1 / 0;
        preparedStatement = connection.prepareStatement(sub);
        preparedStatement.executeUpdate();
        // 提交事务
        connection.commit();
        System.out.println("所有 SQL 操作成功,提交事务!");
    } catch (SQLException | ArithmeticException e) {
    
    
        try {
    
    
            // 发生异常,撤销操作,事务回滚
            System.out.println("程序执行发生了异常,回滚所有操作!!!");
            connection.rollback();
        } catch (SQLException ex) {
    
    
            ex.printStackTrace();
        }
        e.printStackTrace();
    } finally {
    
    
        JdbcUtils.close(null, preparedStatement, connection);
    }
}

839. 批处理应用

  • 当需要成批插入或者更新记录时,可以采用 Java 的批处理更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
方法名 功能
addBatch() 添加需要批量处理的 SQL 语句或参数
executeBatch() 批量发送执行
clearBatch() 清空批处理包
  • JDBC 连接 MySQL 时,如果需要使用批处理功能,需要在 url 中添加参数:
url = "jdbc:mysql://localhost:3306/temp?rewriteBatchedStatements=true"
  • 批处理往往和 PreparedStatement 一起搭配使用,既可以减少编译次数,又可以减少运行次数,效率大大提高
preparedStatement.addBatch();
if (i % 1000 == 0) {
    
    
    preparedStatement.executeBatch();
    preparedStatement.clearBatch();
}

840. 批处理源码分析

/**
 * 第一次会创建 ArrayList<elementData>
 * elementData => Object[] 会存放预处理后的 SQL 语句
 * 当 elementDate 满后会按照 1.5 倍扩容
 * 当达到指定的容量之后,就会发送给 MySQL 执行
 * 批处理会减少发送 SQL 语句的网络开销,减少编译次数,从而提高效率
 * @throws SQLException none
 */
public void addBatch() throws SQLException {
    
    
    synchronized(this.checkClosed().getConnectionMutex()) {
    
    
        if (this.batchedArgs == null) {
    
    
            this.batchedArgs = new ArrayList();
        }

        for(int i = 0; i < this.parameterValues.length; ++i) {
    
    
            this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
        }

        this.batchedArgs.add(new com.mysql.jdbc.PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
    }
}

841. 传统链接弊端分析

  • 传统的 JDBC 数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都需要将 Connection 加载到内存中,再验证 IP 地址、用户名、密码(耗时0.05s ~ 1s)是否正确。需要向数据库连接的时候,就向数据库请求一个连接,频繁的请求操作将占用过多的系统资源,容易造成服务器崩溃
  • 每一次数据库连接,使用完后都得及时断开,如果程序出现异常而导致未能正常关闭,将导致数据库内存泄漏,最终导致数据库崩溃重启
  • 传统获取连接的方式,不能控制创建连接的数量,如果连接过多,也可能导致内存泄漏,从而导致 MySQL 崩溃

842. 数据库连接池原理

  • 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕归还给缓冲池(并不断开与数据库的连接)
  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
  • 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列
  • JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口具体实现留给第三方
连接池 特点
C3P0 速度相对较慢,稳定性不错(hibernate、spring底层均采用)
Druid 阿里巴巴提供的数据库连接池,集 DBCP、C3P0、Proxool 优点于一身
Proxool 有监控连接池状态的功能,稳定性较 C3P0 略差
BoneCP 速度快
DBCP 速度较 C3P0 快,但不稳定

843. C3P0方式1

// 创建数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 设置相关信息
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(pwd);
comboPooledDataSource.setInitialPoolSize(10);
comboPooledDataSource.setMaxPoolSize(50);
// 获得连接
Connection connection = comboPooledDataSource.getConnection();
connection.close();

844. C3P0方式2

// 将 c3p0-config.xml 文件拷贝到工程 src 目录下
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("spring_bear");
Connection connection = comboPooledDataSource.getConnection();
connection.close();

845. 德鲁伊连接池

// 加载配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("config\\druid.properties"));
// 创建一个的连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获得连接
Connection connection = dataSource.getConnection();
connection.close();

846. 德鲁伊工具类

package utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * Druid 连接池
 *
 * @author Spring-_-Bear
 * @version 2021-11-09 22:17
 */
public class JdbcUtilsByDruid {
    
    
    static DataSource dataSource;
    static String path = "config\\druid.properties";

    static {
    
    
        Properties properties = new Properties();
        try {
    
    
            properties.load(new FileInputStream(path));
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 从 Druid 连接池中返回一个数据库连接
     *
     * @return Connection
     * @throws SQLException none
     */
    public static Connection getConnection() throws SQLException {
    
    
        return dataSource.getConnection();
    }

    /**
     * 关闭连接,将连接归还给连接池
     *
     * @param resultSet  none
     * @param statement  none
     * @param connection none
     */
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
    
    
        try {
    
    
            if (resultSet != null) {
    
    
                resultSet.close();
            }
            if (statement != null) {
    
    
                statement.close();
            }
            if (connection != null) {
    
    
                connection.close();
            }
        } catch (SQLException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

847. ApDBUtils引出

  • 关闭 connection 后,resultSet 结果集无法继续使用;然而很多时候我们希望关闭 connection 连接后仍然可以继续使用查询到的数据,resultSet 存储查询结果的方式不利于数据管理;从 resultSet 结果集中获取数据时操作方法不够明确,getXxx() 方法容易出错,含义模糊
  • 定义一个类与数据库表的字段一一对应,这样的类一般称作 JavaBean 或 PoJo 或 Domain
  • 将返回的结果集的字段值封装到自定义的类的对象中,将若干个这样的对象放进集合中,就可以直接访问集合从而获得数据库表的查询结果

848. 土办法完成封装

849. ApDBUtils查询

  • commons-dbutils 是 Apache 组织提供的一个开源 JDBC 工具类,它是对 JDBC 的封装,使用 dbutils 能极大简化 JDBC 编码量
  • QueryRunner 类封装了 SQL 的执行,是线程安全的 ,可以实现增、删、改、查和批处理
  • RessultSetHandler 接口用于处理 java.sql.ResultSet,将查询到的数据转换为另一种形式
接口名 功能
ArrayHandler 将结果集中的第一行数据转换成对象数组
ArrayListHandler 将结果集中的每一行都转换成一个数组,再存放到 List 中
BeanHandler 将结果集中的第一行数据封装到一个对应的 JavaBean 实例中
BeanListHandler 将结果集中的每一行数据封装到对应的 JavaBean 实例中,存放到 List
ColumnListHandler 将结果集中的某一列数据存放到 List 中
KeyedHandler(name) 将每行数据封装到 Map 中,再将 map 存入另一个 map 中
MapHandler 将结果集的第一行数据封装到 Map 中,key 是列名,value 是对应值
MapListHandler 将结果集中的每一行数据封装到 Map 中,再存放到 List 里
public void testApache() throws SQLException {
    
    
    // 获得连接
    Connection connection = JdbcUtilsByDruid.getConnection();
    // 获得 Apache 实现的查询对象
    QueryRunner queryRunner = new QueryRunner();
    String select = "SELECT * FROM cost WHERE id >= ? AND id <= ?";
    List<Fishing> fishings = queryRunner.query(connection, select, new BeanListHandler<>(Fishing.class), 1, 10);
    for (Fishing fishing : fishings) {
    
    
        System.out.println(fishing);
    }
    JdbcUtilsByDruid.close(null, null, connection);
}

850. ApDBUtils源码分析

  • 在创建 JavaBean 类时类的字段的数据类型强制使用八大基本数据类型对应的包装类,因为 MySQL 数据库表中的字段值可能为空,而 Java 只有引用数据类型才有 NULL 值

851. ApDBUtils查询2

852. ApDBUtilsDML

853. BasicDAO问题

  • Apache-dbutils + Druid-connectionPoll 简化了 JDBC 开发,但仍有以下不足:
    1. SQL 语句是固定的,不能通过参数传入,通用性不好,需进行改进,以方便 CRUD 操作
    2. 对于查询 SELECT 操作,如果有返回值,返回类型不能确定,需要使用泛型解决
    3. 未来数据库中的表会有很多,业务需求复杂,不可能只靠一个 Java 类完成
  • 解决方案:为每个表设计一个 JavaBean 类,同时为每一张数据库表设计一个专门操作它的类 Dao,将所有的具体 Dao 类中的共有部分抽象出父类 BasciDao,以更好地利用多态完成功能
  • DAO(data access object):访问数据库数据的对象
  • 创建 JavaBeam 类的时候一定要给一个无参构造器,以方便反射机制获取该类信息

854. BasicDAO分析

855. BasicDAO实现1

package dao.dao;


import dao.utils.JdbcUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

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

/**
 * 封装对数据库表的操作
 *
 * @author Spring-_-Bear
 * @version 2021-11-10 11:03
 */
public class BasicDao<T> {
    
    
    QueryRunner queryRunner = new QueryRunner();

    /**
     * DML操作
     *
     * @param sql    SQL 语句
     * @param params SQL 中参数的值
     * @return 受影响的行数
     */
    public int update(String sql, Object... params) {
    
    
        Connection connection = null;
        try {
    
    
            connection = JdbcUtilsByDruid.getConnection();
            return queryRunner.update(connection, sql, params);
        } catch (SQLException e) {
    
    
            // 将编译异常转换为运行异常,方便调用者处理
            throw new RuntimeException(e);
        } finally {
    
    
            JdbcUtilsByDruid.close(null, null, connection);
        }
    }

    /**
     * 查询多行
     *
     * @param sql    SQL 语句
     * @param clazz  具体类的 Class 对象
     * @param params SQL 语句中的具体值
     * @return 从数据库查询到的结果集,封装进 ArrayList
     */
    public List<T> getMultiRows(String sql, Class<T> clazz, Object... params) {
    
    
        Connection connection = null;
        try {
    
    
            connection = JdbcUtilsByDruid.getConnection();
            return queryRunner.query(connection, sql, new BeanListHandler<T>(clazz), params);
        } catch (SQLException e) {
    
    
            // 将编译异常转换为运行异常,方便调用者处理
            throw new RuntimeException(e);
        } finally {
    
    
            JdbcUtilsByDruid.close(null, null, connection);
        }
    }

    /**
     * 查询一行
     *
     * @param sql    SQL 语句
     * @param clazz  具体类的 Class 对象
     * @param params SQL 语句中的具体值
     * @return 从数据库查询到的一行数据
     */
    public T getOneRow(String sql, Class<T> clazz, Object... params) {
    
    
        Connection connection = null;
        try {
    
    
            connection = JdbcUtilsByDruid.getConnection();
            return queryRunner.query(connection, sql, new BeanHandler<T>(clazz), params);
        } catch (SQLException e) {
    
    
            // 将编译异常转换为运行异常,方便调用者处理
            throw new RuntimeException(e);
        } finally {
    
    
            JdbcUtilsByDruid.close(null, null, connection);
        }
    }

    /**
     * 查询单行单列
     *
     * @param sql    SQL 语句
     * @param params SQL 语句中的具体值
     * @return 从数据库查询到的一个单元格数据
     */
    public Object getOneObj(String sql, Object... params) {
    
    
        Connection connection = null;
        try {
    
    
            connection = JdbcUtilsByDruid.getConnection();
            return queryRunner.query(connection, sql, new ScalarHandler(), params);
        } catch (SQLException e) {
    
    
            // 将编译异常转换为运行异常,方便调用者处理
            throw new RuntimeException(e);
        } finally {
    
    
            JdbcUtilsByDruid.close(null, null, connection);
        }
    }
}

856. BasicDAO实现2

857. JDBC连接池梳理

猜你喜欢

转载自blog.csdn.net/weixin_51008866/article/details/121482673