Sharing database session between multiple methods in Slick 3

927 views
Skip to first unread message

Yadukrishnan K

unread,
May 22, 2015, 1:57:34 AM5/22/15
to scala...@googlegroups.com
Hi,
I have already asked this question in Stack Overflow. I am asking since I didn't get any response there.

In slick-2, I could pass the same session between multiple methods as implicit arguments and handle the multiple db calls in a single transaction. However, with slick-3, withTransaction is deprecated and recommend to use transactionally. I want to abstract the Create/Delete/Update operations to common methods, which can be used by others. However, to use transactionally, we need to have the Query. But if the quer object is used, the extra operations after the create/delete/update needs to be handled by each of the developers when using transactions. Is there a way similar to slick-2, where you can share the same database session to handle the transactions?


def insertWithTransaction(row: TTable#TableElementType)(implicit session: Session) = {
 val entity
= (query returning query.map(obj => obj) += row).asInstanceOf[TEntity]
 
// do some operations after insert
 
// insert record to audit table
 
//eg: invoke another method for sending the notification
 entity
}

override def insert(row: TTable#TableElementType) = {
 db
.withSession {
 
implicit session => {
 insertWithTransaction
(row)
 
}
 
}
}



Christopher Vogt

unread,
May 22, 2015, 5:22:56 PM5/22/15
to scala...@googlegroups.com
The business logic in the following example doesn't exactly make sense,
but conceptually the idea is instead of

def insert(u: User)(implicit session: Session) = Users += u

db.withTransaction{ implicit session =>
val user = Users.filter(_.id = 1).first
val newUser = user.copy(address="new Street")
insert( newUser )
}


you do


def insert(u: User) = Users += u

val transaction = (
for{
user <- Users.filter(_.id = 1).result
newUser = user.copy(address="new Street")
_ <- insert( newUser )
} yield _
).transactionally

db.run( transaction )

Yadukrishnan K

unread,
May 23, 2015, 1:08:09 AM5/23/15
to scala...@googlegroups.com
Thanks for the response. In the sample code you have provided, the insert() method does just the insert operation. However, I want to do some other operations liek insert into audit tables etc after the actual insert action is completed. So, I wrote a method insert() which will first insert into the respective table, and then insert into audit tables and other operations. So the actual query execution is taken place in the insert() method itself. In that case, i will not be  able to perform transaction there.

sample code :

def saveSomething()={\
    vavl emp = Employee(....)
    val user = User(.....)
    val role = Role(....)
    EmployeeRepository.insert(emp)
    UserRepository.insert(use)
    RoleRepository.insert(role)
}

In the above code, i want to insert into Employee, Role and User tables in a single transaction. If I need to do that, i need to use the query directly for each of the tables and insert using transctionally(). But if I do that, I have to do the insert to audit tables explicitly for Employee, Role and User tables. I would like to abstract those logic to some method so that, developers need to just worry about the busniness logic operations only.

Like I said, in Slick-2, i was doing the same thing by passing the session implicitly to an abstracted method, which will run all the queries in a single transaction using withTransaction.

I was looking for something similar. I hope that I was able to explain the question/requirement clearly now.

Thanks
Yadu

virtualeyes

unread,
May 24, 2015, 10:17:52 AM5/24/15
to scala...@googlegroups.com
In Slick 3 nothing happens until you `run` it, so your repository inserts can be made separately without issue.

Only caveat is that you'll need to return a Query, which means saveSomething will need to have repository inserts chained together:

def saveSomething() = {
 
...
  q1
.insert(..) andThen q2.insert(..) andThen q3.insert(..)
}
db
.run( saveSomething().transactionally )

I'm only using Slick codegen to-date with the new 3.0 API, there may be other approaches as well.

Yadukrishnan K

unread,
May 24, 2015, 12:10:43 PM5/24/15
to scala...@googlegroups.com
I guess I will have to go ahead like that. Do not think it's possible to have am abstraction in case of transactions :( . I still believe that passing implicit session or some other similar way should have been supported. May be there is some other way, which I need to dig up.

Christopher Vogt

unread,
May 26, 2015, 11:31:55 AM5/26/15
to scala...@googlegroups.com
I don't see your problem. Just compose the insert actions to the
different tables inside of your custom insert method and return it and
you get the same composability as with passing the session through in
2.0. 3.0 doesn't knowingly cut down on any abstraction capabilities. The
types may be a not more tricky in 3.0 at times.

I saw you submitted a ticket. Maybe Stefan will pick it up and provide
an example.

Chris

Yadukrishnan K

unread,
May 27, 2015, 2:04:38 AM5/27/15
to scala...@googlegroups.com
Hi Christopher,
Thanks for clarifying. I was not that clear with transactionally, that's  why I had the confusion. Now I am able to handle the transactions between different methods. I guess the ticket can be closed, will comment on that.

Kapila Raghubanshi

unread,
Jun 8, 2015, 3:31:29 AM6/8/15
to scala...@googlegroups.com



Hi, 
     I am also stock in same kind of problem. 

     def saveClosing(transactionGrossCommissionPlan: TransactionGrossCommissionPlan, deductions: Seq[TransactionDeduction], transactionAssociateFinancial: Seq[(TransactionAssociateFinancial, Option[Seq[TransactionAssociateDeduction]])])(implicit request: RequestContext) = {
  
val transactionId = transactionGrossCommissionPlan.transactionId
val result = (for {
_ <- f.query.filter(_._id === transactionGrossCommissionPlan._id.get).update(transactionGrossCommissionPlan)
_ <- transactionDeductionRepository.query.filter(_.transactionId === transactionId).delete
_ <- transactionDeductionRepository.query ++= deductions.map(t => t.copy(_id = Some(uuid)))
_ <- transactionAssociateFinanceRepository.query.filter(_.transactionId === transactionId).delete
_ <- for (t <- transactionAssociateFinancial) {
val transactionAssociateDeduction = t._2
transactionAssociateFinanceRepository.query += t._1
transactionAssociateDeduction match {
case Some(s) => for (transAssociateDeduction <- s) {
val r = transAssociateDeduction.copy(associateFinanceId = Some(transactionAssociateId))
val jsObj = writer2.writes(r).as[JsObject]
val entityVal = Json.fromJson(newMetaData(setId(jsObj)))(reader2).get
val t = transAssDeductionRepository.query.returning(transAssDeductionRepository.query.map(_._id)) += entityVal
}
}
}
} yield (Right("Close saved successfully"))).transactionally


val t = f.db.run(result)
}

In slick 2 it worked well because I pass the session for each operation but in slick 3, I cannot insert the sequence of Object transactionally. I get error while inserting seq of transactionAssociateFinancial.

Reply all
Reply to author
Forward
0 new messages