SQLAlchemy:引擎、连接和会话差异

我使用SQLAlchemy,至少有三个实体:enginesessionconnection,它们有execute方法,所以如果我想从table中选择所有记录,我可以这样做

engine.execute(select([table])).fetchall()

connection.execute(select([table])).fetchall()

甚至这个

session.execute(select([table])).fetchall()

-结果是一样的。

据我所知,如果有人使用engine.execute,它会创建connection,打开session (Alchemy会为你照顾它)并执行查询。但是这三种方式是否有整体的区别 任务?< / p >

123558 次浏览

一行概述:

execute()的行为在所有情况下都是相同的,但它们是3个不同的方法,分别位于EngineConnectionSession类中。

execute()到底是什么:

为了理解execute()的行为,我们需要研究Executable类。Executable是所有“语句”类型对象的超类,包括select()、delete()、update()、insert()、text()——用最简单的话来说,Executable是SQLAlchemy支持的SQL表达式构造。

在所有情况下,execute()方法接受SQL文本或构造的SQL表达式,即SQLAlchemy中支持的各种SQL表达式构造并返回查询结果(ResultProxy -包装DB-API游标对象,以便更容易访问行列)。


为了进一步澄清(仅用于概念上的澄清,不是推荐的方法):

除了Engine.execute()(无连接执行)、Connection.execute()Session.execute()之外,还可以在任何Executable构造上直接使用execute()Executable类有自己的execute()实现——根据官方文档,关于execute()功能的一行描述是“Connection.execute()5”。在这种情况下,我们需要显式地将Executable (SQL表达式构造)与Connection.execute()0对象或Connection.execute()1对象(它们隐式地获得Connection.execute()0对象)绑定,因此execute()将知道在哪里执行Connection.execute()4。

下面的例子很好地说明了这一点——给出一个如下表:

from sqlalchemy import MetaData, Table, Column, Integer


meta = MetaData()
users_table = Table('users', meta,
Column('id', Integer, primary_key=True),
Column('name', String(50)))

显式执行Connection.execute()——将SQL文本或构造的SQL表达式传递给Connectionexecute()方法:

engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
# ....
connection.close()

显式无连接执行,即Engine.execute()——将SQL文本或构造的SQL表达式直接传递给Engine的execute()方法:

engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
# ....
result.close()

隐式执行Executable.execute() -也是无连接的,并且调用Executableexecute()方法,也就是说,它直接对SQL表达式构造(Executable的一个实例)本身调用execute()方法。

engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
# ....
result.close()

注意:声明隐式执行示例是为了澄清-强烈不推荐这种执行方式-根据文档:

“隐式执行”是一个非常古老的使用模式,在大多数情况下都是如此 混淆多于帮助,它的使用是不鼓励的。这两个 模式似乎鼓励过度使用权宜之计的“捷径”

.应用程序设计导致以后的问题

你的问题:

据我所知,如果有人使用引擎。执行它创建连接, 打开会话(Alchemy为你关心它)并执行查询。

你说得对,“如果有人使用engine.execute,它会创建connection”,但“打开session (Alchemy会为你关心它)并执行查询”——使用Engine.execute()Connection.execute()(几乎)是同一件事,在形式上,Connection对象是隐式创建的,在后面的情况下,我们显式地实例化它。在这种情况下真正发生的是:

`Engine` object (instantiated via `create_engine()`) -> `Connection` object (instantiated via `engine_instance.connect()`) -> `connection.execute({*SQL expression*})`
但是,这三种方式之间是否存在全局差异 执行这样的任务?< / p >

在DB层是完全一样的,它们都在执行SQL(文本表达式或各种SQL表达式结构)。从应用程序的角度来看,有两个选项:

  • 直接执行-使用Engine.execute()Connection.execute()
  • Using sessions -有效地将事务处理为单个事务 工作单元,通过session.add()session.rollback()session.commit()session.close()轻松实现。它是在ORM情况下与DB交互的方式,即映射表。提供identity_map,用于在单个请求期间立即获取已经访问或新创建/添加的对象

Session.execute()最终使用Connection.execute()语句执行方法来执行SQL语句。使用Session对象是SQLAlchemy ORM推荐的应用程序与数据库交互的方式。

文档的一个节选:

重要的是要注意,当使用SQLAlchemy ORM时,这些 对象通常不被访问;相反,Session对象是 用作数据库的接口。然而,对于应用程序 是建立在直接使用文本SQL语句和/或SQL 表达式的构造不需要ORM的更高级别的参与 管理服务,引擎和连接是王(和皇后?)- 读下去。< / p >

Nabeel的回答涵盖了很多细节,是有帮助的,但我发现它难以遵循。由于这是目前这个问题的第一个谷歌结果,为将来发现这个问题的人添加我对它的理解:

运行. execute ()

正如OP和Nabell Ahmed都指出的那样,当执行普通SELECT * FROM tablename时,所提供的结果没有区别。

这三个对象之间的区别确实变得很重要,这取决于SELECT语句使用的上下文,或者更常见的是,当你想做其他事情,如INSERTDELETE等。

什么时候使用引擎,连接,会话一般

  • 引擎是SQLAlchemy使用的最低级别对象。当应用程序需要与数据库对话时,它维护连接池可用。.execute()是一个方便的方法,它首先调用conn = engine.connect(close_with_result=True),然后调用conn.execute()。close_with_result参数表示连接将自动关闭。(我稍微改写了源代码,但本质上是正确的)。编辑:这是engine.execute . 0的源代码

    您可以使用引擎执行原始SQL。

    result = engine.execute('SELECT * FROM tablename;')
    #what engine.execute() is doing under the hood
    conn = engine.connect(close_with_result=True)
    result = conn.execute('SELECT * FROM tablename;')
    
    
    #after you iterate over the results, the result and connection get closed
    for row in result:
    print(result['columnname']
    
    
    #or you can explicitly close the result, which also closes the connection
    result.close()
    

    这在基本用法下的文档中涵盖

  • 连接是(如上所述)实际执行SQL查询的东西。当您想要更好地控制连接的属性时,您应该这样做,当它关闭时,等等。例如,一个非常重要的例子是事务,它让你决定何时向数据库提交更改。在正常使用中,更改是自动提交的。通过使用事务,您可以(例如)运行几个不同的SQL语句,如果其中一个语句出错,您可以立即撤消所有更改。

    connection = engine.connect()
    trans = connection.begin()
    try:
    connection.execute("INSERT INTO films VALUES ('Comedy', '82 minutes');")
    connection.execute("INSERT INTO datalog VALUES ('added a comedy');")
    trans.commit()
    except:
    trans.rollback()
    raise
    

    这将允许您在一个更改失败时撤消两个更改,例如忘记创建数据日志表。

    因此,如果您正在执行原始SQL代码并需要控制,请使用connections

  • 会话用于SQLAlchemy的对象关系管理(ORM)方面(实际上你可以从它们的导入方式中看到这一点:from sqlalchemy.orm import sessionmaker)。它们在底层使用连接和事务来运行自动生成的SQL语句。.execute()是一个方便函数,它传递给会话绑定到的任何对象(通常是引擎,但也可以是连接)。

    如果你正在使用ORM功能,请使用session;如果你只做直接的SQL查询而不绑定到对象,你最好直接使用连接。< / p >

下面是一个运行DCL(数据控制语言)的例子,比如GRANT

def grantAccess(db, tb, user):
import sqlalchemy as SA
import psycopg2


url = "{d}+{driver}://{u}:{p}@{h}:{port}/{db}".\
format(d="redshift",
driver='psycopg2',
u=username,
p=password,
h=host,
port=port,
db=db)
engine = SA.create_engine(url)
cnn = engine.connect()
trans = cnn.begin()
strSQL = "GRANT SELECT on table " + tb + " to " + user + " ;"
try:
cnn.execute(strSQL)
trans.commit()
except:
trans.rollback()
raise