7

次のメソッドがあるとしましょう:
WebsitesList モデル:

def create(urls: List[String]) = DB.withTransaction(implicit c => {
    val websites = urls.map(Website.create(_))
    val listId: Option[Long] = SQL("INSERT INTO LIST (created_date, active) VALUES ({created_date}, {active})").
      on('created_date -> new Date(), 'active -> true).
      executeInsert()
    websites.foreach(websiteId =>
      SQL("INSERT INTO websites_list (list_id, website_id) VALUES ({listId}, {websiteId})").
      on('listId -> listId.get, 'websiteId -> websiteId.id).executeInsert()
    )

    throw new Exception()
  })

Web サイト モデルでは、次のようになります。

def create(url: String): Website = DB.withConnection {
    implicit c =>
      val currentDate = new Date()
      val insertedId: Option[Long] = SQL("insert into websites (url, Date_Added) values ({url}, {date})").
        on('url -> url, 'date -> currentDate).
        executeInsert()
      Website(insertedId.get, url, currentDate)
  }

ご覧のとおり、WebsitesList create メソッドでトランザクションを開始すると、このメソッドが Web サイト モデルの create メソッドを呼び出します。
私の目的は、何らかの理由で WebsitesList の作成に失敗した場合に、作成された Web サイトのレコードを削除することです。テストするために例外を発生させましたが、予想どおり、WebsitesList および List レコードは作成されません。ただし、例外の後、Web サイトのレコードはロールバックされず、データベースに残ります。

私の理論では、 Website.create メソッドは既存の接続を使用する代わりに新しい接続を作成したというものです。これを修正する方法を知っている人はいますか?

ありがとう。

4

1 に答える 1

11

署名を変更して暗黙的な接続を取得し、create メソッドの外部でトランザクションを制御できます。

Website.scala

object Website {

  def create(url: String): Website = {
    DB.withConnection { implicit connection =>
      createWithConnection(url)
    }
  }

  def createWithConnection(url: String)(implicit connection: Connection): Website = {
    val currentDate = new Date()
    val insertedId: Option[Long] = SQL("insert into websites(url, Date_Added) values ({url}, {date})").
      on('url -> url, 'date -> currentDate).
      executeInsert()
    Website(insertedId.get, url, currentDate)
  }
}

WebsiteList.scala

object WebsiteList {

  def create(urls: List[String]) = DB.withTransaction(implicit c => {
    createWithConnection(urls)
  })

  def createWithConnection(urls: List[String])(implicit connection: Connection) = {
    val websites = urls.map(Website.createWithConnection)
    val listId: Option[Long] = SQL("INSERT INTO LIST (created_date, active) VALUES ({created_date}, {active})").
      on('created_date -> new Date(), 'active -> true).
      executeInsert()
    websites.foreach(websiteId =>
      SQL("INSERT INTO websites_list (list_id, website_id) VALUES ({listId}, {websiteId})").
        on('listId -> listId.get, 'websiteId -> websiteId.id).executeInsert()
    )

    throw new Exception()
  }
}

ご覧のとおり、複数の挿入を行っており、それらを同じトランザクションで一緒にコミットする必要があるため、WebsiteListを に変更しました。DB.withConnectionDB.withTransaction

これにより、 をいつどこでConnection共有するかを制御できます。

たとえば、トランザクションの使用期間をよりよく知っているコントローラーでトランザクション管理を行うことができます。

object SomeController extends Controller {
  def someAction(someData: String) = Action { implicit request =>
    DB.withTransaction { implicit connection =>
      SomeModel.create(someData)
      OtherModel.create(someData)
    }
  }
}

object SomeModel {
  def create(data: String)(implicit connection: Connection) {
  }
}

object OtherModel {
  def create(data: String)(implicit connection: Connection) {
  }
}
于 2013-09-12T07:19:10.823 に答える