架构探险-第二章:为Web应用添加业务功能(6)-优化服务层-数据库连接池

一.前言

上一节将每个线程的Connection对象保存到ThreadLocal中,实现线程安全
对Service方法中获取和关闭数据库Connection对象的操作进行了提取,
提取到DatabaseHelper中,使Service更加简单,实现了代码的重用

目前需要考虑的一个问题:
当前多次对Service的操作会频繁创建Connection
数据库的连接数是有限的,频繁创建数据库连接会对数据库造成大量的系统开销
因此,需要一种方案,实现对数据库的"池化",就是"数据库连接池"来解决这个问题

二.服务层优化-数据库连接池

使用Apache DBCP数据库连接池,对数据库连接进行"池化"

1,认识数据库连接池

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中
当用户需要访问数据库时,从连接池中取出一个已建立的空闲连接对象。
使用完毕后,将连接放回连接池中,供下一个请求访问使用。
连接的建立、断开都由连接池自身来管理。

同时,还可以通过设置连接池的参数来控制连接池中的:
初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。
也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等

2,添加Apache DBCP数据库连接池Maven依赖

pom.xml中添加Apache DBCP数据库连接池依赖

<!-- Apache DBCP -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.0.1</version>
</dependency>

3,修改DatabaseHelper代码,使用数据库连接池

添加了DBCP连接池后,获取和销毁Connection连接操作将交由连接管理
下面修改DatabaseHelper代码,对数据库连接进行"池化"

使用Apache DBCP 的org.apache.commons.dbcp2.BasicDataSource获取数据库连接
通过设置driver, url, username, password对BasicDataSource进行初始化
调用getConnection获得数据库连接

修改后的DatabaseHelper.java:

/**
 * 数据库助手类
 */
public final class DatabaseHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);

    // DbUtils类库提供的QueryRunner对象可以面向实体(Entity)进行查询
    private static final QueryRunner QUERY_RUNNER;

    // 隔离线程:用于保存当前线程的Connection对象
    private static final ThreadLocal<Connection> CONNECTION_HOLDER;

    // DBCP数据库连接池
    private static final BasicDataSource DATA_SOURCE;

    /**
     * 静态初始化
     *      读取数据库配置文件
     */
    static{
        CONNECTION_HOLDER = new ThreadLocal<Connection>();
        QUERY_RUNNER = new QueryRunner();

        Properties conf = PropsUtil.loadProps("config.properties");
        String driver = conf.getProperty("jdbc.driver");
        String url = conf.getProperty("jdbc.url");
        String username = conf.getProperty("jdbc.username");
        String password = conf.getProperty("jdbc.password");

        // 初始化DBCP
        DATA_SOURCE = new BasicDataSource();
        DATA_SOURCE.setDriverClassName(driver);
        DATA_SOURCE.setUrl(url);
        DATA_SOURCE.setUsername(username);
        DATA_SOURCE.setPassword(password);
    }

    /**
     * 获取数据库连接
     */
    public static Connection getConnection() {
        // 每次获取Connection连接,先查看是否存在
        Connection conn = CONNECTION_HOLDER.get();
        if(conn == null){
            try {
                LOGGER.info("创建Connection");
                // 从数据库连接池中获取数据库连接
                conn = DATA_SOURCE.getConnection();
            } catch (SQLException e) {
                LOGGER.error("get connection failure", e);
                throw new RuntimeException(e);
            } finally {
                // 将Connection保存到ThreadLocal中
                CONNECTION_HOLDER.set(conn);
            }
        }
        return conn;
    }

    /**
     * 关闭数据库连接
     */
    public static void closeConnection(){
        // 获取当前线程Connection对象
        Connection conn = CONNECTION_HOLDER.get();
        if(conn != null){
            try {
                conn.close();
                LOGGER.info("销毁Connection");
            } catch (SQLException e) {
                LOGGER.error("close connection failure", e);
                throw new RuntimeException(e);
            } finally {
                // 销毁当前线程的Connection对象
                CONNECTION_HOLDER.remove();
            }
        }
    }

    /**
     * 查询实体列表
     */
    public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
        List<T> entityList;
        try {
            Connection conn = getConnection();
            entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
        } catch (SQLException e) {
            LOGGER.error("query entity list failure", e);
            throw new RuntimeException(e);
        }
        // 执行数据库操作后,将Connection返还给连接池,不再关闭连接
//        finally {
//            closeConnection();
//        }
        return entityList;
    }
}
首先,对DatabaseHelper进行了一些重构工作
将QueryRunner,ThreadLocal的初始化已到了static块中

然后,添加了BasicDataSource,并在static块中对其进行初始化和配置
getConnection方法:
    原先从DriverManager获取Connection对象
    现在从BasicDataSource中获取Connection对象
如queryEntityList等数据库操作后,不再需要关闭数据库链接
    数据库的创建和销毁交由连接池管理
    使用完毕后,将Connection返还给连接池,不再关闭连接

三.结尾

通过使用Apache DBCP数据库连接池对Connection进行管理
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中
当用户需要访问数据库时,从连接池中取出一个已建立的空闲连接对象。
使用完毕后,将连接放回连接池中,供下一个请求访问使用。
连接的建立、断开都由连接池自身来管理。

有效的避免了频繁创建数据库连接带来的大量系统开销问题

至此,对于Service层优化的关键部分就完成了,
后续对其他操作数据库的Service方法和测试进行补齐,以及对测试框架进行标准化完善

猜你喜欢

转载自blog.csdn.net/abap_brave/article/details/80447349