| title |
|---|
SQLAlchemy / Core |
SQLAlchemy / Core
-
Working with Engines and Connections — SQLAlchemy 1.3 Documentation #ril
-
This section details direct usage of the
Engine,Connection, and related objects. Its important to note that when using the SQLAlchemy ORM, these objects are NOT generally accessed; instead, theSessionobject is used as the interface to the database.However, for applications that are built around direct usage of TEXTUAL SQL STATEMENTS and/or SQL EXPRESSION CONSTRUCTS without involvement by the ORM’s higher level MANAGEMENT SERVICES, the
EngineandConnectionare king (and queen?) - read on.確實用 SQLAlchemy 並不一定要走 ORM 就可以讓 code base 不會綁定特定的 DBMS 或 DB-API 實作。
Basic Usage
-
Recall from Engine Configuration that an
Engineis created via thecreate_engine()call:engine = create_engine('mysql://scott:tiger@localhost/test') -
The typical usage of
create_engine()is once per particular DATABASE URL, held globally for the lifetime of a single application process.A single
Enginemanages MANY individual DBAPI CONNECTIONS on behalf of the process and is intended to be called upon in a CONCURRENT fashion.The
Engineis not synonymous to the DBAPIconnectfunction, which represents just one connection resource - theEngineis most efficient when created JUST ONCE AT THE MODULE LEVEL of an application, not per-object or per-function call.Engine背後有多個 DB-API connection (如果沒有停用 connection pooling 的話),整個 process 會共用一個Engine。 -
For a multiple-process application that uses the
os.forksystem call, or for example the Pythonmultiprocessingmodule, it’s usually required that a separateEnginebe used for each child process. This is because theEnginemaintains a reference to a CONNECTION POOL that ultimately references DBAPI connections - these tend to not be portable across process boundaries.An
Enginethat is configured not to use pooling (which is achieved via the usage ofNullPool) does not have this requirement.所謂 the requirement 指的是 "一個 process 共用一個 engine",背後如果沒有 pooling 的話 (
NullPool),就只會對應到一個 connection,這樣 per-object 或 per-function 也沒差。 -
The engine can be used directly to issue SQL to the database. The most generic way is first procure a connection resource, which you get via the
Engine.connect()method:connection = engine.connect() result = connection.execute("select username from users") for row in result: print("username:", row['username']) connection.close()The connection is an instance of
Connection, which is a PROXY OBJECT for an actual DBAPI connection. The DBAPI connection is retrieved from the CONNECTION POOL at the point at whichConnectionis created.Engine.connect()在生成Connection時只是從 pool 拿出一個 DBAPI connection (一直與 DB 保持連線) 包裝起來而已,如果當時 DBAPI connection 不夠 (且數量未達上限),就會動態建立。根據 DB-API connection object 只有
close()、commit()、rollback()及cursor()4 個 method,並沒有connect(),可見 DB-API connection object 在生成的時候就已經與 DB 建立連線;搭配 MySQL 的SHOW PROCESSLIST觀察也是如此。 -
The returned result is an instance of
ResultProxy, which references a DBAPI cursor and provides a LARGELY COMPATIBLE INTERFACE with that of the DBAPI cursor. The DBAPI cursor will be closed by theResultProxywhen all of its result rows (if any) are exhausted. AResultProxythat returns no rows, such as that of anUPDATEstatement (without any returned rows), releases cursor resources immediately upon construction.DB-API cursor object 有個
close(),這意謂著如果沒讀完,要明確呼叫ResultProxy.close()。 -
When the
close()method is called, the referenced DBAPI connection is RELEASED TO THE CONNECTION POOL. From the perspective of the database itself, NOTHING IS ACTUALLY “CLOSED”, assuming pooling is in use. The pooling mechanism issues arollback()call on the DBAPI connection so that any transactional state or locks are removed, and the connection is ready for its next usage.呼應上面 DB-API connection object 沒有
connect(),DB-API connection 一直都是與 DB 保持連線的,只是回到 connection pool 前會透過rollback()把 transactional resources -- 進行到一半的 transaction、要求的 lock 等釋出,也確保下一個拿到這個 DB-API connection 的人不會受影響。 -
The above procedure can be performed in a shorthand way by using the
execute()method ofEngineitself:result = engine.execute("select username from users") for row in result: print("username:", row['username'])Where above, the
execute()method acquires a newConnectionon its own, executes the statement with that object, and returns theResultProxy. In this case, theResultProxycontains a special flag known asclose_with_result, which indicates that when its underlying DBAPI cursor is closed, theConnectionobject itself is also closed, which again returns the DBAPI connection to the connection pool, releasing transactional resources.因為拿不到 connection 以呼叫
close(),所以設計成 result set 讀完時會自動呼叫Connection.close()讓 connection 回到 pool;但如果沒讀完會發生什麼事? 下面在解釋最好明確呼叫ResultProxy.close(),雖然最終也會因為 GC 的關係讓 connection 回到 pool,但最好別依賴這樣的行為。 -
If the
ResultProxypotentially has rows remaining, it can be instructed to close out its resources explicitly:result.close()If the
ResultProxyhas pending rows remaining and is DEREFERENCED by the application without being closed, Python garbage collection will ultimately close out the cursor as well as trigger a return of the pooled DBAPI connection resource to the pool (SQLAlchemy achieves this by the usage of WEAKREF CALLBACKS - never the__del__method) - however it’s never a good idea to rely upon Python garbage collection to manage resources. -
Our example above illustrated the execution of a textual SQL string. The
execute()method can of course accommodate more than that, including the variety of SQL expression constructs described in SQL Expression Language Tutorial.
-
文件:
手冊: