SQLAlchemy:engine、connection和session的区别

275

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

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

而且这个

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

甚至连这个也是如此

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

- 结果将会相同。

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

3个回答

267

运行 .execute()

执行一个简单的 SELECT * FROM tablename 时,提供的结果没有区别。

这三个对象之间的差异在于所使用的上下文及常见的其他操作(如 INSERTDELETE 等)。

一般情况下何时使用 Engine、Connection、Session

  • Engine 是 SQLAlchemy 使用的最底层对象。它维护了一个连接池,在应用程序需要与数据库通信时可供使用。.execute() 是一个方便的方法,它首先调用 conn = engine.connect(close_with_result=True),然后调用 conn.execute()。close_with_result 参数意味着连接会自动关闭。(我稍微改写了源代码,但基本上是正确的)。编辑:这里是 engine.execute 的源代码

    您可以使用 engine 执行原始 SQL。

      result = engine.execute('SELECT * FROM tablename;')
      # engine.execute() 在幕后执行的操作:
      conn = engine.connect(close_with_result=True)
      result = conn.execute('SELECT * FROM tablename;')
    
      # 在迭代结果之后,结果和连接都会关闭
      for row in result:
          print(result['columnname']
    
      # 或者您可以显式关闭结果,这也会关闭连接
      result.close()
    

    这在文档中有基本用法的介绍。

  • Connection(如上所述)实际上执行 SQL 查询的东西。当您想要更多地控制连接属性、关闭时间等时,应该使用它。其中一个重要的例子是事务,它允许您决定何时将更改提交到数据库(如果有的话)。在正常使用中,更改是自动提交的。通过使用事务,您可以(例如)运行几个不同的 SQL 语句,如果其中一个出现问题,您可以一次性撤消所有更改。

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

    这将让您在一个失败的情况下撤消两个更改,比如如果您忘记创建 datalog 表。

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

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

    如果您正在使用 ORM 功能,请使用会话。如果您只是执行与对象无关的直接 SQL 查询,则最好直接使用连接。


已经创建了会话,我的会话如何与我的PostgreSQL连接相关联? - Jeff Bootsholz
@RajuyourPepe my_session.connection()。文档:https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.connection。 - Neal
“Session”对象没有属性“connect”,这是我发现的。 - Jeff Bootsholz
1
@RajuyourPepe 仔细看一下命令,它是 connection() 而不是 connect。请看我提供的文档链接。 - Neal
2
我正在使用会话,并在查询完成后关闭它。但有时仍然会出现数据库被锁定的情况。有什么想法吗? - Yash Tamakuwala

198

一个简短的概述:

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

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()所做的事情的一行描述是“编译并执行此Executable”。在这种情况下,我们需要将Executable(SQL表达式构造)显式绑定到Connection对象或Engine对象(隐式获取Connection对象),以便execute()知道在哪里执行SQL

以下示例很好地演示了这一点-给定如下表:

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()

显式无连接执行,即通过将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()方法。请注意保留HTML标记。
engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
    # ....
result.close()

注意:为了澄清,这里给出隐式执行的示例——这种执行方式极不推荐使用——如 docs 所述:

“隐式执行”是一种非常古老的使用模式,在大多数情况下更加混乱而不是有用,因此不建议使用。这两种模式似乎都鼓励在应用程序设计中过度使用方便的“捷径”,这会导致以后出现问题。


您的问题:

据我了解,如果有人使用engine.execute,它会创建连接,打开会话(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*})`

但是,这三种执行任务的方式之间是否存在全局差异呢?
在DB层面上,它们都执行SQL(文本表达式或各种SQL表达式结构),完全相同。从应用程序的角度来看,有两个选择:
- 直接执行 - 使用Engine.execute()Connection.execute() - 使用sessions - 通过session.add()session.rollback()session.commit()session.close()轻松处理事务作为单个工作单元进行高效处理。这是在ORM情况下与数据库交互的方式,即映射表。提供identity_map,以便在单个请求期间立即获取已访问或新创建/添加的对象。 Session.execute()最终使用Connection.execute()语句执行方法来执行SQL语句。在应用程序与数据库交互时,使用Session对象是SQLAlchemy ORM推荐的方式。

这是文档的一部分:

需要注意的是,当使用SQLAlchemy ORM时,通常不会直接访问这些对象;相反,Session对象用作与数据库交互的接口。但是,对于那些基于直接使用文本SQL语句和/或SQL表达式构造而没有ORM高级管理服务介入的应用程序来说,Engine和Connection是最重要的 - 继续阅读。


1
“无连接”一词意味着没有创建连接,但根据Neal的回答,情况并非如此。 - Atom
使用 Connection,您还可以使用 commit()rollback(),那么什么时候使用 Session 更合理呢?Connection 的好处是它是可连接的,并且因此可以更轻松地与 pandas 一起使用。 - Karol Zlot
Executable.execute()自1.4版本起已被弃用,并将在2.0中删除。2.0中的所有语句执行都由Connection.execute()或Session.execute()执行。https://docs.sqlalchemy.org/en/14/core/selectable.html#sqlalchemy.sql.expression.Executable.execute - hashlash

0
这是一个运行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

你没有检查数据库是否存活? - greendino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接