SQLAlchemy:引擎、连接和会话区别

SQLAlchemy: engine, connection and session difference

我使用 SQLAlchemy 并且至少有三个实体:enginesessionconnection,它们具有 execute 方法,所以如果我例如想要 select 来自 table 的所有记录我可以做到这一点

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

还有这个

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

甚至这个

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

- 结果是一样的。

据我了解,如果有人使用 engine.execute,它会创建 connection,打开 session(Alchemy 会为您处理)并执行查询。但是,这三种执行此类操作的方式之间是否存在全局差异? 任务?

一行概览:

execute() 的行为在所有情况下都是相同的,但它们是 3 种不同的方法,在 EngineConnectionSession [=135= 中]es.

到底是什么execute():

要了解 execute() 的行为,我们需要研究 Executable class。 Executable 是所有“语句”类型对象的超级class,包括 select()、delete()、update()、insert()、text() - 简单来说可能,Executable 是 SQL Alchemy 支持的 SQL 表达式结构。

在所有情况下,execute() 方法采用 SQL 文本或构造的 SQL 表达式,即 SQL 支持的各种 SQL 表达式构造=]Alchemy 和 returns 查询结果(一个 ResultProxy - 包装一个 DB-API 游标对象以提供对行列的更容易访问。)


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

除了Engine.execute()(无连接执行)、Connection.execute()Session.execute()之外,还可以直接在任何[=21=上使用execute() ] 构造。 Executable class 有自己的 execute() 实现 - 根据官方文档,关于 execute() 所做的一行描述是“编译并执行此Executable”。在这种情况下,我们需要显式绑定 Executable(SQL 表达式构造)与 Connection 对象或 Engine 对象(隐式获得 Connection 对象), 因此 execute() 将知道在哪里执行 SQL.

以下示例很好地演示了它 - 给定一个 table 如下:

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表达式传递给execute()方法Connection:

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 表达式直接传递给 execute() 方法引擎数:

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

隐式执行Executable.execute()——也是无连接的,调用Executableexecute()方法,即调用execute() 方法直接在 SQL 表达式构造(Executable 的实例)本身上。

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

注意:出于说明的目的陈述了隐式执行示例 - 强烈不推荐这种执行方式 - 根据 docs:

“implicit execution” is a very old usage pattern that in most cases is more confusing than it is helpful, and its usage is discouraged. Both patterns seem to encourage the overuse of expedient “short cuts” in application design which lead to problems later on.


您的问题:

As I understand if someone use engine.execute it creates connection, opens session (Alchemy cares about it for you) and executes query.

你对 "if someone use engine.execute it creates connection " 部分是正确的,但不适合 "opens session (Alchemy cares about it for you) and executes query " - 使用 Engine.execute()Connection.execute() 是(几乎)一回事,在正式的, Connection 对象被隐式创建,在后面的情况下我们显式实例化它。在这种情况下真正发生的是:

`Engine` object (instantiated via `create_engine()`) -> `Connection` object (instantiated via `engine_instance.connect()`) -> `connection.execute({*SQL expression*})`

But is there a global difference between these three ways of performing such task?

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

  • 直接执行 - 使用Engine.execute()Connection.execute()
  • 使用sessions - 高效地处理单笔交易 工作单元,通过 session.add()session.rollback()session.commit()session.close() 轻松实现。这是在 ORM 的情况下与数据库交互的方式,即映射 tables。提供 identity_map 以在单个请求期间立即获取已访问或新的 created/added 对象。

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

摘自docs

Its important to note that when using the SQLAlchemy ORM, these objects are not generally accessed; instead, the Session object is used as the interface to the database. However, for applications that are built around direct usage of textual SQL statements and/or SQL expression constructs without involvement by the ORM’s higher level management services, the Engine and Connection are king (and queen?) - read on.

涵盖了很多细节并且很有帮助,但我发现它难以理解。由于这是目前这个问题的第一个 Google 结果,为以后发现这个问题的人添加我对此的理解:

运行.execute()

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

这三个对象之间的差异确实变得很重要,具体取决于使用 SELECT 语句的上下文,或者更常见的是,当您想执行其他操作时,例如 INSERTDELETE,等等

一般什么时候使用Engine、Connection、Session

  • Engine 是 SQLAlchemy 使用的最低级对象。它maintains a pool of connections available for use whenever the application needs to talk to the database. .execute() is a convenience method that first calls conn = engine.connect(close_with_result=True) and the then conn.execute(). The close_with_result parameter means the connection is closed automatically. (I'm slightly paraphrasing the source code, but essentially true). edit: Here's the source code for engine.execute

    您可以使用引擎执行原始 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()
    

    basic usage.

  • 下的文档中对此进行了介绍
  • Connection 是(正如我们在上面看到的)实际执行 SQL 查询的东西。每当您想要更好地控制连接的属性、连接何时关闭等时,您都应该这样做。例如,一个非常重要的示例是 Transaction,它可以让您决定何时将更改提交到数据库。在正常使用中,更改是自动提交的。通过使用事务,您可以(例如)运行 几个不同的 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
    

    如果一个更改失败,这会让您撤消两个更改,例如您忘记创建数据日志 table。

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

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

    如果您正在使用 ORM 功能,请使用会话;如果您只是直接进行 SQL 未绑定到对象的查询,那么您最好直接使用连接。

这里有一个运行 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