druid 数据库连接池技术的使用

在实际项目开发中访问操作数据库,肯定要使用到连接池技术,不仅可以提升数据库操作效率,也在某种程度上提高了代码质量。前面我们简单介绍了 c3p0 连接池的使用,今天我们还是以操作 mysql 为例简单介绍一下 druid 数据库连接池技术的使用。

druid 是阿里技术团队提供的一款优秀的开源数据库连接池,在国内使用非常广泛。作为后起之秀,其功能灵活强大稳定,性能也比 c3p0 要高,大有替代其它第三方数据库连接池的趋势。我们也推荐在项目和产品开发中优先考虑 druid 连接池技术的使用。

druid 的 GitHub 地址为:https://github.com/alibaba/druid (GitHub 访问可能比较慢)
druid 的 Gitee 地址为:https://gitee.com/wangmt2000/druid


一、配置 druid 连接池

我们首先创建一个 JavaSE 的 Maven 项目,在 pom.xml 文件中导入 druid 和 mysql 的 jar 包。

我们可以从 https://mvnrepository.com 上进行查找有关各种 jar 包依赖的 xml 配置内容,复制粘贴到 pom.xml 即可。
druid 的 jar 包网址是:https://mvnrepository.com/artifact/com.alibaba/druid
mysql 连接 java 的驱动 jar 包网址是:https://mvnrepository.com/artifact/mysql/mysql-connector-java

我在 pom.xml 配置的都是当前最新版本的 jar 包,如下所示:

<!-- 导入 druid 的 jar 包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>

<!-- 导入 mysql 的 jar 包 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

配置好引用的 jar 包后,打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。

然后在项目的 resources 资源目录下创建 druid 的 properties 配置文件,properties 配置文件可以随意命名,但是文件内容中每项配置的 key 必须是指定的名称,这里我使用 druid.properties 作为配置文件的名称,其文件内容如下所示:

# 数据库连接参数
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/testdb
username=root
password=123456

# 初始化连接的数量
initialSize=3
# 最大连接的数量
maxActive=20
# 获取连接的最大等待时间(毫秒)
maxWait=3000

当然可选配置项有很多,上面列出的只是最常用的核心配置项,有关详细的配置项列表,可以参看以下网址:
https://github.com/alibaba/druid/wiki/DruidDataSource配置 (GitHub 访问可能比较慢)


二、使用 druid 连接池

使用 druid 连接池,主要是使用 DruidDataSourceFactory 根据 properties 配置文件内容创建出 DataSource 数据源对象,然后调用其 getConnection 方法获取数据库连接对象,拿到连接对象之后,其它的操作跟 JDBC 访问数据库的操作一模一样,唯一的区别就是当调用连接的 close 方法时,底层不再是关闭销毁连接对象,而是将连接对象放入到连接池中,以便后续新的请求到来时,直接拿去使用。具体代码如下:

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

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

        //加载配置文件
        InputStream is = druidtest.class.getClassLoader().getResourceAsStream("druid.properties");
        Properties prop = new Properties();
        prop.load(is);

        //根据配置文件内容,创建出数据源对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

        //通过数据源对象获取数据库连接
        //如果连接池中的连接已经被用完,则会等待一定的时间(所配置的时间)
        //如果等待超时,就会抛出异常
        Connection con = dataSource.getConnection();

        //执行 sql 语句,获取并打印结果集
        String sql = "select e_id,e_name,e_age from employee";
        PreparedStatement pst = con.prepareStatement(sql);
        ResultSet rs = pst.executeQuery();
        while(rs.next()) {
            System.out.println(
                    rs.getInt("e_id") + "\t" +
                    rs.getString("e_name") + "\t" +
                    rs.getInt("e_age"));
        }

        //释放资源
        rs.close();
        pst.close();

        //这里的关闭连接,并没有关闭和销毁连接
        //而是把连接对象,放入到连接池中,供后续访问时直接拿去使用
        con.close();
    }
}

如果你之前基于 JDBC 编写过通用的数据库访问类,那么将它改造为基于 druid 的数据库访问类,也是很容易的。
这里我提供一个相对比较完整的基于 druid 连接池的数据库访问类(具体细节就不详细介绍了),内容如下所示:

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class jdbcHelper {
    private static DataSource dataSource;

    //静态初始化,加载 properties 配置文件,创建数据源对象
    static {
        try {
            InputStream is =
                 druidtest.class.getClassLoader().getResourceAsStream("druid.properties");

            Properties prop = new Properties();
            prop.load(is);

            dataSource = DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    //通过数据源对象,获取连接对象
    public static Connection getConnection() {
        Connection con = null;
        try {
            con = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    //释放查询操作相关的资源(结果集对象,SQL语句对象,归还数据库连接)
    public static void close(Connection con, Statement stat, ResultSet rs) {
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (stat != null) {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    //释放增删改相关操作的资源(SQL语句对象,归还数据库连接)
    public static void close(Connection con, Statement stat) {
        close(con, stat, null);
    }

    //执行查询 SQL 语句,返回查询的单个值
    public static <T> T queryForValue(String sql, Class<T> returnType, Object... parameters) {
        T result = null;
        Connection con = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            con = dataSource.getConnection();
            pst = con.prepareStatement(sql);

            if (parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    pst.setObject(i + 1, parameters[i]);
                }
            }

            rs = pst.executeQuery();
            if (rs.next()) {
                result = returnType.cast(rs.getObject(1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, pst, rs);
        }

        return result;
    }

    //执行查询 SQL 语句,返回查询的单个实体对象
    public static <T> T queryForObject(String sql, Class<T> returnType, Object... parameters) {

        T result = null;
        Connection con = null;
        PreparedStatement pst = null;
        ResultSet rs = null;

        try {
            result = returnType.getConstructor().newInstance();
            con = dataSource.getConnection();
            pst = con.prepareStatement(sql);

            if (parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    pst.setObject(i + 1, parameters[i]);
                }
            }

            rs = pst.executeQuery();
            if (rs.next()) {
                ResultSetMetaData metaData = rs.getMetaData();
                int count = metaData.getColumnCount();
                for (int i = 1; i <= count; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = rs.getObject(columnName);
                    PropertyDescriptor pd =
                            new PropertyDescriptor(columnName.toLowerCase(), returnType);
                    Method writeMethod = pd.getWriteMethod();
                    writeMethod.invoke(result, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, pst, rs);
        }

        return result;
    }

    //执行查询 SQL 语句,返回查询的实体对象列表
    public static <T> List<T> queryForList(String sql, Class<T> returnType, Object... parameters) {
        List<T> list = new ArrayList<>();
        Connection con = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            con = dataSource.getConnection();
            pst = con.prepareStatement(sql);

            if (parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    pst.setObject(i + 1, parameters[i]);
                }
            }

            rs = pst.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            int count = metaData.getColumnCount();

            while (rs.next()) {
                T bean = returnType.getConstructor().newInstance();
                for (int i = 1; i <= count; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = rs.getObject(columnName);
                    PropertyDescriptor pd =
                            new PropertyDescriptor(columnName.toLowerCase(), returnType);
                    Method writeMethod = pd.getWriteMethod();
                    writeMethod.invoke(bean, value);
                }
                list.add(bean);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con, pst, rs);
        }

        return list;
    }

    //执行增删改 SQL 语句,返回受影响的行数
    public static int executeSql(String sql,Object...parameters) {
        int count = 0;
        Connection con = null;
        PreparedStatement pst = null;
        try{
            con = dataSource.getConnection();
            pst = con.prepareStatement(sql);

            if (parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    pst.setObject(i + 1, parameters[i]);
                }
            }

            count = pst.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(con,pst);
        }

        return count;
    }
}

然后在 Maven 项目的 test 目录下创建测试类,使用这个 druid 数据库访问类测试其使用方法:

import org.junit.Test;
import java.util.List;

public class jdbcHelperTest {

    @Test
    public void queryForValue() {
        //查询员工数量
        String sql = "select count(*) from employee";
        Long value = jdbcHelper.queryForValue(sql,Long.class);
        System.out.println(value);
    }

    @Test
    public void queryForList() {
        //查询所有员工
        String sql = "select * from employee";
        List<Employee> list = jdbcHelper.queryForList(sql, Employee.class);
        for(Employee emp : list) {
            System.out.println(emp);
        }
    }

    @Test
    public void queryForObject() {
        //查询一条员工,并打印出来
        String sql = "select * from employee WHERE e_id=?";
        Employee emp = jdbcHelper.queryForObject(sql,Employee.class,1);
        System.out.println(emp);
    }

    @Test
    public void insert() {
        //添加一条新员工
        String sql = "insert into employee(e_id,e_name,e_age) values(?,?,?)";
        Object[] params = {6,"任天蓬",40};
        int result = jdbcHelper.executeSql(sql, params);
        System.out.println(result);
    }

    @Test
    public void update() {
        //通过员工id,修改员工的年龄
        String sql = "update employee set e_age=? where e_id=?";
        Object[] params = {38,6};
        int result = jdbcHelper.executeSql(sql, params);
        System.out.println(result);
    }

    @Test
    public void delete() {
        //通过员工姓名,删除员工
        String sql = "DELETE FROM employee WHERE e_name=?";
        int result = jdbcHelper.executeSql(sql, "任天蓬");
        System.out.println(result);
    }
}

OK,以上只是简单的 druid 连接池技术的介绍,总体来说使用起来很简单,希望对大家有用。



posted @ 2022-02-13 12:02  乔京飞  阅读(10356)  评论(0编辑  收藏  举报