@@ -4,14 +4,21 @@ import com.zaxxer.hikari.{HikariConfig, HikariDataSource}
44
55import java .io .FileInputStream
66import java .security .KeyStore
7- import java .util .{UUID , Properties }
7+ import java .util .{Properties , UUID }
88import javax .sql .DataSource
9-
109import com .codahale .metrics .MetricRegistry
1110import com .codahale .metrics .health .HealthCheckRegistry
11+ import com .simple .jdub .Database .Primary
12+ import com .simple .jdub .Database .Replica
13+
14+ import scala .annotation .implicitNotFound
1215
1316object Database {
1417
18+ sealed trait Role
19+ sealed trait Primary extends Role
20+ sealed trait Replica extends Role
21+
1522 /**
1623 * Create a pool of connections to the given database.
1724 *
@@ -20,7 +27,7 @@ object Database {
2027 * @param password the database password
2128 * @param sslSettings if present, uses the given SSL settings for a client-side SSL cert.
2229 */
23- def connect (url : String ,
30+ def connect [ R <: Role ] (url : String ,
2431 username : String ,
2532 password : String ,
2633 name : Option [String ] = None ,
@@ -30,7 +37,7 @@ object Database {
3037 sslSettings : Option [SslSettings ] = None ,
3138 healthCheckRegistry : Option [HealthCheckRegistry ] = None ,
3239 metricRegistry : Option [MetricRegistry ] = None ,
33- connectionInitSql : Option [String ] = None ): Database = {
40+ connectionInitSql : Option [String ] = None ): Database [ R ] = {
3441
3542 val properties = new Properties
3643
@@ -95,8 +102,8 @@ object Database {
95102/**
96103 * A set of pooled connections to a database.
97104 */
98- class Database protected (val source : DataSource , metrics : Option [MetricRegistry ])
99- extends Queryable {
105+ class Database [ R <: Database . Role ] protected (val source : DataSource , metrics : Option [MetricRegistry ])
106+ extends Queryable [ R ] {
100107
101108 private [jdub] def time [A ](klass : java.lang.Class [_])(f : => A ) = {
102109 metrics.fold(f) { registry =>
@@ -110,34 +117,38 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
110117 }
111118 }
112119
113- val transactionProvider : TransactionProvider = new TransactionManager
120+ val transactionProvider : TransactionProvider [R ] = new TransactionManager
121+
122+ def replica : Database [Replica ] = new Database [Replica ](source, metrics)
123+
124+ def primary : Database [Primary ] = new Database [Primary ](source, metrics)
114125
115126 /**
116127 * Opens a transaction which is committed after `f` is called. If `f` throws
117128 * an exception, the transaction is rolled back.
118129 */
119- def transaction [A ](f : Transaction => A ): A = transaction(true , f)
130+ def transaction [A ](f : Transaction [ R ] => A )( implicit ev : R =:= Primary ): A = transaction(true , f)
120131
121132 /**
122133 * Opens a transaction which is committed after `f` is called. If `f` throws
123134 * an exception, the transaction is rolled back, but the exception is not
124135 * logged (since it is rethrown).
125136 */
126- def quietTransaction [A ](f : Transaction => A ): A = transaction(false , f)
137+ def quietTransaction [A ](f : Transaction [ R ] => A )( implicit ev : R =:= Primary ): A = transaction(false , f)
127138
128- def transaction [A ](logError : Boolean , f : Transaction => A ): A = transaction(false , false , f)
139+ def transaction [A ](logError : Boolean , f : Transaction [ R ] => A )( implicit ev : R =:= Primary ): A = transaction(false , false , f)
129140
130141 /**
131142 * Opens a transaction which is committed after `f` is called. If `f` throws
132143 * an exception, the transaction is rolled back.
133144 */
134- def transaction [A ](logError : Boolean , forceNew : Boolean , f : Transaction => A ): A = {
145+ def transaction [A ](logError : Boolean , forceNew : Boolean , f : Transaction [ R ] => A )( implicit ev : R =:= Primary ): A = {
135146 if (! forceNew && transactionProvider.transactionExists) {
136147 f(transactionProvider.currentTransaction)
137148 } else {
138149 val connection = source.getConnection
139150 connection.setAutoCommit(false )
140- val txn = new Transaction (connection)
151+ val txn = new Transaction [ R ] (connection)
141152 try {
142153 logger.debug(" Starting transaction" )
143154 val result = f(txn)
@@ -162,8 +173,8 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
162173 * thread within the scope of `f`. If `f` throws an exception the transaction
163174 * is rolled back. Logs exceptions thrown by `f` as errors.
164175 */
165- def transactionScope [A ](f : => A ): A = {
166- transaction(logError = true , forceNew = false , (txn : Transaction ) => {
176+ def transactionScope [A ](f : => A )( implicit ev : R =:= Primary ) : A = {
177+ transaction(logError = true , forceNew = false , (txn : Transaction [ R ] ) => {
167178 transactionProvider.begin(txn)
168179 try {
169180 f
@@ -181,8 +192,8 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
181192 * exception the transaction is rolled back. Logs exceptions thrown by
182193 * `f` as errors.
183194 */
184- def newTransactionScope [A ](f : => A ): A = {
185- transaction(logError = true , forceNew = true , (txn : Transaction ) => {
195+ def newTransactionScope [A ](f : => A )( implicit ev : R =:= Primary ) : A = {
196+ transaction(logError = true , forceNew = true , (txn : Transaction [ R ] ) => {
186197 transactionProvider.begin(txn)
187198 try {
188199 f
@@ -197,8 +208,8 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
197208 * thread within the scope of `f`. If `f` throws an exception the transaction
198209 * is rolled back. Will not log exceptions thrown by `f`.
199210 */
200- def quietTransactionScope [A ](f : => A ): A = {
201- transaction(logError = false , forceNew = false , (txn : Transaction ) => {
211+ def quietTransactionScope [A ](f : => A )( implicit ev : R =:= Primary ) : A = {
212+ transaction(logError = false , forceNew = false , (txn : Transaction [ R ] ) => {
202213 transactionProvider.begin(txn)
203214 try {
204215 f
@@ -216,8 +227,8 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
216227 * exception the transaction is rolled back. Will not log exceptions
217228 * thrown by `f`.
218229 */
219- def newQuietTransactionScope [A ](f : => A ): A = {
220- transaction(logError = false , forceNew = true , (txn : Transaction ) => {
230+ def newQuietTransactionScope [A ](f : => A )( implicit ev : R =:= Primary ) : A = {
231+ transaction(logError = false , forceNew = true , (txn : Transaction [ R ] ) => {
221232 transactionProvider.begin(txn)
222233 try {
223234 f
@@ -230,7 +241,7 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
230241 /**
231242 * The transaction currently scoped via transactionScope.
232243 */
233- def currentTransaction = {
244+ def currentTransaction ( implicit ev : R =:= Primary ) = {
234245 transactionProvider.currentTransaction
235246 }
236247
@@ -260,7 +271,7 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
260271 /**
261272 * Executes an update, insert, delete, or DDL statement.
262273 */
263- def execute (statement : Statement ) = {
274+ def execute (statement : Statement )( implicit ev : R =:= Primary ) : Int = {
264275 if (transactionProvider.transactionExists) {
265276 transactionProvider.currentTransaction.execute(statement)
266277 } else {
@@ -278,7 +289,7 @@ class Database protected(val source: DataSource, metrics: Option[MetricRegistry]
278289 /**
279290 * Rollback any existing ambient transaction
280291 */
281- def rollback () {
292+ def rollback ()( implicit ev : R =:= Primary ) {
282293 transactionProvider.rollback
283294 }
284295
0 commit comments