These are chat archives for scalikejdbc/ja

10th
Dec 2015
Michihito Shigemura
@shigemk2
Dec 10 2015 12:31
This message was deleted
Michihito Shigemura
@shigemk2
Dec 10 2015 12:39

localTxについて質問なのですが、以下の様なオブジェクトを用意し、

import scalikejdbc._

object Transaction {
  def tx[A](a: => A) = {
    DB.localTx { implicit session =>
      a
    }
  }
}

以下の様な呼び出しをしようと思っていますが、

Transaction.tx(registerA(Some("hoge"), List(1,2,3)))
Transaction.tx(registerB(9999, Map("hoge"->"fuga"), Some("fugafuga")))

トランザクションがうまく働きません。registerA自体は以下のようになっていますが、

def registerA(a: Option[String], List[Int])(implicit session: DBSession = AutoSession) = {
  // insert statement on table_A
  // insert statement on table_B
  // insert statement on table_C
  // insert statement on table_D
}

registerAやBでは複数のinsert文が走っており、1つ目が成功→2つ目が失敗した場合、registerAの処理自体がロールバックされることを期待していますが、1つ目の処理はロールバックされずに残っております。

Transactionオブジェクトのtx関数の書き方が悪いのが原因なのかよくわかっていませんが、解決方法をご教授頂ければ幸いでございます。

KAWACHI Takashi
@tkawachi
Dec 10 2015 12:54
アッアッ、昨日の pull request 不完全でした。
https://github.com/scalikejdbc/scalikejdbc/blob/master/scalikejdbc-core/src/main/scala/scalikejdbc/DBConnection.scala#L295-L298
finishTx() や closeConnection() の結果捨てられてますし、、 commit() で失敗した時の rollback もここでやられてるし、、
TxBoundary としては今のもので正しいと思うんですが、使う側とあっていないというか
Tsukasa Kitachi
@kxbmap
Dec 10 2015 13:10
@shigemk2
各insertの実行にAutoSessionが使われてるのが原因ですね。
def tx[A](a: DBSession => A)にしてaにlocalTxのsessionを渡す必要があります。
registerAの方はトランザクション外で使うことがないならデフォルト引数(= AutoSession)は削除した方がよさそう
Michihito Shigemura
@shigemk2
Dec 10 2015 13:26
ここでいうと、呼び出し側としてはどのようにaにlocalTxのsessionを渡すようにしたらいいでしょうか?
Michihito Shigemura
@shigemk2
Dec 10 2015 13:41
localTx はトランザクションのスコープを明示するものなので DBSession を取り出して値として利用することはできません。
という風に書かれているので、うまい方法が思いつかないです。
https://github.com/scalikejdbc/scalikejdbc-cookbook/blob/master/ja/04_transaction.md
とりあえず、こうですね。
  def tx[A](a: DBSession => A) = {
     DB.localTx { implicit session =>
        a(session)
    }
  }
Michihito Shigemura
@shigemk2
Dec 10 2015 13:46
呼び出すときにどうしたらいいんでしょう…
KAWACHI Takashi
@tkawachi
Dec 10 2015 13:47
自分なに言ってるんだろう、大丈夫ですね。。
完全に見間違えていた…
↑の tx() は DB.localTx() とほぼ同じものになっているような… @shigemk2
KAWACHI Takashi
@tkawachi
Dec 10 2015 13:54
なので呼び出すときは tx { implicit session => registerA(???) } 的な感じですかね
Tsukasa Kitachi
@kxbmap
Dec 10 2015 13:55
`Transaction.tx(registerA(Some("hoge"), List(1,2,3)))` これで動くのでは。
省略せずに書くなら
Transaction.tx { implicit session =>
  registerA(Some("hoge"), List(1,2,3))
}
Michihito Shigemura
@shigemk2
Dec 10 2015 13:59
ありがとうございます。ちなみに、registerAの戻り値を参照したい場合は、いかが致しましょうか。
KAWACHI Takashi
@tkawachi
Dec 10 2015 14:02
tx() が DB.localTx() の戻り値を返していて、localTx() は a(session) の戻り値を返しているので、tx() の戻り値が registerA() の戻り値になってますよ
Michihito Shigemura
@shigemk2
Dec 10 2015 14:37
きちんとロールバックされるようになりました。最終的にはこのように書いています。
import scalikejdbc._

object Transaction {
  def tx[A](a: DBSession => A): A = {
    DB.localTx { implicit session =>
      a(session)
    }
  }
}
ありがとうございます。なお、各insertの実行にAutoSessionが使われると何が行けなかったのでしょうか…?
KAWACHI Takashi
@tkawachi
Dec 10 2015 14:40
tx() 不要そう( localTx()で良さそう
AutoSession よく知らないのですが auto commit mode になるとか?
Michihito Shigemura
@shigemk2
Dec 10 2015 14:57
AutoSessionにするとAutoCommitになり、どういう仕組みか分かりませんが、最初のコードですとAutoCommitModeになるという流れです。
Michihito Shigemura
@shigemk2
Dec 10 2015 15:04

tx() 不要そう( localTx()で良さそう

objectじゃなくてclassにしたら不要になりそうです

あとこれだと単体では呼べないので、試しにnullを入れたらコンパイルが通ったので、ちょっとどうしたものかと思っております。
registerA(Some("hoge"), List(1,2,3))(null)
KAWACHI Takashi
@tkawachi
Dec 10 2015 23:58
単体で呼ぶというのは明示的に DBSession を指定しないで呼ぶということ?、だったら最初のように def registerA(a: Option[String], List[Int])(implicit session: DBSession = AutoSession) = ??? としておくんですかね
null を入れるとコンパイルが通るのはしょうがないです。そういうものです