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(การเล่นแร่แปรธาตุจะดูแลให้คุณ) และดำเนินการค้นหา แต่มีความแตกต่างกันทั่วโลกระหว่างสามวิธีในการปฏิบัติงานดังกล่าวหรือไม่?


ฉันคิดว่าคำตอบของคุณอยู่ที่นี่แล้ว: hackersandslackers.com/…
SeF

คำตอบ:


123

ภาพรวมหนึ่งบรรทัด:

ลักษณะการทำงานของexecute()จะเหมือนกันในทุกกรณี แต่พวกเขามี 3 วิธีที่แตกต่างกันในEngine, ConnectionและSessionชั้นเรียน

คืออะไรกันแน่execute():

เพื่อให้เข้าใจพฤติกรรมของexecute()เราจำเป็นต้องดูในExecutableชั้นเรียน Executableเป็นซูเปอร์คลาสสำหรับอ็อบเจ็กต์ประเภท "คำสั่ง" ทั้งหมดรวมถึง select (), delete (), update (), insert (), text () - ในคำที่ง่ายที่สุด an 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()เมธอดของ the 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*})`

แต่มีความแตกต่างกันทั่วโลกระหว่างสามวิธีในการปฏิบัติงานดังกล่าวหรือไม่?

ที่เลเยอร์ DB มันเหมือนกันทุกประการทุกประการกำลังเรียกใช้ SQL (นิพจน์ข้อความหรือโครงสร้างนิพจน์ SQL ต่างๆ) จากมุมมองของแอปพลิเคชันมีสองตัวเลือก:

  • การดำเนินการโดยตรง - การใช้Engine.execute()หรือConnection.execute()
  • การใช้sessions- มีประสิทธิภาพจัดการกับการทำธุรกรรมเป็นหน่วยเดียวของการทำงานได้อย่างง่ายดายผ่านทางsession.add(), session.rollback(), ,session.commit() session.close()เป็นวิธีการโต้ตอบกับ DB ในกรณีของ ORM เช่นตารางที่แมป จัดเตรียมidentity_mapสำหรับการเข้าถึงทันทีหรืออ็อบเจ็กต์ที่สร้าง / เพิ่มใหม่ในระหว่างการร้องขอเดียว

Session.execute()ในที่สุดใช้Connection.execute()วิธีการดำเนินการคำสั่งเพื่อดำเนินการคำสั่ง SQL การใช้Sessionออบเจ็กต์เป็นวิธีที่แนะนำของ SQLAlchemy ORM สำหรับแอปพลิเคชันเพื่อโต้ตอบกับฐานข้อมูล

ข้อความที่ตัดตอนมาจากเอกสาร :

สิ่งสำคัญที่ควรทราบคือเมื่อใช้ SQLAlchemy ORM วัตถุเหล่านี้จะไม่สามารถเข้าถึงได้โดยทั่วไป แทนที่จะใช้วัตถุเซสชันเป็นส่วนต่อประสานกับฐานข้อมูล อย่างไรก็ตามสำหรับแอปพลิเคชันที่สร้างขึ้นจากการใช้คำสั่ง SQL แบบข้อความและ / หรือนิพจน์ SQL โดยตรงโดยไม่เกี่ยวข้องกับบริการการจัดการระดับที่สูงขึ้นของ ORM Engine และการเชื่อมต่อเป็นแบบราชา (และราชินี?) - อ่านต่อ


คำว่า "ไม่มีการเชื่อมต่อ" หมายความว่าไม่มีการสร้างการเชื่อมต่อซึ่งตามคำตอบของโอนีลไม่เป็นเช่นนั้น
Atom

111

คำตอบของ Nabeelครอบคลุมรายละเอียดมากมายและมีประโยชน์ แต่ฉันพบว่ามันสับสนในการปฏิบัติตาม เนื่องจากปัจจุบันนี่เป็นผลการค้นหาแรกของ 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 ดิบและต้องการการควบคุมให้ใช้การเชื่อมต่อ

  • การประชุมจะใช้สำหรับด้านวัตถุ Relationship Management (ออม) ของ SQLAlchemy (ในความเป็นจริงคุณสามารถดูนี้จากว่าพวกเขากำลังนำเข้า: from sqlalchemy.orm import sessionmaker) พวกเขาใช้การเชื่อมต่อและธุรกรรมภายใต้ประทุนเพื่อรันคำสั่ง SQL ที่สร้างขึ้นโดยอัตโนมัติ .execute()เป็นฟังก์ชั่นอำนวยความสะดวกที่ส่งผ่านไปยังเซสชันใดก็ได้ (โดยปกติคือเครื่องยนต์ แต่สามารถเชื่อมต่อได้)

    หากคุณกำลังใช้ฟังก์ชัน ORM ให้ใช้เซสชัน หากคุณทำเฉพาะแบบสอบถาม SQL แบบตรงที่ไม่ผูกมัดกับวัตถุคุณอาจจะดีกว่าถ้าใช้การเชื่อมต่อโดยตรง


1
ไม่ควรใส่คำสั่งไว้ในเครื่องหมายคำพูดคู่""?
mingchau

2
@mingchau ใช่คุณพูดถูกคำพูดเดี่ยวของฉันจะรบกวนกันและกันคำพูดคู่ง่ายกว่ามากที่จะหลีกเลี่ยงปัญหานั้น Updated
Neal

รับเซสชันที่สร้างขึ้นเซสชันของฉันเชื่อมโยงกับการเชื่อมต่อ PostgreSQL ของฉันอย่างไร
Raju yourPepe

my_session.connection(). เอกสาร: docs.sqlalchemy.org/th/13/orm/… .
Neal

อย่างจริงจัง ? วัตถุ "เซสชัน" ไม่มีแอตทริบิวต์ "เชื่อมต่อ" "คือสิ่งที่ฉันพบ
Raju yourPepe

0

นี่คือตัวอย่างของการเรียกใช้ DCL (Data Control Language) เช่น 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.