読者です 読者をやめる 読者になる 読者になる

FOR UPDATEを利用してオブジェクトを取得する

SQLAlchemy

with_lockmodeをつけてクエリを発行する。

Session.query(Model).with_lockmode('update').filter(criteria)

MySQLのLOCK IN SHARE MODEでクエリを発行するならば、

Session.query(Model).with_lockmode('read').filter(criteria)

OracleのFOR UPDATE NOWAITでクエリを発行するならば、

Session.query(Model).with_lockmode('update_nowait').filter(criteria)
  • read
  • update
  • update_nowait

以外のロックモードを指定した場合には例外が排出される。select関数のfor_updateオプションに指定できるロックモードと微妙に違うので注意が必要。

>>> from sqlalchemy import select, text
>>> from sqlalchemy.databases import mysql, oracle
>>> print select([text("1")], for_update='update').compile(dialect=mysql.MySQLDialect())
SELECT 1 FOR UPDATE
>>> print select([text("1")], for_update='read').compile(dialect=mysql.MySQLDialect())
SELECT 1 LOCK IN SHARE MODE
>>> print select([text("1")], for_update='nowait').compile(dialect=oracle.OracleDialect())
SELECT 1 FROM DUAL FOR UPDATE NOWAIT

select関数の方に利用できるのは、

  • read
  • update
  • nowait

もっと正確に言えば、使用しているデータベースにread, nowaitに相当する機能があればそれを利用し、そうでない場合は、for_updateの値が真と評価されればFOR UPDATEを使用し、偽と評価されれば使用しないということだが、下記のような書き方は冗長以外の何者でもない。(最初の例だけは、場面によっては「あり」かもしれない。)

# FOR UPDATEを使わないことを明示的にしめしてみた。引数自体を省略した方が分かりやすいのではないか?
>>> print select([text("1")], for_update=False).compile(dialect=oracle.OracleDialect())
SELECT 1 FROM DUAL

# bool(None) == Falseだからといってもねぇ・・・
>>> print select([text("1")], for_update=None).compile(dialect=oracle.OracleDialect())
SELECT 1 FROM DUAL

# よく分からない値を与えてみた
>>> print select([text("1")], for_update='spam').compile(dialect=oracle.OracleDialect())
SELECT 1 FROM DUAL FOR UPDATE