注意:本文不会将所有完整源码贴出,只是将具体的思路以及部分源码贴出,需要感兴趣的读者自己实验然后实现吆。
缘起
公司最近的项目需要将之前的部分业务的数据库连接方式改为jdbc,但由于之前的项目都使用sqlarchemy作为orm框架,该框架似乎没有支持jdbc,为了能做最小的修改并满足需求,所以需要修改sqlarchemy的源码。
基本配置介绍
sqlalchemy 版本:1.1.15
使用jaydebeapi模块调用jdbc连接mysql
前提:
1 学会使用jaydebeapi模块,使用方法具体可以参考:
https://pypi.python.org/pypi/JayDeBeApi
介绍的比较详细的可以参考:http://shuaizki.github.io/language_related/2013/06/22/introduction-to-jpype.html
jaydebeapi是一个基于jpype的在Cpython中可以通过jdbc连接数据库的模块。该模块的python代码很少,基本上可以分为连接部分、游标部分、结果转换部分这三个。一般来说我们可能需要修改的就是结果转换部分,比如说sqlalchemy查询时如果某条记录中含TIME字段,那么该字段一般要表现为timedelta对象。而在jaydebeapi中则返回的是字符串对象,这样在sqlalchemy中会报错的。
sqlarchemy为我们实现了ORM对象与语句的转换,连接池,session(包括对线程的支持scope_session)等较为上层的逻辑,但这些东西在这里我们不需要考虑(当然创建一个连接,生成curcor还是要考虑的),我们要考虑的仅仅是当sqlarchemy把sql语句以及参数传过来的时候我们该怎么做,以及当sql语句执行后如何对结果进行转换。
所需注意的问题
1 sql语句以及参数传过来的时候我们该怎么做:
1.1 对参数进行转义,防止sql注入
2 执行完sql语句后对结果如何处理:
2.1 我们知道python的基础sql模块会对结果进行处理,比如说把NUll转换为None,把数据库中的date字段转换为python的date对象等等
2.2 一些不知道该怎么形容的数据:
当我们查询时,获取的数据对应字段的元信息
当我们update或者delete等操作时需要获取影响了多少行
当我们插入数据后,如果主键是自增字段,我们一般(可以说在sqlarchemy中这是必须)需要获取该记录的主键值
实际上就是支持 python DB API
3 sqlalchemy增加代码,使其支持我们修改后的jaydebeapi
如何解决
1.1解决方案:
人家pymysql咋搞,我就咋搞!
在pymysql.corsors文件中Cursor类中有一个叫做mogrify的方法,这个方法不仅对参数转义,而且会将参数放置到sql语句中组成完整的可执行sql语句。所以偷一些代码然后稍加修改就是这样:
#!/usr/bin/env python # -*- coding: utf-8 -*- from functools import partial from pymysql.converters import escape_item, escape_string import sys PY2 = sys.version_info[0] == 2 if PY2: import __builtin__ range_type = xrange text_type = unicode long_type = long str_type = basestring unichr = __builtin__.unichr else: range_type = range text_type = str long_type = int str_type = str unichr = chr def _ensure_bytes(x, encoding="utf8"): if isinstance(x, text_type): x = x.encode(encoding) return x def _escape_args(args, encoding): ensure_bytes = partial(_ensure_bytes, encoding=encoding) if isinstance(args, (tuple, list)): if PY2: args = tuple(map(ensure_bytes, args)) return tuple(escape(arg, encoding) for arg in args) elif isinstance(args, dict): if PY2: args = dict((ensure_bytes(key), ensure_bytes(val)) for (key, val) in args.items()) return dict((key, escape(val, encoding)) for (key, val) in args.items()) def escape(obj, charset, mapping=None): if isinstance(obj, str_type): return "'" + escape_string(obj) + "'" return escape_item(obj, charset, mapping=mapping) def mogrify(query, encoding, args=None): if PY2: # Use bytes on Python 2 always query = _ensure_bytes(query, encoding=encoding) if args is not None: # r = _escape_args(args, encoding) query = query % _escape_args(args, encoding) return query # 调用一下mogrigy函数 # print(mogrify("select * from ll where a in %s and b = %s", "utf8", [[2, 1], 3]))