如题,本文记录如何使用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进行事务提交。