-
Notifications
You must be signed in to change notification settings - Fork 11
DevGuide Gotchas
If you take nothing else away from the developer guide, read this section. It highlights the oddities of this codebase. These are things that will cause you grief if you try to make changes without realizing what's going on.
ScalikeJDBC is a scala-native wrapper around JDBC connections (analogous to Python's Alchemy). It makes extensive use of scala implicits, which can be a little confusing if you're just getting started with scala, so let's start with a quick primer on the part of implicits that you need to know.
Global state is annoying, but can be more programmer friendly when you need to keep passing that state into functions. For example, let's say you have a JDBC connection. You need to keep passing that connection into every function that might potentially need it. That makes code hard to read and write, because you always have that extra argument hanging off the end. It can be easier just to dump the connection into a global variable and use it when needed. Global state, however, drives functional people mad (for good reasons).
Scala (kind of?) avoids the pitfals of global state through something it calls implicit parameters. Take the following function:
def foo(implicit x: Bar) = x.baz
To call this function, you don't need to pass the x
parameter explicitly. You
would just write
println(s"${foo}")
Of course, this example is a little too simple... if you actually try the following, the compiler will yell at you:
def myFunction() =
{
println(s"${foo}")
}
What's happening under the hood here is that instead of manually passing a Bar
object around, the compiler tries to automatically pick and plug in a Bar
object that you happen to have lying around. Since you don't have one, it gets
confused. Let's try the following:
def myFunction() =
{
val x = Bar(baz = "stuff")
println(s"${foo}")
}
That still won't work, because the compiler won't look for just any old variables. You have to explicitly mark a variable as implicit for it to work, like so:
def myFunction() =
{
implicit val x = Bar(baz = "stuff")
println(s"${foo}")
}
Again, looking under the hood, when you call foo (without parameters), the
compiler will see the implicit x
parameter of type Bar
, try to find a
Bar
object in scope (possibly with a different name), and automatically plug
that object in to the function call.
ScalikeJDBC makes extensive use of implicit variables to keep track of session
state. Specifically, in this codebase you will see a lot of functions with
(implicit session: DBSession)
. These are functions that DO assume that
you have an open session. Conversely, functions without this parameter assume
that you DO NOT have an implicit session active.
To create a session, use one of the following two constructs:
DB.readOnly { implicit s => /* your code goes here */ }
or
DB.autoCommit { implicit s => /* your code goes here */ }
Gotcha: Nested session creation is verbotten. If you find code myseriously hanging or getting SQL timeouts, you are creating a nested session.
The following guidelines apply with respect to session creation:
- info.vizierdb.api: All classes/methods assume that you DO NOT have a session.
- info.vizierdb.artifacts: Classes/methods do not care whether you have a session.
- info.vizierdb.catalog: Classes/methods assume that you DO have a session.
- info.vizierdb.commands: Classes/methods assume that you DO NOT have a session.
- info.vizierdb.filestore: Classes/methods do not care whether you have a session.
- info.vizierdb.viztrails.MutableProject: Assumes that you DO NOT have a session.
- info.vizierdb.viztrails.Provenance: Assumes that you DO have a session.
- info.vizierdb.viztrails.Scheduler: Assumes that you DO have a session.