Most examples are using await and that is blocking. Why use Slick if you are going to block? Look at onComplete, onSuccess, onFailure, and promise.
** Chaining transactions looks like this in one case
db.run{
(for {
customer <- customerTable.save();
address <- addressTable.save()
} yield ()).transactionally
}
** Returning a list of ids from a select looks like this
(but you can also use returning keyword)
db = Database.forConfig("myactors.database")
session = db createSession;session.conn.setCatalog("mydb")
session.conn.setSchema("myschema")
val query = Tables.rawStringTest.to[List].map(_.id)
val resultSet = db.run(query.result)
**this prints the list
resultSet.onComplete(res => println(s"string returns $res") )
**this waits for the future to return and uses the success/failure traits
resultSet.onComplete{
case Success(res) => res.foreach(x => db.run(Tables.rawStringTest.filter(_.id === x).delete))
case Failure(ex) => println(s"error $ex")
}
**Testing is a challenge for aysync operations. Using an eventually retry block or a continuation both work. This is with eventually.
it("ids are generated when None is passed in table calls"){
val pk = None
val validation = Tables.rawStringMessages.map(_.message_data)
sut.insertStringMsg(None, "this is a fancy message", mockSuccessFunction(waiter), mockFailureFunction(waiter))
sut.insertStringMsg(None, "this is another fancy message", mockSuccessFunction(waiter), mockFailureFunction(waiter))
eventually(timeout(duration)) {
val resultSet = Await.result(db.run(validation.result), duration)
resultSet.onComplete{
case Success(res) => resultSet.head.equals(99) shouldBe true
case Failure(ex) => fail(s"this failed $ex)
}
**Some ways to insert
table returning table forceInsert JsonMessage(receiptId, messageData, None)
table returning table insertOrUpdate JsonMessage(receiptId, messageData, None)
table returning table += JsonMessageInsert(receiptId, messageData)
**And any of these can return nothing
table += JsonMessageInsert(receiptId, messageData)
table insertOrUpdate JsonMessage(receiptId, messageData, None)
/** * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. * * While occasionally useful, e.g. for testing, it is recommended that you avoid Await * when possible in favor of callbacks and combinators like onComplete and use in * for comprehensions. Await will block the thread on which it runs, and could cause * performance and deadlock issues. */
Clean up is a chore as well for integration tests. In this case I did clean up in the AfterAll block to avoid cleaning data during concurrent test runs.
override def afterAll() {
db = Database.forConfig("rtisystem.database")
session = db createSession; session.conn.setCatalog("rti")
session.conn.setSchema("rti")
val query = Tables.rawStringMessages.to[List].map(_.id)
val resultSet = db.run(query.result)
resultSet.onComplete{
case Success(res) => res.foreach(x => db.run(Tables.rawStringMessages.filter(_.id === x).delete))
case Failure(ex) => println(s"error $ex")
}
val jquery = Tables.jsonMessages.to[List].map(_.id)
val jresultSet = db.run(jquery.result)
jresultSet.onComplete{
case Success(res) => res.foreach(x => db.run(Tables.jsonMessages.filter(_.id === x).delete))
case Failure(ex) => println(s"error $ex")
}
This is an test example where I insert two records through a DAO extension method and compare them in a select query.
it("generates ids when None is passed instead of a long in the table call"){
val pk = None
val msgList: List[String] = List("this is a fancy message", "this is a fancy message also")
val read = Tables.rawStringMessages.filter(row => row.message_data inSet msgList).map(_.id)
sut.insertStringMsg(None, msgList.head, mockSuccessFunction(waiter), mockFailureFunction(waiter))
sut.insertStringMsg(None, msgList.tail.head, mockSuccessFunction(waiter), mockFailureFunction(waiter))
eventually(timeout(duration)) {
val resultSet = db.run(read.result)
resultSet.onComplete {
case Success(res) => assert(res.head != res.tail.head && res.head > 100000 && res.tail.head > 100000)
case Failure(ex) => println(s"error $ex")
}
}
}
val visitorId: Int = // whatever
val locationCodes = List("loc1","loc2","loc3"...)
// your query, with bind params.
val q = for {
v <- Visits
if v.visitor is visitorId.bind
if v.location_code inSetBind locationCodes
} yield v
// have a look at the generated query.
println(q.selectStatement)
// run the query
q.list
*** To use a db default ***
O.AutoInc
column_name.?
Column Option[Timestamp]
table += Class(id, data, Some(timestamp))
Or a detailed version
import slick.driver.PostgresDriver.api._
import slick.lifted._
import java.sql.{Date, Timestamp}
/** A representation of the message decorated for Slick persistence
* created_date should always be null on insert operations.
* It is set at the database level to ensure time syncronicity
* Id is the Twitter snowflake id. All columns NotNull unless declared as Option
* */
class RawMessages(tag: Tag) extends Table[(String, Option[String], Timestamp)](tag, Some("rti"), "RawMessages") {
def id = column[String]("id", O.PrimaryKey)
def MessageString = column[Option[String]]("MessageString")
def CreatedDate = column[Timestamp]("CreatedDate", O.SqlType("timestamp default now()"))
def * = (id, MessageString, CreatedDate)
}