使用会话
在 ORM 映射类配置 中描述的声明基础和 ORM 映射函数是 ORM 的主要配置接口。一旦配置了映射,持久性操作的主要使用接口是 Session
。
- 会话基础知识
- 会话是做什么的?
- 使用会话的基础知识
- 打开和关闭会话
- 构建开始/提交/回滚块
- 使用
sessionmaker
- 查询
- 添加新的或现有的项目
- 删除
- 刷新
- 按主键获取
- 过期/刷新
- 使用任意 WHERE 子句进行 UPDATE 和 DELETE
- 自动开始
- 提交
- 回滚
- 关闭
- 会话常见问题
- 何时创建
sessionmaker
? - 何时构建
Session
,何时提交,何时关闭? - 会话是缓存吗?
- 如何获取特定对象的
Session
? - 会话是线程安全的吗?
AsyncSession
在并发任务中安全共享吗?
- 状态管理
- 对象状态简介
- 获取对象的当前状态
- 会话属性
- 会话引用行为
- 合并
- 合并提示
- 清除
- 刷新/过期
- 实际加载了什么
- 何时使对象过期或刷新
- 级联操作
- 保存更新
- 双向关系中保存更新级联的行为
- 删除
- 使用删除级联与多对多关系
- 使用 ORM 关系的外键 ON DELETE 级联
- 使用外键 ON DELETE 与多对多关系
- 删除孤儿
- 合并
- 刷新过期
- 清除
- 关于删除 - 删除从集合和标量关系引用的对象的注意事项
- 事务和连接管理
- 管理事务
- 使用 SAVEPOINT
- 会话级别与引擎级别的事务控制
- 显式开始
- 启用两阶段提交
- 设置事务隔离级别 / DBAPI 自动提交
- 通过事件跟踪事务状态
- 将会话加入外部事务(例如用于测试套件)
- 其他持久化技术
- 将 SQL 插入/更新表达式嵌入到刷新中
- 在会话中使用 SQL 表达式
- 强制将具有默认值的列设置为 NULL
- 获取服务器生成的默认值
- 情况 1:非主键,支持 RETURNING 或等效
- 情况 2:表包含与 RETURNING 不兼容的触发器生成值
- 情况 3:不支持或不需要非主键、RETURNING 或等效项
- 情况 4:支持主键、RETURNING 或等效项
- 情况 5:不支持主键、RETURNING 或等效项
- 关于急切获取用于 INSERT 或 UPDATE 的客户端调用的 SQL 表达式的注意事项
- 使用 INSERT、UPDATE 和 ON CONFLICT(即 upsert)返回 ORM 对象
- 使用 PostgreSQL ON CONFLICT 和 RETURNING 返回 upserted ORM 对象
- 分区策略(例如每个会话多个数据库后端)
- 简单垂直分区
- 为多引擎会话协调事务
- 自定义垂直分区
- 水平分区
- 批量操作
- 上下文/线程本地会话
- 隐式方法访问
- 线程本地作用域
- 在 Web 应用程序中使用线程本地作用域
- 使用自定义创建的作用域
- 上下文会话 API
scoped_session
ScopedRegistry
ThreadLocalRegistry
QueryPropertyDescriptor
- 使用事件跟踪查询、对象和会话变更
- 执行事件
- 基本查询拦截
- 添加全局 WHERE / ON 条件
- 重新执行语句
- 持久性事件
before_flush()
after_flush()
after_flush_postexec()
- Mapper 级刷新事件
- 对象生命周期事件
- 瞬态
- Transient to Pending
- Pending to Persistent
- Pending to Transient
- Loaded as Persistent
- Persistent to Transient
- Persistent to Deleted
- Deleted to Detached
- Persistent to Detached
- Detached to Persistent
- Deleted to Persistent
- 事务事件
- 属性更改事件
- 会话 API
- Session and sessionmaker()
sessionmaker
ORMExecuteState
Session
SessionTransaction
SessionTransactionOrigin
- Session Utilities
close_all_sessions()
make_transient()
make_transient_to_detached()
object_session()
was_deleted()
- 属性和状态管理工具
object_state()
del_attribute()
get_attribute()
get_history()
init_collection()
flag_modified()
flag_dirty()
instance_state()
is_instrumented()
set_attribute()
set_committed_value()
History
会话基础
会话的作用是什么?
从最一般的意义上讲,Session
建立了与数据库的所有交流,并代表了其生命周期内加载或关联的所有对象的“存储区”。它提供了一个接口,用于进行 SELECT 和其他查询,这些查询将返回并修改 ORM 映射的对象。ORM 对象本身保存在 Session
内部,位于一个称为 identity map 的结构中 - 这是一种维护每个对象唯一副本的数据结构,其中“唯一”意味着“只有一个具有特定主键的对象”。
在其最常见的使用模式中,Session
以大多数无状态形式开始。一旦发出查询或使用其他对象进行持久化,它将从与 Session
关联的 Engine
请求连接资源,然后在该连接上建立事务。该事务保持生效直到 Session
被指示提交或回滚事务。当事务结束时,与 Engine
关联的连接资源将被 释放 到引擎管理的连接池中。然后,使用新的连接检出开始新的事务。
由 Session
维护的 ORM 对象被 instrumented 以便每当 Python 程序中的属性或集合被修改时,都会生成一个变更事件,该事件会被 Session
记录下来。每当数据库即将被查询或事务即将被提交时,Session
首先 flushes 所有存储在内存中的待定更改到数据库中。这被称为 unit of work 模式。
当使用 Session
时,将其维护的 ORM 映射对象视为代理对象,它们对应于事务在 Session
中保持的本地数据库行。为了保持对象的状态与实际数据库中的状态相匹配,存在各种事件会导致对象重新访问数据库以保持同步。可以“分离”对象与 Session
,并继续使用它们,尽管这种做法有其注意事项。通常情况下,当您想要再次使用它们时,您会重新将已分离的对象与另一个 Session
关联起来,以便它们可以恢复其表示数据库状态的正常任务。
使用会话的基础知识
这里介绍了最基本的 Session
使用模式。
打开和关闭会话
Session
可以单独构建,也可以使用 sessionmaker
类构建。通常,它会在开始时传递一个单独的 Engine
作为连接源。典型的用法可能如下所示:
from sqlalchemy import create_engine from sqlalchemy.orm import Session # an Engine, which the Session will use for connection # resources engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/") # create session and add objects with Session(engine) as session: session.add(some_object) session.add(some_other_object) session.commit()
在上述代码中,Session
是使用与特定数据库 URL 关联的 Engine
实例化的。然后,在 Python 上下文管理器(即 with:
语句)中使用它,以便在块结束时自动关闭;这相当于调用 Session.close()
方法。
对 Session.commit()
的调用是可选的,仅在我们与 Session
执行的工作包含要持久化到数据库的新数据时才需要。如果我们只发出 SELECT 调用并且不需要写入任何更改,则对 Session.commit()
的调用是不必要的。
注意
注意,在调用 Session.commit()
之后,无论是显式地还是在使用上下文管理器时,与 Session
关联的所有对象都会被过期,这意味着它们的内容将被清除以在下一个事务中重新加载。 如果这些对象被分离,除非使用 Session.expire_on_commit
参数来禁用此行为,否则它们将无法使用,直到与新的 Session
重新关联。 更多细节请参见提交部分。 ### 划定一个开始/提交/回滚块的框架
对于那些将要向数据库提交数据的情况,我们还可以将 Session.commit()
调用和整个事务的“框架”置于上下文管理器中。 在这里,“框架”意味着如果所有操作成功,则会调用 Session.commit()
方法,但如果引发任何异常,则会立即调用 Session.rollback()
方法,以便在将异常向外传播之前立即回滚事务。 在 Python 中,这主要是通过使用类似 try: / except: / else:
的代码块来表达的:
# verbose version of what a context manager will do with Session(engine) as session: session.begin() try: session.add(some_object) session.add(some_other_object) except: session.rollback() raise else: session.commit()
通过使用 Session.begin()
方法返回的 SessionTransaction
对象可以更简洁地实现上述的长形操作序列,该对象为相同操作序列提供了上下文管理器接口:
# create session and add objects with Session(engine) as session: with session.begin(): session.add(some_object) session.add(some_other_object) # inner context calls session.commit(), if there were no exceptions # outer context calls session.close()
更简洁地说,这两个上下文可以合并:
# create session and add objects with Session(engine) as session, session.begin(): session.add(some_object) session.add(some_other_object) # inner context calls session.commit(), if there were no exceptions # outer context calls session.close()
使用一个 sessionmaker
sessionmaker
的目的是为具有固定配置的 Session
对象提供一个工厂。 由于典型的应用程序会在模块范围内具有一个 Engine
对象,因此 sessionmaker
可以为与此引擎相对应的 Session
对象提供一个工厂:
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # an Engine, which the Session will use for connection # resources, typically in module scope engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/") # a sessionmaker(), also in the same scope as the engine Session = sessionmaker(engine) # we can now construct a Session() without needing to pass the # engine each time with Session() as session: session.add(some_object) session.add(some_other_object) session.commit() # closes the session
sessionmaker
类似于 Engine
,是一个模块级别的工厂,用于函数级别的会话 / 连接。因此,它也有自己的 sessionmaker.begin()
方法,类似于 Engine.begin()
,它返回一个 Session
对象,并维护一个开始 / 提交 / 回滚块:
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # an Engine, which the Session will use for connection # resources engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/") # a sessionmaker(), also in the same scope as the engine Session = sessionmaker(engine) # we can now construct a Session() and include begin()/commit()/rollback() # at once with Session.begin() as session: session.add(some_object) session.add(some_other_object) # commits the transaction, closes the session
在上述 with:
块结束时,Session
的事务将提交,并且 Session
将关闭。
在编写应用程序时,sessionmaker
工厂应与 create_engine()
创建的 Engine
对象的范围相同,通常是模块级或全局范围。由于这些对象都是工厂,因此它们可以被任意数量的函数和线程同时使用。
另请参阅
sessionmaker
Session
查询
查询的主要方法是利用 select()
构造来创建一个 Select
对象,然后使用诸如 Session.execute()
和 Session.scalars()
等方法执行该对象以返回结果。然后以 Result
对象的形式返回结果,包括 ScalarResult
等子变体。
SQLAlchemy ORM 查询的完整指南可在 ORM 查询指南 找到。以下是一些简要示例:
from sqlalchemy import select from sqlalchemy.orm import Session with Session(engine) as session: # query for ``User`` objects statement = select(User).filter_by(name="ed") # list of ``User`` objects user_obj = session.scalars(statement).all() # query for individual columns statement = select(User.name, User.fullname) # list of Row objects rows = session.execute(statement).all()
从 2.0 版本开始更改:现在采用“2.0”样式的查询作为标准。请参阅 2.0 迁移 - ORM 用法 获取从 1.x 系列迁移的注意事项。
另请参阅
ORM 查询指南 ### 添加新项目或现有项目
Session.add()
用于将实例放入会话中。对于暂时的(即全新的)实例,这将在下一次刷新时对这些实例执行插入操作。对于持久的(即由此会话加载的)实例,它们已经存在,不需要添加。已分离的(即已从会话中移除的)实例可以使用此方法重新关联到会话中:
user1 = User(name="user1") user2 = User(name="user2") session.add(user1) session.add(user2) session.commit() # write changes to the database
要一次将一系列项目添加到会话中,请使用 Session.add_all()
:
session.add_all([item1, item2, item3])
Session.add()
操作 级联 到 save-update
级联。有关更多详细信息,请参阅 级联 部分。### 删除
Session.delete()
方法将实例放入会话的待删除对象列表中:
# mark two objects to be deleted session.delete(obj1) session.delete(obj2) # commit (or flush) session.commit()
Session.delete()
标记对象以进行删除,这将导致针对每个受影响的主键发出 DELETE 语句。在待刷新的删除之前,被“删除”标记的对象存在于 Session.deleted
集合中。DELETE 后,它们从 Session
中删除,该会话在事务提交后变为永久。
Session.delete()
操作有各种与其他对象和集合的关系相关的重要行为。有关此操作的详细信息,请参阅 级联 部分,但总的规则是:
- 与通过
relationship()
指令与被删除对象相关的映射对象对应的行默认情况下不会被删除。如果这些对象有一个外键约束返回到被删除的行,这些列将被设置为 NULL。如果这些列是非空的,这将导致约束违规。 - 要将相关对象的行的“SET NULL”更改为删除,请在
relationship()
上使用 delete 级联。 - 当表中的行通过
relationship.secondary
参数链接为“多对多”表时,当它们所指向的对象被删除时,这些行在所有情况下都会被删除。 - 当相关对象包含指向要删除对象的外键约束,并且它们所属的相关集合目前未加载到内存中时,工作单元将发出 SELECT 语句以获取所有相关行,以便它们的主键值可用于发出这些相关行的 UPDATE 或 DELETE 语句。这样,即使在 Core
ForeignKeyConstraint
对象上配置了此功能,ORM 也会在没有进一步指示的情况下执行 ON DELETE CASCADE 的功能。 relationship.passive_deletes
参数可用于调整此行为并更自然地依赖于“ON DELETE CASCADE”;当设置为 True 时,此 SELECT 操作将不再发生,但是仍然存在的行仍将受到显式的 SET NULL 或 DELETE 影响。将relationship.passive_deletes
设置为字符串"all"
将禁用所有相关对象的更新/删除。- 当标记为删除的对象发生 DELETE 时,对象不会自动从引用它的集合或对象引用中删除。当
Session
过期时,这些集合可能会被重新加载,以便对象不再存在。然而,最好的做法是,不要对这些对象使用Session.delete()
,而是应该从其集合中删除对象,然后使用 delete-orphan 来确保它在集合删除的次要影响下被删除。有关此的示例,请参阅删除说明 - 从集合和标量关系中删除对象 部分。
另请参阅
delete - 描述了“删除级联”,当主对象被删除时,标记相关对象以删除。
delete-orphan - 描述了“删除孤立对象级联”,当它们与其主对象解除关联时,将标记相关对象以删除。
删除注释 - 从集合和标量关系中删除引用的对象 - 关于Session.delete()
的重要背景,涉及到在内存中刷新关系。### 刷新
当Session
使用其默认配置时,刷新步骤几乎总是透明完成的。具体来说,在因Query
或 2.0 风格的Session.execute()
调用而发出任何单个 SQL 语句之前,以及在Session.commit()
调用中在事务提交之前,都会发生刷新。当使用Session.begin_nested()
时,也会在发出 SAVEPOINT 之前发生刷新。
可以通过调用Session.flush()
方法在任何时候强制进行Session
刷新:
session.flush()
在某些方法的范围内自动发生的刷新称为自动刷新。自动刷新被定义为一种可配置的自动刷新调用,该调用发生在包括以下方法的开头:
- 当针对 ORM 启用的 SQL 构造(例如引用 ORM 实体和/或 ORM 映射属性的
select()
对象)使用Session.execute()
和其他执行 SQL 的方法时 - 当调用
Query
以将 SQL 发送到数据库时 - 在查询数据库之前的
Session.merge()
方法的过程中 - 当对象被刷新
- 当针对未加载对象属性进行 ORM 延迟加载操作时。
还有一些刷新发生的无条件点; 这些点位于关键的事务边界内,包括:
- 在
Session.commit()
方法的过程中 - 当调用
Session.begin_nested()
时 - 当使用
Session.prepare()
2PC 方法时。
通过构造一个传递 Session.autoflush
参数为 False
的 Session
或 sessionmaker
,可以禁用 autoflush 行为,如前述条目列表所示:
Session = sessionmaker(autoflush=False)
此外,可以在使用 Session
期间通过使用 Session.no_autoflush
上下文管理器临时禁用自动刷新(autoflush):
with mysession.no_autoflush: mysession.add(some_object) mysession.flush()
再次强调: 当调用事务方法,如 Session.commit()
和 Session.begin_nested()
时,无论任何“autoflush”设置,当 Session
仍有待处理的更改时,刷新过程 总是发生。
由于 Session
仅在 DBAPI 事务上下文中调用数据库的 SQL,所有“flush”操作本身仅发生在数据库事务内部(取决于数据库事务的 隔离级别),前提是 DBAPI 不处于 驱动程序级别的自动提交 模式。这意味着假设数据库连接在其事务设置中提供了 原子性,如果 flush 内的任何单个 DML 语句失败,则整个操作将被回滚。
当 flush 中发生故障时,为了继续使用相同的 Session
,需要在 flush 失败后显式调用 Session.rollback()
,即使底层事务已经被回滚(即使数据库驱动程序在技术上处于驱动程序级别的自动提交模式)。这样做是为了始终保持所谓的“子事务”的整体嵌套模式。FAQ 部分 “This Session’s transaction has been rolled back due to a previous exception during flush.” (或类似内容) 中包含对此行为的更详细描述。
另请参阅
“此会话的事务已因刷新期间的先前异常而回滚。”(或类似内容) - 进一步解释为何在刷新失败时必须调用 Session.rollback()
。 ### 通过主键获取
由于 Session
利用了一个身份映射,通过主键引用当前内存中的对象,因此 Session.get()
方法被提供为一种通过主键定位对象的方法,首先在当前身份映射中查找,然后查询数据库以获取不存在的值。例如,要定位主键标识为 (5, )
的 User
实体:
my_user = session.get(User, 5)
Session.get()
也包括用于复合主键值的调用形式,可以作为元组或字典传递,以及允许特定加载器和执行选项的其他参数。有关完整参数列表,请参阅 Session.get()
。
另请参阅
Session.get()
### 过期 / 刷新
使用 Session
时经常遇到的一个重要考虑因素是处理从数据库加载的对象上存在的状态,以使其与当前事务的状态保持同步。SQLAlchemy ORM 基于身份映射的概念,这意味着当对象从 SQL 查询中“加载”时,将维护一个对应于特定数据库标识的唯一 Python 对象实例。这意味着如果我们发出两个单独的查询,每个查询都针对同一行,并获得一个映射对象,则两个查询将返回相同的 Python 对象:
>>> u1 = session.scalars(select(User).where(User.id == 5)).one() >>> u2 = session.scalars(select(User).where(User.id == 5)).one() >>> u1 is u2 True
接下来,当 ORM 从查询中获取行时,它将跳过已经加载的对象的属性填充。这里的设计假设是假设一个完全隔离的事务,然后在事务不完全隔离的程度上,应用程序可以根据需要从数据库事务中刷新对象。 此 FAQ 条目 在更详细地讨论了这个概念。
当 ORM 映射对象加载到内存中时,有三种常见方法可以使用当前事务中的新数据刷新其内容:
- expire() 方法 -
Session.expire()
方法将擦除对象的选定或全部属性的内容,以便在下次访问时从数据库加载它们,例如使用 延迟加载 模式:
session.expire(u1) u1.some_attribute # <-- lazy loads from the transaction
- refresh() 方法 - 相关联的是
Session.refresh()
方法,它做的事情与Session.expire()
方法相同,但还会立即发出一个或多个 SQL 查询,以实际刷新对象的内容:
session.refresh(u1) # <-- emits a SQL query u1.some_attribute # <-- is refreshed from the transaction
- populate_existing() 方法或执行选项 - 这现在是一个在 Populate Existing 中记录的执行选项;在传统形式中,它位于
Query
对象上,作为Query.populate_existing()
方法。无论以哪种形式,此操作都表示从查询返回的对象应无条件地从数据库中重新填充:
u2 = session.scalars( select(User).where(User.id == 5).execution_options(populate_existing=True) ).one()
关于刷新 / 过期概念的进一步讨论,请参阅 Refreshing / Expiring。
参见
刷新 / 过期
我正在使用我的 Session 重新加载数据,但它没有看到我在其他地方提交的更改
使用任意 WHERE 子句的 UPDATE 和 DELETE
SQLAlchemy 2.0 包括增强的功能,用于发出几种类型的启用 ORM 的 INSERT、UPDATE 和 DELETE 语句。有关文档,请参阅 ORM-Enabled INSERT, UPDATE, and DELETE statements。
参见
启用 ORM 的 INSERT、UPDATE 和 DELETE 语句
使用自定义 WHERE 条件的 ORM UPDATE 和 DELETE
自动开始
Session
对象具有一种称为 autobegin 的行为。这表示,一旦使用 Session
执行了任何工作,无论是涉及修改 Session
内部状态的工作还是涉及需要数据库连接的操作,Session
将在内部将自身视为处于“事务”状态。
当首次构建 Session
时,不存在事务状态。 当调用诸如 Session.add()
或 Session.execute()
这样的方法时,或者类似地执行 Query
以返回结果(最终使用 Session.execute()
),或者如果在 持久化 对象上修改属性时,将自动开始事务状态。
可以通过访问 Session.in_transaction()
方法来检查事务状态,该方法返回 True
或 False
,指示“自动开始”步骤是否已执行。 虽然通常不需要,但 Session.get_transaction()
方法将返回表示此事务状态的实际 SessionTransaction
对象。
Session
的事务状态也可以通过显式调用 Session.begin()
方法来启动。 当调用此方法时,Session
无条件地置于“事务性”状态。 Session.begin()
可以像在 框架化一个 begin / commit / rollback 块 中描述的那样用作上下文管理器。
禁用自动开始以防止隐式事务
可以使用 Session.autobegin
参数将“自动开始”行为设置为 False
以禁用该行为。通过使用此参数,Session
将要求显式调用 Session.begin()
方法。在构造后以及调用 Session.rollback()
、Session.commit()
或 Session.close()
方法后,Session
不会隐式开始任何新事务,并且如果在首次调用 Session.begin()
之前尝试使用 Session
,则会引发错误:
with Session(engine, autobegin=False) as session: session.begin() # <-- required, else InvalidRequestError raised on next call session.add(User(name="u1")) session.commit() session.begin() # <-- required, else InvalidRequestError raised on next call u1 = session.scalar(select(User).filter_by(name="u1"))
新版本 2.0 中新增了 Session.autobegin
,允许禁用“自动开始”行为。 ### 提交
Session.commit()
用于提交当前事务。在核心上,这表示它会对所有当前具有进行中事务的数据库连接发出 COMMIT
;从 DBAPI 的角度来看,这意味着会在每个 DBAPI 连接上调用 connection.commit()
DBAPI 方法。
当对 Session
没有进行事务操作时,表示自上次调用 Session.commit()
以来未对此 Session
执行任何操作时,该方法将开始并提交一个仅限内部的“逻辑”事务,通常不会影响数据库,除非检测到挂起的刷新更改,但仍会调用事件处理程序和对象过期规则。
Session.commit()
操作在发出相关数据库连接的 COMMIT 前无条件发出 Session.flush()
。如果未检测到挂起的更改,则不会向数据库发出 SQL。此行为不可配置,并且不受 Session.autoflush
参数的影响。
在此之后,假设Session
绑定到一个Engine
,那么Session.commit()
将会提交实际的数据库事务,如果有的话。提交之后,与该事务相关联的Connection
对象将被关闭,导致其底层的 DBAPI 连接被释放回与Session
绑定的Engine
相关联的连接池中。
对于绑定到多个引擎的Session
(例如在分区策略中描述的),对于每个正在进行“逻辑”提交的Engine
/ Connection
,相同的提交步骤将继续进行。这些数据库事务在未启用两阶段特性的情况下是不协调的。
还有其他的连接交互模式可用,通过直接将Session
绑定到一个Connection
;在这种情况下,假定存在一个外部管理的事务,并且在这种情况下不会自动发出真正的 COMMIT;请参阅将会话加入外部事务(例如用于测试套件)部分了解此模式的背景。
最后,在关闭事务时,Session
中的所有对象都将被过期。这样,当实例下次被访问时,无论是通过属性访问还是通过它们出现在 SELECT 的结果中,它们都会接收到最新的状态。此行为可以通过Session.expire_on_commit
标志来控制,当此行为不希望时,可以将其设置为False
。
另请参见
自动开始 ### 回滚
Session.rollback()
方法用于回滚当前事务(如果有的话)。当没有事务存在时,该方法会悄然通过。
使用默认配置的会话,在通过自动开始或显式调用Session.begin()
方法开始事务后,会话的回滚后状态如下:
- 数据库事务被回滚。对于绑定到单个
Engine
的Session
,这意味着对当前正在使用的最多一个Connection
进行 ROLLBACK。对于绑定到多个Engine
对象的Session
对象,将对所有检出的Connection
对象发出 ROLLBACK。- 数据库连接被释放。这遵循与提交中注意到的相同的与连接相关的行为,其中从
Engine
对象获取的Connection
对象被关闭,导致 DBAPI 连接被释放到Engine
中的连接池。如果开始新的事务,新连接将从Engine
中检出。- 对于直接绑定到
Connection
的Session
(如加入外部事务的会话(例如用于测试套件)中描述的),此Connection
上的回滚行为将遵循由Session.join_transaction_mode
参数指定的行为,这可能涉及回滚保存点或发出真正的 ROLLBACK。- 在事务生命周期内将初始处于 pending 状态的对象从被添加到
Session
中的情况下,将被清除,对应于它们的 INSERT 语句被回滚。它们属性的状态保持不变。- 在事务生命周期内标记为 deleted 的对象将被提升回 persistent 状态,对应于它们的 DELETE 语句被回滚。请注意,如果这些对象在事务中首先是 pending,那么该操作将优先执行。
- 所有未被清除的对象都完全过期 - 这与
Session.expire_on_commit
设置无关。
在理解了这种状态之后,Session
可以在发生回滚后安全地继续使用。
从版本 1.4 开始更改:Session
对象现在具有延迟“begin”行为,如 autobegin 中所述。如果没有开始事务,则Session.commit()
和Session.rollback()
等方法将无效。在 1.4 之前不会观察到此行为,因为在非自动提交模式下,事务总是隐式存在。
当Session.flush()
失败时,通常是由于主键、外键或“非空”约束违反等原因,会自动发出 ROLLBACK(目前不可能在部分失败后继续刷新)。然而,此时Session
进入一种称为“不活动”的状态,调用应用程序必须始终显式调用Session.rollback()
方法,以便Session
可以恢复到可用状态(也可以简单地关闭和丢弃)。有关进一步讨论,请参阅“由于刷新期间的先前异常,此会话的事务已被回滚。”(或类似)的常见问题解答。
参见
自动开始 ### 关闭
Session.close()
方法会调用 Session.expunge_all()
,从会话中移除所有 ORM 映射的对象,并释放任何与其绑定的 Engine
对象的事务/连接资源。当连接返回到连接池时,事务状态也会回滚。
默认情况下,当 Session
关闭时,它实际上处于创建时的原始状态,可以再次使用。从这个意义上说,Session.close()
方法更像是一个“重置”到干净状态,而不是像一个“关闭数据库”的方法。在这种操作模式下,方法 Session.reset()
是 Session.close()
的别名,并且行为相同。
通过将参数 Session.close_resets_only
设置为 False
,可以更改 Session.close()
的默认行为,表示在调用方法 Session.close()
后不能重新使用 Session
。在这种操作模式下,当 Session.close_resets_only
设置为 True
时,方法 Session.reset()
将允许会话的多次“重置”,行为类似于当 Session.close_resets_only
设置为 True
时的 Session.close()
。
版本 2.0.22 中新增。
建议通过在结尾处调用 Session.close()
来限制 Session
的范围,特别是如果没有使用 Session.commit()
或 Session.rollback()
方法。Session
可以作为上下文管理器使用,以确保调用 Session.close()
:
with Session(engine) as session: result = session.execute(select(User)) # closes session automatically
在版本 1.4 中做了更改:Session
对象具有延迟“开始”行为,如 autobegin 中所述。在调用 Session.close()
方法后不再立即开始新的事务。
到此为止,许多用户已经对会话有了问题。本节介绍了使用 Session
时所面临的最基本问题的迷你 FAQ(请注意,我们还有一个 真正的 FAQ)。
何时创建 sessionmaker
?
只需一次,在应用程序的全局范围的某个地方。它应被视为应用程序配置的一部分。如果您的应用程序在一个包中有三个 .py 文件,您可以将 sessionmaker
行放在 __init__.py
文件中;从那时起,您的其他模块会说“from mypackage import Session”。这样,其他所有人只需使用 Session()
,并且该会话的配置由该中心点控制。
如果您的应用程序启动,进行导入操作,但不知道将要连接到哪个数据库,您可以稍后在“类”级别将 Session
绑定到引擎,使用 sessionmaker.configure()
。
在本节中的示例中,我们经常会在实际调用 Session
的行的正上方显示 sessionmaker
被创建。但那只是为了举例说明!实际上,sessionmaker
会在模块级别的某个地方。对 Session
进行实例化的调用将放置在应用程序中开始数据库会话的地方。
SqlAlchemy 2.0 中文文档(二十二)(2)https://developer.aliyun.com/article/1560448