1 PyMySQL及数据库连接池
PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,由于频繁连接数据库很耗时,因此将 PyMySQL 对数据库的一系列操作封装到一个类中,实现连接一次数据库就可以完成多次操作,以提高性能。
2 普通的数据库连接
import pymysql
class SQLHelper(object):
"""
PyMySQL操作数据库
"""
def __init__(self):
"""
在实例化对象时自动连接数据库
"""
# 也可以根据需要将连接数据库的参数放到配置文件中,然后在初始化时读取
self.connect()
def open(self):
"""
创建数据库连接对象和游标对象
"""
self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password="xxx", database='xxx', charset='utf8')
self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor) # 游标设置为字典类型
def get_list(self, sql, args=None):
"""
获取列表数据
"""
self.cur.execute(sql, args)
data_list = self.cur.fetchall()
return data_list
def get_one(self, sql, args=None):
"""
获取单条数据
"""
self.cur.execute(sql, args)
data = self.cur.fetchone()
return data
def modify(self, sql, args=None):
"""
更新、删除单条数据
"""
self.cur.execute(sql, args)
self.conn.commit()
def bulk_modify(self, sql, args=None):
"""
批量增加、更新、删除数据(好处:只连接一次,批量操作,只提交一次)
"""
self.cur.executemany(sql, args)
self.conn.commit()
def create(self, sql, args=None):
"""
增加单条数据,并返回最新自增ID
"""
self.cur.execute(sql, args)
self.conn.commit()
return self.cur.lastrowid
def close(self):
"""
关闭连接
"""
self.cur.close()
self.conn.close()import pymysql
class SQLHelper(object):
"""
PyMySQL操作数据库
"""
def __init__(self):
"""
在实例化对象时自动连接数据库
"""
# 也可以根据需要将连接数据库的参数放到配置文件中,然后在初始化时读取
self.connect()
def open(self):
"""
创建数据库连接对象和游标对象
"""
self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password="xxx", database='xxx', charset='utf8')
self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor) # 游标设置为字典类型
def get_list(self, sql, args=None):
"""
获取列表数据
"""
self.cur.execute(sql, args)
data_list = self.cur.fetchall()
return data_list
def get_one(self, sql, args=None):
"""
获取单条数据
"""
self.cur.execute(sql, args)
data = self.cur.fetchone()
return data
def modify(self, sql, args=None):
"""
更新、删除单条数据
"""
self.cur.execute(sql, args)
self.conn.commit()
def bulk_modify(self, sql, args=None):
"""
批量增加、更新、删除数据(好处:只连接一次,批量操作,只提交一次)
"""
self.cur.executemany(sql, args)
self.conn.commit()
def create(self, sql, args=None):
"""
增加单条数据,并返回最新自增ID
"""
self.cur.execute(sql, args)
self.conn.commit()
return self.cur.lastrowid
def close(self):
"""
关闭连接
"""
self.cur.close()
self.conn.close()
3 数据库连接池
数据库连接池帮我们维护了若干个与数据库的连接,当我们需要连接数据库时,只需向连接池申请一个连接,当操作完数据库后,再将连接放回到连接池。在实际项目中,如果不用 ORM 要写原生 SQL 操作数据库,建议用数据库连接池,可以处理并发请求的场景。在 Python 中,可以通过 DBUtils 模块实现一个数据库连接池。此连接池有两种连接模式:
- 模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭。
- 模式二:创建一批连接到连接池,供所有线程共享使用。
4 DBUtils 的安装
在虚拟环境下安装:
pip install pymysql DBUtils
5 基于函数封装
from dbutils.pooled_db import PooledDB
import pymysql
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
ping=0, # ping MySQL服务端,检查是否服务可用。如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='xxx',
database='xxx',
charset='utf8'
)
def get_list(sql, args=None):
"""
获取所有数据
:param sql: SQL语句
:param args: SQL语句的占位参数
:return: 查询结果
"""
conn = POOL.connection() # 去连接池中获取一个连接
cur = conn.cursor()
cur.execute(sql, args)
result = cur.fetchall()
cur.close()
conn.close() # 将连接放回到连接池,并不会关闭连接,当线程终止时,连接自动关闭
return result
def get_one(sql, args=None):
"""
获取单条数据
:return: 查询结果
"""
conn = POOL.connection()
cur = conn.cursor()
cur.execute(sql, args)
result = cur.fetchone()
cur.close()
conn.close()
return result
def modify(sql, args=None):
"""
修改、增加、删除操作
:return: 受影响的行数
"""
conn = POOL.connection()
cur = conn.cursor()
result = cur.execute(sql, args)
conn.commit()
cur.close()
conn.close()
return result
def bulk_modify(sql, args=None):
"""
批量修改、增加、删除操作
:return: 受影响的行数
"""
conn = POOL.connection()
cur = conn.cursor()
result = cur.executemany(sql, args)
conn.commit()
cur.close()
conn.close()
return result
def create(sql, args=None):
"""
增加数据
:return: 新增数据行的ID
"""
conn = POOL.connection()
cur = conn.cursor()
cur.execute(sql, args)
conn.commit()
cur.close()
conn.close()
return cur.lastrowid
if __name__ == '__main__':
result = get_list("select * from student")
# result = get_one("select * from class where id=%s and title=%s", ['10', '五班'])
# result = modify("update class set title=%s where id=%s", ['五班', '10'])
# result = bulk_modify("update class set title=%s where id=%s", [('五班1', '10'), ('五班2', '11')])
# result = create("insert into student (name, class_id) values (%s, %s)", ['张三', '10'])
# result = bulk_modify("insert into student (name, class_id) values (%s, %s)", [('李四', '10'), ('王五', '10')])
# result = modify("delete from student where id=%s", ['20', ])
print(result)
6 基于类封装
from dbutils.pooled_db import PooledDB
import pymysql
class SQLHelper(object):
def __init__(self):
self.pool = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
ping=0, # ping MySQL服务端,检查是否服务可用。如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='xxx',
database='xxx',
charset='utf8'
)
def open(self):
conn = self.pool.connection() # 去连接池中获取一个连接
cur = conn.cursor()
return conn, cur
def close(self, conn, cur):
cur.close()
conn.close() # 将连接放回到连接池,并不会关闭连接,当线程终止时,连接自动关闭
def get_list(self, sql, args=None):
"""
获取所有数据
:param sql: SQL语句
:param args: SQL语句的占位参数
:return: 查询结果
"""
conn, cur = self.open()
cur.execute(sql, args)
result = cur.fetchall()
self.close(conn, cur)
return result
def get_one(self, sql, args=None):
"""
获取单条数据
:return: 查询结果
"""
conn, cur = self.open()
cur.execute(sql, args)
result = cur.fetchone()
self.close(conn, cur)
return result
def modify(self, sql, args=None):
"""
修改、增加、删除操作
:return: 受影响的行数
"""
conn, cur = self.open()
result = cur.execute(sql, args)
conn.commit()
self.close(conn, cur)
return result
def bulk_modify(self, sql, args=None):
"""
批量修改、增加、删除操作
:return: 受影响的行数
"""
conn, cur = self.open()
result = cur.executemany(sql, args)
conn.commit()
self.close(conn, cur)
return result
def create(self, sql, args=None):
"""
增加数据
:return: 新增数据行的ID
"""
conn, cur = self.open()
cur.execute(sql, args)
conn.commit()
self.close(conn, cur)
return cur.lastrowid
if __name__ == '__main__':
db = SQLHelper()
result = db.get_list("select * from student")
# result = db.get_one("select * from class where id=%s and title=%s", ['10', '五班'])
# result = db.modify("update class set title=%s where id=%s", ['五班', '10'])
# result = db.bulk_modify("update class set title=%s where id=%s", [('五班1', '10'), ('五班2', '11')])
# result = db.create("insert into student (name, class_id) values (%s, %s)", ['张三', '10'])
# result = db.bulk_modify("insert into student (name, class_id) values (%s, %s)", [('李四', '10'), ('王五', '10')])
# result = db.modify("delete from student where id=%s", ['20', ])
print(result)
7 支持上下文管理
使用 threading.local 实现
import threading
from dbutils.pooled_db import PooledDB
import pymysql
class SQLHelper(object):
"""
使用 threading.local 实现上下文管理,且是单例模式
"""
def __init__(self):
self.pool = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
ping=0, # ping MySQL服务端,检查是否服务可用。如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='xxx',
database='xxx',
charset='utf8'
)
self.local = threading.local() # 维护一个栈
"""
storage = {
线程ID: {'stack': [(conn, cusor), ]},
}
"""
def open(self):
conn = self.pool.connection() # 去连接池中获取一个连接
cur = conn.cursor()
return conn, cur
def close(self, conn, cur):
cur.close()
conn.close() # 将连接放回到连接池,并不会关闭连接,当线程终止时,连接自动关闭
def get_list(self, sql, args=None):
"""
获取所有数据
:param sql: SQL语句
:param args: SQL语句的占位参数
:return: 查询结果
"""
conn, cur = self.open()
cur.execute(sql, args)
result = cur.fetchall()
self.close(conn, cur)
return result
def get_one(self, sql, args=None):
"""
获取单条数据
:return: 查询结果
"""
conn, cur = self.open()
cur.execute(sql, args)
result = cur.fetchone()
self.close(conn, cur)
return result
def modify(self, sql, args=None):
"""
修改、增加、删除操作
:return: 受影响的行数
"""
conn, cur = self.open()
result = cur.execute(sql, args)
conn.commit()
self.close(conn, cur)
return result
def bulk_modify(self, sql, args=None):
"""
批量修改、增加、删除操作
:return: 受影响的行数
"""
conn, cur = self.open()
result = cur.executemany(sql, args)
conn.commit()
self.close(conn, cur)
return result
def create(self, sql, args=None):
"""
增加数据
:return: 新增数据行的ID
"""
conn, cur = self.open()
cur.execute(sql, args)
conn.commit()
self.close(conn, cur)
return cur.lastrowid
def __enter__(self):
conn, cur = self.open()
rv = getattr(self.local, 'stack', None)
if not rv:
self.local.stack = [(conn, cur), ]
else:
self.local.stack.append((conn, cur))
return conn, cur
def __exit__(self, exc_type, exc_val, exc_tb):
rv = getattr(self.local, 'stack', None)
if not rv:
return
conn, cur = self.local.stack.pop()
cur.close()
conn.close()
if __name__ == '__main__':
db = SQLHelper()
with db as (conn, cur):
cur.execute("insert into student (name, class_id) values (%s, %s)", ['赵六', '10'])
conn.commit()
with db as (conn, cur):
cur.execute("select * from student")
# result = cur.fetchmany(3) # 取前3条
result = cur.fetchall()
print(result)
8 普通实现方式
from dbutils.pooled_db import PooledDB
import pymysql
# 全局变量定义连接池,只加载一次
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
ping=0, # ping MySQL服务端,检查是否服务可用。如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='xxx',
database='xxx',
charset='utf8'
)
class SQLHelper(object):
"""
支持上下文管理,非单例模式
"""
def __init__(self):
self.conn = None
self.cur = None
def open(self):
conn = POOL.connection() # 去连接池中获取一个连接
cur = conn.cursor()
return conn, cur
def close(self, conn, cur):
cur.close()
conn.close() # 将连接放回到连接池,并不会关闭连接,当线程终止时,连接自动关闭
def get_list(self, sql, args=None):
"""
获取所有数据
:param sql: SQL语句
:param args: SQL语句的占位参数
:return: 查询结果
"""
conn, cur = self.open()
cur.execute(sql, args)
result = cur.fetchall()
self.close(conn, cur)
return result
def get_one(self, sql, args=None):
"""
获取单条数据
:return: 查询结果
"""
conn, cur = self.open()
cur.execute(sql, args)
result = cur.fetchone()
self.close(conn, cur)
return result
def modify(self, sql, args=None):
"""
修改、增加、删除操作
:return: 受影响的行数
"""
conn, cur = self.open()
result = cur.execute(sql, args)
conn.commit()
self.close(conn, cur)
return result
def bulk_modify(self, sql, args=None):
"""
批量修改、增加、删除操作
:return: 受影响的行数
"""
conn, cur = self.open()
result = cur.executemany(sql, args)
conn.commit()
self.close(conn, cur)
return result
def create(self, sql, args=None):
"""
增加数据
:return: 新增数据行的ID
"""
conn, cur = self.open()
cur.execute(sql, args)
conn.commit()
self.close(conn, cur)
return cur.lastrowid
def __enter__(self):
self.conn, self.cur = self.open()
return self.conn, self.cur
def __exit__(self, exc_type, exc_val, exc_tb):
self.cur.close()
self.conn.close()
if __name__ == '__main__':
with SQLHelper() as (conn, cur):
cur.execute("insert into student (name, class_id) values (%s, %s)", ['孙七', '10'])
conn.commit()
with SQLHelper() as (conn, cur):
cur.execute("select * from student")
# result = cur.fetchmany(3) # 取前3条
result = cur.fetchall()
print(result)