JDBC 连接 MySQL 时碰到的小坑
最近从MS SQL Server换到了MySQL,已经是8.11版本了,安装的时候似乎还用了新的身份认证方式之类的,连接过程中也是磕磕绊绊,碰到很多奇奇怪怪的问题,在此记录下来。
驱动加载:
以前使用JDBC时,都是导入相应的JDBC驱动jar包,然后使用
Class.forName(driverName);
加载驱动,再进行数据库连接,在使用MySQL 8.11版本及其对应的Connector时,如果使用上述代码加载com.mysql.jdbc.Driver的话,控制台会输出一行信息:
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
大概意思是加载的这个类已被弃用,新驱动类是com.mysql.cj.jdbc.Driver,通过SPI自动注册,通常不需要手动加载。注释掉Class.forName后运行就不会产生该输出了。
连接数据库时的URL:
URL前半部分格式固定,需要附加的参数是通过类似网址中的QueryString附着在后面的,格式如下:
jdbc:<_database>://<address>:<port>/<database_name>[?_key1=_value1[&_key2=_value2[&_key3=_value3]...]]
在此仅列举用到的几个参数:
不加参数进行连接时首先会输出这样一行提示信息:
Tue May 08 20:16:40 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
大概意思是不建议在没有服务器身份验证的情况下建立SSL连接等等,由于本人对SSL并不了解,就直接按照提示在加上参数useSSL=false,就不会产生该输出了。
接下来遇到的错误是一个关于时区的SQLException:The server time zone value......
java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
大概意思是服务器的时区值有问题,我这直接就是一团乱码了,按照提示加上参数serverTimezone=Hongkong,就不会产生这个异常了,对于该参数的取值可以查阅相关资料获得,已知东8区可以用Hongkong、Asia/Shanghai或者Asia/Hongkong作为参数取值。
SQLNonTransientConnectionException: Public Key Retrieval is not allowed
这时可能程序已经能够正常连接数据库了,不过我在调试成功后第二天再次连接时(未对代码进行改动),抛出了一个异常并连接失败,多次尝试无果,该异常如下:
SQLNonTransientConnectionException: Public Key Retrieval is not allowed
对于这个异常网上相关资料很少,多为英文,误打误撞下竟然解决了这个问题,方法为添加一个参数allowPublicKeyRetrieval=true,即可正常连接,而且在连接成功一次后,删除此参数仍然可以正常连接(不懂得都是玄学)......,该方法来自(https://bugs.mysql.com/bug.php?id=75670)下一位dalao的评论:
[20 Aug 2015 19:57] Daniel So Added the following entry to the Connector/J 6.0 changelog: "If the MySQL server's default authentication method was SHA256 but neither one of the Connector/J connection properties allowPublicKeyRetrieval and serverRSAPublicKeyFile was set, the authentication failed with a TransientConnectionException, complaining that the public key could not be retrieved. With this fix, authentication continues in the situation, allowing other enabled authentication methods to be tried."
然而并没有看懂。
另外就是字符编码问题,本人服务器默认使用的编码为UTF-8MB4,Eclipse默认编码均使用UTF-8,所以并未处理编码问题。
若需要进行编码转换需要附加参数useUnicode=true 和 characterEncoding=UTF-8。
其他重要参数(转自:https://blog.csdn.net/wpydaguan/article/details/41148047):
参数名称 | 参数说明 | 缺省值 | 最低版本要求 |
user | 数据库用户名(用于连接数据库) | 所有版本 | |
password | 用户密码(用于连接数据库) | 所有版本 | |
useUnicode | 是否使用Unicode字符集,如果参数characterEncoding设置为gb2312或gbk,本参数值必须设置为true | false | 1.1g |
characterEncoding | 当useUnicode设置为true时,指定字符编码。比如可设置为gb2312或gbk | false | 1.1g |
autoReconnect | 当数据库连接异常中断时,是否自动重新连接? | false | 1.1 |
autoReconnectForPools | 是否使用针对数据库连接池的重连策略 | false | 3.1.3 |
failOverReadOnly | 自动重连成功后,连接是否设置为只读? | true | 3.0.12 |
maxReconnects | autoReconnect设置为true时,重试连接的次数 | 3 | 1.1 |
initialTimeout | autoReconnect设置为true时,两次重连之间的时间间隔,单位:秒 | 2 | 1.1 |
connectTimeout | 和数据库服务器建立socket连接时的超时,单位:毫秒。 0表示永不超时,适用于JDK 1.4及更高版本 | 0 | 3.0.1 |
socketTimeout | socket操作(读写)超时,单位:毫秒。 0表示永不超时 | 0 | 3.0.1 |
特别注意!在某些通过配置文件来指定URL的情况下(如xml配置文件),URL后参数的隔离符号&需要使用&转义为&,其他符号也是如此:
HTML中常用的特殊字符(Character Entities)
显示结果 | 说明 | Entity Name | Entity Number |
---|---|---|---|
显示一个空格 | |
  |
|
< | 小于 | < |
< |
> | 大于 | > |
> |
& | &符号 | & |
& |
“ | 双引号 | " |
" |
其他常用的字符实体(Character Entities)
显示结果 | 说明 | Entity Name | Entity Number |
---|---|---|---|
? | 版权 | © |
© |
? | 注册商标 | ® |
® |
× | 乘号 | × |
× |
÷ | 除号 | ÷ |
÷ |
Java Web中遇到的坑:
No suitable driver found for jdbc:mysql://...
前面提到MySQL启用的旧驱动使用了新的通过SPI自动注册的驱动,在普通Java程序下可以正常运行,但是在Java Web项目中运行在Tomcat8.5环境下时,进行数据库连接操作会抛出异常:
java.sql.SQLException: No suitable driver found for jdbc:mysql://...
大概的意思是找不到合适的JDBC驱动程序,经过检查,已经将使用到的驱动jar包放到了Tomcat的lib目录下,Eclipse中的构建路径也包含了该jar包,在执行连接操作前向控制台打印所用驱动类也存在:
System.out.println(com.mysql.cj.jdbc.Driver.class); /* *输出: ** *class com.mysql.cj.jdbc.Driver * */
经过搜索得到的答案基本一致(转自:http://www.blogjava.net/w2gavin/articles/217864.html):
今天出现编码出现了No suitable driver found for jdbc,又是找遍了网上的资料,基本上都说是三个问题: 一是:连接URL格式出现了问题(Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/XX","root","XXXX") 二是:驱动字符串出错(com.mysql.jdbc.Driver) 三是Classpath中没有加入合适的mysql_jdbc驱动 经过我的仔细检查,这三种错误我都没有犯,为什么呢? 尝试着将mysql-connector-java-3.1.14-bin.jar的jar包加入C:\Program Files\Java\jre1.6.0_02\lib\ext文件夹下,问题解决了!! 原来是不仅仅要求将驱动加入classpath中,而且需要将该jar包加入到java运行环境的外部jar包中,唉,下次这种低级错误还是少犯为妙。
对于这三个说法,本人针对前两条进行了检查,并没有错误,第三条个人认为不正确,Tomcat会自动加载lib下的jar包,况且上面在打印驱动类的Class对象时也确实能看到该类已经被加载,对于下面将驱动jar包放入系统jre下的方(玄)法(学)也并未尝试,不过,在连接数据库操作前加了主动加载驱动类的代码后竟然神(玄)奇(学)的好了:
System.out.println(com.mysql.cj.jdbc.Driver.class); Class.forName(driverName); System.out.println("connecting..."); conn = DriverManager.getConnection(URL, uName, uPwd); System.out.println("connect success!"); /* *输出: ** *class com.mysql.cj.jdbc.Driver *connecting... *connect success! * */
可以看到在手动加载之前该类已经注册,因此这个玄学问题还希望请知道的dalao指教一二!
目前遇到的问题就这些,日后遇到新的问题继续补充,文中理解的若有错误还请dalao们指正,欢迎大家的讨论交流。