如題,本文記錄如何使用python上下文管理器的方式管理sqlite3的句柄建立和釋放以及事務機制。python
一、python上下文管理(with)mysql
python上下文管理(context),解決的是這樣一類問題,在進入邏輯以前須要進行一些準備工做,在退出邏輯以前須要進行一些善後工做,上下文管理可使得這種場景變得清晰和可控。sql
with語句是python上下文管理的基本用法,例如讀寫文件數據庫
with open('filea', r) as f: f.readlines()
file使用的就是上下文管理機制,這樣對於打開文件句柄和釋放文件句柄無須咱們額外的投入精力。函數
二、sqlite3測試
sqlite3是一個嵌入式的文件數據庫,無須開啓額外的進程和端口,就能夠經過文件讀取的方式實現數據庫的操做。優勢是輕量級而且支持事務和觸發器等高級特性。fetch
sqlite3在python句柄建立和管理上跟mysql表現的很類似。spa
三、代碼code
咱們先貼上本文簡述的這段代碼,而後後面咱們在作詳細解釋。sqlite
# -*- coding:utf-8 -*- import sqlite3 import traceback class SqliteDB(object): def __init__(self, database='sqlitedb', isolation_level='', ignore_exc=False): self.database = database self.isolation_level = isolation_level self.ignore_exc = ignore_exc self.connection = None self.cursor = None def __enter__(self): try: self.connection = sqlite3.connect(database=self.database, isolation_level=self.isolation_level) self.cursor = self.connection.cursor() return self.cursor except Exception, ex: traceback.print_exc() raise ex def __exit__(self, exc_type, exc_val, exc_tb): try: if not exc_type is None: self.connection.rollback() return self.ignore_exc else: self.connection.commit() except Exception, ex: traceback.print_exc() raise ex finally: self.cursor.close() self.connection.close()
咱們給出一個使用的case
if __name__ == '__main__': # 建表 with SqliteDB('test') as db: db.execute('create table if not exists user (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100), age INTEGER)') # 建立一條記錄, 若是拋出異常, 能夠測試事務回滾 with SqliteDB('test') as db: db.execute('insert into user (name, age) values (?, ?)', ('Tom', 10)) #raise Exception() # 查詢記錄 with SqliteDB('test') as db: query_set = db.execute('select * from user where name=? limit ?', ('Tom', 100,)).fetchall() print len(query_set) for item in query_set: print item # 刪除記錄 with SqliteDB('test') as db: query_set = db.execute('delete from user where name=?', ('Tom',))
能夠看到經過with語句打開了數據庫的句柄,執行數據庫操做後,咱們並無管理句柄的釋放和事務回滾。
代碼的輸出是:
1 (6, u'Tom', 10)
當打開raise Exception()的註釋,表示在插入的過程當中遇到了異常。這時候全部connection中未被提交的數據將被回滾。
那麼,這些如何作到的呢?
上下文管理是經過類SqliteDB中的__enter__和__exit__兩個魔法函數實現的。
一、enter函數,用來實現處理進入with_body以前的準備工做,這裏是建立connect和cursor,enter方法返回了cursor。
enter函數若是有返回值,那麼能夠賦值給as後面的變量,若是沒有返回,能夠簡單的去掉as子句便可。咱們給出一個沒有as子句的例子
lock = threading.Lock() with lock: pass
若是enter函數拋出異常,那麼在執行with語句的時候會拋出這個異常,而且中斷程序。
二、邏輯上,enter函數以後,便開始執行with_body內的代碼,with_body裏的代碼包含sql語句和一些業務邏輯,這裏說明一下,只要是拋出異常就會觸發事務的回滾機制,而不會區分究竟是sql語句執行異常仍是業務邏輯出現的異常。
三、exit函數,在with_body執行成功或者拋出異常後會執行exit函數。
exit函數傳入三個變量,分別是exc_type異常類型,exc_val異常值,exc_tb錯誤堆棧信息。若是程序正常,那麼三個值都是None,相反若是不是None,那麼能夠就此判斷with_body產生了異常。
這裏,咱們判斷了exc_type是否爲None,來區分是否拋出了異常,若是拋出了異常咱們使用connection.rollback進行了事務的回滾,不然咱們使用connection.commit進行事務提交。
要注意的是,在出現異常的時候,返回了一個ignore_exc,這個返回若是是True,表示忽略這個異常,這個異常將不會向上級調用拋出,若是返回的是None或者False,異常將會向上拋出。實際中咱們仍是但願異常可以跑出來,方便處理,因此這裏咱們默認爲False。
注意:
isolation_level這個字段是隔離級別,這裏咱們不作深刻的說明。須要知道的是這個字段
1)傳入空字符串‘’,表示手動提交commit,這時須要程序中顯示的執行connection.commit進行事務提交,sql中的dml語句纔會生效。
2)傳入None,表示開啓自動提交,這時候自動提交commit,無需在程序中connection.commit進行事務提交。