scala version: 2.12.7
scalikejdbc version: 4.0.0
HikariCP version: 4.0.3

近日使用scala开发flink与spark程序,遇到需要读写数据库的场景,因为手写jdbc太过繁琐,就找到了scalikejdbc的框架,该框架对jdbc进行了封装,支持scala的多种特性,也支持多数据源,用起来很方便。

但是因为其自带连接池是dbcp,性能不是太好,所以打算换成性能较高的HikariCP连接池,并进行多数据配置,写此文档进行记录。

有一点需要注意,使用第三方连接池需要配置closer参数,否则会出现数据库连接池关不掉的情况,产生大量数据库连接,造成资源泄露

首先引入scalikejdbc与HikariCP(5.0.0以上版本不支持jdk1.8)的依赖:

<dependency>
    <groupId>org.scalikejdbc</groupId>
    <artifactId>scalikejdbc_2.12</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>4.0.3</version>
</dependency>

配置单数据源

object ConfigDao {
  private var init = false
  def initDbPool(config: DbConfigDTO): Unit = {
    if (!init) {
      val dataSource: DataSource = {
        val ds = new HikariDataSource()
        ds.setJdbcUrl(config.url)
        //这里需要写一下数据库对应的Driver,有时无法自动获取
        ds.setDriverClassName("org.postgresql.Driver")
        ds.setUsername(config.username)
        ds.setPassword(config.password)
        ds
      }
      ConnectionPool.singleton(new DataSourceConnectionPool(dataSource, closer = () => dataSource.close()))
      LOG.info("初始化pg数据库连接池")
      init = true
    }
  }
}

配置多数据源

object ConfigDao {
  var initOracle: Boolean = false
  var initMysql: Boolean = false

  def initOracleDbPool(dbConfig: DbConfigDTO): Unit = {
    if (!initOracle) {
      val dataSource: DataSource = {
        val ds = new HikariDataSource()
        ds.setJdbcUrl(dbConfig.url)
        ds.setDriverClassName("oracle.jdbc.driver.OracleDriver")
        ds.setUsername(dbConfig.username)
        ds.setPassword(dbConfig.password)
        ds
      }
      //添加连接池的时候加上symbol参数,便于使用时通过NamedDB获取对应连接池
      ConnectionPool.add('oracle, new DataSourceConnectionPool(dataSource, closer = () => dataSource.close()))
      logger.info("初始化oracle数据库连接池")
      initOracle = true
    }
  }

  def initMysqlDbPool(dbConfig: DbConfigDTO): Unit = {
    if (!initMysql) {
      val dataSource: DataSource = {
        val ds = new HikariDataSource()
        ds.setJdbcUrl(dbConfig.url)
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver")
        ds.setUsername(dbConfig.username)
        ds.setPassword(dbConfig.password)
        ds
      }
      //添加连接池的时候加上symbol参数,便于使用时通过NamedDB获取对应连接池
      ConnectionPool.add('mysql, new DataSourceConnectionPool(dataSource))
      logger.info("初始化mysql数据库连接池")
      initMysql = true
    }
  }
}

然后需要使用数据库时先调用该方法初始化连接池:

单数据源使用方法

def getAreaInfo(dbConfig: DbConfigDTO): Map[String, (String, String, Int)] = {
  initDbPool(dbConfig)
  DB readOnly { implicit session =>
    sql"select area_code, area_name, up_area_code, area_lev from dim_o_ias_n_area"
      .fetchSize(500)
      .map(rs => (rs.string("area_code"), (rs.string("area_name"), rs.string("up_area_code"), rs.int("area_lev"))))
      .list
      .apply
      .toMap
  }
}

多数据源使用方法

def getAreaInfo(dbConfig: DbConfigDTO): Map[String, (String, String, Int)] = {
  initOracleDbPool(dbConfig)
  //使用NamedDB,参数填初始化连接池时注册的对应symbol
  NamedDB('oracle) readOnly { implicit session =>
    sql"select area_code, area_name, up_area_code, area_lev from dim_o_ias_n_area"
      .fetchSize(500)
      .map(rs => (rs.string("area_code"), (rs.string("area_name"), rs.string("up_area_code"), rs.int("area_lev"))))
      .list
      .apply
      .toMap
  }
}