SQLAlchemy 不仅通过主键从身份映射中获取项目

Posted

技术标签:

【中文标题】SQLAlchemy 不仅通过主键从身份映射中获取项目【英文标题】:SQLAlchemy get items from the identity map not only by primary key 【发布时间】:2015-11-27 13:46:10 【问题描述】:

是否可以使用不是来自主键的几个字段从身份映射中检索项目(之前已经获取)?比如我经常通过(external_id, platform_id)pair查询一张表,它是唯一键,但不是主键。在这种情况下,我想省略不必要的 SQL 查询。

【问题讨论】:

【参考方案1】:

identity_map 和 get() 的简要概述:

为 SQLAlchemy 的 session 对象的生命周期保留身份映射,即在 Web 服务或 RESTful api 的情况下,session 对象的生命周期不超过单个 request(推荐)。

发件人:http://martinfowler.com/eaaCatalog/identityMap.html

Identity Map 记录已读取的所有对象 单个业务事务中的数据库。每当您想要一个对象时,您首先检查身份映射以查看您是否已经拥有它。

在 SQLAlchemy 的 ORM 中,有一个特殊的查询方法 get(),它首先使用 pk(仅允许的参数)查看 identity_map 并从身份映射中返回对象,实际执行 SQL 查询并访问数据库。

来自docs:

get(ident)

根据给定的主键标识符返回一个实例,或None 如果没有找到。

get() 的特殊之处在于它提供了对身份的直接访问 拥有Session 的地图。如果给定的主键标识符是 存在于本地标识映射中,直接返回对象 从此集合中,并且不发出任何 SQL,除非该对象已被 标记为完全过期。如果不存在,则按顺序执行 SELECT 定位对象。


只有get() 使用identity_map - official docs:

它有点用作缓存,因为它实现了identity map 模式,并存储以primary key 为键的对象。然而,它 不做任何类型的查询缓存。这意味着,如果你说 session.query(Foo).filter_by(name='bar'),即使Foo(name='bar') 就在那里,在身份映射中,会话不知道 那。 它必须向数据库发出 SQL,取回行,然后 然后当它在行中看到主键时,它可以查看 本地身份映射并查看对象已经存在。 这是 只有当你说query.get(some primary key) Session 不必发出查询


P.S.如果您不使用pk 进行查询,那么您一开始就没有点击identity_map


一些相关的 SO 问题,有助于理清概念:

Forcing a sqlalchemy ORM get() outside identity map

【讨论】:

是的,我也几乎阅读了所有这些文档。但是,我仍然不明白为什么在通过非 PK 唯一参数查询的情况下不使用身份映射。但是,现在我认为我的问题应该直接向 SQLAlchemy 开发人员提出。 这与目的背道而驰,因为identity_map必须映射很多东西,尤其是多次映射单个对象,并将各种字段作为映射键。 是的,您可以要求 SQLAlchemy 开发人员允许某些参数设置 get() 方法的默认字段(而不仅仅是 pk),因此 ' 将用作 indentity_map 中的键 - 但它有在应用范围内保持一致。 为什么我必须多次映射单个对象,以防在访问身份映射期间使用唯一键?正如您从文档中引用的那样,SQLAlchemy 在执行任意 SQL 查询以检测之前已获取的对象后检查身份映射。可以使用相同的方法来减少重复(因为PK <-> Unique Key 始终是一对一的关系)。 我是在“如果使用 identity_map 的 get() 方法被设置/允许获取除 PK 之外的字段”的上下文中说的 - 在这种情况下会有多个单个对象的副本,即以各种字段作为身份映射中的键。【参考方案2】:

可以按顺序访问整个身份映射:

for obj in session.identity_map.values():
    print(obj)

要通过任意属性获取对象,您必须先过滤对象类型,然后检查您的属性。

不是固定时间的查找,但可以防止不必要的查询。

有一个论点,即对象可能已被另一个进程修改并且身份映射不保持当前状态,但此论点无效:如果您的事务隔离级别为read committed(或更低) - 并且通常情况下,数据总是可能在查询完成后立即更改。

【讨论】:

很久以前了,但问题是当你在一个应用进程中访问同一个(在db级别)对象两次(第一次通过PK,第二次通过Unique Key)时,你将有两个完整的对象副本而不是两个引用。并且在您的进程中在这两个地方修改此类对象不会更改同一对象的另一个副本而不刷新它。所以即使你没有任何并发​​访问,问题也存在。

以上是关于SQLAlchemy 不仅通过主键从身份映射中获取项目的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy 一个映射类中的多个外键到同一个主键

在身份映射之外强制使用 sqlalchemy ORM get()

oracle自增主键从多少开始

Django ORM和SQLAlchemy类比

mysql表主键从给定值开始自动增长是怎么回事?

mysql表主键从给定值开始自动增长是怎么回事?