背景
最近有个项目,是在用户原有的仓储系统中集成RFID硬件。用户希望我们在他们原系统上直接开发,添加相关的几个功能模块,以节约开发成本。
原系统使用JDK8,数据库SQLServer,Dao层hiberante5,连接池dbcp,JDBC版本是4.2。
问题
在使用原生sql实现一个查询时,发生了以下错误。
Exception in thread "JavaFX Application Thread" java.lang.AbstractMethodError: Method org/apache/commons/dbcp/DelegatingResultSet.getNString(Ljava/lang/String;)Ljava/lang/String; is abstract
at org.apache.commons.dbcp.DelegatingResultSet.getNString(DelegatingResultSet.java)
at org.hibernate.type.descriptor.sql.NVarcharTypeDescriptor$2.doExtract(NVarcharTypeDescriptor.java:62)
at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:260)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:256)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:252)
at org.hibernate.loader.custom.ScalarResultColumnProcessor.extract(ScalarResultColumnProcessor.java:54)
at org.hibernate.loader.custom.ResultRowProcessor.buildResultRow(ResultRowProcessor.java:83)
at org.hibernate.loader.custom.ResultRowProcessor.buildResultRow(ResultRowProcessor.java:60)
at org.hibernate.loader.custom.CustomLoader.getResultColumnOrRow(CustomLoader.java:413)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:769)
at org.hibernate.loader.Loader.processResultSet(Loader.java:985)
at org.hibernate.loader.Loader.doQuery(Loader.java:943)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
at org.hibernate.loader.Loader.doList(Loader.java:2615)
at org.hibernate.loader.Loader.doList(Loader.java:2598)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2430)
at org.hibernate.loader.Loader.list(Loader.java:2425)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:335)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2153)
at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:987)
at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:148)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1410)
分析
从错误信息分析,数据sql查询没有问题,结果集已经获得,错误发生在将结果集SQL字符串类型NVarcharType转换为String类型时,原因是dbcp.DelegatingResultSet未实现getNString()这个虚方法。
查看了一下使用的dbcp.DelegatingResultSet,确实只实现了getString()方法而并没有看到getNString()。
呃,粗略搜索了一下解决方法,好像没有人遇到相同的问题,看来是哪里用得不对。。。但看到了有人遇到dbcp关于版本以起的一些问题,想着有可能也是版本问题,当前的dbcp版本是1.3。
DelegatingResultSet看着是对ResultSet接口的实现类。于是看了一下JDK8中关于ResultSet.getNString()的说明,如下,即JDK1.6以后加入,用于转换NCHAR、NVARCHAR、LONGNVARCHAR等sql类型。
/**
* Retrieves the value of the designated column in the current row
* of this <code>ResultSet</code> object as
* a <code>String</code> in the Java programming language.
* It is intended for use when
* accessing <code>NCHAR</code>,<code>NVARCHAR</code>
* and <code>LONGNVARCHAR</code> columns.
*
* @param columnLabel the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the column
* @return the column value; if the value is SQL <code>NULL</code>, the
* value returned is <code>null</code>
* @exception SQLException if the columnLabel is not valid;
* if a database access error occurs
* or this method is called on a closed result set
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since 1.6
*/
String getNString(String columnLabel) throws SQLException;
上dhcp官网看了一下,1.3版本确实是有够旧了啊,release时间是2010年,并且仅适用于Java 1.4-5.0。
•DBCP 2.6.0 compiles and runs under Java 8 only (JDBC 4.2)
•DBCP 2.5.0 compiles and runs under Java 8 only (JDBC 4.2)
•DBCP 2.4.0 compiles and runs under Java 7 only (JDBC 4.1)
•DBCP 1.4 compiles and runs under Java 6 only (JDBC 4)
•DBCP 1.3 compiles and runs under Java 1.4-5.0 only (JDBC 3)
最新版本是2.2.6,适配的环境是Java8及JDBC 4.2,然而,看网友的一些文章说dbcp2相比dbcp有很多差异。
解决方法
升级版本:
1)将dbcp替换成2.2.6版本,
2)commons-pool2 同步替换为commons-pool2-2.6.1.jar
3)参考https://blog.csdn.net/ahuyangdong/article/details/78554058 一文修改
修改spring配置文件,主要是包名如下:
datasource类型:org.apache.commons.dbcp.BasicDataSource->org.apache.commons.dbcp2.BasicDataSource
参数:
maxWait -> maxWaitMillis
maxActive -> maxTotal
结果
getNString()问题解决。
初步测试原业务功能未受影响。
关于几种常用数据库连接池的对比
摘自:https://blog.csdn.net/qq_31125793/article/details/51241943
1:性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0 。hikariCP的高性能得益于最大限度的避免锁竞争。
2:druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。
3:综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池。
Springboot2默认数据库连接池是HikariCP,原因:代码量少、口碑好、速度最快、稳定可靠。