mybatis中的DataSource

这篇博客介绍了MyBatis如何利用工厂方法模式实现数据源扩展,当需要添加新的数据源时,只需提供相应的Factory实现。文章详细剖析了DataSourceFactory接口及其实现,以及数据库连接池的关键操作,如获取连接、创建连接和连接的核销。在数据库连接池的管理中,重点展示了如何处理空闲连接、活跃连接、超时连接以及阻塞等待的情况,确保系统遵循‘开放-封闭’原则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工厂方法模式最终也是符合“开放-封闭”原则的,可以通过添加新的 Factory 接口实现和 Product 接口实现来扩展整个体系的功能 。

MyBatis 的数据源模块也是用到了工厂方法模式,如果需要扩展新的数据源实现时,只需要添加对应的 Factory 实现类,新的数据源就可以被 MyBatis 使用。

DataSourceFactory接口及其实现

DataSource及实现

 全局配置文件的mybatis-config.xml

 解析全局配置文件的mybatis-config.xml

 

看一下,数据库连接池的几个关键方法

1. 获取连接

2.创建连接,通过class PooledConnection implements InvocationHandler动态创建

主要是拦截Close方法

 

 3.PooledDataSource的核销方法 push和pop

 

private PooledConnection popConnection(String username, String password) throws SQLException {

    while (conn == null) {

        synchronized (state) { // 加锁同步

            // 步骤1:检测空闲连接集合

            if (!state.idleConnections.isEmpty()) { 

                // 获取空闲连接

                conn = state.idleConnections.remove(0);

            } else { // 没有空闲连接

                // 步骤2:活跃连接数没有到上限值,则创建新连接

                if (state.activeConnections.size() < poolMaximumActiveConnections) {

                    // 创建新数据库连接,并封装成PooledConnection对象

                    conn = new PooledConnection(dataSource.getConnection(), this);

                } else {// 活跃连接数已到上限值,则无法创建新连接

                    // 步骤3:检测超时连接

                    // 获取最早的活跃连接

                    PooledConnection oldestActiveConnection = state.activeConnections.get(0);

                    long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();

                    // 检测该连接是否超时

                    if (longestCheckoutTime > poolMaximumCheckoutTime) {

                        // 对超时连接的信息进行统计

                        state.claimedOverdueConnectionCount++;

                        state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;

                        state.accumulatedCheckoutTime += longestCheckoutTime;

                        // 将超时连接移出activeConnections集合

                        state.activeConnections.remove(oldestActiveConnection);

                        // 如果超时连接上有未提交的事务,则自动回滚

                        if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {

                            try {

                                oldestActiveConnection.getRealConnection().rollback();

                            } catch (SQLException e) {

                            }

                        }

                        // 创建新PooledConnection对象,但是真正的数据库连接

                        conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);

                        conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());

                        conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());

                        // 将超时PooledConnection设置为无效

                        oldestActiveConnection.invalidate();

                    } else {

                        // 步骤4:无空闲连接、无法创建新连接且无超时连接,则只能阻塞等待

                        if (!countedWait) { // 统计阻塞等待次数

                            state.hadToWaitCount++;

                            countedWait = true;

                        }

                        long wt = System.currentTimeMillis();

                        state.wait(poolTimeToWait);// 阻塞等待

                        // 统计累积的等待时间

                        state.accumulatedWaitTime += System.currentTimeMillis() - wt;

                    }

                }

            }

            if (conn != null) { // 对连接进行统计

                if (conn.isValid()) { // 检测PooledConnection是否有效

                    // 配置PooledConnection的相关属性,设置connectionTypeCode、checkoutTimestamp、lastUsedTimestamp字段的值

                    conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));

                    conn.setCheckoutTimestamp(System.currentTimeMillis());

                    conn.setLastUsedTimestamp(System.currentTimeMillis());

                    state.activeConnections.add(conn); // 添加到活跃连接集合

                    state.requestCount++;

                    state.accumulatedRequestTime += System.currentTimeMillis() - t;

                } else { 

                    ... ...// 统计失败的情况

                }

            }

        }

    }

    return conn;

}

protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {

        state.activeConnections.remove(conn); // 步骤1:从活跃连接集合中删除该连接

        if (conn.isValid()) {// 步骤2:检测该 PooledConnection 对象是否可用

            // 步骤3:检测当前PooledDataSource连接池中的空闲连接是否已经达到上限值

            if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {

                // 累计增加accumulatedCheckoutTime

                state.accumulatedCheckoutTime += conn.getCheckoutTime();

                if (!conn.getRealConnection().getAutoCommit()) {

                    // 回滚未提交的事务

                    conn.getRealConnection().rollback();

                }

                // 步骤4:将底层连接重新封装成PooledConnection对象,

                // 并添加到空闲连接集合(也就是前面提到的 idleConnections 集合)

                PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);

                state.idleConnections.add(newConn);

                // 设置新PooledConnection对象的创建时间戳和最后使用时间戳

                newConn.setCreatedTimestamp(conn.getCreatedTimestamp());

                newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());

                conn.invalidate(); // 丢弃旧PooledConnection对象

                // 唤醒所有阻塞等待空闲连接的线程

                state.notifyAll();

            } else {

                // 当前PooledDataSource连接池中的空闲连接已经达到上限值

                // 当前数据库连接无法放回到池中



                // 累计增加accumulatedCheckoutTime

                state.accumulatedCheckoutTime += conn.getCheckoutTime();

                if (!conn.getRealConnection().getAutoCommit()) {

                    // 回滚未提交的事务

                    conn.getRealConnection().rollback();

                }

                // 关闭真正的数据库连接

                conn.getRealConnection().close();

                // 将PooledConnection对象设置为无效

                conn.invalidate(); 

            }

        } else {

            // 统计无效PooledConnection对象个数

            state.badConnectionCount++; 

        }

    }

}

mybatis dynamic datasource 提供了负载平衡的解决方案。通过使用 dynamic-datasource-spring-boot-starter,你可以轻松地集成多个数据源,并实现负载均衡。 在 dynamic-datasource 中,你可以配置多个数据源,并将它们分组。每个数据源都有一个权重值,用于控制负载均衡的分配。当你执行数据库操作时,dynamic-datasource 会根据权重值选择一个数据源来执行操作,从而实现负载均衡。 以下是一个使用 dynamic-datasource 实现负载平衡的示例: ```java @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } @Bean public DynamicDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", masterDataSource); targetDataSources.put("slave", slaveDataSource); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(masterDataSource); return dynamicDataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dynamicDataSource); return sessionFactory.getObject(); } @Bean public PlatformTransactionManager transactionManager(DynamicDataSource dynamicDataSource) { return new DataSourceTransactionManager(dynamicDataSource); } } ``` 在上述示例中,我们配置了两个数据源:master 和 slave。然后,我们使用 DynamicDataSource 将这两个数据源组合起来,并设置 master 数据源为默认数据源。这样,当执行数据库操作时,dynamic-datasource 会根据权重值选择一个数据源来执行操作。 请注意,上述示例中的配置是基于 Spring Boot 的,你需要根据自己的项目配置进行相应的调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值