トランザクション#
注釈はありません。囲んだところがトランザクションです。
try (DBTransaction transaction = new DBTransaction(db)) {
transaction.beginTransaction();
long id = db.insert(
SQL.insert(Post.instance())
.value(Post.title, request.getString("title"))
.value(Post.body, request.getString("body"))
.value(Post.image_name, request.getStringOptional("image_name"))
.value(Post.published, request.getBoolean("published"))
.value(Post.created_at, new Date())
);
if (id <= 0) {
transaction.rollbackEndTransaction();
return -1;
}
new NoticeExecutor().put(db, new Data()
.putData("post_id", id)
.putData("title", request.getString("title")));
transaction.commitEndTransaction();
/*
* コミットしてから流す。
* 先に流すと、ロールバックしたときに
* 「入っていない記事のお知らせ」だけが届く。
*/
PostFeedHandler.notifyNewPost(request.getStringOptional("title"));
return id;
}
使い分け#
| メソッド | 何をするか |
|---|---|
beginTransaction() |
始める。他で始まっていれば何もしない |
commit() |
確定する。トランザクションは続く |
commitEndTransaction() |
確定して終わる |
rollback() |
戻す。トランザクションは続く |
rollbackEndTransaction() |
戻して終わる |
close() |
開いたままなら戻す |
commit() と commitEndTransaction() の違いに注意してください。
commit() は「ここまでを確定して、まだ続ける」です。
移送元のコードでは commit() が中で終わらせていたため、
そこから先が黙って自動コミットになるという穴がありました。jimble では直っています。
畳み忘れ#
try-with-resources を使わずに beginTransaction() して、途中で return すると、
ロールバックもされず接続もプールへ戻りません。
jimble は実行(Context)の終わりに拾います。
ERROR コミットもロールバックもされていないトランザクションが残っていました。ロールバックして閉じます: blog_example
黙って戻すと「入れたつもりが入っていない」が残るので、ERROR を出してからロールバックします。 このログが出たら、囲み忘れです。
拾うのは DBTransaction を使ったときだけではありません。
db.beginTransaction() を直に呼んだときも同じです
(コネクションを握っているのは DB のほうなので、そちらで見ています)。
閉じ忘れた DB#
ふつうの SQL は、DB を閉じ忘れても漏れません。
1文ごとにコネクションをプールへ返しているので、DBUtil.getMainDB() を
使い捨てにする書き方でかまいません。
握ったまま抜ける道は2つだけで、どちらも実行の終わりに拾います。
| 握るもの | いつ返るか |
|---|---|
| トランザクション中 | commitEndTransaction() / rollbackEndTransaction() / close() |
カーソル(selectListWithFetcher) |
db.close() |
ERROR 閉じられていない DB が残っていました。閉じます: blog_example(selectListWithFetcher はカーソルなので、close() までコネクションを返しません)
落とし穴
**拾うのは最後の砦です。**拾われた時点で ERROR が出ているので、 ログが出たら直すものだと思ってください。 実行が長いバッチでは、実行の終わりまで1本が握られたままになります。
短く書く#
DBTransaction.transaction(db, transaction -> {
db.insert(...);
db.update(...);
});
始めて、渡した処理を走らせて、commitEndTransaction() まで済ませます。
例外が出れば close() がロールバックします。
MQ と混ぜるとき#
キューに積むのはコミットしてからです。
transaction.commitEndTransaction();
PostFeedHandler.notifyNewPost(title);
先に流すと、ロールバックしたときに「入っていない記事のお知らせ」だけが届きます。 逆に、DB のキュー(MQ)へ積むのはトランザクションの中です。 同じ DB なので、ロールバックすれば積んだものも消えます。