会话基础
会议的作用是什么?
一般来说, Session
建立与数据库的所有对话,并表示在数据库生命周期内已加载或与之关联的所有对象的“保留区域”。它提供了一个界面,在这里进行SELECT和其他查询,这些查询将返回和修改ORM映射的对象。ORM对象本身在 Session
,在名为 identity map -维护每个对象唯一副本的数据结构,其中“唯一”表示“只有一个具有特定主键的对象”。
这个 Session
以无状态的形式开始。一旦发出查询或其他对象被持久化,它将从 Engine
与 Session
,然后在该连接上建立一个事务。在该交易保持有效之前 Session
指示提交或回滚事务。
由一个 Session
是 instrumented 这样,每当在Python程序中修改属性或集合时,都会生成一个更改事件,该事件由 Session
. 无论何时查询数据库,或当事务即将提交时 Session
第一 冲洗 所有挂起的更改存储在数据库的内存中。这被称为 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://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
被实例化为 Engine
与特定数据库URL相关联。然后在Python上下文管理器中使用它(即 with:
语句),以便它在挡路结束时自动关闭;这等效于调用 Session.close()
方法。
呼唤 Session.commit()
是可选的,只有在我们完成了 Session
包括要持久化到数据库的新数据。如果我们只发出SELECT调用而不需要写入任何更改,则 Session.commit()
没必要。
构建begin/commit/rollback块
我们也可以附上 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()
通过使用 SessionTransaction
返回的对象 Session.begin()
方法,它为相同的操作序列提供上下文管理器接口:
# 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://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
对象,并且还维护一个begin/commit/rollback块:
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # an Engine, which the Session will use for connection # resources engine = create_engine('postgresql://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
在上面的地方, Session
将两者都提交其事务以及 Session
将关闭,当上面的 with:
块结束。
当您编写应用程序时 sessionmaker
工厂的作用域应与 Engine
对象创建者 create_engine()
,通常在模块级或全局范围内。由于这些对象都是工厂,因此它们可以同时被任意数量的函数和线程使用。
参见
查询(1.x样式)
这个 Session.query()
函数需要一个或多个 实体 并返回新的 Query
对象,该对象将在此会话的上下文中发出映射器查询。“实体”指的是映射类、映射类的属性或其他ORM构造,如 aliased()
结构:
# query from a class results = session.query(User).filter_by(name='ed').all() # query with multiple classes, returns tuples results = session.query(User, Address).join('addresses').filter_by(name='ed').all() # query using orm-columns, also returns tuples results = session.query(User.name, User.fullname).all()
当ORM对象在结果中返回时,它们也存储在标识映射中。当传入数据库行的主键与已存在的对象匹配时,将返回相同的对象,并且不会重新填充已具有值的对象的那些属性。
这个 Session
自动终止事务边界上的所有实例(即当当前事务被提交或回滚时),因此对于通常隔离的事务,数据将在新事务开始时自动刷新。
这个 Query
对象在 对象关系教程(1.x API) ,并进一步记录在 查询API .
参见
查询(2.0样式)
1.4 新版功能.
SQLAlchemy 2.0将通过直接使用 Select
对象,不需要在ORM中 Query
对象。这种操作模式现在在SQLAlchemy 1.4中可用,以支持将要迁移到2.0的应用程序。这个 Session
必须用 Session.future
标志设置为 True
;从那时起 Session.execute()
方法将通过标准返回ORM结果 Result
调用时的对象 select()
使用ORM实体的语句:
from sqlalchemy import select from sqlalchemy.orm import Session session = Session(engine, future=True) # query from a class statement = select(User).filter_by(name="ed") # list of first element of each row (i.e. User objects) result = session.execute(statement).scalars().all() # query with multiple classes statement = select(User, Address).join('addresses').filter_by(name='ed') # list of tuples result = session.execute(statement).all() # query with ORM columns statement = select(User.name, User.fullname) # list of tuples result = session.execute(statement).all()
需要注意的是 Query
如 Query.all()
和 Query.one()
如果只请求了一个完整的实体,则将直接返回ORM映射对象的实例 Result
对象返回者 Session.execute()
默认情况下,将始终传递行(命名元组);这样一来,针对单个或多个ORM对象、列、表等的结果可以完全相同地处理。
如果只查询了一个ORM实体,则返回的行将只有一列,由每行的ORM映射对象实例组成。若要将这些行转换为不带元组的对象实例,则 Result.scalars()
方法首先对结果应用“标量”过滤器;然后 Result
可以通过诸如 Result.all()
, Result.first()
等。
参见
添加新项目或现有项目
Session.add()
用于在会话中放置实例。为了 transient (即全新的)实例,这将具有在下次刷新时为这些实例进行插入的效果。例如 persistent (即由本次会话加载),它们已经存在,不需要添加。实例是 detached (即已从会话中删除)可以使用以下方法与会话重新关联:
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语句。在刷新挂起的删除之前,标记为“delete”的对象将出现在 Session.deleted
收藏。删除后,将从 Session
,在提交事务后将成为永久性的。
有很多重要的行为 Session.delete()
操作,特别是如何处理与其他对象和集合的关系。在这个部分有更多的信息 级联 ,但一般规则是:
与映射对象相对应的行,这些对象通过
relationship()
指令是 默认不删除 . 如果要将这些列设置为NULL,则这些列将被删除。如果列不可为null,这将导致约束冲突。要将“setnull”更改为删除相关对象的行,请使用 删除 级联
relationship()
.“通过”链接到表中的许多行
relationship.secondary
参数, are 当它们所引用的对象被删除时,在所有情况下都会被删除。当相关对象包含一个外键约束返回到要删除的对象,并且它们所属的相关集合当前未加载到内存中时,工作单元将发出一个SELECT以获取所有相关行,以便它们的主键值可用于在这些相关行上发出UPDATE或DELETE语句。这样,没有进一步指令的ORM将执行ON DELETE CASCADE功能,即使这是在内核上配置的
ForeignKeyConstraint
物体。这个
relationship.passive_deletes
参数可用于调整此行为并更自然地依赖于“ON DELETE CASCADE”;当设置为True时,将不再执行此SELECT操作,但是本地存在的行仍将受到显式set NULL或DELETE的约束。设置relationship.passive_deletes
弦"all"
将禁用 all 相关对象更新/删除。当删除标记为删除的对象时,不会自动从引用该对象的集合或对象引用中删除该对象。当
Session
已过期,则可以再次加载这些集合,以便对象不再存在。但是,最好不要使用Session.delete()
对于这些对象,应该从其集合中删除该对象,然后 删除孤儿 应将其作为该集合移除的次要效果删除。参见章节 删除引用的对象和标量关系的注释 举个例子。
参见
删除 -描述“删除级联”,它在删除引导对象时标记要删除的相关对象。
删除孤儿 -描述“删除孤立级联”,当相关对象与其主对象取消关联时,将其标记为删除。
删除引用的对象和标量关系的注释 -重要背景 Session.delete()
as涉及到在内存中刷新关系。
冲洗
当 Session
与默认配置一起使用时,刷新步骤几乎总是透明地完成的。具体地说,刷新发生在发出任何单个SQL语句之前,因为 Query
或A 2.0-style Session.execute()
呼叫,以及 Session.commit()
在提交事务之前调用。当 Session.begin_nested()
使用。
无论自动冲洗设置如何,都可以通过发出 Session.flush()
::
session.flush()
可以通过构造 sessionmaker
带旗 autoflush=False
::
Session = sessionmaker(autoflush=False)
此外,通过设置 autoflush
随时标记:
mysession = Session() mysession.autoflush = False
更方便的是,可以使用 Session.no_autoflush
::
with mysession.no_autoflush: mysession.add(some_object) mysession.flush()
冲洗过程 总是 在事务中发生,即使 Session
已配置为 autocommit=True
,禁用会话的持久事务状态的设置。如果没有交易, Session.flush()
创建自己的事务并提交它。刷新期间的任何失败都将始终导致回滚任何存在的事务。如果会话不在 autocommit=True
模式,对 Session.rollback()
在刷新失败后是必需的,即使基础事务已经回滚了——这是为了使所谓的“子事务”的整体嵌套模式始终保持不变。
过期/刷新
在使用 Session
是指处理从数据库加载的对象上的状态,使它们与事务的当前状态保持同步。SQLAlchemy ORM基于一个 identity map 这样,当从SQL查询“加载”一个对象时,将有一个惟一的Python对象实例,该实例与特定的数据库标识相对应。这意味着,如果我们发出两个单独的查询,每个查询针对同一行,并返回一个映射对象,那么这两个查询将返回相同的Python对象:
>>> u1 = session.query(User).filter(id=5).first() >>> u2 = session.query(User).filter(id=5).first() >>> u1 is u2 True
此后,当ORM从查询中获取行时,它将 跳过属性填充 对于已加载的对象。这里的设计假设是假设事务是完全隔离的,然后在事务没有被隔离的情况下,应用程序可以根据需要采取步骤从数据库事务中刷新对象。FAQ条目 我正在用会话加载数据,但没有看到我在其他地方所做的更改。 更详细地讨论了这个概念。
将当前事务从ORM加载到内存中时,通常有三种方式将其从当前的ORM加载到内存中:
expire()方法 -
Session.expire()
方法将删除对象的选定属性或所有属性的内容,以便下次访问时从数据库加载这些属性,例如使用 lazy loading 模式: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
the populate_existing() method -这个方法实际上是在
Query
对象ASQuery.populate_existing()
并指示它应返回从数据库中的内容无条件重新填充的对象:u2 = session.query(User).populate_existing().filter(id=5).first()
有关刷新/过期概念的进一步讨论,请访问 刷新/过期 .
参见
使用任意WHERE子句更新和删除
上面的部分位于 Session.flush()
和 Session.delete()
详细说明如何根据应用程序中映射的Python对象所指向的主键标识在数据库中插入、更新和删除行。这个 Session
还可以发出带有任意WHERE子句的UPDATE和DELETE语句,同时本地刷新与这些行匹配的对象。
在中发出启用了ORM的更新 1.x style , the Query.update()
可使用的方法:
session.query(User).filter(User.name == "squidward").\ update({"name": "spongebob"}, synchronize_session="fetch")
在上面,将针对所有匹配名称“squidward”的行发出更新,并将更新为名称“spongebo”。这个 Query.update.synchronize_session
引用“fetch”的参数表示应该通过单独的SELECT语句或通过return(如果后端数据库支持)来获取受影响的主键列表;内存中本地存在的对象将根据这些主键标识在内存中更新。
中启用ORM的更新 2.0 style , Session.execute()
与核心一起使用 Update
结构:
from sqlalchemy import update stmt = update(User).where(User.name == "squidward").values(name="spongebob").\ execution_options(synchronize_session="fetch") result = session.execute(stmt)
上面, Update.execution_options()
方法可用于建立执行时间选项,如“同步会话”。
返回的结果对象是 CursorResult
;若要检索与任何UPDATE或DELETE语句匹配的行数,请使用 CursorResult.rowcount
::
num_rows_matched = result.rowcount
除了没有建立“values/set”子句外,删除操作与UPDATE相同。使用synchronizeu session时,在 Session
将标记为已删除和已删除。
启用ORM删除, 1.x style ::
session.query(User).filter(User.name == "squidward").\ delete(synchronize_session="fetch")
启用ORM删除, 2.0 style ::
from sqlalchemy import delete stmt = delete(User).where(User.name == "squidward").execution_options(synchronize_session="fetch") session.execute(stmt)
选择同步策略
由于启用了ORM的1.x和2.0形式的更新和删除,下面的值 synchronize_session
支持:
False
-不要同步会话。此选项是最有效的,并且在会话过期后非常可靠,通常在commit()或显式使用expireu all()之后发生。在过期之前,数据库中已更新或删除的对象可能仍保留在会话中,但值已过时,这可能会导致混淆的结果。'fetch'
-通过在UPDATE或DELETE之前执行SELECT或使用RETURNURN(如果数据库支持)检索受影响行的主键标识,以便可以使用新值(更新)刷新受操作影响的内存中对象或从Session
(删除)。请注意,在给定的情况下,此同步策略不可用update()
或delete()
构造为以下项指定列UpdateBase.returning()
明确地说。'evaluate'
-评估Python中UPDATE或DELETE语句中给定的WHERE条件,以在Session
。此方法不会增加任何往返行程,并且在没有返回支持的情况下效率更高。对于具有复杂条件的UPDATE或DELETE语句,'evaluate'
策略可能无法计算Python中的表达式,并将引发错误。如果发生这种情况,请使用'fetch'
取而代之的是行动的战略。警告
这个
"evaluate"
如果更新操作要在Session
它有许多对象已经过期,因为它必须在这些对象所在的位置刷新这些对象,这些对象将为每个对象发出一个SELECT。这个Session
如果在多个Session.commit()
电话和Session.expire_on_commit
标志的默认值为True
.
警告
Additional Caveats for ORM-enabled updates and deletes
支持ORM的UPDATE和DELETE特性绕过了ORM的工作单元自动化,而能够发出一个同时匹配多行的UPDATE或DELETE语句,而不会带来复杂性。
这些操作在Python中不提供关系的级联-假定为任何需要的外键引用配置了ON UPDATE CASCADE和/或ON DELETE CASCADE,否则,如果实施外键引用,数据库可能会发出完整性冲突。
更新或删除后,中的依赖对象
Session
受相关表上的ON UPDATE CASCADE或ON DELETE CASCADE影响的可能不包含当前状态;一旦Session
过期,通常发生在Session.commit()
或者可以通过使用Session.expire_all()
.这个
'fetch'
该策略在不支持返回的数据库(如MySQL或SQLite)上运行时,会导致发出额外的SELECT语句,这可能会降低性能。在开发时使用SQL回显来评估发出的SQL的影响。启用ORM的更新和删除不会自动处理联接表继承。如果操作针对多个表,通常应针对单个表使用单独的UPDATE/DELETE语句。有些数据库支持多个表更新。类似指南,详见 多个表更新 可以应用。
为了将多态标识限制为单表继承映射的特定子类,需要使用WHERE条件 自动包含 . 这只适用于没有自己的表的子类mapper。
在 1.4 版更改: ORM更新/删除现在自动适应为单继承映射添加的WHERE条件。
这个
with_loader_criteria()
选项 支持 通过ormupdate和delete操作;这里的条件将添加到所发出的update或delete语句中,并在“synchronize”过程中被考虑在内。为了使用事件处理程序拦截启用了ORM的更新和删除操作,请使用
SessionEvents.do_orm_execute()
事件。
选择与UPDATE内联的ORM对象..正在返回或插入..正在返回
此部分已移动。看见 使用INSERT、UPDATE和ON CONFLICATION(即upsert)返回ORM对象 。
自动开始
1.4 新版功能: 本节介绍SQLAlChemy 1.4中的新行为,该行为不适用于以前的版本。有关“自动开始”更改的更多详细信息,请访问 会话具有新的“自动注册”行为 。
这个 Session
对象具有一种称为 自动开始 。这表明 Session
执行任何工作后,将在内部认为自己处于“事务性”状态。 Session
的内部状态的修改,或者涉及对 Session
关于对象状态更改或需要数据库连接的操作。
当 Session
是第一次构造的,则不存在事务状态。事务状态是自动开始的,当方法(如 Session.add()
或 Session.execute()
将被调用,或者类似地,如果 Query
被执行以返回结果(最终使用 Session.execute()
),或者如果在 persistent 对象。
事务状态可以通过访问 Session.in_transaction()
方法,该方法返回 True
或 False
指示“自动开始”步骤是否已经进行。虽然通常不需要,但 Session.get_transaction()
方法将返回实际的 SessionTransaction
对象,该对象表示此事务状态。
对象的事务状态。 Session
也可以显式启动,方法是调用 Session.begin()
方法。调用此方法时, Session
无条件地置于“事务性”状态。 Session.begin()
可以用作上下文管理器,如 构建begin/commit/rollback块 。
在 1.4.12 版更改: -如果修改了对象属性,则立即正确发生自动开始;以前不会发生这种情况。
提交
Session.commit()
用于提交当前事务(如果有)。当没有适当的事务时,该方法以静默方式传递。
什么时候 Session.commit()
操作当前打开的事务,则它总是首先发出 Session.flush()
预先将任何剩余状态刷新到数据库;这与“自动刷新”设置无关。
在那之后, Session.commit()
然后提交实际的一个或多个数据库事务(如果有的话)。
最后,对象中的所有对象 Session
是 expired 随着交易的结束。这是因为,当下次访问实例时,无论是通过属性访问还是通过它们出现在SELECT结果中,它们都会收到最新的状态。此行为可能由 Session.expire_on_commit
标志,该标志可以设置为 False
当这种行为不受欢迎时。
在 1.4 版更改: 这个 Session
对象现在具有延迟的“开始”行为,如 autobegin 。如果没有开始事务,像这样的方法 Session.commit()
和 Session.rollback()
没有任何效果。在1.4之前不会观察到此行为,因为在非自动提交模式下,事务将始终隐式存在。
参见
回滚
Session.rollback()
回滚当前事务(如果有)。当没有适当的事务时,该方法以静默方式传递。
对于默认配置的会话,在事务通过以下任一方式开始后,会话的回滚后状态 autobegin 或通过调用 Session.begin()
方法显式,如下所示:
所有事务都会回滚,所有连接都会返回到连接池,除非会话直接绑定到某个连接,在这种情况下,连接仍然保持不变(但仍会回滚)。
最初在 pending 说明何时将它们添加到
Session
在事务的生命周期内将被删除,对应于它们的insert语句将被回滚。其属性的状态保持不变。标记为的对象 deleted 在事务的生命周期内,将提升回 persistent 状态,与要回滚的DELETE语句相对应。请注意,如果这些对象是第一个 pending 在事务中,该操作优先。
所有未删除的对象都完全过期-这与
Session.expire_on_commit
设置。
了解了这种状态后, Session
可以在回滚发生后安全地继续使用。
在 1.4 版更改: 这个 Session
对象现在具有延迟的“开始”行为,如 autobegin 。如果没有开始事务,像这样的方法 Session.commit()
和 Session.rollback()
没有任何效果。在1.4之前不会观察到此行为,因为在非自动提交模式下,事务将始终隐式存在。
当一个 Session.flush()
如果失败,通常是由于主键、外键或“不可为空的”约束冲突等原因,会自动发出回滚(目前不可能在部分失败后继续刷新)。然而, Session
此时进入称为“非活动”的状态,调用应用程序必须始终调用 Session.rollback()
方法,以便 Session
可以返回到可用状态(也可以简单地关闭并丢弃)。请参阅以下位置的常见问题解答条目 “由于在刷新过程中出现以前的异常,此会话的事务已回滚。”(或类似) 以供进一步讨论。
参见
关闭
这个 Session.close()
方法问题A Session.expunge_all()
从会话中移除所有ORM映射对象,以及 releases 来自的任何事务/连接资源 Engine
绑定到的对象。当连接返回到连接池时,事务状态也将回滚。
当 Session
是封闭的,它基本上处于最初建造时的原始状态,并且 可以再次使用 . 从这个意义上说 Session.close()
方法更像是“重置”回干净状态,而不像“数据库关闭”方法。
建议 Session
受限于 Session.close()
尤其是在最后 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()
方法被调用。
会话常见问题
到目前为止,许多用户已经对会话产生了疑问。本节介绍了一个小型常见问题解答(请注意,我们还有一个 real FAQ )在使用 Session
.
我什么时候做 sessionmaker
是吗?
只是一次,在应用程序的全局范围内的某个地方。它应该被视为应用程序配置的一部分。如果应用程序在一个包中有三个.py文件,则可以将 sessionmaker
线在你 __init__.py
文件;从这一点开始,您的其他模块会说“从mypackage导入会话”。那样的话,其他人都会用 Session()
,该会话的配置由该中心点控制。
如果应用程序启动、导入但不知道它将连接到哪个数据库,则可以绑定 Session
在“Class”(等级)级别,稍后使用 sessionmaker.configure()
.
在本节的示例中,我们将经常显示 sessionmaker
在我们实际调用的行的正上方创建 Session
. 但这只是举个例子!事实上, sessionmaker
在模块级的某个地方。要实例化的调用 Session
然后将放置在应用程序中数据库对话开始的位置。
我什么时候做一个 Session
,什么时候提交,什么时候关闭?
tl;dr;
作为一般规则,保持会话的生命周期 独立和外部 访问和/或操作数据库数据的函数和对象。这将大大有助于实现可预测和一致的事务范围。
确保您清楚地了解事务的开始和结束位置,并保留事务 短的 也就是说,它们以一系列操作结束,而不是无限期地保持打开状态。
A Session
通常在逻辑操作开始时构造,在逻辑操作开始时可能需要数据库访问。
这个 Session
,每当它用于与数据库通信时,只要它开始通信就开始数据库事务。此事务处理将继续进行,直到 Session
回滚、提交或关闭。这个 Session
如果再次使用新事务,将在上一个事务结束后开始新事务;在此之后,将执行 Session
能够跨多个事务拥有寿命,但一次只能有一个。我们将这两个概念称为 交易范围 和 会话范围 .
通常很难确定开始和结束 Session
尽管各种各样的应用程序架构可能会带来挑战性的情况。
一些示例场景包括:
网络应用程序。在这种情况下,最好使用正在使用的web框架提供的SQLAlchemy集成。否则,基本模式是创建
Session
在web请求开始时,调用Session.commit()
方法,这些请求执行POST、PUT或DELETE操作,然后在web请求结束时关闭会话。这通常也是一个好主意Session.expire_on_commit
设置为False,以便后续访问来自Session
如果事务已经提交,则视图层中不需要发出新的SQL查询来刷新对象。从子分叉派生的后台守护程序将要创建一个
Session
在每个子进程的本地,使用它Session
在叉处理的“作业”的整个生命周期中,当作业完成时将其拆下。对于命令行脚本,应用程序将创建单个全局
Session
当程序开始执行其工作时建立的,并在程序完成其任务时将其正确提交。对于GUI界面驱动的应用程序,
Session
最好是在用户生成的事件范围内,如按钮按下。或者,作用域可能对应于显式的用户交互,例如用户“打开”一系列记录,然后“保存”它们。
作为一般规则,应用程序应该管理会话的生命周期 外部的 处理特定数据的函数。这是关注点的基本分离,使特定于数据的操作与它们访问和操作该数据的上下文无关。
例如。 不要这样做 ::
### this is the **wrong way to do it** ### class ThingOne(object): def go(self): session = Session() try: session.query(FooBar).update({"x": 5}) session.commit() except: session.rollback() raise class ThingTwo(object): def go(self): session = Session() try: session.query(Widget).update({"q": 18}) session.commit() except: session.rollback() raise def run_my_program(): ThingOne().go() ThingTwo().go()
保留会话的生命周期(通常是事务) 独立和外部 . 下面的示例演示了它的外观,另外还使用了Python上下文管理器(即 with:
关键字)以管理 Session
以及它的自动交易:
### this is a **better** (but not the only) way to do it ### class ThingOne(object): def go(self, session): session.query(FooBar).update({"x": 5}) class ThingTwo(object): def go(self, session): session.query(Widget).update({"q": 18}) def run_my_program(): with Session() as session: with session.begin(): ThingOne().go(session) ThingTwo().go(session)
在 1.4 版更改: 这个 Session
可以用作上下文管理器,而不使用外部助手函数。
会话是缓存吗?
是的…不。它有点用作缓存,因为它实现了 identity map 模式,并存储键控其主键的对象。但是,它不执行任何类型的查询缓存。这意味着,如果你说 session.query(Foo).filter_by(name='bar')
,即使 Foo(name='bar')
就在那里,在身份图中,会话不知道这一点。它必须向数据库发出SQL,返回行,然后当它看到行中的主键时, then 它可以在本地标识映射中查看对象已经存在。只有当你说 query.get({{some primary key}})
那 Session
不必发出查询。
此外,默认情况下,会话使用弱引用存储对象实例。这也破坏了将会话用作缓存的目的。
这个 Session
不是设计为全局对象,每个人都从中作为对象的“注册表”进行查询。这更像是 二级缓存 . SQLAlchemy提供了一种模式,用于实现二级缓存,使用 dogpile.cache ,通过 狗堆缓存 例子。
我怎么才能拿到 Session
为了某个目标?
使用 Session.object_session()
ClassMethod可用于 Session
::
session = Session.object_session(someobject)
更新者 运行时检查API 系统也可以使用:
from sqlalchemy import inspect session = inspect(someobject).session
会话线程安全吗?
这个 Session
非常适合用于 non-concurrent 时尚,通常一次只穿一条线。
这个 Session
应该以这样的方式使用:在单个事务中,对于单个操作系列,存在一个实例。获得这种效果的一种权宜之计是将 Session
使用当前线程(请参见 上下文/线程本地会话 作为背景)。另一种方法是使用 Session
在函数之间传递,否则不与其他线程共享。
更重要的是你不应该 want 使用多个并发线程的会话。这就像让每个人在一家餐厅都吃同一个盘子。会话是用于特定任务集的本地“工作区”;您不希望或不需要与正在执行其他任务的其他线程共享该会话。
确保 Session
一次只在一个并发线程中使用被称为“无共享”并发方法。但实际上,不是分享 Session
意味着一个更重要的模式;它不仅仅意味着 Session
对象本身,但也 与该会话关联的所有对象 必须保持在单个并发线程的范围内。与 Session
基本上是通过数据库连接访问的数据库行中的数据代理,因此就像 Session
整个对象集本身实际上只是数据库连接(或连接)的大型代理。最终,我们远离并发访问的主要是DBAPI连接本身;但是由于 Session
与之相关联的所有对象都是DBAPI连接的代理,整个图对于并发访问基本上是不安全的。
如果事实上有多个线程参与同一个任务,那么您可以考虑在这些线程之间共享会话及其对象;但是,在这种极不寻常的情况下,应用程序需要确保实现适当的锁定方案,以便 同时发生的 访问 Session
或者它的状态。在这种情况下,更常见的方法是保持 Session
每个并发线程,但改为 copy 对象来自一个 Session
对另一个,通常使用 Session.merge()
方法将一个对象的状态复制到另一个对象的本地新对象中 Session
.