MySQL Sqlalchemy session.refresh方法不刷新对象

MySQL Sqlalchemy session.refresh方法不刷新对象

如果你使用过Python中的SQLAlchemy工具包,你可能已经知道了在SQLAlchemy中使用session来操作数据库。针对session.refresh() 方法不刷新对象的问题,本文提供一些解决方案。

阅读更多:MySQL 教程

什么是session.refresh()?

在SQLAlchemy中,session是一个关键的对象,它代表着与数据库的会话。其中,session.refresh() 方法用于将当前对象的属性更新为数据库中的最新值。如果你使用了session.query().first() 方法获取了一个对象,但此后数据库中的数据被更改了,你可以使用session.refresh() 方法手动刷新对象,以便在应用中使用最新的数据。

以下是session.refresh() 方法的常用参数:

  • instance: 必选参数,指定需要刷新的对象
  • attribute_names: 可选参数,指定需要刷新的属性名称,它应该是一个字符串列表或者None。当attribute_names=None时,session.refresh() 方法会刷新所有属性。

为什么session.refresh()方法不刷新对象

默认情况下,当使用session.refresh() 方法时,SQLAlchemy总是尝试从数据库中获取最新的数据来刷新对象。然而,当一个对象被同一session对象多次使用时,它的状态可能会出现问题。下面将详细介绍这个问题。

引用同一个对象多次

当一个对象被引用了多次时,session.refresh() 方法可能会不工作。考虑以下例子:

from sqlalchemy.orm import sessionmaker

Session = sessionmaker()
session = Session()

class User(Base):
    # 定义表结构

admin = session.query(User).filter_by(username='admin').first()
print(admin.username)   # 'admin'

session.query(User).filter_by(username='admin').update({'username': 'root'})
print(admin.username)   # 'admin'

session.refresh(admin)
print(admin.username)   # 'admin'

在上面的例子中,当我们使用session.query().update() 更新表中的数据时,admin用户的username属性应该被更新为’root’。但是,函数调用之后我们会发现仍然是’admin’。原因是更新操作并未立即刷新在session中保存的该对象,此时admin对象已经失效,因为它没有得到数据库中‘root’所对应的数据。其实这种行为被预期,是由于SQLAlchemy session使用了identity map(身份映射)的缓存技术来减少对数据库的重复查询,从而提高查询性能。

因此,当我们执行session.refresh(admin) 方法时,我们会发现它不刷新唯一对象。

同一个事务中重复查询

如果我们多次查询同一个对象,而且这些查询是在同一个事务中执行的,就会出现类似的问题。考虑以下例子:

from sqlalchemy.orm import sessionmaker

Session = sessionmaker()
session = Session()

class User(Base):
    # 定义表结构

admin = session.query(User).filter_by(username='admin').first()
print(admin.username)   # 'admin'

admin = session.query(User).filter_by(username='admin').first()
print(admin.username)   # 'admin'

session.query(User).filter_by(username='admin').update({'username': 'root'})

admin = session.query(User).filter_by(username='admin').first()
print(admin.username)   # 'root'

session.refresh(admin)
print(admin.username)   # 'admin'

在上面的例子中,我们多次查询admin对象,并在其中一次查询后更新了该对象。当我们再次查询admin对象时,我们得到了’root’作为username属性的值。然而,当我们再使用session.refresh() 方法时,我们会发现它将admin对象刷新为之前的值(‘admin’),而不是更新后的值(‘root’)。

session.expire_on_commit属性

以上两种情况下,session是把对象的状态缓存在identity map中,因此我们不能再次获取它。SQLAlchemy提供了一个简单的解决方案:设置session.expire_on_commit属性。当这个属性设置为True时,在每次的事务提交之后,session会自动将所有对象缓存刷新为数据库中的最新值。可以通过以下方式设置:

session.expire_on_commit = True

在上面的例子中,我们只需要在session对象中设置此属性,然后再执行session.query().update() 和 session.refresh() 方法即可解决问题。这样,我们就可以获得最新的数据,而不是被缓存的之前的实例。

如何正确使用session.refresh()方法

在使用session.refresh() 方法之前,我们需要确保它会刷新我们需要的对象。我们可以通过以下方式进行检查:

  1. 检查identity map中是否有该对象的实例;
  2. 每次操作之后都调用session.commit() 方法提交当前事务,然后在执行session.refresh() 方法之前查询数据库以确保该对象不在identity map缓存中。

如果使用session.expire_on_commit属性,则不需要进行这些检查。

下面是使用session.refresh() 方法的正确方式:

from sqlalchemy.orm import sessionmaker

Session = sessionmaker()
session = Session()

class User(Base):
    # 定义表结构

# 检查是否有该对象的实例
admin = session.query(User).filter_by(username='admin').first()
if admin is None:
    print("No such user")
else:
    # 更新对象并提交事务
    session.query(User).filter_by(username='admin').update({'username': 'root'})
    session.commit()

    # 刷新对象并进行操作
    session.refresh(admin)
    print(admin.username)

总结

在SQLAlchemy中,session.refresh() 方法可以将当前对象的所有属性更新为数据库的最新值。然而,当一个对象在同一session中被重复查询或引用时,可能会出现session.refresh() 方法不刷新对象的问题。这可以通过设置session.expire_on_commit属性来解决。在正确使用session.refresh() 方法之前,我们需要确保该对象的状态已经正确更新,并且在执行session.refresh() 方法之前需要仔细检查identity map中是否存在该对象的实例。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程