即上次 一个websocket框架是怎么诞生的 后,我又来了。
事情是这样的
因为我们的业务需要用python来实现一些定时脚本、数据爬虫之类的工具,这些工具也经常使用到SQL操作。
在python里调用数据库肯定优先会想到 cx_Oracle
和 pymysql
这样现成的工具库啦!
(我这次也是在这两个的工具库的基础上二次封装开发的)
但是对于用习惯thinkphp、laravel这种框架的我来说,pymysql和cx_Oracle这种操作数据库的方式多少有点不太习惯
ini
import pymysql
# 打开数据库连接
db = pymysql.connect(host='localhost', user='testuser', password='test123', database='TESTDB')
# 使用cursor()方法获取操作游标
cursor = db.cursor()
# SQL 插入语句
sql = """INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME) VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except:
# 如果发生错误则回滚
db.rollback()
# 关闭数据库连接
scss
import pymysql
# 打开数据库连接
db = pymysql.connect(host='localhost', user='testuser', password='test123', database='TESTDB')
# 使用 cursor() 方法创建一个游标对象
cursor cursor = db.cursor()
# 使用 execute() 方法执行 SQL 查询
cursor.execute("SELECT VERSION()")
# 使用 fetchone() 方法获取单条数据.
data = cursor.fetchone()
print ("Database version : %s " % data)
# 关闭数据库连接
db.close()
那么,开始改造它吧
我可是用习惯了世界上最好的编程语言的开发仔😄
在php或者go的编码过程中我早已经习惯那种链式操作,所以我在想python中是不是也可以继续沿用这种操作习惯或者类似这种风格也可以,比如:
scss
// php
$data = Model::where()->fields()->order()->select();
// go
data := XXXX.Where().Select().First()
于是乎,它来了
第一步,先确定一下调用方式
python
'''
Author: psq
Date: 2023-09-12 10:38:46
LastEditors: psq
LastEditTime: 2023-09-12 11:21:50
'''
from utils.databases.mysql import mysql
from utils.databases.oracle import oracle
if __name__ == '__main__':
db = mysql.construct(env = 'mysql')
'''
description: 查询数据
return {*}
'''
select = db.select(
table = 'test', # 查询表名
alias = 'u', # 设置别名
where = {'u.xxx': 'xxx', 'u.xxx': 'xxx'}, # 查询条件
fields = ['u.xxx', 'a.xxx', 't.xxx'], # 限制输出字段
orderby = 'u.xxx asc', # 排序方式
limit = [5, 1], # 每页显示数量, 页码
join = [
{
'connect': 'left', # 此项入不传则默认使用left连接
'alias': 'a',
'table': 'account',
'on': 'u.xxx = a.xxx'
},
{
'connect': 'left', # 此项入不传则默认使用left连接
'alias': 't',
'table': 'account_tree',
'on': 'u.xxxx = t.xxx'
}
],
)
print(select) # 返回list对象
'''
description: 修改数据
return {*}
'''
update = db.update(
table = 'test',
values = {'test_name': 2},
where = {'test_id': 1}
)
print(update) # 返回布尔对象
'''
description: 插入数据
return {*}
'''
insert = db.insert(
table = 'test',
values = {'test_id': 21, 'test_name': 1, 'test_value': 1, 'test_age': 1}
)
print(insert) # 返回布尔对象
'''
description: 一次插入多条数据
return {*}
'''
insertall = db.insertall(
table = 'test',
values = [
{'test_id': 22, 'test_name': 1, 'test_value': 1, 'test_age': 1},
{'test_id': 23, 'test_name': 1, 'test_value': 1, 'test_age': 1}
]
)
print(insertall) # 返回布尔对象
'''
description: 自定义读语句
return {*}
'''
query = db.query(
sql = 'select test_id from test'
)
print(query) # 返回读语句
'''
description: 自定义写语句
return {*}
'''
execute = db.execute(
sql = 'insert into test (test_id) values (23)'
)
print(execute) # 返回布尔对象
'''
description: 跨库事务
return {*}
'''
XAtransaction = db.XAtransaction(
env = ['sit', 'sit2'],
sql = {
'sit': [
'insert into test_table_1 (tb_a) values ("1");',
'insert into test_table_1 (tb_a) values ("2");'
],
'sit2': [
'insert into test_table_2 (tb_b) values ("3");',
'insert into test_table_2 (tb_v) values ("4");',
'insert into test_table_2 (tb_v) values ("5");'
]
}
)
print(XAtransaction) # 返回布尔对象
# 目前仅支持mysql !!!
# 参与事务的数据表必须为innodb存储引擎 !!!
# env内所有参与事务的示例必须有配套的写入语句,否则会导致事务整体回滚 !!!
# 如果两个库在同一个连接实例时会导致调用报错 !!!
看上去就舒服多了😄!
那怎么连接数据库、怎么将参数转化成SQL语句呢、怎么打日志、怎么处理异常,这就开始
python
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
import uuid
import json
sys.path.append('./')
from utils.databases.mysql.connect import pyconnection
from utils.databases.mysql.build import queryconstruction
from utils.databases.mysql.build import executeconstruction
class construct(object):
def __init__(self, env:str):
with open("./config/db.json", "r") as f:
f = json.load(f)[env]
self.db = pyconnection.Connect(env = f)
'''
description: 数据查询
param {*} self
param {object} parameter
return {*}
'''
def select(self, **parameter:any) -> bool:
return self.db.querySQL(queryconstruction.Construction(parameter).sql)
'''
description: 数据写入
param {*} self
param {object} parameter
return {*}
'''
def insert(self, **parameter:any) -> bool:
return self.db.executeSQL(
sql = executeconstruction.Construction(parameter).sql,
autocommit = parameter['autocommit'] if parameter.__contains__('autocommit') else True
)
'''
description: 批量数据写入
param {*} self
param {object} parameter
return {*}
'''
def insertall(self, **parameter:any) -> bool:
return self.db.executeSQL(
sql = executeconstruction.Construction(parameter).sql,
autocommit = parameter['autocommit'] if parameter.__contains__('autocommit') else True
)
'''
description: 数据修改
param {*} self
param {object} parameter
return {*}
'''
def update(self, **parameter:any) -> bool:
return self.db.executeSQL(
sql = executeconstruction.Construction(parameter).sql,
autocommit = parameter['autocommit'] if parameter.__contains__('autocommit') else True
)
'''
description: 数据删除
param {*} self
param {object} parameter
return {*}
'''
def delete(self, **parameter:any) -> bool:
return self.db.executeSQL(
sql = executeconstruction.Construction(parameter).sql,
autocommit = parameter['autocommit'] if parameter.__contains__('autocommit') else True
)
'''
description: 自定义读语句
param {*} self
param {object} parameter
return {*}
'''
def query(self, **parameter:any) -> bool:
return self.db.querySQL(parameter['sql'])
'''
description: 自定义写语句
param {*} self
param {object} parameter
return {*}
'''
def execute(self, **parameter:any) -> bool:
return self.db.executeSQL(
sql = parameter['sql'],
autocommit = parameter['autocommit'] if parameter.__contains__('autocommit') else True
)
'''
description: 分布式跨库事务
param {*} self
param {object} parameter
return {*}
'''
def XAtransaction(self, **parameter:any) -> bool:
transactionID, sqlResult = uuid.uuid1(), []
for x in range(len(parameter['env'])):
self.__init__(parameter['env'][x])
self.query(sql = f"xa start '{transactionID}';")
for s in range(len(parameter['sql'][parameter['env'][x]])):
sqlResult.append(self.execute(
sql = parameter['sql'][parameter['env'][x]][s],
autocommit = False
))
self.query(sql = f"xa end '{transactionID}';")
self.query(sql = f"xa prepare '{transactionID}';")
for x in range(len(parameter['env'])):
self.__init__(parameter['env'][x])
if False in sqlResult:
self.query(sql = f"xa rollback '{transactionID}';")
else:
self.query(sql = f"xa commit '{transactionID}';")
return False if False in sqlResult else True
这里第一步当然是对语句进行分流啦
python
'''
Author: psq
Date: 2021-04-28 16:21:01
LastEditors: psq
LastEditTime: 2023-09-12 10:52:59
'''
#!/usr/bin/python
# -*- coding: UTF-8 -*-
#
from ast import Try
import sys
import time
import pymysql
sys.path.append('./')
class Connect():
def __init__(self, env:any):
self.starttime = int(round(time.time() * 1000))
try:
self.db = pymysql.connect(
port = int(env['port']),
host = str(env['host']),
user = str(env['user']),
charset = str(env['charset']),
password = str(env['password']),
db = str(env['db']),
)
except Exception as e:
raise RuntimeError('connectErr:', e)
self.cursor = self.db.cursor(cursor = pymysql.cursors.DictCursor)
def querySQL(self, sql:str) -> tuple:
try:
self.cursor.execute(self.__sql(sql))
self.result = True
except Exception as e:
self.result = False
raise RuntimeError('executeErr:', e)
# 操作日志,可以根据自己的方式保存下来
log = {
"dbType": "mysql",
"startTime": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.starttime / 1000)),
"endTime": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(time.time()))),
"runResult": self.result,
"runTime": (int(round(time.time() * 1000)) - self.starttime) / 1000,
"sql": self.sql
}
return self.cursor.fetchall()
def executeSQL(self, sql:str, autocommit:bool) -> bool:
try:
self.cursor.execute(self.__sql(sql))
if autocommit: self.db.commit()
self.result = True
except Exception as e:
if autocommit: self.db.rollback()
self.result = False
# 操作日志,可以根据自己的方式保存下来
log = {
"logType": "DBLogs",
"dbType": "mysql",
"startTime": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.starttime / 1000)),
"endTime": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(time.time()))),
"runResult": self.result,
"runTime": (int(round(time.time() * 1000)) - self.starttime) / 1000,
"sql": self.sql
}
return self.result
def __sql(self, sql):
self.sql = sql
return self.sql
def __del__(self):
try:
self.db.close
except Exception as e:
...