在一个Web App中,所有的数据,包括用户的信息,日志,评论等,都存在数据库中。在piblog中使用MySQL作为数据库。
Web App中由很多地方需要使用数据库。访问数据库需要创建数据库连接、游标对象,然后执行SQL操作,最后处理异常,清理资源。这些访问数据库的代码如果分散在不同的函数中,势必无法维护,也不利于代码复用。
此外,对于一个Web App,会有多个用户同时访问,系统以多线程或多进程的方式来处理每个用户的请求。以多线程为例,每个线程在访问数据库时,都必须创建属于自己的连接,对别的线程不可见,否则,就会造成数据库操作混乱。
所以,我们还要创建一个简单的数据库访问模型,在一个线程中,能既安全又简单地操作数据库。
不选择SQLAlchemy的原因:SQLAlchemy太庞大,API太庞大。(我现在还不会)
于是决定设计一个封装了基本的SELECT、INSERT、UPDATE和DELETE操作的db模块:basic.db
设计db接口
设计底层模块的原则时,根据上层调用者设计简单易用的API接口,然后,实现模块内部代码。
假设basic.db模块已经编写完毕,我们希望通过如下的方式来调用它:
首先,初始化数据库连接信息,通过create_engine()函数:

from basic import db
db.create_engine(user='root', password='password', database='test', host='127.0.0.1', port=3306)

然后,就可以直接操作SQL了。
如果需要做一个查询,可以直接调用select()方法,返回的是list,每一个元素是用dict表示的对应的行:

users = db.select('select * from user')
users =>
[
{ "id": 1, "name": "John" },
{ "id": 2, "name": "Alex" },
{ "id": 3, "name": "Lenovo" }
]

如果要执行INSERT、UPDATE或DELETE操作,直接执行update()方法,返回受影响的行数:

n = db.update('insert into user(id, name) values(?, ?)', 4, 'Alex')

update()函数签名为:

update(sql, *args)

统一用?作为占位符,并传入可变参数来绑定,从根本上避免SQL注入攻击。
每个select()或update()调用,都隐含地自动打开并关闭了数据库连接,这样,上层调用者就完全不必关心数据库底层连接。
但是,如果要在一个数据库中执行多个SQL语句怎么办?我们用一个with语句实现:

with db.connection():
db.select('...')
db.update('...')
db.update('...')

实现db模块
由于模块是全局对象,模块变量是全剧唯一变量,所以,有两个重要的模块变量:

# db.py

# 数据库引擎对象:
class _Engine(object):
def __init__(self, connect):
self._connect = connect
def connect(self):
return self._connect() engine = None # 持有数据库连接的上下文对象:
class _DbCtx(threading.local):
def __init__(self):
self.connection = None
self.transactions = 0
def is_init(self):
return not self.connection is None
def init(self):
self.connection = _LazyConnection()
self.transactions = 0
def cleanup(self):
self.connection.cleanup()
self.connection = None
def cursor(self):
return self.connection.cursor() _db_ctx = _DbCtx()

由于_db_ctx是threadlocal对象,所以它持有的数据库连接对每个线程来说都是不一样的。任何一个线程都无法访问到其它线程持有的数据库连接。
有了这两个全局变量,我们继续实现数据库连接的上下文,目的是自动获取和释放连接:

class _ConnectionCtx(object):
def __enter__(self):
global _db_ctx
self.should_cleanup = False
if not _db_ctx.is_init():
_db_ctx.init()
self.should_cleanup = True
return self
def __exit__(self, exctype, excvalue, trackback):
global _db_ctx
if self.should_cleanup:
_db_ctx.cleanup() def connection():
return _ConnectionCtx()

最新文章

  1. OpenCV笔记大集锦(转载)
  2. 用正则从html代码中提取图片路径
  3. 修改linux系统时间的方法(date命令)
  4. maven 的一些基本操作
  5. 递归神经网络之理解长短期记忆网络(LSTM NetWorks)(转载)
  6. SCTP 关联的建立和终止
  7. Pycharm在创建py文件时,如何自动添加文件头注释(类似于钩子特性)?
  8. 查询在应用程序运行得很慢, 但在SSMS运行得很快的原因探究
  9. Android中的广播
  10. TCP/IP、Http、Socket的区别与关系
  11. ROM型启动方式概述
  12. 实战ELK(8) 安装ElasticSearch中文分词器
  13. PEM证书加密方法(python)
  14. turtle库的学习笔记
  15. ThreadPoolExecutor代码解析
  16. JVM学习(3)——总结Java内存模型---转载自http://www.cnblogs.com/kubixuesheng/p/5202556.html
  17. AtCoder Regular Contest 077 D - 11
  18. C++类型转换的注意事项
  19. exception java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11GraphicsEnvironment
  20. Eclipse如何安装插件,查看已经安装的插件

热门文章

  1. 自定义maven变量以及maven内置常量
  2. 跨服务器sql操作
  3. linux挂载根文件系统过程
  4. Window 2008 R2组策略之一——组策略管理控制台
  5. 【Navicat_Premium_11.0.10】破解版
  6. java-jdbc循环设置sql参数
  7. archdexls主题设置每页显示游戏数目
  8. mysql数据库对时间进行默认的设置
  9. 【面试】hibernate n+1问题
  10. 【转】C#获取电脑客户端IP地址及当前用户名