Database transactions in Play framework scala applications (anorm)

匆匆过客 提交于 2019-12-20 03:48:14

问题


I am developing an application using Play framework and scala. I am using anorm for data-access layer. And I've got a problem I could not solve.

Brief: I want to be able to have methods in data-access objects (dao) to work inside transactions as well as being called alone.

Details:

I have data-access layer consist of class with methods that only executes particular SQL over database. Traditionally they looks like:

def list() = DB.withConnection { implicit cn =>
  ...
}

Now I want to have some methods to be executed in a transaction scope. Like traditional select-update service methods but still be able to run them alone. So, what I have in my mind is like this:

class Service {
  def fooTransacted() = {
    inTransaction {
      val old = dao.select(id = 2)
      val newObj = old.copy(isActive = true)
      dao.update(newObj)
    }
  }

  def fooSinle() = {
    dao.select(id = 2)
  }
}

I tried around several ways, but could not come up with any solution.


回答1:


What about

class Dao {
  def foo(id: Long)(implicit connection: Connection) = {
    SQL("select * from foo where id={id}").on('id->id).as(...)
  }
}

class Service{
  def withConnection = {
    DB.withConnection {implicit connection =>
      Dao.foo(1)
      Dao.foo(2)
    }
  }

  def withTransaction = {
    DB.withTransaction {implicit connection =>
      Dao.foo(1)
      Dao.foo(2)
  }
}



回答2:


The solution I've seen used elsewhere (principally in Squeryl), is roughly the following:

import java.sql.Connection
object Helper {
  private val conn: ThreadLocal[Connection] = new ThreadLocal

  def inTransaction[X](f: Connection => X) = {
    conn.get() match {
      case null =>
        DB.withConnection { newConn =>
          conn.set(newConn)
          try f(newConn)
          finally conn.set(null)
        }
      case c => f(c)
    }
  }
}

This way, the inTransaction method is re-entrant, so there's no harm in calling it redundantly inside dao.select.

If you prefer, you can expose conn via a public method, and change the signature of f to => X - you lose some compile-time safety, but the API is a little cleaner.

One pitfall with this approach is that connections are tied to threads, which may cause problems if you're using futures or actors, and a process can resume on a different thread (this is a tricky area anyway, but one you should be aware of).

You might want to look into Squeryl too - it may already do what you need.



来源:https://stackoverflow.com/questions/22527160/database-transactions-in-play-framework-scala-applications-anorm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!