我正在使用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)
}
}
我尝试了几种方法,但无法提出任何解决方案。
答案 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 - 它可能已经满足您的需求。