All Downloads are FREE. Search and download functionalities are using the official Maven repository.

doobie.hikari.HikariTransactor.scala Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
// Copyright (c) 2013-2020 Rob Norris and Contributors
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT

package doobie
package hikari

import cats.effect.kernel.Async
import cats.effect.kernel.Resource
import cats.effect.kernel.Sync
import cats.effect.syntax.resource.*
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import com.zaxxer.hikari.metrics.MetricsTrackerFactory

import java.util.Properties
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ThreadFactory
import javax.sql.DataSource
import scala.concurrent.ExecutionContext

object HikariTransactor {

  /** Construct a `HikariTransactor` from an existing `HikariDatasource`. */
  def apply[M[_]: Async](
    hikariDataSource: HikariDataSource,
    connectEC: ExecutionContext,
  ): HikariTransactor[M] =
    Transactor.fromDataSource[M](hikariDataSource, connectEC)

  /** Resource yielding an unconfigured `HikariTransactor`. */
  def initial[M[_]: Async](
    connectEC: ExecutionContext,
  ): Resource[M, HikariTransactor[M]] = {
    Resource.fromAutoCloseable(Sync[M].delay(new HikariDataSource))
      .map(Transactor.fromDataSource[M](_, connectEC))
  }

  /**
   * Resource yielding a new `HikariTransactor` configured with the given
   * Config. Unless you have a good reason, consider using `fromConfig` which
   * creates the `connectEC` for you.
   */
  def fromConfigCustomEc[M[_]: Async](
    config: Config,
    connectEC: ExecutionContext,
    dataSource: Option[DataSource] = None,
    dataSourceProperties: Option[Properties] = None,
    healthCheckProperties: Option[Properties] = None,
    healthCheckRegistry: Option[Object] = None,
    metricRegistry: Option[Object] = None,
    metricsTrackerFactory: Option[MetricsTrackerFactory] = None,
    scheduledExecutor: Option[ScheduledExecutorService] = None,
    threadFactory: Option[ThreadFactory] = None,
  ): Resource[M, HikariTransactor[M]] = {
    Resource
      .liftK(
        Config.makeHikariConfig(
          config,
          dataSource,
          dataSourceProperties,
          healthCheckProperties,
          healthCheckRegistry,
          metricRegistry,
          metricsTrackerFactory,
          scheduledExecutor,
          threadFactory,
        ),
      )
      .flatMap(fromHikariConfig(_, connectEC))
  }

  /**
   * Resource yielding a new `HikariTransactor` configured with the given
   * Config. The `connectEC` is created automatically, with the same size as the
   * Hikari pool.
   */
  def fromConfig[M[_]: Async](
    config: Config,
    dataSource: Option[DataSource] = None,
    dataSourceProperties: Option[Properties] = None,
    healthCheckProperties: Option[Properties] = None,
    healthCheckRegistry: Option[Object] = None,
    metricRegistry: Option[Object] = None,
    metricsTrackerFactory: Option[MetricsTrackerFactory] = None,
    scheduledExecutor: Option[ScheduledExecutorService] = None,
    threadFactory: Option[ThreadFactory] = None,
  ): Resource[M, HikariTransactor[M]] = {
    Resource
      .liftK(
        Config.makeHikariConfig(
          config,
          dataSource,
          dataSourceProperties,
          healthCheckProperties,
          healthCheckRegistry,
          metricRegistry,
          metricsTrackerFactory,
          scheduledExecutor,
          threadFactory,
        ),
      )
      .flatMap(fromHikariConfig(_))
  }

  /**
   * Resource yielding a new `HikariTransactor` configured with the given
   * HikariConfig. Unless you have a good reason, consider using the overload
   * without explicit `connectEC`, it will be created automatically for you.
   */
  def fromHikariConfig[M[_]: Async](
    hikariConfig: HikariConfig,
    connectEC: ExecutionContext,
  ): Resource[M, HikariTransactor[M]] = Resource
    .fromAutoCloseable(Sync[M].delay(new HikariDataSource(hikariConfig)))
    .map(Transactor.fromDataSource[M](_, connectEC))

  /**
   * Resource yielding a new `HikariTransactor` configured with the given
   * HikariConfig. The `connectEC` is created automatically, with the same size
   * as the Hikari pool.
   */
  def fromHikariConfig[M[_]: Async](hikariConfig: HikariConfig): Resource[M, HikariTransactor[M]] =
    for {
      // to populate unset fields with default values, like `maximumPoolSize`
      _ <- Sync[M].delay(hikariConfig.validate()).toResource
      // Note that the number of JDBC connections is usually limited by the underlying JDBC pool.
      // You may therefore want to limit your connection pool to the same size as the underlying JDBC pool
      // as any additional threads are guaranteed to be blocked.
      // https://tpolecat.github.io/doobie/docs/14-Managing-Connections.html#about-threading
      connectEC <- ExecutionContexts.fixedThreadPool(hikariConfig.getMaximumPoolSize)
      result <- fromHikariConfig(hikariConfig, connectEC)
    } yield result

  /**
   * Resource yielding a new `HikariTransactor` configured with the given info.
   * Consider using `fromConfig` for better configurability.
   */
  def newHikariTransactor[M[_]: Async](
    driverClassName: String,
    url: String,
    user: String,
    pass: String,
    connectEC: ExecutionContext,
  ): Resource[M, HikariTransactor[M]] =
    for {
      _ <- Resource.eval(Async[M].delay(Class.forName(driverClassName)))
      t <- initial[M](connectEC)
      _ <- Resource.eval {
        t.configure { ds =>
          Async[M].delay {
            ds.setJdbcUrl(url)
            ds.setUsername(user)
            ds.setPassword(pass)
          }
        }
      }
    } yield t

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy