Permalink
Browse files

Add docs about DBIO composition.

This also explains the difference between the strict and eager
combinators and the more flexible ones like `flatMap`. Fixes #1090.
  • Loading branch information...
szeiger committed Apr 7, 2015
1 parent e9ab330 commit 0cab15ed834b1aa3a58f63a6c68991120c1cf1a1
Showing with 72 additions and 2 deletions.
  1. +72 −2 slick/src/sphinx/dbio.rst
View
@@ -73,13 +73,83 @@ convenience method ``mapResult`` is provided for this purpose:
single: transaction
.. _transactions:
Composing Database I/O Actions
------------------------------
``DBIOAction``\ s describe sequences of individual actions to execute in strictly sequential order on
one database session (at least conceptually), therefore the most commonly used combinators deal with
sequencing. Since a ``DBIOAction`` eventually results in a ``Success`` or ``Failure``, its combinators,
just like the ones on ``Future``, have to distinguish between successful and failed executions. Unless
specifically noted, all combinators only apply to successful actions. Any failure will abort the
sequence of execution and result in a failed ``Future`` or *Reactive Stream*.
Sequential Execution
____________________
The simplest combinator is :api:`DBIO.seq <slick.dbio.DBIO$@seq[E<:Effect](DBIOAction[_,NoStream,E]*):DBIOAction[Unit,NoStream,E]>`
which takes a varargs list of actions to run in sequence, discarding their return value. If you
need the return value, you can use :api:`andThen <slick.dbio.DBIOAction@andThen[R2,S2<:NoStream,E2<:Effect](DBIOAction[R2,S2,E2]):DBIOAction[R2,S2,EwithE2]>`
to combine two actions and keep the result of the second one. If you need both return values of two
actions, there is the :api:`zip <slick.dbio.DBIOAction@zip[R2,E2<:Effect](DBIOAction[R2,NoStream,E2]):DBIOAction[(R,R2),NoStream,EwithE2]>`
combinator. For getting all result values from a sequence of actions (of compatible types), use
:api:`DBIO.sequence <slick.dbio.DBIO$@sequence[R,M[+_]<:TraversableOnce[_],E<:Effect](M[DBIOAction[R,NoStream,E]])(CanBuildFrom[M[DBIOAction[R,NoStream,E]],R,M[R]]):DBIOAction[M[R],NoStream,E]>`.
All these combinators work with pre-existing ``DBIOAction``\ s which are composed eagerly.
If an action depends on a previous action in the sequence, you have to compute it on the fly with
:api:`flatMap <slick.dbio.DBIOAction@flatMap[R2,S2<:NoStream,E2<:Effect]((R)⇒DBIOAction[R2,S2,E2])(ExecutionContext):DBIOAction[R2,S2,EwithE2]>`
or :api:`map <slick.dbio.DBIOAction@map[R2]((R)⇒R2)(ExecutionContext):DBIOAction[R2,NoStream,E]>`.
These two methods plus :api:`filter <slick.dbio.DBIOAction@filter((R)⇒Boolean)(ExecutionContext):DBIOAction[R,NoStream,E]>`
enable the use of *for comprehensions* for action sequencing. Since they take function
arguments, they also require an implicit ``ExecutionContext`` on which to run the function. This
way Slick ensures that no non-database code is run on the database thread pool.
.. note::
You should prefer the less flexible methods without an ``ExecutionContext`` where possible. The
resulting actions can be executed more efficiently.
Similar to :api:`DBIO.sequence <slick.dbio.DBIO$@sequence[R,M[+_]<:TraversableOnce[_],E<:Effect](M[DBIOAction[R,NoStream,E]])(CanBuildFrom[M[DBIOAction[R,NoStream,E]],R,M[R]]):DBIOAction[M[R],NoStream,E]>`
for upfront composition, there is :api:`DBIO.fold <slick.dbio.DBIO$@fold[T,E<:Effect](Seq[DBIOAction[T,NoStream,E]],T)((T,T)⇒T)(ExecutionContext):DBIOAction[T,NoStream,E]>`
for working with sequences of actions and composing them based on the previous result.
Error Handling
______________
You can use :api:`andFinally <slick.dbio.DBIOAction@andFinally[E2<:Effect](DBIOAction[_,NoStream,E2]):DBIOAction[R,S,EwithE2]>`
to perform a cleanup action, no matter whether the previous action succeeded or failed. This is similar to using
``try ... finally ...`` in imperative Scala code. A more flexible version of
:api:`andFinally <slick.dbio.DBIOAction@andFinally[E2<:Effect](DBIOAction[_,NoStream,E2]):DBIOAction[R,S,EwithE2]>`
is :api:`cleanUp <slick.dbio.DBIOAction@cleanUp[E2<:Effect]((Option[Throwable])⇒DBIOAction[_,NoStream,E2],Boolean)(ExecutionContext):DBIOAction[R,S,EwithE2]>`.
It lets you transform the failure and decide how to fail the resulting action if both the original
one and the cleanup failed.
.. note::
For even more flexible error handling use :api:`asTry <slick.dbio.DBIOAction@asTry:DBIOAction[Try[R],NoStream,E]>`
and :api:`failed <slick.dbio.DBIOAction@failed:DBIOAction[Throwable,NoStream,E]>`. Unlike with
:api:`andFinally <slick.dbio.DBIOAction@andFinally[E2<:Effect](DBIOAction[_,NoStream,E2]):DBIOAction[R,S,EwithE2]>`
and :api:`cleanUp <slick.dbio.DBIOAction@cleanUp[E2<:Effect]((Option[Throwable])⇒DBIOAction[_,NoStream,E2],Boolean)(ExecutionContext):DBIOAction[R,S,EwithE2]>`
the resulting actions cannot be used for streaming.
Primitives
__________
You can convert a ``Future`` into an action with :api:`DBIO.from <slick.dbio.DBIO$@from[R](Future[R]):DBIOAction[R,NoStream,Effect]>`.
This allows the result of the ``Future`` to be used in an action sequence. A pre-existing value or
failure can be converted with :api:`DBIO.successful <slick.dbio.DBIO$@successful[R](R):DBIOAction[R,NoStream,Effect]>`
and :api:`DBIO.failed <slick.dbio.DBIO$@failed(Throwable):DBIOAction[Nothing,NoStream,Effect]>`, respectively.
Debugging
_________
The :api:`named <slick.dbio.DBIOAction@named(String):DBIOAction[R,S,E]>` combinator names an
action. This name can be seen in debug logs if you enable the
``slick.backend.DatabaseComponent.action`` logger.
Transactions and Pinned Sessions
________________________________
When executing a ``DBIOAction`` which is composed of several smaller actions, Slick acquires sessions from
the connection pool and releases them again as needed so that a session is not kept in use
unnecessarily while waiting for the result from a non-database computation (e.g. the function
passed to
unnecessarily while waiting for the result from a non-database computation (e.g. the function passed to
:api:`flatMap <slick.dbio.DBIOAction@flatMap[R2,S2<:NoStream,E2<:Effect]((R)⇒DBIOAction[R2,S2,E2])(ExecutionContext):DBIOAction[R2,S2,EwithE2]>`
that determines the next Action to run). All :api:`DBIOAction combinators <slick.dbio.DBIOAction>`
which combine two database actions without any non-database computations in between (e.g.

0 comments on commit 0cab15e

Please sign in to comment.