Skip to content
Permalink
Browse files

Add JDBC interop docs

  • Loading branch information
cvogt committed Feb 25, 2014
1 parent f85c9c5 commit 7ae79d61bea2da6894a4ec8a857981d61584969e
Showing with 38 additions and 12 deletions.
  1. +8 −0 src/main/scala/scala/slick/jdbc/JdbcBackend.scala
  2. +26 −12 src/sphinx/connection.rst
  3. +4 −0 src/sphinx/sql.rst
@@ -153,6 +153,7 @@ trait JdbcBackend extends DatabaseComponent {
})
}

/** A wrapper around the JDBC Connection's prepareStatement method, that automatically closes the statement. */
final def withPreparedStatement[T](sql: String,
defaultType: ResultSetType = ResultSetType.ForwardOnly,
defaultConcurrency: ResultSetConcurrency = ResultSetConcurrency.ReadOnly,
@@ -161,18 +162,21 @@ trait JdbcBackend extends DatabaseComponent {
try f(st) finally st.close()
}

/** A wrapper around the JDBC Connection's prepareInsertStatement method, that automatically closes the statement. */
final def withPreparedInsertStatement[T](sql: String,
columnNames: Array[String] = new Array[String](0))(f: (PreparedStatement => T)): T = {
val st = prepareInsertStatement(sql, columnNames)
try f(st) finally st.close()
}

/** A wrapper around the JDBC Connection's prepareInsertStatement method, that automatically closes the statement. */
final def withPreparedInsertStatement[T](sql: String,
columnIndexes: Array[Int])(f: (PreparedStatement => T)): T = {
val st = prepareInsertStatement(sql, columnIndexes)
try f(st) finally st.close()
}

/** A wrapper around the JDBC Connection's createStatement method, that automatically closes the statement. */
final def withStatement[T](defaultType: ResultSetType = ResultSetType.ForwardOnly,
defaultConcurrency: ResultSetConcurrency = ResultSetConcurrency.ReadOnly,
defaultHoldability: ResultSetHoldability = ResultSetHoldability.Default)(f: (Statement => T)): T = {
@@ -197,6 +201,10 @@ trait JdbcBackend extends DatabaseComponent {
*/
def withTransaction[T](f: => T): T

/**
* Create a new Slick Session wrapping the same JDBC connection, but using the given values as defaults for
* resultSetType, resultSetConcurrency and resultSetHoldability.
*/
def forParameters(rsType: ResultSetType = resultSetType, rsConcurrency: ResultSetConcurrency = resultSetConcurrency,
rsHoldability: ResultSetHoldability = resultSetHoldability): Session = new Session {
override def resultSetType = rsType
@@ -68,7 +68,7 @@ Session handling

Now you have a :api:`Database <scala.slick.jdbc.JdbcBackend@Database:Database>` object
and you can use it to open database connections, which Slick encapsulates in
:api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` objects.
:api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` objects.

.. _session-scope:

@@ -78,16 +78,16 @@ Automatically closing Session scope
The :api:`Database <scala.slick.jdbc.JdbcBackend@Database:Database>` object's
:api:`withSession <scala.slick.jdbc.JdbcBackend$DatabaseDef@withSession[T]((Session)⇒T):T>`
method creates a
:api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>`, passes it to a given function and closes it
:api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>`, passes it to a given function and closes it
afterwards. If you use a connection pool, closing the
:api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` returns the connection to the pool.
:api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` returns the connection to the pool.

.. includecode:: code/Connection.scala#withSession

You can see how we are able to already define the query outside of the
:api:`withSession <scala.slick.jdbc.JdbcBackend$DatabaseDef@withSession[T]((Session)⇒T):T>`
scope. Only the methods actually executing the query in the database require a
:api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>`. Here we use the
:api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>`. Here we use the
:api:`list <scala.slick.jdbc.Invoker@list(P)(SessionDef):List[R]>`
method to execute the query
and return the results as a :scalaapi:`scala.collection.immutable.List`. (The
@@ -100,7 +100,7 @@ or :api:`insertAll <scala.slick.driver.JdbcInvokerComponent$BaseInsertInvoker@in
executes atomically (i.e. it succeeds or fails completely).
To bundle several statements use :ref:`transactions`.

**Be careful:** If the :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` object escapes the
**Be careful:** If the :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` object escapes the
:api:`withSession <scala.slick.jdbc.JdbcBackend$DatabaseDef@withSession[T]((Session)⇒T):T>`
scope, it has already been closed and is invalid. It can escape in several ways,
which should be avoided, e.g. as state of a closure (if you use a
@@ -112,7 +112,7 @@ as the return value of the withSession scope or else.
Implicit Session
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

By marking the :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` as implicit you can avoid
By marking the :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` as implicit you can avoid
having to pass it to the executing methods explicitly.

.. includecode:: code/Connection.scala#withSession-implicit
@@ -124,7 +124,7 @@ This is optional of course. Use it if you think it makes your code cleaner.
Transactions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can use the :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` object's
You can use the :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` object's
:api:`withTransaction <scala.slick.jdbc.JdbcBackend$SessionDef@withTransaction[T](⇒T):T>`
method to create a transaction when you need one. The block passed to it
is executed in a single transaction. If an exception is thrown, Slick rolls
@@ -135,7 +135,7 @@ Slick only rolls back database operations, not the effects of other Scala code.

.. includecode:: code/Connection.scala#transaction

If you don't have a :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` yet you can use the
If you don't have a :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` yet you can use the
:api:`Database <scala.slick.jdbc.JdbcBackend@Database:Database>` object's
:api:`withTransaction <scala.slick.jdbc.JdbcBackend$DatabaseDef@withTransaction[T]((Session)⇒T):T>`
method as a shortcut.
@@ -146,17 +146,17 @@ Manual Session handling
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This is not recommended, but if you have to, you can handle the lifetime of a
:api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` manually.
:api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` manually.

.. includecode:: code/Connection.scala#manual-session

Passing sessions around
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can write re-useable functions to help with Slick queries. They mostly do
not need a :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` as they just produce query
not need a :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` as they just produce query
fragments or assemble queries. If you want to execute queries inside of them
however, they need a :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>`. You can either put it
however, they need a :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>`. You can either put it
into the function signature and pass it as a (possibly implicit) argument. Or
you can bundle several such methods into a class, which stores the session to
reduce boilerplate code:
@@ -181,7 +181,7 @@ without a session argument.
.. includecode:: code/Connection.scala#withSession-empty

:api:`dynamicSession <scala.slick.jdbc.JdbcBackend$DatabaseFactoryDef@dynamicSession:Session>` is an
implicit def that returns a valid :api:`Session <scala.slick.jdbc.JdbcBackend@Session:Session>` if a
implicit def that returns a valid :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` if a
:api:`withDynSession <scala.slick.jdbc.JdbcBackend$DatabaseDef@withDynSession[T](⇒T):T>`
or :api:`withDynTransaction :<scala.slick.jdbc.JdbcBackend$DatabaseDef@withDynTransaction[T](⇒T):T>`
scope is open somewhere on the current call stack.
@@ -213,3 +213,17 @@ Note that Slick uses *prepared* statements wherever possible but it does not
cache them on its own. You should therefore enable prepared statement caching
in the connection pool's configuration and select a sufficiently large pool
size.


.. _jdbc-interop:

JDBC interoperability
---------------------
To access features not available in Slick directly it can be useful to drop down to JDBC level.

You can access the underlying :javaapi:`JDBC Connection <java/sql/Connection>` of a Slick :api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>` using the :api:`conn <scala.slick.jdbc.JdbcBackend$SessionDef@conn:Connection>` method.

:api:`Session <scala.slick.jdbc.JdbcBackend$SessionDef>`'s
:api:`withPreparedInsertStatement <scala.slick.jdbc.JdbcBackend$SessionDef@withPreparedInsertStatement[T](String,Array[String])((PreparedStatement)⇒T):T>`,
:api:`withPreparedStatement <scala.slick.jdbc.JdbcBackend$SessionDef@withPreparedStatement[T](String,ResultSetType,ResultSetConcurrency,ResultSetHoldability)((PreparedStatement)⇒T):T>`,
:api:`withStatement <scala.slick.jdbc.JdbcBackend$SessionDef@withStatement[T](ResultSetType,ResultSetConcurrency,ResultSetHoldability)((Statement)⇒T):T>` methods allow you to create automatically closing :javaapi:`JDBC Statements <java/sql/Statement>`.
@@ -124,3 +124,7 @@ is hardcoded to return an ``Int`` value so it does not need the extra ``.as``
call:

.. includecode:: code/PlainSQL.scala#interpolate.sqlu

Other JDBC features
-------------------
For accessing other JDBC features like OUT parameters or ``getGeneratedKeys`` you need to use JDBC directly, see :ref:`jdbc-interop`.

0 comments on commit 7ae79d6

Please sign in to comment.
You can’t perform that action at this time.