sqlalchemy 的文檔可謂典范,誰叫作者還開發著模板語言(myghty、mako)呢,呵呵。其實 sqlalchemy 的文檔就是用 myghty 寫的。
不過系統復雜了,功能多了,再好的文檔也會讓人迷路。最近用了用 sqlalchemy ,對這一點感受頗深,故把臨時想到的幾個比較常用的功能摘錄如下,提綱挈領,既為自己整理一下思路,也讓新手一窺 sqlalchemy 的精華。
Eager Loading
Join,本是關系數據庫中多麼常見的操作,怎奈 django 的 orm 就是不支持,SQLObject 的做法也很不如人意。
Association Object
many-to-many 關系都是通過增加一個中間表來實現,映射到對象後,這個中間表就不需要我們再操心了,會隱式地進行處理。
不過對於多個實體兩兩之間多對多關系,往往另外再增加一個關聯對象會更方便。
這樣的例子其實也不少,比如:user-bookmark-tags、產品-元件-元件供應商(這是一次期末考試題目裡面的 ;-)
Deferred Column Loading
比如文章表裡面的 body 字段通常比較大,在獲取文章列表時這個字段就不必取出來了。甚至如果你有某個字段存的是文件的話,這個功能就更加重要了。
這本是個不起眼的小功能,不過上次看到 javaeye 中有一貼說到大名鼎鼎的 Hibernate 都對這個功能實現得這麼痛苦後,我蓦然發現 sa 真的很 nb。呵呵,托了動態語言的福了吧。
Mapping a Class with Table Inheritance
如何把對象間繼承關系映射到關系數據庫,sqlalchemy 提供三種方式:
single table inheritance 所有子類型都放在一個表中;
concrete table inheritance 每一種子類型存在獨立的表中;
multiple table inheritance 父子類型都存在獨立的表中,查詢的時候進行連接;
顯然最後一種是冗余最少的,不過查詢的時候要做一次連接操作,如何選擇還是看具體情況了。
Mapping a Class against Arbitary Selects
將對象映射到任意的 select,其實也就是任意的 sql 子查詢。
這功能太強大了,有了這個後,我們就可以驕傲地宣稱,(幾乎)沒有什麼是 sqlalchemy 做不了的了!
Identity Map
session 在 sqlalchemy 中是一個非常重要的概念,session 跟蹤對象的修改情況,跟蹤對象之間的關聯,智能判斷數據庫操作執行的順序等等。
Identity Map 是 session 中一個容易讓人掉入陷阱的概念,你可以把它想象成一個以數據表主鍵為key的cache。每次從數據庫查詢後,如果 sqlalchemy 發現 Identity Map 中已經有了相同主鍵的實例,那就不會重新生成實例了。因為如果存在多個實例會帶來許多問題,比如多個實例分別修改並保存時就會產生混亂。
偶爾 Identity Map 也會產生一些意想不到的行為,比如 ticket 458 ,不過理解了 Identity Map 的機理後,也就沒什麼問題了。
值得一提的是,Mapper Options 有一個 always_refresh 參數,如果把它設為True,則對該 mapper 的任何查詢操作都會自動使用從數據庫中查詢到的數據覆蓋 Identity Map 中已有的實例,這樣要是對舊實例做過什麼還沒保存的修改的話,就都沒了。所以要慎用!
Cascade rules
最後這個也是很有用的功能,舉個例子來說吧,user 和 article 有一對多的關系,現在刪除一個 user,是否應該把相關的 article 也刪了呢,要 article 還有其他的依賴關系呢?這些決定當然是要根據實際的需求來,而控制這些行為的方法就是通過 relation 的 cascade 參數,具體取值及其含義看文檔去吧。
總滴來說,本文只是個提綱的作用,具體還得去看文檔,看示例,看unittest。
最後還想說兩句的就是,大家之所以選擇 ORM ,主要原因是逃離 SQL,然而我感覺不能掌握 SQL 是不能(很好)掌握sqlalchemy的。至少要對關系數據庫的這些概念了解,理解 SQL 就是理解關系數據庫。只有這樣才能利用sqlalchemy將關系數據庫發揮到極致!
使用 sqlalchemy 的好處就是不用寫 sql 了,屏蔽不同dbms之間SQL語法的區別,同時又讓你在需要的時候能夠利用到不同 DBMS 提供的一些獨特特性,讓你以對象的方式管理數據庫訪問代碼,提高代碼重用性!