forked from slick/slick
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDatabase.scala
117 lines (100 loc) · 3.97 KB
/
Database.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package org.scalaquery.session
import java.util.Properties
import java.sql._
import javax.sql.DataSource
import javax.naming.InitialContext
import scala.util.DynamicVariable
import org.scalaquery.SQueryException
/**
* A database instance to which connections can be created.
* Encapsulates either a DataSource or parameters for DriverManager.getConnection().
*/
abstract class Database {
protected[session] def createConnection(): Connection
/**
* The DatabaseCapabilities, accessed through a Session and created by the
* first Session that needs them. Access does not need to be synchronized
* because, in the worst case, capabilities will be determined multiple
* times by different concurrent sessions but the result should always be
* the same.
*/
@volatile
protected[session] var capabilities: DatabaseCapabilities = null
/**
* Create a new session. The session needs to be closed explicitly by calling its close() method.
*/
def createSession(): Session = new BaseSession(this)
/**
* Run the supplied function with a new session and automatically close the session at the end.
*/
def withSession[T](f: Session => T): T = {
val s = createSession()
try { f(s) } finally s.close()
}
/**
* Run the supplied thunk with a new session and automatically close the session at the end.
* The session is stored in a thread-local variable which can be accessed with the implicit
* function in Database.Implicit.
*/
def withSession[T](f: => T): T = withSession { s: Session => Database.dyn.withValue(s)(f) }
/**
* Run the supplied function with a new session in a transaction and automatically close the session at the end.
*/
def withTransaction[T](f: Session => T): T = withSession { s => s.withTransaction(f(s)) }
/**
* Run the supplied thunk with a new session in a transaction and automatically close the session at the end.
* The session is stored in a thread-local variable which can be accessed with the implicit
* function in Database.Implicit.
*/
def withTransaction[T](f: => T): T = withSession { Database.threadLocalSession.withTransaction(f) }
}
/**
* Factory methods for creating Database objects.
*/
object Database {
private[session] val dyn = new DynamicVariable[Session](null)
/**
* An implicit function that returns the thread-local session in a withSession block
*/
implicit def threadLocalSession: Session = {
val s = dyn.value
if(s eq null)
throw new SQLException("No implicit session available; threadLocalSession can only be used within a withSession block")
else s
}
/**
* Create a Database based on a DataSource.
*/
def forDataSource(ds: DataSource): Database = new Database {
protected[session] def createConnection(): Connection = ds.getConnection
}
/**
* Create a Database based on the JNDI name of a DataSource.
*/
def forName(name: String) = new InitialContext().lookup(name) match {
case ds: DataSource => forDataSource(ds)
case x => throw new SQueryException("Expected a DataSource for JNDI name "+name+", but got "+x)
}
/**
* Create a Database that uses the DriverManager to open new connections.
*/
def forURL(url:String, user:String = null, password:String = null, prop: Properties = null, driver:String = null): Database = new Database {
if(driver ne null) Class.forName(driver)
val cprop = if(prop.ne(null) && user.eq(null) && password.eq(null)) prop else {
val p = new Properties(prop)
if(user ne null) p.setProperty("user", user)
if(password ne null) p.setProperty("password", password)
p
}
protected[session] def createConnection(): Connection = DriverManager.getConnection(url, cprop)
}
/**
* Create a Database that uses the DriverManager to open new connections.
*/
def forURL(url:String, prop: Map[String, String]): Database = {
val p = new Properties
if(prop ne null)
for((k,v) <- prop) if(k.ne(null) && v.ne(null)) p.setProperty(k, v)
forURL(url, prop = p)
}
}