// 驱动
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 的类中去寻找。
1
Vedar 2021-09-07 22:56:06 +08:00
因为你这个方法就不是线程安全的
|
2
liangch 2021-09-07 23:02:02 +08:00
并发再多,url 不都是 oracle 开头么。不如你把 GetDriver 返回值 log 下看看。
|
4
thetbw 2021-09-07 23:13:10 +08:00
我记得 jdbc 驱动是 不需要手动 Class.forName 的,java 有个 SPI 机制
|
5
EscYezi 2021-09-08 00:55:37 +08:00 via iPhone
具体场景是什么?单看这个方法没什么问题,看下用这个方法返回值的地方是不是线程安全的
|
6
chihiro2014 2021-09-08 01:26:18 +08:00
为什么会有这么诡异的写法,不考虑下 baomidou 的数据源切换么
|
7
chenshun00 2021-09-08 08:02:00 +08:00
Arthas 看下,这里就不存在并发问题,并发是由于存在共享数据导致的竞争条件才成立的,这里就是一个无状态的方法。
肯定是传进来的 URL 有问题。 |
8
xiao109 2021-09-08 08:36:35 +08:00
贴上来的代码是线程安全了。可后续的加载数据库驱动就不一定是线程安全了。
|
9
among OP @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 代码量很少,帮忙给点思路。 |
10
AlkTTT 2021-09-08 09:20:48 +08:00
DriverManager.getConnection 这里线程不安全,要么加锁,要么用框架连
|
11
JYii 2021-09-08 09:21:32 +08:00
没太仔细看代码,写的有点乱... 但是这种跨数据库查询,应该为每次加载数据库驱动单独设置一个线程变量把
|
13
lonenol 2021-09-08 09:31:03 +08:00
用数据库连接池就好了。。没必要搞这么费劲。。
你这个问题是因为 Class.forName 那里是线程不安全的 |
15
thetbw 2021-09-08 10:24:12 +08:00
@among 同楼上,获取驱动的时候加锁试下,还有就是 Class.forName 一次就可以了,没必要每次都加载,还有这段代码应该是多余的,对应驱动会自动加载,如果没有自动加载,系统启动的时候全加载就行了,没必要每次都加载一次
|
17
x66 2021-09-08 13:14:30 +08:00
我看了半天我也没看到哪里有并发问题的风险,加点日志再找下原因吧,万一是参数穿错了呢
|
18
JinTianYi456 2021-09-08 13:47:35 +08:00 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 |
19
ikas 2021-09-08 13:59:02 +08:00 1
不要用 getConnection,换成 getDriver,然后调用 Driver 的 connect
|
20
MineDog 2021-09-08 14:33:50 +08:00 1
从 18 楼的代码你就能看到,jdbc getConnection 方法是一个个驱动去试的,能拿到非空的 Connection 就认为驱动能处理当前 url,你既然能根据 url 映射对应驱动,那直接通过 DriverManager 拿到驱动的实例自己调用 driver.connect 方法就行
|
21
selca 2021-09-08 14:44:40 +08:00
由于 SPI 机制,Class.forName 基本不会影响到下一行 DriverManager.getConnection 的调用,我跟 18 和 19 楼的观点一样,Java 的 getConnection 方法本身就是依次遍历所有注册驱动,如果成功后就直接返回,哪知道 mysql 的驱动只成功了一部分,既然有判断 url 类型的代码,就直接通过 url 去手动取得连接就行。
|
22
among OP |
23
ikas 2021-09-08 15:08:55 +08:00
@among 这里基本说的基本就是上面那个..最好的方式,就是你自己先 getDriver
Driver 这个是可以缓存的..还有也没有必要每次 class.forname.. |
24
buster 2021-09-08 17:24:11 +08:00
又学到新的一招,从没遇到过类似的问题。
曾经做过的连多 DB 的事情,每种 DB 都会搞一个带线程池的工具类。 |
25
pjntt 2021-09-12 00:49:02 +08:00
调用这个方法时候是线程安全的吗?
|