第20章 SQLAlchemy
作者:Michael Bayer
译者:谢路云
状态:翻译中
原文地址:http://www.aosabook.org/en/sqlalchemy.html
SQLAlchemy是一个Python语言的数据库工具包和关系对象映射(ORM)系统,始于2005年。从一开始,它就力求通过Python的数据库API(即DBAPI)提供一种和关系数据库交互的端对端系统。从早期的版本开始,SQLAlchemy的功能就吸引了很多人的注意力。它的主要特性包括能够流畅的表达复杂的SQL查询和对象映射,以及实现了"Unit of Work"模式来高度自动化的完成数据在数据库中的持久化。
从一个渺小而粗糙的概念性实现开始,SQLAlchemy迅速经历了一系列蜕变和打磨。随着用户群的增长,内部架构和公共API也在不断的迭代。到了2009年一月发布0.5版本之时,SQLAlchemy已经在大量的生产环境中部署并证明了自己,并开始稳定下来。0.6(2010年四月)和0.7(2011年五月)两个版本对架构和API的改进使我们成为了最高效和最稳定的第三方库。截至撰写本文之时,大量不同领域的组织都在使用SQLAlchemy。它已经被大家认可并事实上成为使用Python操作关系型数据库的标准库。
20.1.数据库抽象层的挑战
“数据库抽象层”通常指的是一种用来和数据库通讯并隐藏数据的存储和查询的大部分细节的系统。对这个词的理解有时会走向一个极端,认为这样的系统应该隐藏的不仅是所使用的关系数据库的细节,还应该包括数据的关系结构,甚至不再关心底层的存储机制是否是基于关系的。
对于ORM最常见的批评认为以上就是这种工具的最主要目的——把关系数据库“藏起来”,接管和数据库的交互并将它们转化为实现的细节。这种方式的核心意义在于将设计和查询关系数据结构的工作从开发者转移到一个不透明的第三方库中。
经常和关系数据库打交道的人都知道,这种认识完全是不切实际的。关系结构和SQL查询都是功能性的,它们组成了一个应用程序设计的核心。应该如何在查询中设计、组织和操作这些结构不仅取决于要查询哪些数据,也取决于数据的结构。如果连这些信息都要隐藏起来,那么也就根本没有使关系数据库的必要了。
既要满足应用程序屏蔽底层关系数据库复杂性的期望,又要满足使用关系数据库所必要的繁复,这种矛盾被称为“对象-关系的阻抗不匹配”问题。SQLAlchemy采用了一些新颖的方法来解决这个问题。
SQLAlchemy的数据库抽象层
SQLAlchemy认为开发人员必须要考虑他或者她的数据的关系形式。预判并隐藏数据库schema和查询的系统所设计的行为只会降低使用关系数据库的必要性,进而导致阻抗不匹配所带来的一系列经典问题。(*)
但同时,这些行为的实现应该也必须执行在一个尽可能高的层次上。将一个对象模型和一个schema关联起来并通过SQL语句将对象持久化是一项重复性极高的任务。使用工具来将这些任务自动化才能开发出更加简洁、高效和强大的应用程序。而手动实现这些操作的应用程序所需的开发时间将会是它的数倍。(*)
因此,SQLAlchemy对自己的定位是一个工具包 ,这是为了强调开发者才是所有关系结构以及这些结构和应用程序之间的联系的设计者和构造者,而不是第三方库的盲目使用者。通过开放“关系”,SQLAlchemy实现的理念是“不完全抽象”,(*)它不利开发者在应用程序和关系数据库之间自定义一个自动化的交互层。SQLAlchemy的创新在于它能够在不牺牲对关系数据库的控制能力的前提下做到高度的自动化。
20.2.两个世界:核心层(Core)与对象关系映射层(ORM)
为了达到工具包这个目标,SQLAlchemy将与数据库交互的每一层都开放成为了一组成熟的API,这产生了两种主要的交互方式,分别是核心层(Core)和对象关系映射层(ORM)。核心层负责和Python的数据库API(DBAPI)的交互、拼接数据库能够理解的SQL语句并管理schema。这些功能都对应着公开的API。
图20.1的SQLAlchemy的层图
核心/ ORM SQLAlchemy的分离一直是最本质的特征,它既有优点和缺点。目前在SQLAlchemy的明确的核心导致ORM到关系数据库映射类的属性结构被称为一个Table ,而不是直接将其字符串表示在数据库中的列名,产生一个SELECT查询使用结构称为select ,而不是拼凑对象的属性直接转换成字符串声明和接收结果行通过的的门面被称为ResultProxy ,透明映射的select 每个结果行,而不是将数据直接从数据库游标一个用户定义的对象。
核心元素的一个非常简单的ORM为中心的应用程序是不可见的。但是,为核心的精心整合的ORM,让流动的过渡在ORM和核心之间的结构,更复杂的ORM为中心的应用可以“向下移动”,以便处理与数据库中的更多的水平或两个具体和微调的方式,随着形势的需要。SQLAlchemy的有成熟,核心API已经变得不那么明确经常使用的ORM继续提供更复杂和更全面的图案。但是,核心的可用性也是一个贡献SQLAlchemy的早期成功的,因为它允许早期的用户来完成,更比可能的ORM仍在发展。
的ORM /核心方法的缺点是,指示必须穿越更多的步骤。Python的传统的C实现有一个显着的开销处罚为独立的功能调用,这是主要原因在运行时缓慢。传统方法的改善这包括缩短呼叫通过重排链和内联与C代码,并更换性能的关键领域。SQLAlchemy的已经花了多年使用这两种方法提高性能。然而,越来越多的人接受的PyPy为Python解释器可壁球余下的承诺性能问题,而不需要更换的多数SQLAlchemy的内部结构与C语言代码,PyPy大大刚刚在时间的调用链长通过减少的影响内联和编译。
20.3驯服DBAPI
在该基地的SQLAlchemy的是一个系统与数据库进行交互,通过DBAPI。DBAPI本身是不是一个实际的库,只有规范。因此,实现的DBAPI是可用于特别是目标数据库,如MySQL或PostgreSQL,或者特别是非DBAPI,如ODBC和JDBC数据库适配器。
的DBAPI提出了两方面的挑战。第一是提供一种易于使用且功能齐全的门面周围的DBAPI的最基本的使用模式。二是要处理的变量的性质具体的DBAPI的实现,以及底层的数据库引擎。 方言系统
由DBAPI接口是非常简单的。其核心部件DBAPI模块本身,连接对象,并且将光标对象的“光标”数据库中的说法表示的上下文中特别声明以及其相关的结果。一个简单的互动与这些对象连接和检索数据库中的数据如下:
连接= dbapi.connect(用户=“用户”,PW =“私服”,主机=“主机”)光标通过connection.cursor()cursor.execute(“SELECT * FROM user_table,其中name =?”(“杰克”,))打印列在“结果:”,说明[0],描述在cursor.description]为排在cursor.fetchall():打印的“行”,行cursor.close()connection=close
SQLAlchemy的创建一个门面周围的经典DBAPI谈话。“点进入这个门面是create_engine电话,组装连接和配置信息。一个实例作为结果产生的Engine 。该对象,然后网关DBAPI,这本身是永远不会直接暴露。
对于简单的语句执行, Engine提供什么被称为隐式执行接口。本工作?获取和关闭一个DBAPI连接和光标在后台处理:
引擎create_engine(“与postgresql :/ /用户名:密码:主机/数据库”)结果engine.execute(“select * from表”)或打印result.fetchall(0)
SQLAlchemy的0.2的Connection对象补充说,提供的能力显式地维护范围的DBAPI连接:
康恩= engine.connect()专题专题查询栏目位置(“select * from表”)或打印result.fetchall(0)conn.close()
返回的结果的execute方法的Engine 或Connection被称为ResultProxy ,它提供了类似的DBAPI光标,但具有更丰富的接口行Engine ,Connection ,并ResultProxy对应于DBAPI一个特定的模块,例如DBAPI连接,和一个实例的一个特定的DBAPI光标。
在幕后, Engine引用的对象所谓的Dialect 。Dialect是一个抽象的类存在许多实现的,每一个有针对性的在一个特定的的DBAPI /数据库组合。创建一个ConnectionEngine代表将参照这个Dialect 可用于所有的决定,这取决于使用的目标的DBAPI和数据库可能具有不同的行为。
该Connection创建时,从一个仓库,采购和维护的实际DBAPI连接被称为一个Pool ,还与Engine相关联的。Pool是负责创建新DBAPI连接的,通常情况下,他们保持经常重复使用的内存池。
在语句执行期间,一个附加的对象被称为ExecutionContext创建的Connection 。持续的对象点执行的ResultProxy的整个生命周期。很显然,那位同事是位多产教案,列表名单很大,所以教师选择更先进的搜索,并增加了也可以提供作为一个特定的子类的一些DBAPI /数据库的组合。
图20.2说明了所有这些对象及其关系到每个以及其他的DBAPI组件。 图20.2:发动机,连接,ResultProxy API 处理DBAPI变异
对于任务的管理DBAPI行为的变化,首先我们考虑问题的范围。在DBAPI在第二版规范,目前,写的是一个系列API定义,允许广泛的变异程度行为,并留下一个不确定的领域。其结果是,现实生活中的DBAPIs表现出很大程度的变化在若干领域,包括当Python如何unicode字符串是可以接受的,当他们都没有;“最后插入的ID” - 这是一个自动生成的主键可能是收购后的INSERT语句,以及如何绑定参数值可以指定和解释。他们也有大量面向类型的特质的行为,包括二进制处理,精度数值,日期,布尔和Unicode数据。
SQLAlchemy的接近允许在这两个Dialect变化和ExecutionContext通过多层次的子类。如图20.3所示Dialect之间的关系和ExecutionContext时用于与psycopg2的方言。PGDialect类的行为,特定的PostgreSQL数据库的使用,如ARRAY数据类型和架构目录; PGDialect_psycopg2 类,然后提供特定的psycopg2的DBAPI的行为,包括Unicode数据处理程序和服务器端游标的行为。 图20.3:简单的方言/的ExecutionContext层次
上述模式的一个变体提出了自己在处理一个DBAPI支持多个数据库。这方面的例子包括pyodbc,其中涉及任意数量的后端数据库通过ODBC,和一个Jython与JDBC驱动程序,其中涉及zxjdbc,。以上关系是增加了一个mixin类,从在使用sqlalchemy.connectors包提供DBAPI的行为,是常见的多个后端。图20.4说明了常见的功能sqlalchemy.connectors.pyodbc之间共享MySQL和Microsoft SQL Server的的pyodbc特定方言。 图20.4:普通DBAPI方言层次之间共享的行为
Dialect ExecutionContext对象提供了一种手段定义与数据库和DBAPI每一次互动,包括如何连接参数的格式和如何特殊在语句执行期间被处理的怪癖。的Dialect 也是一个SQL编译结构,使工厂SQL正确的目标数据库,和类型的对象定义Python的数据应如何封从目标DBAPI和数据库。 20.4。架构定义
建立与数据库的连接和交互性,接下来的任务提供创建和操纵的后端无关SQL语句。要做到这一点,首先,我们需要确定我们将如何参照的表和列呈现在一个数据库中的所谓的綱目表和列代表数据是有组织的,和大多数的SQL语句组成的表达式,参照这些结构的命令。
一个的ORM或数据访问层需要提供编程访问SQL的语言,在该基地是一个编程的系统描述表。这是SQLAlchemy的核心提供了第一个强大的部门ORM,提供的Table和Column的结构描述数据库的结构独立于用户的模型类确定目标背后的分工模式定义对象关系映射的关系架构可以设计明确的关系型数据库,包括特定于平台的没有被混乱的对象 - 关系的概念,这些细节,如果有必要,仍然是一个单独的关注。作为独立的的ORM成分也架构描述系统是一样有用的任何其他类型的对象 - 关系系统可建立在核心。
Table和Column模型属于的范围是什么被称为元数据 ,提供一个集合对象的MetaData来表示Table对象的集合。这种结构来源于Martin Fowler的描述大多来自“元数据映射”企业应用架构模式 。图20.5所示一些关键要素的sqlalchemy.schema包。 图20.5:基本sqlalchemy.schema的对象
Table代表一个实际的表的名称和其他属性目前在目标模式。它的Column对象的集合代表命名并键入有关单个表列的信息。一个完整的数组对象的描述约束,索引和序列设置,以填补在有更多的细节,其中一些影响的引擎和SQL施工系统行为。特别是, ForeignKeyConstraint 是中央确定两个表中应加入。
架构中的包Column Table和Column相对于其余的是唯一的包,因为它们是双继承,无论从sqlalchemy.schema包,sqlalchemy.sql.expression包,服务不只是为模式水平结构,但也可作为核心的SQL表达式语言的语法单位。图20.6中示出这种关系。 图20.6:表和列的双重生活
在图20.6中,我们可以看到, Table和Column继承的SQL世界具体形式“的东西,你可以选择”,被称为一个FromClause ,和“东西,你可以使用SQL表达式”,被称为一个ColumnElement 。 20.5SQL表达式
在SQLAlchemy的创作中,SQL生成的方法是不明确的。文本语言可能是一个可能的候选人,这是一种常见的方法是在知名的核心对象 - 关系的工具像Hibernate的HQL。然而,对于Python,更有趣的选择是:使用Python对象和表达式generatively建设的表达式目录树结构,甚至利用Python的运营商,所以运营商可以给定的SQL语句行为。
虽然它可能不会一直是这样做的第一个工具,全归功于在伊恩的SQLBuilder库Bicking SQLObject来的灵感系统的使用Python对象和运营商SQLAlchemy的表达语言。在这种方法中,Python对象代表一个SQL的词汇部分表达。在这些对象上的方法,以及重载运算符,产生新的词汇结构来源于它们。最常见的目的是“列”将代表这些对象SQLObject的一个ORM映射类.q属性可以通过使用一个命名空间;SQLAlchemy的命名属性.c 。的.c 属性今天仍然是核心的可选元素,如表和select语句。 表达式树
一个SQLAlchemy的SQL表达式结构,这种结构是非常你,如果你想创建解析SQL语句,它是一个解析树,除了开发人员创建的解析树直接,而不是它从一个字符串导出。的核心类型的节点,在该解析树被称为的ClauseElement , 图20.7示出的关系ClauseElement一些关键类。 图20.7:基本表达层次
通过使用构造函数,方法和重载的Python操作功能,这样的语句结构为:
SELECT ID FROM用户,其中name =?
可能建造在Python中,如:
从导入表中sqlalchemy.sql,列中,选择用户表('用户'('身份证'),列,列(“名称”))到stmt =选择(user.c.id])。(user.c.name =='编辑')
在select 图20.8中示出的结构的上述select构造。注意:表示包含的文本值'ed'内_BindParam构造,从而导致它被呈现为绑定参数标记的SQL字符串使用一个问号。 图20.8:例表达式树
从树形图中,人们可以看到,通过一个简单的降穿越节点可以快速创建一个呈现的SQL语句,我们会看到更细节一节中的语句编译。 Python的操作方法
在SQLAlchemy,这样的表达式:
列('A')== 2
生产既不True也不False ,而是一个SQL表达式兴建。 关键是使用Python特殊的重载操作符操作员功能:例如,方法如eq , ne ,le , lt , add , mul 。面向列表达式节点提供重载通过使用Python的操作人员的行为一个mixin ColumnOperators 。使用操作符重载,一个表达column('a') == 2等价于:
从sqlalchemy.sql.expression进口_BinaryExpression从进口柱sqlalchemy.sql,bindparam,距离sqlalchemy.operators进口EQ _BinaryExpression(左=('a')的列中,右= bindparam('A',值= 2,独特的= TRUE),=操作符式)
eq结构实际上是一个函数从Python的内置的operator 。代表运营商作为一个对象(例如,operator.eq ),而不是一个字符串(即, = )允许字符串表示定义在语句的编译时间,当数据库方言信息是已知的。 精选集
负责渲染成文本的SQL表达式树的中央级SQL是Compiled类。这个类有两个主要的子类, SQLCompilerDDLCompiler 。SQLCompiler处理SQL的渲染操作为SELECT,INSERT,UPDATE和DELETE语句,统称为数据查询语言(DQL)DML(数据操纵语言),而DDLCompiler处理各种CREATE和列为DROP语句,数据定义语言(DDL)。有一个额外的类层次结构,重点围绕字符串表示形式的类型,开始在TypeCompiler 。个人方言然后提供自己的所有三个编译器类型的子类定义特定的目标数据库的SQL语言方面。图20.9提供了一个相对于这个类层次的概述PostgreSQL的话。 图20.9:编译器的层次结构,包括PostgreSQL的具体实施
Compiled的子类定义了一系列的访问方法,每个一提到一个特定的子类的ClauseElement 。层次结构ClauseElement节点的一份声明中行走,是通过每次访问函数的递归连接的字符串输出。由于这个收益, Compiled对象维护国家有关匿名标识符名称,绑定参数名称,嵌套子查询,除其他事项外,所有的生产的SQL语句的字符串,以及作为最终目的收集绑定的参数使用默认值。图20.10说明访问方法的过程中,在文本单元。 图20.10:呼叫层次的语句编译
一个完成的Compiled结构包含完整的SQL字符串,并绑定值的集合。这些都是强制的ExecutionContext到DBAPI的execute预期的格式 方法,其中包括这样的考虑,治疗的Unicode语句对象的集合类型使用存储绑定值,以及具体如何绑定值自己应该被强迫交涉适当的DBAPI目标数据库。 20.6类映射的ORM
现在我们的注意力转移到ORM。第一个目标是使用系统表的元数据中,我们定义了允许一个用户定义的类映射到数据库表中的列的集合。第二个目标是让用户定义的类之间的关系的定义,根据数据库中的表之间的关系。
SQLAlchemy的“映射”,是指这众所周知的数据映射器模式描述在福勒的企业架构模式 。总体而言,SQLAlchemy的ORM大量借鉴由福勒的做法详细介绍。它也严重影响了著名的Java关系映射工具Hibernate和伊恩Bicking为Python的SQLObject的产品。 古典与声明
我们使用的术语古典映射到参考SQLAlchemy的系统的应用对象 - 关系数据映射到一个现有的用户类。这形式参考Table对象和用户定义的类有两个单独定义的实体连接在一起,通过一个函数调用對映表,一旦mapper已应用到一个用户定义的类,类需要新的属性对应表中的列:
类用户(对象):通过 映射(用户user_table) #现在用户有一个“id”属性用户名
mapper也可以贴上其他各种属性的类,包括属性对应于其他种对象的引用,以及为任意的SQL表达式。粘贴任意属性的过程中一类是被称为在Python世界为“的monkeypatching”的,但由于我们是在数据驱动的和非任意的方式,这样做的精神,操作的更好地表达这个术语类仪器仪表 。
现代使用的SQLAlchemy的中心,周围的声明的扩展,这是一种可配置的系统,类似于共同有效记录系统所使用的许多其他类的声明对象 - 关系的工具。在这个系统中,最终用户明确定义属性内联类的定义,每个代表一个属性类,它是要被映射的。Table对象,在大多数情况下,是不明确提到,也不是mapper功能,只有类,Column对象,与其他ORM相关的属性被命名为:
类用户(基本):tablename ='用户'ID =列(如Integer,primary_key的= TRUE)
它可能会出现,上面的类仪器直接实现由我们的放置id = Column()但这种情况并非如此。的声明扩展使用Python元类,这是一个方便的方法来执行一系列的操作,每次一个新的类首先声明,生成一个新的Table 从什么被宣布的对象,并通过它的mapper功能,随着类。mapper功能,然后在完全相同的方式,它的工作修补它自己的属性类上,在这种情况下向id属性,和更换有以前。元类初始化的时候是完整的(也就是,当执行的流程离开由User划定的块),标记的id Table User.id Column对象被移动到一个新的Table , User.id 已被替换特定映射由一个新的属性。
它总是SQLAlchemy的将有一个的简写,声明的形式配置。然而,创造的声明延迟赞成继续工作,巩固了力学的经典测绘的。中期的扩展名为ActiveMapper,这后来成为药剂项目,早在存在。它重新定义映射构造在一个更高的级别申报制度。声明的目标是扭转药剂的大量抽象的方向通过建立一个系统的方法保留SQLAlchemy的经典地图绘制概念,几乎确切地说,只有重组它们是如何使用不再繁琐,更适合类级别的扩展比经典的映射。
无论是古典或声明的映射,映射的类需要新的行为,允许它来表达其属性中的SQL结构。SQLAlchemy的最初跟着SQLObject的使用一个特殊的行为通过SQLAlchemy的属性为SQL列表达式的来源,提到.c ,在这个例子中:
结果= session.query(用户)。过滤器(User.c.username =='编辑')。所有的()
然而,在0.4版本中,SQLAlchemy的移动到映射的功能属性本身:
结果= session.query(用户)。过滤器(User.username =='编辑')。所有的()
在属性的访问证明了这种变化有很大的改进,因为它宠物的列状的物体目前的类,以获得额外的类特定的功能目前来自直接从底层Table对象。很显然,那位同事是位多产教案,列表名单很大,所以教师选择更先进的搜索,并增加了也允许使用不同的类属性之间的整合,如直接指到表列的属性,属性,来自这些列的SQL表达式和属性,请参阅相关的类。最后,它提供了一个对称之间的映射类,同样的属性,以及该映射的类的实例,可以采取不同的行为取决于类型的父。绑定类属性返回SQL表达式,同时结合实例属性返回实际的数据。 映射的剖析
一直连接到我们的id User类的id属性,是一种在Python中的对象,对象为一个描述符有get , set ,和del方法,它的Python运行时按照所有涉及该属性的类和实例操作。SQLAlchemy的实施称为一个InstrumentedAttribute ,我们将举例说明背后所呈现的世界与另一个例子。从一个Table和一个用户定义的类,我们建立了一个映射只有一个映射列relationship ,以及相关的类,它定义了一个参考:
user_table表(“用户”,元数据,列('身份证',整数,primary_key的= TRUE),) 类用户(对象):通过 映射器(用户,user_table,属性= {“相关”的关系(地址)})
当映射的结构是完整的,有关的类的对象的详细的图20.11中。 图20.11:映射的剖析
该图说明了SQLAlchemy的映射定义为两个独立的层用户定义的类和表的元数据之间的互动,它被映射。图向左类仪器仪表,而SQL和数据库功能是朝着正确的合照。一般模式玩的是对象的组合是用来隔离行为的角色和对象继承用来区分在一个特定的角色之间的行为差异。
类仪器仪表领域内, ClassManager被映射的类,而其收集的InstrumentedAttribute对象是与每个属性映射的类。InstrumentedAttribute是面向公众的Python的描述符前面提到的,产生SQL表达式时,使用基于类的表达式(例如, User.id==5 )。.在...时候, 何时处理的一个实例User , InstrumentedAttribute代表的行为归因于AttributeImpl对象,这是一个专门对几个品种所表示的数据类型。
建立的映射侧, Mapper代表一个用户定义的类和一个可选择的单元的联动,最典型的Table 。Mapper维护一组每个属性的对象,被称为为MapperProperty ,其中涉及的SQL表示的特定属性。最常见的变种MapperProperty ColumnProperty ,一个映射的字段或SQL表达式,并RelationshipProperty ,代表一个连接到另一个映射。
MapperProperty代表属性装载行为,包括属性如何呈现在一个SQL语句,以及如何从结果来填充行一个LoaderStrategy对象,其中有几个品种。不同LoaderStrategies确定的装载行为,属性被延迟 , 跃跃欲试 ,或直接 。选择默认的版本映射配置时间,在查询时可以选择使用一个备用的策略。RelationshipProperty也的引用了DependencyProcessor ,处理,如何映射器间的依赖关系和属性同步进行冲洗时间。父和目标的关系几何形状的基础上选择DependencyProcessor selectables链接关系。
的的Mapper / RelationshipProperty结构形成一个图,其中Mapper对象的节点RelationshipProperty对象的有向边。一旦全套映射器已被宣布由一个应用程序,递延“初始化”步骤被称为组态前进。它主要用于每个RelationshipProperty ,以巩固其母公司和细节之处的目标映射器,包括选择的AttributeImpl以及作为DependencyProcessor 。此图是一个关键的数据结构,用于整个操作过程中的ORM。它参与操作,如所谓的“级联”的行为,定义了如何操作应该传播沿着对象的路径,查询操作相关的对象和集合“眼巴巴”加载一次,以及对对象冲洗侧一个之前建立的所有对象引发了一系列的依赖图持久性的步骤。 20 / 7查询和装载行为
SQLAlchemy的启动通过一个对象调用Query所有对象的装载行为。基础状态Query开始,包括实体 ,这是映射类列表和/或独立的SQL表达式进行查询。它也有一个参考Session ,它表示连接到一个或多个数据库,以及一个高速缓存的数据的相对于累计这些连接上的交易。下面是一个基本的用法示例:
从sqlalchemy.orm导入Session届会议(发动机)查询session.query(用户)
我们创建了一个Query ,将产生的User情况下,相对于一个新的Session ,我们已经创建。在同一Query提供了一个生成生成器模式select结构的方式前面所讨论的,额外的标准和修饰符与在一份声明中构造一个方法调用的时间。当一个迭代运算被称为上的Query ,它构造一个SQL表达式构造代表一个SELECT,把它发射到数据库中,然后解释的结果集行面向ORM的结果对应于所请求的实体的初始集合。
Query进行硬的SQL渲染的区别 和数据加载的操作的部分。前者是指建设一个SELECT语句,后者的解释SQL结果行ORM映射的构造。数据加载,其实,继续进行没有一个SQL呈现步骤,的Query可能会被要求解释结果一个文本查询手由用户组成。
这两个SQL渲染和数据加载利用递归下降形成的曲线图由一系列铅Mapper对象,考虑每一列或SQL表达式控股ColumnProperty作为一个叶子结点,每个这是通过所谓的“急切负荷”要包含在查询中的RelationshipProperty作为一个边缘到另一个Mapper节点。遍历和要采取的行动,在每个节点最终是每个LoaderStrategy与每MapperProperty相关工作,添加列,并加入到正在兴建的SELECT语句在SQL呈现阶段,Python函数来处理结果行中的数据加载阶段。
Python函数中的数据加载阶段,每个接收数据库中的一行,因为它们是牵强,产生的状态可能有变在存储器中的映射的属性作为一个结果。它们产生的一个特定的属性有条件的,根据检查结果集的第一个入行,以及加载选项。如果负载的属性是不继续进行,没有可调用的函数。
图20.12说明了遍历几个LoaderStrategy对象中加入的渴望加载 情况下,说明其提供的SQL语句的连接过程中发生的_compile_context 方法Query 。这也表明新一代的的行人口的功能,收到的结果行和填充单个对象的属性,这个过程发生在instances的Query方法。 图20.12:穿越的装载机的战略,包括一个加入预先加载
SQLAlchemy的早期结果来填充方法使用传统的穿越固定对象的方法与每一个收到的每一行的战略和采取相应的行动。加载程序可调用的系统,首先在0.5版本中引入的,代表一个巨大的飞跃,性能,因为很多决策就行可以了只是一次处理了前面,而不是为每一行,和一个显着数量的函数调用没有净影响可能会被淘汰。 20.8会议/标识映射
在SQLAlchemy,Session对象的实际使用情况,提出了公共接口ORM的,也就是说,加载和持久化数据。它提供的起始角度为给定的数据库连接查询和坚持行动。
Session ,除了作为数据库连接的网关,这是目前所有映射实体的集合保持一个积极的参考在内存中相对于该Session 。在这样的Session 的身份地图和单位的工作模式,既实现了一个门面确定由福勒。一个数据库唯一的身份标识映射保持映射为一个特定的Session中的所有对象,消除了存在的问题重复的身份介绍。工作单位的基础上的身份地图提供的持久化状态的变化过程自动化系统数据库中的尽可能最有效的方式。实际的持久性步骤是被称为“冲洗”,和在现代的SQLAlchemy此步骤通常是自动的。 发展历史
Session开始大多隐蔽系统负责单散发出齐平的任务。冲洗过程中涉及发光SQL报表到数据库中,对应的对象的状态中的变化跟踪工作制的单位,从而同步的当前状态在内存中的数据库。的冲洗一直是一个最SQLAlchemy的复杂的操作。
方法的调用非常早期版本的同花顺开始在背后被称为commit ,这是方法上存在一个隐式的,线程局部对象objectstore 。当一个人使用SQLAlchemy的0.1,因此没有必要打电话Session.add ,也没有任何一个明确的概念Session的。唯一的面向用户的步骤是创建映射器,创建新的对象,修改现有对象加载查询(查询每个Mapper对象直接从自己被调用),然后坚持所有通过objectstore.commit命令。池中的对象的一组操作无条件模块全局的和无条件的线程局部。
与第一组objectstore.commit模型是直接命中的用户,但此模型的刚性很快撞上了墙。现代SQLAlchemy的新用户有时感叹,需要定义一个工厂,可能是注册表,Session对象,以及需要保持自己的对象组织成只是一个Session的时间,但是这是远远最好的初期,当整个系统完全是隐含的。“0.1使用模式的方便,在很大程度上仍是在现代社会本SQLAlchemy的,其特点通常配置为一个会话注册表使用线程局部范围。
Session本身只介绍SQLAlchemy的0.2版,建模后在Hibernate Session对象存在松散。这个版本的特色综合事务控制,其中的Session可以被放置到一个事务中通过begin方法,并完成通过commit方法。objectstore.commit方法更名为objectstore.flush ,新的Session对象可以在任何时间创建。Session本身被打破了从另一个对象UnitOfWork ,这仍然是一个私人反对负责执行实际的刷新操作。
,虽然冲洗过程中显式调用方法用户,0.4系列的SQLAlchemy的概念引入的自动刷新 ,这意味着,刷新每次查询前立即发出。它的优点自动刷新的是,所发出的SQL语句的查询总是有关系侧访问的确切状态,存在于存储器中,所有的改变都送了过来。早期版本的SQLAlchemy的不包括此功能,因为最常见的使用模式FLUSH语句也承诺永久性的变化。但是,当自动刷新据介绍,它是伴随着另一个特点所谓的事务性 Session ,它提供了一个Session会自动启动的交易中,一直持续到用户名为commit明确。此功能的推出, flush方法不再犯,它刷新的数据,并能安全被称为一个自动化的基础上。Session ,而现在,提供了一步一步的同步内存中的状态和SQL查询状态之间进行冲洗根据需要,什么也没有永久持续,直到明确commit第一步。这种行为是,事实上,在Hibernate中完全相同的Java。然而,SQLAlchemy的拥抱为Python相同的行为在风暴ORM的基础上,使用这种风格的介绍SQLAlchemy的是在0.3版本的时候。
0.5版带来了更多的交易整合后交易到期推出后,每一个commit或rollback ,默认所有国家内的Session已过期(清除),来填充后续SQL语句时再重新选择数据时,或当在剩余的过期的对象的属性中访问新事务的上下文中。最初,SQLAlchemy的周围建造假设SELECT语句应尽可能少排放,无条件的。过期的提交行为是缓慢的,在此原因,但是,它完全解决问题的Session载有过时的数据交易后,没有简单的方式来加载新的数据没有重建的全套已加载的对象。在早期,它似乎这个问题不能合理解决,因为它是看不出来的时Session应考虑目前的状态是过时的,从而昂贵的新集的SELECT语句在下次访问。但是,一旦Session转移到一个始终保持在一个交易模型,交易结束点变得明显,自然点的数据过期,作为一个事务的性质具有高度隔离是, 它不能看到新的数据,直到它的承诺或回滚了。不同的数据库和配置,当然有不同程度的事务隔离,包括没有在所有的交易。这些模式的使用是完全可以接受的,SQLAlchemy的到期使用较低的隔离模型,开发人员只需要知道,水平内如果有多个会话的会话可能会使未隔离的变化共享相同的行。这是不是在所有不同,什么都可以发生时直接使用两个数据库连接。 会议概述 图20.13说明了Session的主要结构处理。 图20.13:会议概述
面向公众的部分上面是Session本身和用户对象的集合,每一个映射的类的一个实例。在这里,我们看到映射的对象保持一个SQLAlchemy的建设的参考InstanceState ,跟踪ORM一个单独的实例包括待处理的属性的变化和属性的状态过期状态。InstanceState 在前面的讨论是实例级侧的属性仪表节, 映射的剖析 ,一流水平的ClassManager相对应,并保持状态的映射对象的字典(即Python的dict 代表的AttributeImpl相关联的对象的类属性) 。 状态跟踪
IdentityMap是数据库身份InstanceState对象的映射,对于那些有一个数据库的身份,这被称为持久性的对象。的默认实现IdentityMap与InstanceState自我管理其大小所有强引用删除用户映射的情况下,一旦他们已被删除的它的工作方式以同样的方式作为Python的WeakValueDictionary 。、 保护组的所有对象标记为脏或删除 ,以及有待对象的标新 ,收集垃圾,通过建立强大的对这些对象的引用挂起的更改。所有的强引用,然后被丢弃后冲水。
InstanceState也执行的关键任务,保持“有什么变化”对于一个特定的对象的属性,使用此举变化系统“先前”的一个特定的属性值存储在字典中称为committed_state前传入的值赋给对象的电流词典。冲洗时间,内容committed_state 和与该对象相关联的dict比较,以产生组净变化对每个对象。
在集合的情况下,一个单独的collections包坐标与InstrumentedAttribute / InstanceState 系统,以保持一个特定的映射集合的集合变动净额del常见的Python类,如set , list和dict的子类在使用前和增强与历史跟踪mutator方法。收集系统在0.4〜是开放式的,可用于任何集合类对象进行了修改。 事务控制
Session ,在默认状态下的使用,维护打开事务的所有操作完成时commit或rollback被称为。“SessionTransaction维护一组零个或多个Connection对象,每个对象代表一个开放的交易在一个特定的数据库。SessionTransaction是一个懒惰的初始化的对象,开始没有数据库的状态存在。作为一个特殊的后端需要参加在一份声明中执行, Connection对应于该数据库添加到SessionTransaction的名单连接安全要求.虽然在一个时间是一个单一的连接常见的,支持多种连接方案在特定的连接,用于一个特定的操作基于配置与Table , Mapper或SQL构建自己参与运作。多重连接也可以使用协调事务两相的的行为,它提供那些DBAPIs。 20.9工作单位
提供flush Session将在其工作的flush方法一个独立的模块unitofwork 。正如前面提到的,冲洗过程可能是SQLAlchemy的最复杂的功能。
工作单位的工作是将所有的挂起状态,目前在一个特定的Session到数据库中,掏空了new , dirty ,和deleted 收藏保持的Session 。完成后,内存中的状态Session的,什么是在当前事务中的比赛。面临的主要挑战是确定正确的一系列的持久性的步骤,然后按正确的顺序来执行它们。这包括确定INSERT,UPDATE和DELETE语句的列表,其中包括因从级联的相关行被删除或以其他方式移动,确保UPDATE报表只包含那些真正被修改的列,建立“同步”操作外键引用的主键列,将复制的状态列,在该点在该新生成的主密钥标识符可确保发生插入的对象添加到Session的顺序尽可能有效,并确保UPDATE和DELETE语句出现在一个确定顺序所以,以减少机会死锁。 历史
作为一个纠缠不清的系统结构,工作落实的单位开始写在一个特设的方式,它的发展可以进行比较,发现没有出路的森林地图。早期的错误和缺少行为解决与螺栓连接修复,而一些重构,改善的事项通过0.5版本,它直到0.6版本,工作单位,时间稳定,很好理解,所涵盖的上百个测试完全从头开始重写。经过几个星期的考虑一个新的的方法,将驱动由一致的数据结构,过程重写它使用这个新的模型只用了几天,当时的想法是这个时候,充分的了解。它也是的事实,极大地促进了新实施的行为可能是仔细对现有版本进行交叉检查。此过程显示如何在第一次迭代的东西,但是可怕的,仍然是有价值的,因为只要它提供了一个工作模型。这进一步显示了总的一个子系统重写往往是不仅是适当的,但一个不可分割的一部分的发展难以开发的系统。 拓扑排序
后面的工作单元的主要范式是组装的完整列表,要采取的行动到一个数据结构中,每个节点代表一个单一的步骤;在设计模式中的说法,这被称为命令模式 。的一系列在该结构中的“命令”,然后组织成一个特定的顺序使用拓扑排序 。拓扑排序是一个过程,一个偏序 ,各种项目的基础上,也就是说,只有某些元素必须先于其他人。图20.14说明了拓扑排序的行为。 图20.14:拓扑排序
工作单位构建了一个偏序,那些持久的命令,必须先别人的基础上。“命令,然后拓扑排序,以便调用。确定哪些命令之前,主要来自存在的relationship ,填补了两个Mapper对象一般, Mapper 被认为是依赖于其他,作为relationship意味着一个Mapper 有一个外键依赖于其他。类似的规则存在许多to-many关联表,但在这里,我们专注于的情况下,one-to-many/many-to-one关系。外键的依赖性问题解决为了防止发生,没有依赖于无需违反约束标记作为“递延”的约束。但同样重要的是,排序允许主键标识符,这在许多平台上时,只产生一个INSERT其实时,被公正执行INSERT语句的结果填充到参数依赖的行是关于要插入列表。删除,使用相同的顺序在反向相关的行之前被删除那些他们赖以生存,这些行所指的外键是不存在的,没有演示文稿
工作单位提供系统拓扑排序在两个不同的级别,进行依赖关系的结构的基础上演示文稿第一个层次,持久性的步骤组织成桶的基础上之间的依赖关系的映射器,即,完整的“桶”的对象对应某个特定的类。第二电平打破了零个或多个这些“桶”成较小的批次,处理的情况下,参考周期或自参照表。如图20.15所示的“桶”产生的插入的一组User的对象,那么一组Address的对象,其中一个中间步骤,复制新生成的主键的值到User每个Address对象的user_id外键列。 图20.15:组织对象的映射
在每个映射分拣情况,任何数目的User和Address对象可以被刷新没有任何影响的复杂的步骤或多少“依赖关系”必须考虑的。
排序第二个层次的组织之间的直接依赖关系的基础上的持久性步骤在一个单一的映射器的范围之内的单个对象。当发生这种情况时,最简单的例子是一个表,其中包含一个外键约束本身需要一个特定的表中的行前插入另一行同一表中的,是指它的。另一是一系列的表格时有一个参考周期 :A引用了表B表,它引用表C,然后参考表A.在别人面前,一些A的对象必须插入允许的B和C的对象,也可以插入。表,该表是指本身是一种特殊的情况下,参考周期。
要确定哪些业务可以保持在其合并的,每个Mapper桶,将被分解成一个更大的集合的每个对象的命令,一个周期检测算法被施加到映射器之间存在的依赖关系的设定,使用一个修改的版本的一个周期Guido van Rossum的博客上发现的检测算法。这些桶在周期是然后再破碎成每个对象的操作和通过混入的集合的每个映射器水桶此外,每个对象桶的新的依赖规则每映射桶。如图20.16所示桶中的User对象被分解成单个每个对象的命令,此外,从relationship User到一个新的contact relationship本身称为contact造成的。 图20.16:组织各个步骤的参考周期
铲斗结构背后的基本原理是,它允许共同的批处理报表尽可能地,既减少所需的步数Python和实现更有效的互动与DBAPI,有时可以执行一个Python的报表内方法调用。只有当一个参考周期之间存在着映射器每个对象的依赖模式更昂贵的踢,即使如此,它只发生对象图需要它的那些部分。 {0}·{/0}{1} {/1}{2}20 10{/2}总结
SQLAlchemy的目的是非常高的的目标是自成立以来,最丰富的功能和灵活的数据库产品。它已完成因此,虽然维持其专注于关系型数据库,认识到在深入和全面的方式支持关系型数据库的用处是一大创举,即使是现在,范围的承诺继续显露自己比以前认为的大。
的基于组件的方法的目的是提取每个区域的最可能值从的功能,提供了许多不同的单位,应用程序可以单独使用或组合使用。该系统已具有挑战性的创建,维护,和交付。
发展课程的目的是缓慢的基础上,理论,坚实的功能是有条不紊的,基础广泛的建设,最终更有价值比快速传递功能,而无需基础。它采取了很长一段时间的SQLAlchemy兴建。 一个一致的,记录用户的故事,但在整个过程中,底层构架始终领先一步,在某些情况下,“时间机器”的效果功能,可几乎在添加用户要求他们。
Python语言一直是一个可靠的主机(如果有点挑剔的,特别是在该地区的性能)。语言的SQLAlchemy的一致性,极大地开放运行模式允许提供一个更好的比其他语言编写的同类产品所提供的经验。
这是希望的SQLAlchemy的项目,Python的收益不断更深的接纳到尽可能广泛的各种各样的尽可能的领域和行业,使用关系型数据库仍然充满活力和进步的。这么做的目标是?SQLAlchemy是表明,关系型数据库,Python中,考虑的对象模型都是非常有价值的开发工具。