通过不同的模型使数据库调用成为原子
Make DB calls through different models atomic
假设有两个模型,Model1
和Model2
,每个模型都有一组调用DB来检索或写入数据的基本方法。对于一个 Model1
,可以存在多个 Model2
,当插入一个 (Model1, List[Model2])
时,所有这些数据都来自同一个表单。当前的实现执行以下操作:
- 使用
Model1
的插入方法插入 Model1
实例。
- 正确插入
Model1
后,使用 Model2
的插入方法继续插入 List[Model2]
。
问题是,如果在插入 Model2
之一时出现问题,Model1
将保留在数据库中。一个解决方案是捕获 anorm
抛出的任何异常,并通过执行与它完全相反的操作来撤消之前执行的任何内容。但是是否已经有可以使用的解决方案?捕获所有已执行的数据库调用并在需要时还原它们的东西?
您要找的是DB.withTransaction
。和DB.withConnection
完全一样,只是把autocommit
设置为false
,这样如果抛出任何异常,整个事务都会回滚。
示例:
case class Model1(id: Long, something: String, children: List[Model2])
case class Model2(id: Long, name: String)
object Model1 {
def create(model: Model1): Option[Model1] = {
DB.withTransaction { implicit c =>
SQL(...).executeInsert().map { id =>
model.copy(
id = id,
children = Model2.create(model.children)
)
}
}
}
}
object Model2 {
def create(models: List[Model2])(implicit c: java.sql.Connection): List[Model2] = {
...
}
}
注意 Model2.create
如何接受隐含的 Connection
参数。这样它将使用与 Model1.create
事务相同的 Connection
,并允许在失败时回滚。我省略了精细的实现细节,因为关键只是使用 withTransaction
,并且 运行 每个查询都使用相同的 Connection
.
假设有两个模型,Model1
和Model2
,每个模型都有一组调用DB来检索或写入数据的基本方法。对于一个 Model1
,可以存在多个 Model2
,当插入一个 (Model1, List[Model2])
时,所有这些数据都来自同一个表单。当前的实现执行以下操作:
- 使用
Model1
的插入方法插入Model1
实例。 - 正确插入
Model1
后,使用Model2
的插入方法继续插入List[Model2]
。
问题是,如果在插入 Model2
之一时出现问题,Model1
将保留在数据库中。一个解决方案是捕获 anorm
抛出的任何异常,并通过执行与它完全相反的操作来撤消之前执行的任何内容。但是是否已经有可以使用的解决方案?捕获所有已执行的数据库调用并在需要时还原它们的东西?
您要找的是DB.withTransaction
。和DB.withConnection
完全一样,只是把autocommit
设置为false
,这样如果抛出任何异常,整个事务都会回滚。
示例:
case class Model1(id: Long, something: String, children: List[Model2])
case class Model2(id: Long, name: String)
object Model1 {
def create(model: Model1): Option[Model1] = {
DB.withTransaction { implicit c =>
SQL(...).executeInsert().map { id =>
model.copy(
id = id,
children = Model2.create(model.children)
)
}
}
}
}
object Model2 {
def create(models: List[Model2])(implicit c: java.sql.Connection): List[Model2] = {
...
}
}
注意 Model2.create
如何接受隐含的 Connection
参数。这样它将使用与 Model1.create
事务相同的 Connection
,并允许在失败时回滚。我省略了精细的实现细节,因为关键只是使用 withTransaction
,并且 运行 每个查询都使用相同的 Connection
.