Skip to main content
Version: 2.x

TranzactIO

TranzactIO is a ZIO wrapper for some Scala database access libraries, currently for Doobie and Anorm.

Introduction

Using functional effect database access libraries like Doobie enforces us to use their specialized monads like ConnectionIO for Doobie. The goal of TranzactIO is to provide seamless integration with these libraries to help us to stay in the ZIO world.

Installation

In order to use this library, we need to add the following line in our build.sbt file:

libraryDependencies += "io.github.gaelrenoux" %% "tranzactio" % "2.1.0"

In addition, we need to declare the database access library we are using. For example, for the next example we need to add following dependencies for Doobie integration:

libraryDependencies += "org.tpolecat" %% "doobie-core" % "0.13.4"
libraryDependencies += "org.tpolecat" %% "doobie-h2" % "0.13.4"

Example

Let's try an example of simple Doobie program:

import doobie.implicits._
import io.github.gaelrenoux.tranzactio.doobie
import io.github.gaelrenoux.tranzactio.doobie.{Connection, Database, TranzactIO, tzio}
import org.h2.jdbcx.JdbcDataSource
import zio.blocking.Blocking
import zio.clock.Clock
import zio.console.{Console, putStrLn}
import zio.{ExitCode, Has, URIO, ZIO, ZLayer, blocking}

import javax.sql.DataSource

object TranzactIOExample extends zio.App {

val query: ZIO[Connection with Console, Throwable, Unit] = for {
_ <- PersonQuery.setup
_ <- PersonQuery.insert(Person("William", "Stewart"))
_ <- PersonQuery.insert(Person("Michelle", "Streeter"))
_ <- PersonQuery.insert(Person("Johnathon", "Martinez"))
users <- PersonQuery.list
_ <- putStrLn(users.toString)
} yield ()

val myApp: ZIO[zio.ZEnv, Throwable, Unit] =
Database.transactionOrWidenR(query).provideCustom(services.database)

override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
myApp.exitCode
}

case class Person(firstName: String, lastName: String)

object PersonQuery {
def list: TranzactIO[List[Person]] = tzio {
sql"""SELECT first_name, last_name FROM person""".query[Person].to[List]
}

def setup: TranzactIO[Unit] = tzio {
sql"""
CREATE TABLE person (
first_name VARCHAR NOT NULL,
last_name VARCHAR NOT NULL
)
""".update.run.map(_ => ())
}

def insert(p: Person): TranzactIO[Unit] = tzio {
sql"""INSERT INTO person (first_name, last_name) VALUES (${p.firstName}, ${p.lastName})""".update.run
.map(_ => ())
}
}

object services {
val datasource: ZLayer[Blocking, Throwable, Has[DataSource]] =
ZLayer.fromEffect(
blocking.effectBlocking {
val ds = new JdbcDataSource
ds.setURL(s"jdbc:h2:mem:mydb;DB_CLOSE_DELAY=10")
ds.setUser("sa")
ds.setPassword("sa")
ds
}
)

val database: ZLayer[Any, Throwable, doobie.Database.Database] =
(Blocking.live >>> datasource ++ Blocking.live ++ Clock.live) >>> Database.fromDatasource
}