注意:本文不会将所有完整源码贴出,只是将具体的思路以及部分源码贴出,需要感兴趣的读者自己实验然后实现吆。 

缘起

  公司最近的项目需要将之前的部分业务的数据库连接方式改为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]))
View Code

相关文章: