Druid阅读(七)设计并实现Pikaqiu连接池

目录

1 设计Pikaqiu连接池

2 实现PikaqiuDataSource

3 实现PikaqiuConnectionHolder

4 实现PikaqiuPooledConnection

5 实现PikaqiuPreparedStatementHolder

6 实现PikaqiuPooledPreparedStatement

7 运行结果


1 设计Pikaqiu连接池

上一篇文章已分析过Druid的对象设计,具体可查看:Druid阅读(六)对象设计分析_he_cha_bu的博客-CSDN博客

弄清楚主要的对象设计后,不妨尝试学着Druid的设计思路,我们来自定义设计并实现一波自己的连接池。自定义连接池取名为Pikaqiu(皮卡丘),为啥取名这个,当时脑子就这么蹦出了这么个名字。。仿照Druid的对象设计思路,设计了PikaqiuDataSource、PikaqiuConnectionHolder、PikaqiuPooledConnection、PikaqiuPreparedStatementHolder、PikaqiuPooledPreparedStatement五个对象,连接池为PikaqiuConnectionHolder[]数组。

  • PikaqiuDataSource实现DataSource接口,定义一个自定义数据源
  • PikaqiuConnectionHolder定义一个Connection的封装对象
  • PikaqiuPooledConnection为client获取连接返回的对象,对象里提供关闭以及创建PreparedStatement方法
  • PikaqiuPreparedStatementHolder定义一个PreparedStatement的封装对象
  • PikaqiuPooledPreparedStatement为client获取statement返回的对象,提供关闭以及获取结果等自定义方法

2 实现PikaqiuDataSource

首先定义lock锁,notEmpty和empty的Condition对象,用于创建线程和收缩线程间的信号通信

定义lock锁,notEmpty和empty的Condition对象,用于创建线程和收缩线程间的信号通信
public PikaqiuDataSource(){
        lock = new ReentrantLock();

        notEmpty = lock.newCondition();
        empty = lock.newCondition();
    }

然后则是提供init方法,创建Active个连接的连接池、创建连接线程、创建收缩线程

// init方法创建Active个连接的连接池、创建连接线程、创建收缩线程
public void init() throws SQLException {
        // 获取驱动
        driver = JdbcUtils.createDriver(null, JdbcUtils.getDriverClassName(basicProperties.getUrl()));

        if (pikaqiuProperities.getMaxActive() != null) {
            maxActive = pikaqiuProperities.getMaxActive();
        }

        // 初始化连接池
        connections = new PikaqiuConnectionHolder[maxActive];
        for (int i = 0; i < maxActive; i++) {
            Connection physicalConn = createPhysicalConnection();
            connections[i] = new PikaqiuConnectionHolder(this, physicalConn);
            poolingCount++;
        }

        // 创建连接线程
        createAndStartCreatorThread();

        // 创建收缩线程
        createAndStartDestroyThread();

        System.out.println("Init PikaqiuDataSource");
    }

    // 真实创建物理连接
    private Connection createPhysicalConnection() throws SQLException {
        Properties physicalConnectProperties = new Properties();
        physicalConnectProperties.put("user", basicProperties.getUsername());
        physicalConnectProperties.put("password", basicProperties.getPassword());
        return this.driver.connect(basicProperties.getUrl(), physicalConnectProperties);
    }

    // 获取连接池里的连接对象,并包装成PikaqiuPooledConnection返回
    @Override
    public Connection getConnection() {
        poolingCount--;
        // 获取最后一个连接
        PikaqiuConnectionHolder pikaqiuConnectionHolder = connections[poolingCount];
        connections[poolingCount]=null;
        Connection conn = pikaqiuConnectionHolder.conn;
        return new PikaqiuPooledConnection(conn, pikaqiuConnectionHolder);
    }

 最后面配置注册自定义PikaqiuDataSource交给Spring管理,代码如下:

@Configuration
@EnableConfigurationProperties({PikaqiuProperities.class, DataSourceProperties.class})
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class PoolConfigurer {

    @Bean(initMethod = "init")
    @ConditionalOnMissingBean
    public PikaqiuDataSource dataSource() {
        return new PikaqiuDataSource();
    }
}

 spring.datasource.type配置为自定义的PikaqiuDataSource,配置如下:

spring.datasource.url=jdbc:mysql://localhost:3306/shenyu?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver

spring.datasource.pikaqiu.maxActive=20

// type配置为自定义的PikaqiuDataSource
spring.datasource.type=com.yrz.pikaqiu.pool.PikaqiuDataSource

3 实现PikaqiuConnectionHolder

这里就做一个简单的PikaqiuConnectionHolder封装,对DataSource和Connection进行包装,代码如下:

public class PikaqiuConnectionHolder {

    protected final DataSource dataSource;
    protected final Connection conn;


    public PikaqiuConnectionHolder(DataSource dataSource, Connection conn) {
        this.dataSource = dataSource;
        this.conn = conn;
    }
}

4 实现PikaqiuPooledConnection

对Connection和PikaqiuConnectionHolder进行包装,生成PikaqiuPooledConnection,代码如下:

 public PikaqiuPooledConnection(Connection conn, PikaqiuConnectionHolder holder) {
        this.conn = conn;
        this.holder = holder;
    }

实现 prepareStatement方法,返回PikaqiuPooledPreparedStatement对象给到client,代码如下:

 @Override
    public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException {
        // 此处可以设计从statementPool先获取
        // todo
        PreparedStatement preparedStatement = conn.prepareStatement(s, i, i1);
        // 包装holder对象
        PikaqiuPreparedStatementHolder pikaqiuPreparedStatementHolder = new PikaqiuPreparedStatementHolder(preparedStatement);
        // 返回自定义preparedstatement
        return new PikaqiuPooledPreparedStatement(this, pikaqiuPreparedStatementHolder,preparedStatement);
    }

5 实现PikaqiuPreparedStatementHolder

PikaqiuPreparedStatementHolder更简单,只是简单的包装了PreparedStatement对象,当然还可以做很多事,这里省略了。。

public class PikaqiuPreparedStatementHolder {
    public final PreparedStatement statement;

    public PikaqiuPreparedStatementHolder(PreparedStatement statement) {
        this.statement = statement;
    }
}

6 实现PikaqiuPooledPreparedStatement

对PikaqiuPooledConnection、PikaqiuPreparedStatementHolder、PreparedStatement进行包装生成PikaqiuPooledPreparedStatement对象,代码如下:

public PikaqiuPooledPreparedStatement(PikaqiuPooledConnection conn, PikaqiuPreparedStatementHolder holder, PreparedStatement stmt) {
        this.conn = conn;
        this.holder = holder;
        this.stmt = stmt;
    }

实现excute()方法:

    @Override
    public boolean execute() throws SQLException {
        // todo
        return stmt.execute();
        // todo
    }

实现返回结果集方法: 

 @Override
    public ResultSet getResultSet() throws SQLException {
        return stmt.getResultSet();
    }

 实现close方法

 @Override
    public void close() throws SQLException {
        if (this.closed) {
            return;
        }

        // 关闭结果集对象
        // todo
        // 关闭statement,这里可以不是物理回收,而是放回到一个stament pool
        stmt.close();
        // 关闭连接
        this.conn.close();

        this.closed = true;

    }

 

7 运行结果

最终实现的目录结构如下:

Druid阅读(七)设计并实现Pikaqiu连接池

写一个查询方法(查询方法参考

Druid阅读(一)环境准备_he_cha_bu的博客-CSDN博客
),运行结果如下:

Druid阅读(七)设计并实现Pikaqiu连接池

 最后做下总结,虽然只是仿照Druid写了一个类似的连接池,并且细节完全没有实现,但从学到自己设计与实现,这过程本身就是一个巨大的成长,表明东西学到手,你入门会用了。

 

上一篇:JDBC 链接数据库方式


下一篇:MyBatis(技术NeiMu):基础支持层(DataSource)