Play框架scala应用程序中的数据库事务(anorm)

时间:2014-03-20 08:20:53

标签: scala playframework-2.0 anorm

我正在使用Play框架和scala开发应用程序。我正在使用anorm进行数据访问层。而且我遇到了一个我无法解决的问题。

简短:我希望能够让数据访问对象(dao)中的方法在事务内部工作以及单独调用。

详细信息:

我的数据访问层由类组成,其方法只能在数据库上执行特定的SQL。传统上他们看起来像:

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

现在我想要在事务范围中执行一些方法。与传统的选择更新服务方法一样,但仍然可以单独运行它们。所以,我的想法是这样的:

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)
  }
}

我尝试了几种方法,但无法提出任何解决方案。

2 个答案:

答案 0 :(得分:2)

怎么样?
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)
  }
}

答案 1 :(得分:0)

我见过的其他地方(主要是Squeryl)使用的解决方案大致如下:

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)
    }
  }
}

这样,inTransaction方法是可重入的,因此在dao.select内对它进行冗余调用没有任何害处。

如果您愿意,可以通过公共方法公开conn,并将f的签名更改为=> X - 您将失去一些编译时安全性,但API是一个小清洁工。

这种方法的一个缺陷是连接与线程绑定,如果你使用期货或演员,这可能会导致问题,并且一个进程可以在不同的线程上恢复(无论如何这是一个棘手的区域,但你应该请注意)。

您可能也希望了解Squeryl - 它可能已经满足您的需求。