Springboot 配置 doris 连接

Springboot 配置 doris 连接

一. 使用 druid 连接池

因为 Doris 的前端(FE)兼容了 MySQL 协议,可以像连 MySQL 一样连 Doris。这是 Doris 的一个核心设计特性,目的是方便接入、简化生态兼容。

首先需要引入 pom 依赖:
xml 复制代码
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.1</version>
        </dependency>

在springboot 的yml文件中配置:

yml 复制代码
spring:
  datasource:
      url: jdbc:mysql://192.168.1.111:9030/database_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=3
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        initial-size: 20 # 初始化时预创建的连接数
        min-idle: 20  #最低 保持空闲的连接数
        max-active: 200 # 最大连接池数量
        max-wait: 30000 # 连接池最大允许等待的时间(单位:毫秒)
        validation-query: SELECT 1 # 验证连接是否有效
        test-while-idle: true # 在连接池空闲时是否验证连接的有效性
        test-on-borrow: true # 在从连接池中借用连接时是否验证连接的有效性
        test-on-return: false # 在连接被归还到连接池时是否验证连接的有效性
        time-between-eviction-runs-millis: 30000 # 空闲连接回收的频率(多久进行一次检查),单位为毫秒
        min-evictable-idle-time-millis: 300000 # 空闲超过多长时间后在下次检查时会被回收,单位为毫秒
        max-evictable-idle-time-millis: 600000 # 最大空闲时间的上限, 超过被强制回收
        keep-alive: true # 主动保活连接,避免网络层断链
        phy-timeout-millis: 1800000   # 物理连接的 最大空闲时间,单位是毫秒。
        remove-abandoned: true # 指定时间内没有被正常释放(例如没有及时关闭),连接池会主动回收这个连接。
        remove-abandoned-timeout: 300 # 连接被视为废弃的超时时间,单位为秒。

二.多数据源配置 doris 连接

有的时候我们需要连接多数据源, 比如要同时连接 mysql 和 doris, 这时候我们就要进行一些额外的配置。

1.配置文件配置

yml 复制代码
spring:
  datasource:
    mysql: # mysql 配置
      url: jdbc:mysql://192.168.1.111:3306/mysql_database_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      password: 654321
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        validation-query: SELECT 1
        max-active: 10
        min-idle: 2
        initial-size: 2

    doris: # doris配置
      url: jdbc:mysql://192.168.1.111:9030/database_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=3
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        initial-size: 20 # 初始化时预创建的连接数
        min-idle: 20  #最低 保持空闲的连接数
        max-active: 200 # 最大连接池数量
        max-wait: 30000 # 连接池最大允许等待的时间(单位:毫秒)
        validation-query: SELECT 1 # 验证连接是否有效
        test-while-idle: true # 在连接池空闲时是否验证连接的有效性
        test-on-borrow: true # 在从连接池中借用连接时是否验证连接的有效性
        test-on-return: false # 在连接被归还到连接池时是否验证连接的有效性
        time-between-eviction-runs-millis: 30000 # 空闲连接回收的频率(多久进行一次检查),单位为毫秒
        min-evictable-idle-time-millis: 300000 # 空闲超过多长时间后在下次检查时会被回收,单位为毫秒
        max-evictable-idle-time-millis: 600000 # 最大空闲时间的上限, 超过被强制回收
        keep-alive: true # 主动保活连接,避免网络层断链
        phy-timeout-millis: 1800000   # 物理连接的 最大空闲时间,单位是毫秒。
        remove-abandoned: true # 指定时间内没有被正常释放(例如没有及时关闭),连接池会主动回收这个连接。
        remove-abandoned-timeout: 300 # 连接被视为废弃的超时时间,单位为秒。

spring.datasource下的 mysql 和 doris可以自己自定义, 因为不管用什么名字, 我们能需在java中进行数据库数据源配置。

2.doris 数据库数据源配置:

java 复制代码
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * doris 数据库数据源配置
 * 指定要扫描的 Mapper 接口包路径(Doris用)
 * 指定对应的 SqlSessionFactory Bean 名称
 * @author HY
 * @date 2025-06-23
 */
@Configuration
@MapperScan(basePackages  = "com.ashen.test.mapper.doris" , sqlSessionFactoryRef = "dorisSqlSessionFactory")
public class DorisConfig {
    /**
     * mybatis xml 文件位置
     */
    private static final String MYBATIS_LOCATION = "classpath*:mybatis/doris/*.xml";

    /**
     * 实体类文件位置
     */
    private static final String TYPE_ALIASES_PACKAGE = "com.ashen.test.common.model.entity.doris.*";

    /**
     * 创建 Doris 数据源 Bean,注入名称为 "dorisDataSource"
     * @return
     */
    @Bean("dorisDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.doris")
    public DataSource getDb1DataSource(){
        // 构建数据源对象(默认用的是 HikariDataSource, 这个需要注意)
        //        return DataSourceBuilder.create().build();
        return new com.alibaba.druid.pool.DruidDataSource();
    }

    /**
     * 创建 SqlSessionFactory Bean,供 MyBatis 使用,注入名为 "dorisSqlSessionFactory"
     * @param dataSource 注入上面定义的 Doris 数据源
     * @return
     * @throws Exception
     */
    @Bean("dorisSqlSessionFactory")
    public SqlSessionFactory dorisSqlSessionFactory(@Qualifier("dorisDataSource") DataSource dataSource) throws Exception {
        // 创建工厂 Bean
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 设置数据源
        bean.setDataSource(dataSource);
        // 加载 Mybatis XML 文件
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MYBATIS_LOCATION));
        // 设置实体类包路径,用于简化 XML 中类型的全路径书写
        bean.setTypeAliasesPackage(TYPE_ALIASES_PACKAGE);
        // 获取 SqlSessionFactory 实例
        return bean.getObject();
    }

    /**
     * 创建 SqlSessionTemplate Bean(线程安全的 SqlSession 封装)
     * 用于执行 SQL、提交/回滚事务等
     * @param sqlSessionFactory
     * @return
     */
    @Bean("dorisSqlSessionTemplate")
    public SqlSessionTemplate dorisSqlSessionTemplate(@Qualifier("dorisSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
        // 创建并返回模板实例
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

3.msyql 数据库数据源配置:

java 复制代码
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * mysql 数据库数据源配置
 * 指定要扫描的 Mapper 接口包路径(Mysql 用)
 * 指定对应的 SqlSessionFactory Bean 名称
 * @author HY
 * @date 2025-06-23
 */
@Configuration
@MapperScan(basePackages = "com.ashen.test.mapper.mysql", sqlSessionFactoryRef = "mysqlSqlSessionFactory")
public class MysqlConfig {

    /**
     * mybatis xml 文件位置
     */
    private static final String MYBATIS_LOCATION = "classpath*:mybatis/mysql/*.xml";

    /**
     * 实体类文件位置
     */
    private static final String TYPE_ALIASES_PACKAGE = "com.ashen.test.common.model.entity.mysql.*";

    /**
     * 创建 MySQL 数据源对象
     * 被 @Primary 标记为主数据源,默认注入优先使用这个
     * 从 application.yml 读取以 spring.datasource.mysql 为前缀的属性进行绑定
     */
    @Primary
    @Bean(name="mysqlDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.mysql")
    public DataSource mysqlDataSource() {
        // 使用 DataSourceBuilder 构建数据源,支持 HikariCP、Druid 等(取决于依赖)
//        return DataSourceBuilder.create().build();
        return new com.alibaba.druid.pool.DruidDataSource();
    }

    /**
     * 创建 MySQL 对应的 SqlSessionFactory,供 MyBatis 使用
     * 指定数据源、Mapper XML 文件路径、实体别名路径
     * @param dataSource 注入 mysqlDataSource
     */
    @Primary
    @Bean("mysqlSqlSessionFactory")
    public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
        // 创建 SqlSessionFactoryBean(MyBatis 与 Spring 整合的桥梁)
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 设置数据源
        bean.setDataSource(dataSource);
        // 指定 MyBatis 的 mapper XML 文件路径(如果不配会找不到 SQL 映射)
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MYBATIS_LOCATION));
        // 设置实体类所在包,用于自动生成别名
        bean.setTypeAliasesPackage(TYPE_ALIASES_PACKAGE);
        // 返回 SqlSessionFactory 实例
        return bean.getObject();
    }

    /**
     * 创建 MyBatis 的 SqlSessionTemplate(线程安全、Spring 管理的 SqlSession)
     * 用于执行 SQL 操作、事务管理等
     * @param sqlSessionFactory 注入上一步创建的 SqlSessionFactory
     */
    @Primary
    @Bean("mysqlSqlSessionTemplate")
    public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
        // 用工厂创建出模板
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

注: 这里有个坑, 就是在 mysqlDataSource() 方法中, 必须显示的返回 。

java 复制代码
return new com.alibaba.druid.pool.DruidDataSource();

如果采用:

java 复制代码
return DataSourceBuilder.create().build();

最终将默认采用 Spring Boot 内建的 Hikari 数据源模块。

三.druid 状态监控

java 复制代码
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class DruidMonitor {

    @Lazy
    @Autowired
    private DruidDataSource dataSource;

    // 每1分钟输出一次连接池的状态
    @Scheduled(fixedRate = 600000)
    public void printDruidStats() {
        System.out.println("活跃连接数 : " + dataSource.getActiveCount());
        System.out.println("空闲连接数 : " + dataSource.getPoolingCount());
        System.out.println("最大允许的活跃连接数 : " + dataSource.getMaxActive());
        System.out.println("连接池中获取连接的最大等待时间 : " + dataSource.getMaxWait());
        System.out.println("连接池创建过的连接总数 : " + dataSource.getCreateCount());
        System.out.println("已经关闭的连接总数 : " + dataSource.getCloseCount());
        System.out.println("===================================");
    }
}

四. 连接超时问题

空闲连接超时后, 会被连接池回收, 当再次使用该连接的时候, 会报连接已关闭的错误, 这种情况并不常见, 但是有时候又会突然出现, 让我们以为是配置上有问题。

例如:

当我们遇到需要从socket或者消息队列中持续取数据时, 经常会在 while(true) 中接收消息并插入数据库, 如果我们将 @Transactional 放在 while(true) 之上的方法上, 那么整个事务周期内都将使用同一个 durid 连接, 如果长时间未从远程消息队列或者socket中获取到数据, 那么该连接就会被回收, 当再次来到数据并写库时, 就会报连接已关闭的错误。

java 复制代码
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void insert() {
        ....

        while(true){
            testMapper.insert(param);
        }
    }

因此要注意: 不要将 @Transactional 加到需要长时间运行的方法之上。

而是将插入方法脱离出去, 如下:

java 复制代码
    public void insert() {
        ....

        while(true){
            testMapper.insertData(param);
        }
    }


    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void insertData() {
        testMapper.insert(param);
    }
相关推荐
用户83071968408213 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解14 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解14 小时前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记18 小时前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者2 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺2 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart2 天前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot