SQLAlchemy: chênh lệch động cơ, kết nối và phiên


134

Tôi sử dụng SQLAlchemy và có ít nhất ba đơn vị: engine, sessionconnection, trong đó có executephương pháp, vì vậy nếu tôi ví dụ muốn chọn tất cả các bản ghi từ tabletôi có thể làm điều này

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

và điều này

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

và thậm chí này

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

- kết quả sẽ giống nhau.

Theo tôi hiểu, nếu ai đó sử dụng engine.executenó tạo ra connection, sẽ mở ra session(Alchemy chăm sóc nó cho bạn) và thực hiện truy vấn. Nhưng có một sự khác biệt toàn cầu giữa ba cách thực hiện một nhiệm vụ như vậy?


Tôi nghĩ câu trả lời của bạn ở ngay đây: hackersandslackers.com/ Kẻ
SeF

Câu trả lời:


123

Tổng quan một dòng:

Hành vi của execute()là như nhau trong tất cả các trường hợp, nhưng họ là 3 phương pháp khác nhau, trong Engine, ConnectionSessioncác lớp học.

Chính xác là gì execute():

Để hiểu hành vi của execute()chúng ta cần nhìn vào Executablelớp học. Executablelà một siêu lớp cho tất cả các câu lệnh của các loại đối tượng, bao gồm select (), xóa (), update (), insert (), text () - bằng những từ đơn giản nhất có thể, một Executablecấu trúc biểu thức SQL được hỗ trợ trong SQLAlchemy.

Trong tất cả các trường hợp, execute()phương thức lấy văn bản SQL hoặc biểu thức SQL được xây dựng, tức là bất kỳ cấu trúc biểu thức SQL nào được hỗ trợ trong SQLAlchemy và trả về kết quả truy vấn (a ResultProxy- Kết hợp một DB-APIđối tượng con trỏ để cung cấp quyền truy cập dễ dàng hơn vào các cột hàng.)


Để làm rõ hơn nữa (chỉ để làm rõ khái niệm, không phải là một cách tiếp cận được đề xuất) :

Ngoài Engine.execute()(thực thi không kết nối), Connection.execute()Session.execute(), cũng có thể sử dụng execute()trực tiếp trên bất kỳ Executablecấu trúc nào . Các Executablelớp học có thực hiện riêng của nó trong execute()- Theo tài liệu chính thức, một dòng mô tả về những gì execute()làm là " Compile và thực hiện điều nàyExecutable ". Trong trường hợp này, chúng ta cần liên kết rõ ràng Executable(cấu trúc biểu thức SQL) với một Connectionđối tượng hoặc, Engineđối tượng (mà hoàn toàn có được một Connectionđối tượng), vì vậy execute()sẽ biết nơi để thực hiện SQL.

Ví dụ sau đây cho thấy điều đó tốt - Đưa ra một bảng như dưới đây:

from sqlalchemy import MetaData, Table, Column, Integer

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

Thực hiện rõ ràng tức là Connection.execute()- chuyển văn bản SQL hoặc biểu thức SQL được xây dựng sang execute()phương thức Connection:

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

Rõ ràng thực thi không kết nối tức là Engine.execute()- chuyển văn bản SQL hoặc biểu thức SQL được xây dựng trực tiếp sang execute()phương thức Engine:

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

Thực hiện ngầm định tức là Executable.execute()- cũng không có kết nối và gọi execute()phương thức của Executable, nghĩa là, nó gọi execute()phương thức trực tiếp trên chính SQLcấu trúc biểu thức (một thể hiện của Executable).

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

Lưu ý: Đã nêu ví dụ thực thi ngầm cho mục đích làm rõ - cách thực hiện này rất không được khuyến nghị - theo tài liệu :

Thực hiện ngầm định là một mô hình sử dụng rất cũ mà trong hầu hết các trường hợp là khó hiểu hơn là hữu ích, và việc sử dụng nó không được khuyến khích. Cả hai mô hình này dường như đều khuyến khích việc sử dụng quá mức các đoạn cắt ngắn ngắn gọn trong thiết kế ứng dụng, điều này dẫn đến các vấn đề về sau.


Những câu hỏi của bạn:

Theo tôi hiểu nếu ai đó sử dụng engine.execute, nó sẽ tạo kết nối, mở phiên (Alchemy quan tâm đến nó cho bạn) và thực hiện truy vấn.

Bạn đúng với phần "nếu ai đó sử dụng engine.executenó tạo ra connection" nhưng không phải cho "mở session(Alchemy quan tâm đến nó cho bạn) và thực hiện truy vấn" - Sử dụng Engine.execute()Connection.execute()(gần như) một điều tương tự, chính thức, Connectionđối tượng được tạo ra một cách ngầm định và trong trường hợp sau, chúng tôi khởi tạo nó một cách rõ ràng. Điều thực sự xảy ra trong trường hợp này là:

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

Nhưng có một sự khác biệt toàn cầu giữa ba cách thực hiện nhiệm vụ như vậy?

Ở lớp DB, nó giống hệt nhau, tất cả chúng đều thực thi SQL (biểu thức văn bản hoặc các cấu trúc biểu thức SQL khác nhau). Từ quan điểm của ứng dụng, có hai lựa chọn:

  • Thực hiện trực tiếp - Sử dụng Engine.execute()hoặcConnection.execute()
  • Sử dụng sessions- xử lý một cách hiệu quả giao dịch như là duy nhất đơn vị-of-nơi làm việc, một cách dễ dàng thông qua session.add(), session.rollback(), session.commit(), session.close(). Đó là cách để tương tác với DB trong trường hợp ORM tức là các bảng được ánh xạ. Cung cấp nhận dạng_map để nhận ngay các đối tượng đã được truy cập hoặc được tạo / thêm mới trong một yêu cầu.

Session.execute()cuối cùng sử dụng Connection.execute()phương thức thực thi câu lệnh để thực thi câu lệnh SQL. Sử dụng Sessionđối tượng là cách được khuyến nghị của SQLAlchemy ORM để ứng dụng tương tác với cơ sở dữ liệu.

Một đoạn trích từ các tài liệu :

Điều quan trọng cần lưu ý là khi sử dụng SQLAlchemy ORM, các đối tượng này thường không được truy cập; thay vào đó, đối tượng Phiên được sử dụng làm giao diện cho cơ sở dữ liệu. Tuy nhiên, đối với các ứng dụng được xây dựng xung quanh việc sử dụng trực tiếp các câu lệnh SQL văn bản và / hoặc các cấu trúc biểu thức SQL mà không có sự tham gia của các dịch vụ quản lý cấp cao hơn của ORM, Engine và Connection là vua (và nữ hoàng?) - hãy đọc tiếp.


Từ "không kết nối" ngụ ý không có kết nối nào được tạo ra, mà theo câu trả lời của Neal không phải là trường hợp.
Atom

110

Câu trả lời của Nabeel bao gồm rất nhiều chi tiết và rất hữu ích, nhưng tôi thấy khó hiểu khi làm theo. Vì đây hiện là kết quả đầu tiên của Google cho vấn đề này, nên tôi hiểu thêm về những người trong tương lai tìm thấy câu hỏi này:

Chạy .execute ()

Như OP và Nabell Ahmed đều lưu ý, khi thực hiện đồng bằng SELECT * FROM tablename, kết quả được cung cấp không có sự khác biệt.

Sự khác biệt giữa ba đối tượng làm trở nên quan trọng tùy thuộc vào bối cảnh các SELECTtuyên bố được sử dụng trong hay, phổ biến hơn, khi bạn muốn làm những việc khác như INSERT, DELETEvv

Khi nào nên sử dụng Engine, Connection, session

  • Engine là đối tượng cấp thấp nhất được SQLAlchemy sử dụng. Nó duy trì một nhóm các kết nối có sẵn để sử dụng bất cứ khi nào ứng dụng cần nói chuyện với cơ sở dữ liệu. .execute()là một phương pháp thuận tiện mà lần đầu tiên gọi conn = engine.connect(close_with_result=True)và sau đó conn.execute(). Tham số close_with_result có nghĩa là kết nối được đóng tự động. (Tôi hơi diễn giải mã nguồn, nhưng về cơ bản là đúng). chỉnh sửa: Đây là mã nguồn cho engine.execute

    Bạn có thể sử dụng công cụ để thực thi SQL thô.

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

    Điều này được đề cập trong các tài liệu theo cách sử dụng cơ bản .

  • Kết nối là (như chúng ta đã thấy ở trên) điều thực sự làm công việc thực hiện một truy vấn SQL. Bạn nên làm điều này bất cứ khi nào bạn muốn kiểm soát nhiều hơn các thuộc tính của kết nối, khi nó bị đóng, v.v. Ví dụ, một ví dụ rất quan trọng của việc này là Giao dịch , cho phép bạn quyết định khi nào sẽ thay đổi cơ sở dữ liệu. Trong sử dụng bình thường, những thay đổi được tự động hóa. Với việc sử dụng các giao dịch, bạn có thể (ví dụ) chạy một số câu lệnh SQL khác nhau và nếu có lỗi xảy ra với một trong số chúng, bạn có thể hoàn tác tất cả các thay đổi cùng một lúc.

    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

    Điều này sẽ cho phép bạn hoàn tác cả hai thay đổi nếu một lần thất bại, như nếu bạn quên tạo bảng dữ liệu.

    Vì vậy, nếu bạn đang thực thi mã SQL thô và cần kiểm soát, hãy sử dụng các kết nối

  • Các phiên được sử dụng cho khía cạnh Quản lý mối quan hệ đối tượng (ORM) của SQLAlchemy (trên thực tế bạn có thể thấy điều này từ cách chúng được nhập khẩu from sqlalchemy.orm import sessionmaker:). Họ sử dụng các kết nối và giao dịch dưới mui xe để chạy các câu lệnh SQL được tạo tự động. .execute()là một chức năng tiện lợi đi qua bất cứ thứ gì mà phiên bị ràng buộc (thường là một động cơ, nhưng có thể là một kết nối).

    Nếu bạn đang sử dụng chức năng ORM, hãy sử dụng phiên; nếu bạn chỉ thực hiện các truy vấn SQL thẳng không bị ràng buộc với các đối tượng, có lẽ bạn nên sử dụng các kết nối trực tiếp.


1
Không nên chèn các câu lệnh được đặt trong dấu ngoặc kép ""?
mingchau

2
@mingchau Vâng, bạn nói đúng, các trích dẫn đơn lẻ của tôi sẽ can thiệp lẫn nhau, trích dẫn kép dễ dàng hơn nhiều để tránh vấn đề đó. Cập nhật.
Neal

Với phiên đã tạo, Phiên của tôi được liên kết với kết nối PostgreSQL của tôi như thế nào?
Raju yourPepe

@RajuyourPepe my_session.connection(). Tài liệu: docs.sqlalchemy.org/en/13/orm/ .
Neal

Nghiêm túc ? Đối tượng 'Phiên' không có thuộc tính 'kết nối' ", là những gì tôi đã tìm thấy
Raju yourPepe

0

Dưới đây là một ví dụ về việc chạy DCL (Ngôn ngữ điều khiển dữ liệu) như 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.