SQLAlchemy: движок, соединение и разница в сеансе

135

Я использую SQLAlchemy и есть, по крайней мере , три лица: engine, sessionи connection, которые имеют executeметод, поэтому , если я , например , хочу , чтобы выбрать все записи из tableя могу сделать это

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

и это

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

и даже это

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

- результаты будут такими же.

Насколько я понимаю, если кто-то engine.executeего использует, он создает connection, открывает session(Алхимия заботится об этом за вас) и выполняет запрос. Но есть ли глобальная разница между этими тремя способами выполнения такой задачи?

ololobus
источник
Думаю, ваш ответ прямо здесь: hackersandslackers.com/…
SeF

Ответы:

123

Однострочный обзор:

Поведение execute()такое же во всех случаях, но они 3 различных методов, в Engine, Connectionи Sessionклассы.

Что именно execute():

Чтобы понять поведение, execute()нам нужно заглянуть в Executableкласс. Executableявляется суперклассом для всех типов объектов «операторов», включая select (), delete (), update (), insert (), text () - проще говоря, это Executableконструкция выражения SQL, поддерживаемая в SQLAlchemy.

Во всех случаях execute()метод принимает текст SQL или сконструированное выражение SQL, то есть любую из разнообразных конструкций выражения SQL, поддерживаемых в SQLAlchemy, и возвращает результаты запроса (a ResultProxy- Оборачивает DB-APIобъект курсора, чтобы обеспечить более легкий доступ к столбцам строк).


Чтобы прояснить это дополнительно (только для концептуального пояснения, не рекомендуемый подход) :

В дополнение к Engine.execute()(выполнение без установления соединения), Connection.execute()и Session.execute()также можно использовать execute()непосредственно в любой Executableконструкции. У этого 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 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:

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

Неявное выполнение, то есть Executable.execute()- также без установления соединения и вызывает execute()метод объекта Executable, то есть вызывает execute()метод непосредственно для самой SQLконструкции выражения (экземпляра Executable).

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

Примечание: приведен пример неявного выполнения с целью пояснения - этот способ выполнения настоятельно не рекомендуется - согласно документации :

«Неявное выполнение» - очень старый шаблон использования, который в большинстве случаев больше сбивает с толку, чем полезен, и его использование не рекомендуется. Оба шаблона, кажется, поощряют чрезмерное использование целесообразных «сокращений» в дизайне приложений, что в дальнейшем приводит к проблемам.


Ваши вопросы:

Насколько я понимаю, если кто-то использует engine.execute, он создает соединение, открывает сеанс (Alchemy заботится об этом за вас) и выполняет запрос.

Вы правы в части «если кто-то использует engine.executeэто создает connection», но не «открывает» session(Алхимия заботится об этом за вас) и выполняет запрос »- Использование Engine.execute()и Connection.execute()(почти) одно и то же, формально Connectionобъект создается неявно , а в более позднем случае мы явно создаем его экземпляр. Что действительно происходит в этом случае:

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

Но есть ли глобальная разница между этими тремя способами выполнения такой задачи?

На уровне БД это одно и то же, все они выполняют 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 для взаимодействия приложения с базой данных.

Выдержка из документации :

Важно отметить, что при использовании ORM SQLAlchemy эти объекты обычно не доступны; вместо этого объект Session используется как интерфейс к базе данных. Однако для приложений, построенных на прямом использовании текстовых операторов SQL и / или конструкций выражений SQL без участия служб управления более высокого уровня ORM, Engine и Connection являются королем (и королевой?) - читайте дальше.

Набиль Ахмед
источник
Слово "без установления соединения" означает, что соединение не создается, что, согласно ответу Нила, не соответствует действительности.
Atom
111

Ответ Набиль охватывает множество деталей и полезен, но мне было сложно следить за ним. Поскольку в настоящее время это первый результат Google по этой проблеме, добавляю свое понимание этого для будущих людей, которые найдут этот вопрос:

Запуск .execute ()

Как отмечают OP и Nabell Ahmed, при выполнении простого результата SELECT * FROM tablenameнет никакой разницы в полученном результате.

Различия между этими тремя объектами становятся важными в зависимости от контекста, в SELECTкотором используется оператор, или, что более часто, когда вы хотите сделать другие вещи, например INSERT, DELETEи т. Д.

Когда использовать Engine, Connection, Session в целом

  • Engine - это объект самого низкого уровня, используемый SQLAlchemy. Он поддерживает пул соединений, доступных для использования всякий раз, когда приложению необходимо взаимодействовать с базой данных. .execute()- это удобный метод, который сначала вызывает, conn = engine.connect(close_with_result=True)а затем conn.execute(). Параметр close_with_result означает, что соединение закрывается автоматически. (Я немного перефразирую исходный код, но по сути это правда). изменить: вот исходный код для 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()
    

    Это описано в документации по базовому использованию .

  • Соединение (как мы видели выше) - это то, что на самом деле выполняет работу по выполнению 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 и нуждаетесь в управлении, используйте соединения.

  • Сеансы используются для аспекта управления отношениями объектов (ORM) SQLAlchemy (на самом деле вы можете видеть это по тому, как они импортируются :) from sqlalchemy.orm import sessionmaker. Они используют внутренние соединения и транзакции для выполнения своих автоматически сгенерированных операторов SQL. .execute()- это вспомогательная функция, которая передает все, к чему привязан сеанс (обычно движок, но может быть и соединением).

    Если вы используете функциональность ORM, используйте session; если вы выполняете только прямые SQL-запросы, не привязанные к объектам, вам, вероятно, лучше использовать соединения напрямую.

Нил
источник
1
Не следует заключать операторы вставки в двойные кавычки ""?
mingchau
2
@mingchau Да, вы правы, мои одинарные кавычки мешали бы друг другу, двойных кавычек гораздо проще избежать этой проблемы. Обновлено.
Нил
Учитывая созданный сеанс, как мой сеанс связан с моим подключением к PostgreSQL?
Raju yourPepe
@RajuyourPepe my_session.connection(). Документы: docs.sqlalchemy.org/en/13/orm/… .
Neal
Шутки в сторону ? У объекта 'Session' нет атрибута 'connect' ", вот что я обнаружил
Raju yourPepe
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
Jie
источник