使用jaydebeapi同时连接两个不同数据库(oracle+mysql)的问题

在使用jaydebeapi只连接一种数据库时,是没问题的,但是如果需要同时连接两种数据库,比如同时连接oracle和mysql

例如以下测试代码:

import jaydebeapi  ##使用jdbc驱动连接数据库
import pandas as pd

dirver='oracle.jdbc.driver.OracleDriver'
jarFile='D:\\WORK\\PYScript\\BiDataMonitor\\jdbc\\64\\ojdbc14.jar'
addr_='xxx.xxx.xxx.xxx'+':'+'1521'+'/'+'xxx'
url='jdbc:oracle:thin:@'+addr_
print('url',url)
DBUser='用户名'
DBPwd='密码'
conn=jaydebeapi.connect(dirver,[url,DBUser,DBPwd],jarFile) ##使用jdbc驱动连接数据库
sql_str="select 'oracle' from dual"
df=pd.read_sql_query(sql_str,conn)
print(df)
conn.close()


dirver='com.mysql.jdbc.Driver'
jarFile='D:\\WORK\\PYScript\\BiDataMonitor\\jdbc\\64\\mysql-connector-java-5.1.30.jar'
addr_='xxx.xxx.xxx.xxx'+':'+'3306'+'/'+'xxx'
url='jdbc:mysql://'+addr_
print('url',url)
DBUserMySql='用户名'
DBPwdMySql='密码'
conn2=jaydebeapi.connect(dirver,[url,DBUserMySql,DBPwdMySql],jarFile)
sql_str="select 'mysql' from dual"
df=pd.read_sql_query(sql_str,conn2)
print(df)
conn2.close()

在连接第二种数据库时,就会报错误:

Py4JJavaError: An error occurred while calling z:java.sql.DriverManager.getConnection.
: java.sql.SQLException: No suitable driver found for jdbc:mysql://10.1.164.5:3306/vvm
	at java.sql.DriverManager.getConnection(Unknown Source)
	at java.sql.DriverManager.getConnection(Unknown Source)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244)
	at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357)
	at py4j.Gateway.invoke(Gateway.java:282)
	at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
	at py4j.commands.CallCommand.execute(CallCommand.java:79)
	at py4j.GatewayConnection.run(GatewayConnection.java:214)
	at java.lang.Thread.run(Unknown Source)

错误信息中显示,找不到渠道,而实际上并不是驱动或者jar包的问题。

要分析和解决这个问题,还需要从jaydebeapi的源码中分析,打开jaydebeapi源码(下载地址:https://pypi.python.org/pypi/JayDeBeApi3),从报错信息中我们可以得到,是在...jaydebeapi\__init__.py", line 68


jaydebeapi.connect方法源码如下:

# DB-API 2.0 Module Interface connect constructor
def connect(jclassname, driver_args, jars=None, libs=None):
    """Open a connection to a database using a JDBC driver and return
    a Connection instance.

    jclassname: Full qualified Java class name of the JDBC driver.
    driver_args: Argument or sequence of arguments to be passed to the
           Java DriverManager.getConnection method. Usually the
           database URL. See
           http://docs.oracle.com/javase/6/docs/api/java/sql/DriverManager.html
           for more details
    jars: Jar filename or sequence of filenames for the JDBC driver
    libs: Dll/so filenames or sequence of dlls/sos used as shared
          library by the JDBC driver
    """

    if _gateway_is_running():
        gateway = java_gateway.JavaGateway()
    else:
        driver_args = [driver_args] if isinstance(driver_args, str) else driver_args

        if jars:
            classpath = os.pathsep.join(jars) if isinstance(jars, list) else jars
        else:
            classpath = None

        if libs:
            javaopts = libs if isinstance(libs, list) else [libs]
        else:
            javaopts = []

        gateway = java_gateway.JavaGateway.launch_gateway(
            port=25333, classpath=classpath, javaopts=javaopts, die_on_exit=True)

        java_gateway.java_import(gateway.jvm, 'java.sql.DriverManager')
        gateway.jvm.Class.forName(jclassname)

    connection = gateway.jvm.DriverManager.getConnection(*driver_args)
    if _converters is None:
        types_map = {}
        for type in gateway.jvm.Class.forName("java.sql.Types").getFields():
            types_map[type.getName()] = type.getInt(None)
        _init_converters(types_map)

    return Connection(connection, _converters)

报错信息中提示的68行也就是:

connection = gateway.jvm.DriverManager.getConnection(*driver_args)

其实,我不需要跟深层次是剖析,只需要分析下jaydebeapi.connect方法即可,该方法大概是逻辑是:

                判断 java_gateway是否running
            |----------是---|-----否-----|	
            |				 |
	获取gateway实例		    1、初始化参数
				    2、创建gateway实例

问题就出在这里,第一次连接oracle时,通过右边分支先初始化参数,然后创建了gateway实例,但是第二次调用jaydebeapi.connect方法连mysql时,由于_gateway_is_running()返回真,所以走左边分支,直接获取了上一次连接oracle时创建gateway实例,其中最重要的两个个参数jclassname和classpath,也就是“oracle.jdbc.driver.OracleDriver”和"ojdbc14.jar"

        gateway = java_gateway.JavaGateway.launch_gateway(
            port=25333, classpath=classpath, javaopts=javaopts, die_on_exit=True)

        java_gateway.java_import(gateway.jvm, 'java.sql.DriverManager')
        gateway.jvm.Class.forName(jclassname)

而第二次连mysql时,用的是mysql的连接,所以当然就是报No suitable driver found for jdbc错误。

明白了报错的原因,下面就来需求解决方法

解决的方法应该有多种,我这里采取了最简单,最暴力的方法,就是在判断_gateway_is_running()时,将其关闭,然后重新重建新的gateway实例,这样虽然简单,但是如果频繁连接数据库时,就会因为不能重用上一次实例而消耗额外的资源。

通过帮助函数可知,gateway实例有一个shutdown方法

修改后的connect方法如下:

# DB-API 2.0 Module Interface connect constructor
def connect(jclassname, driver_args, jars=None, libs=None):
    """Open a connection to a database using a JDBC driver and return
    a Connection instance.

    jclassname: Full qualified Java class name of the JDBC driver.
    driver_args: Argument or sequence of arguments to be passed to the
           Java DriverManager.getConnection method. Usually the
           database URL. See
           http://docs.oracle.com/javase/6/docs/api/java/sql/DriverManager.html
           for more details
    jars: Jar filename or sequence of filenames for the JDBC driver
    libs: Dll/so filenames or sequence of dlls/sos used as shared
          library by the JDBC driver
    """

    if _gateway_is_running():
        gateway = java_gateway.JavaGateway()
        gateway.shutdown()
    else:
        pass
    driver_args = [driver_args] if isinstance(driver_args, str) else driver_args

    if jars:
        classpath = os.pathsep.join(jars) if isinstance(jars, list) else jars
    else:
        classpath = None

    if libs:
        javaopts = libs if isinstance(libs, list) else [libs]
    else:
        javaopts = []

    gateway = java_gateway.JavaGateway.launch_gateway(
        port=25333, classpath=classpath, javaopts=javaopts, die_on_exit=True)

    java_gateway.java_import(gateway.jvm, 'java.sql.DriverManager')
    gateway.jvm.Class.forName(jclassname)

    connection = gateway.jvm.DriverManager.getConnection(*driver_args)
    if _converters is None:
        types_map = {}
        for type in gateway.jvm.Class.forName("java.sql.Types").getFields():
            types_map[type.getName()] = type.getInt(None)
        _init_converters(types_map)

    return Connection(connection, _converters)


然后卸载jaydebeapi,用修改后的源码重新安装一次即可,源码安装方法,可以查看之前的一篇博客:

https://blog.csdn.net/cakecc2008/article/details/79073181




猜你喜欢

转载自blog.csdn.net/cakecc2008/article/details/79786378
今日推荐