【Python + Oracle】Python Oracle连接池—改进版

Oracle 连接池第四版。

编程语言 Python
语言版本 3.5.3
实现功能 oracle 连接池,解决频繁连接oracle数据库带来的连接资源耗费问题
程序托管 GitHub-OraclePool
修改日期 2020年8月3日

 

version 4.0 改进说明:

**[2020-04-21]**

+ 修复连接用完后,不放回连接池的`bug`
+ 修复使用类名称获取`pool`时的类名称错误(`OrclConnPool`),改为用`self`
+ 改进了对`config`连接信息的判断逻辑,使得`config`的配置更加简化
+ 改进了新的注释,使用更加清晰
+ 改进了部分代码的格式,使得其更规范
+ 新增`fetch_all`和`fetch_one`
+ 优化了`demo`内容和格式,使得其更规范

程序源码:

# -*- coding: utf-8 -*-

"""
--------------------------------------
@File       : oracle_pool.py
@Author     : maixiaochai
@Created on : 2020/4/21 15:47
--------------------------------------
"""

import cx_Oracle as Oracle
from DBUtils.PooledDB import PooledDB


class OraclePool:
    """
    1) 这里封装了一些有关oracle连接池的功能;
    2) sid和service_name,程序会自动判断哪个有值,
        若两个都有值,则默认使用service_name;
    3) 关于config的设置,注意只有 port 的值的类型是 int,以下是config样例:
        config = {
            'user':         'maixiaochai',
            'password':     'maixiaochai',
            'host':         '192.168.158.1',
            'port':         1521,
            'sid':          'maixiaochai',
            'service_name': 'maixiaochai'
        }
    """

    def __init__(self, config):
        """
        获得连接池
        :param config:      dict    Oracle连接信息
        """
        self.__pool = self.__get_pool(config)

    @staticmethod
    def __get_pool(config):
        """
        :param config:        dict    连接Oracle的信息
        ---------------------------------------------
        以下设置,根据需要进行配置
        maxconnections=6,   # 最大连接数,0或None表示不限制连接数
        mincached=2,        # 初始化时,连接池中至少创建的空闲连接。0表示不创建
        maxcached=5,        # 连接池中最多允许的空闲连接数,很久没有用户访问,连接池释放了一个,由6个变为5个,
                            # 又过了很久,不再释放,因为该项设置的数量为5
        maxshared=0,        # 在多个线程中,最多共享的连接数,Python中无用,会最终设置为0
        blocking=True,      # 没有闲置连接的时候是否等待, True,等待,阻塞住;False,不等待,抛出异常。
        maxusage=None,      # 一个连接最多被使用的次数,None表示无限制
        setession=[],       # 会话之前所执行的命令, 如["set charset ...", "set datestyle ..."]
        ping=0,             # 0  永远不ping
                            # 1,默认值,用到连接时先ping一下服务器
                            # 2, 当cursor被创建时ping
                            # 4, 当SQL语句被执行时ping
                            # 7, 总是先ping
        """
        dsn = None
        host, port = config.get('host'), config.get('port')

        if 'service_name' in config:
            dsn = Oracle.makedsn(host, port, service_name=config.get('service_name'))

        elif 'sid' in config:
            dsn = Oracle.makedsn(host, port, sid=config.get('sid'))

        pool = PooledDB(
            Oracle,
            mincached=5,
            maxcached=10,
            user=config.get('user'),
            password=config.get('password'),
            dsn=dsn
        )

        return pool

    def __get_conn(self):
        """
        从连接池中获取一个连接,并获取游标。
        :return: conn, cursor
        """
        conn = self.__pool.connection()
        cursor = conn.cursor()

        return conn, cursor

    @staticmethod
    def __reset_conn(conn, cursor):
        """
        把连接放回连接池。
        :return: 
        """
        cursor.close()
        conn.close()

    def __execute(self, sql, args=None):
        """
        执行sql语句
        :param sql:     str     sql语句
        :param args:    list    sql语句参数列表
        :param return:  cursor
        """
        conn, cursor = self.__get_conn()
        
        if args:
            cursor.execute(sql, args)
        else:
            cursor.execute(sql)

        return conn, cursor

    def fetch_all(self, sql, args=None):
        """
        获取全部结果
        :param sql:     str     sql语句
        :param args:    list    sql语句参数
        :return:        tuple   fetch结果
        """
        conn, cursor = self.__execute(sql, args)
        result = cursor.fetchall()
        self.__reset_conn(conn, cursor)

        return result

    def fetch_one(self, sql, args=None):
        """
        获取全部结果
        :param sql:     str     sql语句
        :param args:    list    sql语句参数
        :return:        tuple   fetch结果
        """
        conn, cursor = self.__execute(sql, args)
        result = cursor.fetchone()
        self.__reset_conn(conn, cursor)

        return result

    def execute_sql(self, sql, args=None):
        """
        执行SQL语句。
        :param sql:     str     sql语句
        :param args:    list    sql语句参数
        :return:        tuple   fetch结果
        """
        conn, cursor = self.__execute(sql, args)
        conn.commit()
        self.__reset_conn(conn, cursor)

    def __del__(self):
        """
        关闭连接池。
        """
        self.__pool.close()


def demo():
    config = {
        'user': 'maixiaochai',
        'password': 'maixiaochai',
        'host': '192.168.158.1',
        'port': 1521,
        'sid': 'maixiaochai',
        'service_name': 'maixiaochai'
    }

    sql = "SELECT COUNT(*) FROM MAIXIAOCHAI"

    orcl = OraclePool(config)
    result = orcl.fetch_all(sql)
    print(result)


if __name__ == "__main__":
    demo()

version 3.0 改进说明:

*)修复一个 Bug,该 Bug导致连接池中有且仅有一个连接资源被使用,其余空闲。
*) 2019-12-27 12:45:21 by MaiXiaochai
version 2.0 改进说明:
*)将原来的 orcl_pool.py 文件单独拿出来,创建新库 OracleConnectionPool,便于编辑和分享;
*)更新"程序链接"内容和 URL。
*)2019-5-29 16:54:37 by MaiXiaochai
*)由原来的只能维持一个连接池改为可维持多个连接池;
*)加入了对 service_name 连接方式的支持;
*)添加了析构函数,对连接池资源进行回收;
*)对相关的代码注释进行了规范,使其看起来比较整洁;
*)添加了一个使用样例。
*)2019-3-28 19:13:47 by MaiXiaochai

程序源码: 

version 3.0

# -*- coding: utf-8 -*-

# @File:     oracle_conn_pool.py
# @Project:  OraclePool
# @Date:     2019/5/29 15:19
# @Author:   MaiXiaochai
# @Modify:   2020/1/6 16:02

import cx_Oracle as Oracle
from DBUtils.PooledDB import PooledDB


class OraclePool(object):
    """
    1) 这里封装了一些有关oracle连接池的功能;
    2) sid和service_name,程序会自动判断哪个有值,
        若两个都有值,则默认使用sid;
        若只想用其中一个,则只需要把另一个设置为空即可。如,service_name = ''
    3) 关于config的设置,注意只有 port 的值的类型是 int,以下是config样例:
        orcl_cfg = {
                    'user': 'user_name_str',
                    'passwd': 'passwd_str',
                    'host': 'xxx.xxx.xxx.xxx_str',
                    'port': port_int,
                    'sid': 'sid_str',
                    'service_name': 'service_name_str'}
    """

    def __init__(self, config):
        self.pool = OrclConnPool.__get_pool(config)

    @staticmethod
    def __get_pool(conf):
        """
        一些 PoolDB 中可能会用到的参数,根据实际情况自己选择
        mincached:       启动时开启的空连接数量
        maxcached:       连接池最大可用连接数量
        maxshared:       连接池最大可共享连接数量
        maxconnections:  最大允许连接数量
        blocking:        达到最大数量时是否阻塞
        maxusage:        单个连接最大复用次数

        :param conf:        dict    连接Oracle的信息
        """
        host, port, sid, service_name = conf.get('host'), conf.get('port'), conf.get('sid'), conf.get('service_name')
        dsn = None

        if sid:
            dsn = Oracle.makedsn(host, port, sid=sid)

        elif service_name:
            dsn = Oracle.makedsn(host, port, service_name=conf.get('service_name'))

        __pool = PooledDB(Oracle, user=conf['user'], password=conf['passwd'], dsn=dsn, mincached=5, maxcached=30)

        return __pool

    def execute_sql(self, sql, args=None):
        """
        执行sql语句
        :param sql:     str     sql语句
        :param args:    list    sql语句参数列表
        """

        cur = self.pool.connection().cursor()
        if args:
            cur.execute(sql, args)
        else:
            cur.execute(sql)

    def fetch_all(self, sql, args=None):
        """
        获取全部结果
        :param sql:     str     sql语句
        :param args:    list    sql语句参数
        :return:        tuple   fetch结果
        """
        cur = self.pool.connection().cursor()
        if args:
            cur.execute(sql, args)
        else:
            cur.execute(sql)
        return cur.fetchall()

    def __del__(self):
        """
        在实例资源被回收时,关闭该连接池
        """
        try:
            self.pool.close()
        except Exception:
            pass


def simple_demo():
    orcl_cfg = {
        'user': 'hello',
        'passwd': 'Python',
        'host': '192.168.158.xxx',
        'port': 1521,
        'sid': '',
        'service_name': 'MaiXiaochai'}

    test_sql = "SELECT COUNT(1) FROM TEST_PYTHON"

    orcl = OraclePool(orcl_cfg)
    orcl.execute_sql(test_sql)
    res = orcl.cur.fetchone()
    print(res)


if __name__ == "__main__":
    simple_demo()

传送门:GitHub:OraclePool

The end.

你可能感兴趣的:(Tools,Python,Oracle)