SQLAlchemy: diferença de mecanismo, conexão e sessão


134

Eu uso SQLAlchemy e há pelo menos três entidades: engine, sessione connection, que tem executemétodo, por isso, se eu por exemplo, quer selecionar todos os registros de tableque posso fazer isso

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

e isto

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

e até isso

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

- os resultados serão os mesmos.

Pelo que entendi, se alguém usa, engine.executeele cria connection, abre session(a Alquimia cuida disso para você) e executa a consulta. Mas existe uma diferença global entre essas três maneiras de executar essa tarefa?


Eu acho que sua resposta está aqui: hackersandslackers.com/…
SeF

Respostas:


123

Uma visão geral de uma linha:

O comportamento do execute()é o mesmo em todos os casos, mas eles são 3 métodos diferentes, em Engine, Connectione Sessionclasses.

O que exatamente é execute():

Para entender o comportamento execute(), precisamos olhar para a Executableclasse. Executableé uma superclasse para todos os tipos de objetos "instrução", incluindo selecionar (), excluir (), atualizar (), inserir (), texto () - nas palavras mais simples possíveis, um Executableé um construto de expressão SQL suportado no SQLAlchemy.

Em todos os casos, o execute()método utiliza o texto SQL ou a expressão SQL construída, ou seja, qualquer uma das várias construções de expressão SQL suportadas no SQLAlchemy e retorna os resultados da consulta (a ResultProxy- Envolve um DB-APIobjeto cursor para fornecer acesso mais fácil às colunas da linha.)


Para esclarecer melhor (apenas para esclarecimentos conceituais, não uma abordagem recomendada) :

Além de Engine.execute()(execução sem conexão),, Connection.execute()e Session.execute(), também é possível usar o execute()diretamente em qualquer Executableconstrução. A Executableclasse possui sua própria implementação execute()- Conforme a documentação oficial, uma descrição de linha sobre o que execute()faz é " Compilar e executar issoExecutable ". Nesse caso, precisamos vincular explicitamente o Executable(construto de expressão SQL) a um Connectionobjeto ou, Engineobjeto (que obtém implicitamente um Connectionobjeto), para que execute()você saiba onde executar o SQL.

O exemplo a seguir demonstra bem isso - Dada uma tabela como abaixo:

from sqlalchemy import MetaData, Table, Column, Integer

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

Execução explícita, ou seja Connection.execute()- passar o texto SQL ou a expressão SQL construída para o execute()método de Connection:

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

Execução explícita sem conexão, isto é Engine.execute()- passando o texto SQL ou a expressão SQL construída diretamente para o execute()método do Engine:

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

A execução implícita, ie Executable.execute()-, também é sem conexão e chama o execute()método do Executable, isto é, chama o execute()método diretamente na SQLconstrução da expressão (uma instância de Executable) em si.

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

Nota: Indique o exemplo implícito de execução para fins de esclarecimento - este modo de execução não é altamente recomendado - conforme os documentos :

"Execução implícita" é um padrão de uso muito antigo que, na maioria dos casos, é mais confuso do que útil, e seu uso é desencorajado. Ambos os padrões parecem incentivar o uso excessivo de "atalhos" convenientes no design de aplicativos, o que leva a problemas mais tarde.


Suas perguntas:

Pelo que entendi, se alguém usar o engine.execute, cria conexão, abre a sessão (a Alquimia se preocupa com você) e executa a consulta.

Você está certo quanto à parte "se alguém usá- engine.executelo cria connection", mas não para "abre session(a Alquimia se preocupa com você) e executa a consulta" - Usando Engine.execute()e Connection.execute()é (quase) a mesma coisa, formal, o Connectionobjeto é criado implicitamente e, posteriormente, instanciamos explicitamente. O que realmente acontece neste caso é:

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

Mas existe uma diferença global entre essas três maneiras de executar essa tarefa?

Na camada DB, é exatamente a mesma coisa, todos eles estão executando SQL (expressão de texto ou várias construções de expressão SQL). Do ponto de vista do aplicativo, existem duas opções:

  • Execução direta - usando Engine.execute()ouConnection.execute()
  • Usando sessions- eficiente lida transação como única unidade-de-obra, com facilidade via session.add(), session.rollback(), session.commit(), session.close(). É a maneira de interagir com o banco de dados no caso de ORM, ou seja, tabelas mapeadas. Fornece identity_map para obter instantaneamente objetos já acessados ​​ou recém-criados / adicionados durante uma única solicitação.

Session.execute()em última análise, usa o Connection.execute()método de execução de instrução para executar a instrução SQL. Usar Sessionobjeto é a maneira recomendada do SQLAlchemy ORM para um aplicativo interagir com o banco de dados.

Um trecho dos documentos :

É importante observar que, ao usar o SQLAlchemy ORM, esses objetos geralmente não são acessados; em vez disso, o objeto Session é usado como interface para o banco de dados. No entanto, para aplicativos criados com base no uso direto de instruções SQL textuais e / ou construções de expressão SQL sem o envolvimento dos serviços de gerenciamento de nível superior do ORM, o Mecanismo e a Conexão são o rei (e a rainha?) - continue lendo.


A palavra "sem conexão" implica que nenhuma conexão está sendo criada, o que, de acordo com a resposta de Neal, não é o caso.
Atom

111

A resposta de Nabeel cobre muitos detalhes e é útil, mas achei confuso seguir. Como atualmente este é o primeiro resultado do Google para esse problema, adiciono meu entendimento a pessoas futuras que encontrarem essa pergunta:

Executando .execute ()

Como OP e Nabell Ahmed observam, ao executar uma planície SELECT * FROM tablename, não há diferença no resultado fornecido.

As diferenças entre estes três objetos se tornam importantes, dependendo do contexto que a SELECTdeclaração é usada em ou, mais comumente, quando você quer fazer outras coisas como INSERT, DELETE, etc.

Quando usar o Mecanismo, Conexão, Sessão geralmente

  • O mecanismo é o objeto de nível mais baixo usado pelo SQLAlchemy. Ele mantém um conjunto de conexões disponíveis para uso sempre que o aplicativo precisar conversar com o banco de dados. .execute()é um método de conveniência que primeiro chama conn = engine.connect(close_with_result=True)e depois conn.execute(). O parâmetro close_with_result significa que a conexão é fechada automaticamente. (Estou parafraseando um pouco o código fonte, mas essencialmente verdadeiro). edit: Aqui está o código fonte do engine.execute

    Você pode usar o mecanismo para executar o SQL bruto.

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

    Isso é abordado nos documentos em uso básico .

  • A conexão é (como vimos acima) a coisa que realmente faz o trabalho de executar uma consulta SQL. Você deve fazer isso sempre que quiser maior controle sobre os atributos da conexão, quando ela for fechada, etc. Por exemplo, um exemplo muito importante disso é uma transação , que permite decidir quando confirmar suas alterações no banco de dados. No uso normal, as alterações são confirmadas automaticamente. Com o uso de transações, você pode (por exemplo) executar várias instruções SQL diferentes e, se algo der errado com uma delas, poderá desfazer todas as alterações de uma só vez.

    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

    Isso permitiria desfazer as duas alterações se uma falhasse, como se você se esquecesse de criar a tabela de registro de dados.

    Portanto, se você estiver executando código SQL bruto e precisar de controle, use conexões

  • As sessões são usadas para o aspecto ORM (Object Relationship Management) de SQLAlchemy (na verdade, você pode ver isso de como são importadas:) from sqlalchemy.orm import sessionmaker. Eles usam conexões e transações ocultas para executar suas instruções SQL geradas automaticamente. .execute()é uma função de conveniência que passa para o que a sessão está vinculada (geralmente um mecanismo, mas pode ser uma conexão).

    Se você estiver usando a funcionalidade ORM, use session; se você estiver fazendo apenas consultas SQL diretas e não vinculadas a objetos, provavelmente será melhor usar conexões diretamente.


1
As instruções de inserção não devem estar entre aspas duplas ""?
Mingchau

2
@mingchau Sim, você está certo, minhas aspas simples teriam interferido uma com a outra, aspas duplas são muito mais fáceis de evitar esse problema. Atualizada.
Neal

Dada a sessão criada, como a minha sessão está vinculada à minha conexão PostgreSQL?
Raju yourPepe

@RajuyourPepe my_session.connection(). Documentos: docs.sqlalchemy.org/en/13/orm/… .
Neal

Seriamente ? O objeto 'Session' não tem nenhum atributo 'connect' ", foi o que eu encontrei
Raju yourPepe

0

Aqui está um exemplo de execução de DCL (Data Control Language) como 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.