This is a very short book about doobie, which is a pure-functional JDBC layer for Scala.
doobie provides low-level access to everything in
java.sql (as of Java 8), allowing you to write any JDBC program in a pure functional style. However the focus of this book is the high-level API, which is where most users will spend their time.
This book is organized cookbook-style: we demonstrate a common task and then explain how it works, perhaps in more detail than you want right now. The goal is to get you up and running quickly, but give you a handle on the deeper stuff if you need it later.
This library is designed for people who are interested in typed, pure functional programming. If you are not a Cats user or are not familiar with functional I/O and monadic effects, you may need to go slowly and may want to spend some time reading Functional Programming in Scala, which introduces all of the ideas that you will find when exploring doobie.
Having said this, if you find yourself confused or frustrated by this documentation or the doobie API, please ask a question on Gitter, file an issue and ask for help. Both the library and the documentation are young and are changing quickly, and it is inevitable that some things will be unclear. Accordingly, this book is updated continuously to address problems and omissions.
This book is compiled as part of the build using the tut tutorial generator, so the code examples are guaranteed to compile (and with luck should also work correctly). Each page stands on its own: if you copy and paste code samples starting from the top, it will work in your REPL as long as you have the proper setup, described here.
Sample Database Setup
The example code assumes a local PostgreSQL server with a
postgres user with no password, PostGIS extensions (optional), and the sample
world database loaded up. If you’re on a Mac you might check out the excellent Postgres.app if you don’t want to install PostgreSQL as a service. You can set up the user and sample database (and an
enum we use in a few examples) as follows:
$ curl -O https://raw.githubusercontent.com/tpolecat/doobie/series/0.7.x/world.sql $ psql -c 'create user postgres createdb' postgres $ psql -c 'create database world;' -U postgres $ psql -c '\i world.sql' -d world -U postgres $ psql -d world -c "create type myenum as enum ('foo', 'bar')" -U postgres $ psql -d world -c "create extension postgis" -U postgres
Skip the last statement if you don’t have PostGIS installed. Note that the final
ANALYZE comand in the import will emit a few errors for system tables. This is expected and is fine. Try a query or two to double-check your setup:
$ psql -d world -U postgres psql (9.3.5) Type "help" for help. world=> select name, continent, population from country where name like 'U%'; name | continent | population --------------------------------------+---------------+------------ United Arab Emirates | Asia | 2441000 United Kingdom | Europe | 59623400 Uganda | Africa | 21778000 Ukraine | Europe | 50456000 Uruguay | South America | 3337000 Uzbekistan | Asia | 24318000 United States | North America | 278357000 United States Minor Outlying Islands | Oceania | 0 (8 rows) world=> \q $
You can of course change this setup if you like, but you will need to adjust your JDBC connection information accordingly. Most examples will work with any compliant database, but in a few cases (noted in the text) we rely on vendor-specific behavior.
On the Scala side you just need a console with the proper dependencies. A minimal
build.sbt would look something like this.
scalaVersion := "2.13.0" // Scala 2.12/13 scalacOptions += "-Ypartial-unification" // 2.11.9+ lazy val doobieVersion = "0.8.8" libraryDependencies ++= Seq( "org.tpolecat" %% "doobie-core" % doobieVersion, "org.tpolecat" %% "doobie-postgres" % doobieVersion, "org.tpolecat" %% "doobie-specs2" % doobieVersion )
-Ypartial-unification compiler flag enables a bug fix that makes working with functional code significantly easier. See the Cats Getting Started for more info on this if it interests you.
If you are not using PostgreSQL you can omit
doobie-postgres and will need to add the appropriate JDBC driver as a dependency. Note that there is a
doobie-h2 add-on if you happen to be using H2.
Each page begins with some imports, like this.
import cats._, cats.data._, cats.implicits._ import doobie._
After that there is text interspersed with code examples. Sometimes definitions will stand alone.
case class Person(name: String, age: Int) val nel = NonEmptyList.of(Person("Bob", 12), Person("Alice", 14))
And sometimes they will appear with output.
nel.head // res0: Person = Person("Bob", 12) nel.tail // res1: List[Person] = List(Person("Alice", 14))
Sometimes we demonstrate that something doesn’t compile. In such cases it will be clear from the context that this is expected, and not a problem with the documentation.
woozle(nel) // doesn't compile // error: not found: value woozle // woozle(nel) // doesn't compile // ^^^^^^