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

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

The 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 java.util.Properties
import java.util.concurrent.{ScheduledExecutorService, ThreadFactory}

import cats.effect.implicits.*
import cats.effect.kernel.{Async, Resource, Sync}
import com.zaxxer.hikari.metrics.MetricsTrackerFactory
import com.zaxxer.hikari.{HikariConfig, HikariDataSource}
import javax.sql.DataSource

import scala.concurrent.ExecutionContext

object HikariTransactor {

  /** Construct a `HikariTransactor` from an existing `HikariDatasource`. */
  def apply[M[_]] = new HikariTransactorPartiallyApplied[M]

  class HikariTransactorPartiallyApplied[M[_]] {
    def apply(
        hikariDataSource: HikariDataSource,
        connectEC: ExecutionContext,
        logHandler: Option[LogHandler[M]] = None
    )(implicit ev: Async[M]): HikariTransactor[M] = {
      Transactor.fromDataSource[M](hikariDataSource, connectEC, logHandler)
    }
  }

  /** Resource yielding an unconfigured `HikariTransactor`. */
  def initial[M[_]: Async](
      connectEC: ExecutionContext,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M, HikariTransactor[M]] = initialWithResEffect[M, M](connectEC, logHandler)

  /** Similar to [[initial]], but with a separate effect for the construction of the Transactor
    *
    * @tparam M0
    *   the effect to construct the [[HikariTransactor]] in
    * @tparam M
    *   the effect under which the [[HikariTransactor]] runs
    */
  def initialWithResEffect[M0[_]: Sync, M[_]: Async](
      connectEC: ExecutionContext,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M0, HikariTransactor[M]] = {
    Resource.fromAutoCloseable(Sync[M0].delay(new HikariDataSource))
      .map(Transactor.fromDataSource[M](_, connectEC, logHandler))
  }

  /** 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,
      logHandler: Option[LogHandler[M]] = None,
      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]] = {
    fromConfigCustomEcWithResEffect[M, M](
      config = config,
      connectEC = connectEC,
      logHandler = logHandler,
      dataSource = dataSource,
      dataSourceProperties = dataSourceProperties,
      healthCheckProperties = healthCheckProperties,
      healthCheckRegistry = healthCheckRegistry,
      metricRegistry = metricRegistry,
      metricsTrackerFactory = metricsTrackerFactory,
      scheduledExecutor = scheduledExecutor,
      threadFactory = threadFactory
    )
  }

  /** Similar to [[fromConfigCustomEc]], but with a separate effect for the construction of the Transactor
    *
    * @tparam M0
    *   the effect to construct the [[HikariTransactor]] in
    * @tparam M
    *   the effect under which the [[HikariTransactor]] runs
    */
  def fromConfigCustomEcWithResEffect[M0[_]: Sync, M[_]: Async](
      config: Config,
      connectEC: ExecutionContext,
      logHandler: Option[LogHandler[M]] = None,
      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[M0, HikariTransactor[M]] = {
    Resource
      .liftK(
        Config.makeHikariConfig[M0](
          config = config,
          dataSource = dataSource,
          dataSourceProperties = dataSourceProperties,
          healthCheckProperties = healthCheckProperties,
          healthCheckRegistry = healthCheckRegistry,
          metricRegistry = metricRegistry,
          metricsTrackerFactory = metricsTrackerFactory,
          scheduledExecutor = scheduledExecutor,
          threadFactory = threadFactory
        )
      )
      .flatMap(fromHikariConfigCustomEcWithResEffect[M0, M](_, connectEC, logHandler))
  }

  /** Resource yielding a new `HikariTransactor` configured with the given Config. The `connectEC` is created
    * automatically, with the same size as the Hikari pool.
    *
    * @tparam M
    *   the effect under which the [[HikariTransactor]] runs
    */
  def fromConfig[M[_]: Async](
      config: Config,
      logHandler: Option[LogHandler[M]] = None,
      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]] = {
    fromConfigWithResEffect[M, M](
      config = config,
      logHandler = logHandler,
      dataSource = dataSource,
      dataSourceProperties = dataSourceProperties,
      healthCheckProperties = healthCheckProperties,
      healthCheckRegistry = healthCheckRegistry,
      metricRegistry = metricRegistry,
      metricsTrackerFactory = metricsTrackerFactory,
      scheduledExecutor = scheduledExecutor,
      threadFactory = threadFactory
    )
  }

  /** Similar to [[fromConfig]], but with a separate effect for the construction of the Transactor
    *
    * @tparam M0
    *   the effect to construct the [[HikariTransactor]] in
    * @tparam M
    *   the effect under which the [[HikariTransactor]] runs
    */
  def fromConfigWithResEffect[M0[_]: Sync, M[_]: Async](
      config: Config,
      logHandler: Option[LogHandler[M]] = None,
      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[M0, HikariTransactor[M]] = {
    Resource
      .liftK(
        Config.makeHikariConfig[M0](
          config = config,
          dataSource = dataSource,
          dataSourceProperties = dataSourceProperties,
          healthCheckProperties = healthCheckProperties,
          healthCheckRegistry = healthCheckRegistry,
          metricRegistry = metricRegistry,
          metricsTrackerFactory = metricsTrackerFactory,
          scheduledExecutor = scheduledExecutor,
          threadFactory = threadFactory
        )
      )
      .flatMap(fromHikariConfigWithResEffect[M0, M](_, logHandler))
  }

  /** Resource yielding a new `HikariTransactor` configured with the given HikariConfig. Unless you have a good reason,
    * consider using [[fromHikariConfig]], it will be created automatically for you.
    */
  def fromHikariConfigCustomEc[M[_]: Async](
      hikariConfig: HikariConfig,
      connectEC: ExecutionContext,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M, HikariTransactor[M]] =
    fromHikariConfigCustomEcWithResEffect[M, M](hikariConfig, connectEC, logHandler)

  /** Similar to [[fromHikariConfigCustomEc]], but with a separate effect for the construction of the Transactor
    *
    * @tparam M0
    *   the effect to construct the [[HikariTransactor]] in
    * @tparam M
    *   the effect under which the [[HikariTransactor]] runs
    */
  def fromHikariConfigCustomEcWithResEffect[M0[_]: Sync, M[_]: Async](
      hikariConfig: HikariConfig,
      connectEC: ExecutionContext,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M0, HikariTransactor[M]] = Resource
    .fromAutoCloseable(Sync[M0].delay(new HikariDataSource(hikariConfig)))
    .map(Transactor.fromDataSource[M](_, connectEC, logHandler))

  /** Resource yielding a new `HikariTransactor` configured with the given HikariConfig. The connection ExecutionContext
    * (used for waiting for a connection from the connection pool) is created automatically, with the same size as the
    * Hikari connection pool.
    */
  def fromHikariConfig[M[_]: Async](
      hikariConfig: HikariConfig,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M, HikariTransactor[M]] =
    fromHikariConfigWithResEffect[M, M](hikariConfig, logHandler)

  /** Similar to [[fromHikariConfig]], but with a separate effect for the construction of the Transactor
    *
    * @tparam M0
    *   the effect to construct the [[HikariTransactor]] in
    * @tparam M
    *   the effect under which the [[HikariTransactor]] runs
    */
  def fromHikariConfigWithResEffect[M0[_]: Sync, M[_]: Async](
      hikariConfig: HikariConfig,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M0, HikariTransactor[M]] =
    for {
      // to populate unset fields with default values, like `maximumPoolSize`
      _ <- Sync[M0].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://typelevel.org/doobie/docs/14-Managing-Connections.html#about-threading
      connectEC <- ExecutionContexts.fixedThreadPool[M0](hikariConfig.getMaximumPoolSize)
      result <- fromHikariConfigCustomEcWithResEffect[M0, M](hikariConfig, connectEC, logHandler)
    } 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,
      logHandler: Option[LogHandler[M]] = None
  ): Resource[M, HikariTransactor[M]] =
    for {
      _ <- Resource.eval(Sync[M].delay(Class.forName(driverClassName)))
      t <- initial[M](connectEC, logHandler)
      _ <- Resource.eval[M, Unit] {
        t.configure { ds =>
          Sync[M].delay[Unit] {
            ds `setJdbcUrl` url
            ds `setUsername` user
            ds `setPassword` pass
          }
        }
      }
    } yield t

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy