SQLAlchemy - select for update示例

50

我正在寻找在SQLAlchemy中使用select for update的完整示例,但在Google搜索中没有找到。我需要锁定一行并更新一列,以下代码无法运行(永久阻塞):

s = table.select(table.c.user=="test",for_update=True)
# Do update or not depending on the row
u = table.update().where(table.c.user=="test")         
u.execute(email="foo") 

我需要进行提交吗?如何进行提交?据我所知,您需要执行以下步骤:

  • 开始事务
  • 选择...以进行更新锁定
  • 更新
  • 提交

2
还想指出Query对象有一个新的方法:http://docs.sqlalchemy.org/en/rel_0_9/orm/query.html#sqlalchemy.orm.query.Query.with_for_update - Alan Hamlett
3个回答

55

如果您正在使用ORM,请尝试使用with_for_update函数:

foo = session.query(Foo).filter(Foo.id==1234).with_for_update().one()
# 此行现在已被锁定
foo.name = 'bar' session.add(foo)
session.commit() # 此行现在已解锁

1
@MatthewMoisen,你能告诉我为什么要使用 add 吗?如果不使用它,会有问题吗? - fishshrimp鱼虾爆栈
2
@小文件,您不需要使用 add。在执行 with_for_update().one() 后,该行将被锁定,并且只有在 session.commit()session.rollback() 时才会解锁。 - Matthew Moisen

19

虽然回答晚了,但是也许有人会发现它有用。

首先,在查询之间您不需要提交(至少我假设您在询问此问题)。您的第二个查询会无限期地挂起,因为您实际上正在创建两个并发连接到数据库。第一个连接正在获取所选记录的锁定,然后第二个连接尝试修改已锁定的记录。因此,它无法正常工作。(顺便说一下,在给定示例中,您根本没有调用第一个查询,因此我假设在您的真实测试中,您做了像s.execute()这样的操作)。所以,要点是——工作实现应该看起来更像:

s = conn.execute(table.select(table.c.user=="test", for_update=True))
u = conn.execute(table.update().where(table.c.user=="test"), {"email": "foo"})
conn.commit()
当然,在这种简单情况下,没有必要进行任何锁定,但我猜这只是一个例子,您计划在这两个调用之间添加一些额外的逻辑。

2

是的,你需要提交(commit),可以在Engine上执行或者显式地创建一个Transaction。同时,修改器(modifiers)应该在values(...)方法中指定,而不是在execute中指定:

>>> conn.execute(users.update().
...              where(table.c.user=="test").
...              values(email="foo")
...              ) 
>>> my_engine.commit()

9
这个回答没有针对问题中提到的SELECT ... FOR UPDATE用法示例作出主要回应。它提供的代码可以简化为建议的形式,但这样做就不再使用请求的结构了。当然,如果@Mark在获取锁并更新记录之间没有计划添加任何其他逻辑,那么这种简化是可以接受的。 - RobertT
@RobertT:我看到这个答案明显没有回答问题的主要点。我应该删除这个答案(因为它不是一个好答案),还是保留它(并继续得到一些负评)?;) - van
个人而言,我可能会将其删除,但当然这取决于您的选择。实际上,您提出的使用 .values() 的语法当然是有效的,将值作为 .execute() 的关键字参数传递也可以在当前 SQLAlchemy 版本中正常工作(我刚刚检查过)。因此,这个答案唯一真正有价值的地方就是指出要使用 session 并调用 .commit(),对于大多数当前的 SQLAlchemy 用户来说,这是显而易见的,因为问题中使用的语法据我所知已经被认为是过时的很长时间了。这可能是为什么会有人给它点踩的原因,因为大多数人不记得 0.9 或更早的版本 :) - RobertT
谢谢,@RobertT。 我会记住的。即使那些错误并没有改变生命,被提醒我们都会犯错是很好的。 - van

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接