根据字符串,引入不通的 jdbc 驱动,为什么会错乱? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
among
0.01D
V2EX    Java

根据字符串,引入不通的 jdbc 驱动,为什么会错乱?

  •  
  •   among Sep 7, 2021 4065 views
    This topic created in 1695 days ago, the information mentioned may be changed or developed.
     // 驱动 public String GetDriver(String url) { try { if (url.startsWith("jdbc:as400")) { return "com.ibm.as400.access.AS400JDBCDriver"; } else if (url.startsWith("jdbc:oracle")) { return "oracle.jdbc.driver.OracleDriver"; } else if (url.startsWith("jdbc:mysql")) { return "com.mysql.cj.jdbc.Driver"; } else if (url.startsWith("jdbc:sqlserver")) { return "com.microsoft.sqlserver.jdbc.SQLServerDriver"; } else if (url.startsWith("jdbc:informix-sqli")) { return "com.informix.jdbc.IfxDriver"; } else if(url.startsWith("jdbc:sybase")) { return "com.sybase.jdbc4.jdbc.SybDriver"; }else if(url.startsWith("jdbc:db2")) { return "com.ibm.db2.jcc.DB2Driver"; }else if(url.startsWith("jdbc:dm")) { return "dm.jdbc.driver.DmDriver"; }else { log.error("连接字符串有误:" + url); return ""; } } catch (Exception e) { log.error("返回驱动异常:" + e); return ""; } } 

    com.tp.util.DataBaseRun : 连接:jdbc:oracle:thin:@xx.xx.xx.xx:1521/db 异常:java.sql.SQLNonTransientConnectionException: Cannot load connection class because of underlying exception: com.mysql.cj.exceptions.WrongArgumentException: Malformed database URL, failed to parse the main URL sections.

    在并发较多的情况下,会出现错乱的情况。

    如 oracle 的,会到 mysql 的类中去寻找。

    25 replies    2021-09-12 00:49:02 +08:00
    Vedar
        1
    Vedar  
       Sep 7, 2021
    因为你这个方法就不是线程安全的
    liangch
        2
    liangch  
       Sep 7, 2021
    并发再多,url 不都是 oracle 开头么。不如你把 GetDriver 返回值 log 下看看。
    teliang
        3
    teliang  
       Sep 7, 2021 via iPhone
    @Vedar 这里没有修改数据,不存在数据一致问题吧
    thetbw
        4
    thetbw  
       Sep 7, 2021
    我记得 jdbc 驱动是 不需要手动 Class.forName 的,java 有个 SPI 机制
    EscYezi
        5
    EscYezi  
       Sep 8, 2021 via iPhone
    具体场景是什么?单看这个方法没什么问题,看下用这个方法返回值的地方是不是线程安全的
    chihiro2014
        6
    chihiro2014  
       Sep 8, 2021
    为什么会有这么诡异的写法,不考虑下 baomidou 的数据源切换么
    chenshun00
        7
    chenshun00  
       Sep 8, 2021
    Arthas 看下,这里就不存在并发问题,并发是由于存在共享数据导致的竞争条件才成立的,这里就是一个无状态的方法。

    肯定是传进来的 URL 有问题。
    xiao109
        8
    xiao109  
       Sep 8, 2021
    贴上来的代码是线程安全了。可后续的加载数据库驱动就不一定是线程安全了。
    among
        9
    among  
    OP
       Sep 8, 2021
    @Vedar @thetbw @EscYezi @all

    这个最初的需求是这样的:
    我有个 py 的项目,需要连接各种各样的数据库,informix 、oracle 、db2 、sybase 、达梦、mysql 等等等。
    在 py 中连接,很多老的数据库并没有驱动,如 sybase,也会有些麻烦的环境和依赖问题。

    所以,就找了外包的同事,做了一个基于 REST 的数据库服务,采用的是 sprint boot,等于说做了一个对外的接口。
    接收 py 这边的数据库查询、修改请求,调用 jdbc 驱动去做。

    后来,实现是实现了,但是并发较多的情况下,会发现会有串号的情况,就是传过去的字符串明明是 oracle 开头的,最后的 java 的报错信息里面,报的在 mysql 的 lib 中寻找。


    对 java 不熟,外包的同事也不在了,结果。。。

    代码,我都贴在这里了: https://gitee.com/among/demo

    代码量很少,帮忙给点思路。
    AlkTTT
        10
    AlkTTT  
       Sep 8, 2021
    DriverManager.getConnection 这里线程不安全,要么加锁,要么用框架连
    JYii
        11
    JYii  
       Sep 8, 2021
    没太仔细看代码,写的有点乱... 但是这种跨数据库查询,应该为每次加载数据库驱动单独设置一个线程变量把
    sutra
        12
    sutra  
       Sep 8, 2021 via iPhone
    @AlkTTT DriverManager.getConnection 这个静态方法本身是线程安全的吧。
    lonenol
        13
    lonenol  
       Sep 8, 2021
    用数据库连接池就好了。。没必要搞这么费劲。。
    你这个问题是因为 Class.forName 那里是线程不安全的
    xiao109
        14
    xiao109  
       Sep 8, 2021
    @sutra 但 Class.forName 和 DriverManager.getConnection 一起就不一定是了
    thetbw
        15
    thetbw  
       Sep 8, 2021
    @among 同楼上,获取驱动的时候加锁试下,还有就是 Class.forName 一次就可以了,没必要每次都加载,还有这段代码应该是多余的,对应驱动会自动加载,如果没有自动加载,系统启动的时候全加载就行了,没必要每次都加载一次
    ffkjjj
        16
    ffkjjj  
       Sep 8, 2021
    @xiao109 #14
    @thetbw #15
    楼主这里都是 new 出来的对象, 而且没有类属性, 没有什么问题吧
    x66
        17
    x66  
       Sep 8, 2021
    我看了半天我也没看到哪里有并发问题的风险,加点日志再找下原因吧,万一是参数穿错了呢
    JinTianYi456
        18
    JinTianYi456  
       Sep 8, 2021   1
    @among #9 报错行 https://gitee.com/among/demo/blob/master/SpringBoot/src/main/java/com/tp/util/DataBaseRun.java#L279
    该行所在函数是没有线程安全问题的(没去看它调用别的函数),也不是他们说的 forName 等之类的
    问题在于 getConnection 函数,该函数内部为:(删减版)

    ----
    for (DriverInfo aDriver : registeredDrivers) {
    try {
    Connection con = aDriver.driver.connect(url, info);
    if (con != null) {
    return (con);
    }
    } catch (SQLException ex) {
    if (reason == null) {
    reason = ex;
    }
    }
    }
    if (reason != null) {
    throw reason;
    }
    ----

    假设 registeredDrivers=[mysql,oracle]. 进入 getConnection,用 mysql 连,假设返回 SQLException,赋值给 reason,
    然后用 oracle 连,假设返回 SQLException 但 reason 已有值了,又或者返回 null,最后都是 throw reason(by mysql)
    所以错误原因是: 传入的 url jdbc:oracle:xxxxx 有错误(或者其它),导致获得不到 Connection
    ikas
        19
    ikas  
       Sep 8, 2021   1
    不要用 getConnection,换成 getDriver,然后调用 Driver 的 connect
    MineDog
        20
    MineDog  
       Sep 8, 2021   1
    从 18 楼的代码你就能看到,jdbc getConnection 方法是一个个驱动去试的,能拿到非空的 Connection 就认为驱动能处理当前 url,你既然能根据 url 映射对应驱动,那直接通过 DriverManager 拿到驱动的实例自己调用 driver.connect 方法就行
    selca
        21
    selca  
       Sep 8, 2021
    由于 SPI 机制,Class.forName 基本不会影响到下一行 DriverManager.getConnection 的调用,我跟 18 和 19 楼的观点一样,Java 的 getConnection 方法本身就是依次遍历所有注册驱动,如果成功后就直接返回,哪知道 mysql 的驱动只成功了一部分,既然有判断 url 类型的代码,就直接通过 url 去手动取得连接就行。
    among
        22
    among  
    OP
       Sep 8, 2021   1
    ikas
        23
    ikas  
       Sep 8, 2021
    @among 这里基本说的基本就是上面那个..最好的方式,就是你自己先 getDriver
    Driver 这个是可以缓存的..还有也没有必要每次 class.forname..
    buster
        24
    buster  
       Sep 8, 2021
    又学到新的一招,从没遇到过类似的问题。
    曾经做过的连多 DB 的事情,每种 DB 都会搞一个带线程池的工具类。
    pjntt
        25
    pjntt  
       Sep 12, 2021
    调用这个方法时候是线程安全的吗?
    About     Help     Advertise     Blog     API     FAQ     Solana     933 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 86ms UTC 21:27 PVG 05:27 LAX 14:27 JFK 17:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86