jimble

トランザクション#

注釈はありません。囲んだところがトランザクションです。

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 なので、ロールバックすれば積んだものも消えます。