#常见问题
本章节列出使用YashanDB JDBC驱动时常见问题的现象、可能原因和解决方法。对于需要进一步分析的问题,请结合异常信息和JDBC日志进行定位。
除特别说明外,本章节中的错误信息使用YashanDB JDBC驱动1.10.3、JDK 17和YashanDB 23.4.11.7验证。异常类型、SQLSTATE和数据库错误码可用于判断问题类型;错误消息文本可能随驱动或数据库版本变化。
# 应用启动时提示找不到JDBC驱动类
问题现象
应用启动时抛出ClassNotFoundException或NoClassDefFoundError,提示找不到com.yashandb.jdbc.Driver或驱动依赖类。未将驱动JAR包加入运行时类路径并调用Class.forName("com.yashandb.jdbc.Driver")时,实测错误信息如下:
java.lang.ClassNotFoundException: com.yashandb.jdbc.Driver
可能原因
- YashanDB JDBC驱动JAR包未加入应用运行时类路径。
- 编译环境中存在驱动,但部署包或容器运行环境中没有驱动。
- 同时引入多个版本的驱动,类加载器加载了错误版本。
解决方法
确认驱动JAR包位于应用运行时类路径,并检查构建工具的依赖作用域。不要在同一应用中放置多个YashanDB JDBC驱动版本。JDBC 4及以上版本通常可以通过SPI自动加载驱动;需要显式加载时使用:
Class.forName("com.yashandb.jdbc.Driver");
# 调用getConnection时无法连接数据库
问题现象
DriverManager.getConnection()抛出连接异常,应用无法获得Connection对象。例如,目标地址未监听时,实测错误信息如下:
异常类型:com.yashandb.jdbc.exception.YasException
SQLSTATE:08001
错误码:0
错误消息:Connection to 127.0.0.1:1 refused. Check that the hostname and port are correct
根因:java.net.ConnectException: Connection refused: getsockopt
可能原因
- JDBC URL中的主机、端口或数据库名称错误。
- 数据库服务未启动或端口未监听。
- 客户端和数据库之间的防火墙、路由或代理配置异常。
- 使用的驱动版本与数据库版本或连接模式不兼容。
- 高可用连接配置中的节点地址不可访问。
解决方法
- 核对JDBC URL,确认使用
jdbc:yasdb://host:port/database格式。 - 从应用所在主机检查数据库地址解析和端口连通性。
- 确认数据库服务状态、监听地址和监听端口。
- 记录异常类、SQLSTATE、数据库错误码及根因异常。
- 开启JDBC连接日志,确认失败发生在TCP连接、登录认证还是会话初始化阶段。
日志配置方法请参考JDBC日志诊断。
# 通过SCAN或多地址URL无法连接备集群
问题现象
通过SCAN地址或多地址JDBC URL连接备集群、备库或备节点时,长时间无法建立连接,应用日志中可能出现如下错误:
java.sql.SQLException: Connection refused (Connection timed out)
可能原因
多地址JDBC URL的格式如下:
jdbc:yasdb:serverType://host1:port1,host2:port2;host3:port3,host4:port4/databasename
其中,serverType用于指定要连接的数据库角色。多地址连接未指定serverType时,驱动默认按primary处理,因而不会保留与备节点建立的连接,最终可能表现为连接超时。
解决方法
将serverType设置为standby,并配置备集群或备节点的监听地址:
jdbc:yasdb:standby://host1:port1,host2:port2/databasename
使用SCAN域名时,也应显式指定standby:
jdbc:yasdb:standby://scan.example.com:1688/databasename
确认URL中的地址均属于目标备集群,并从应用主机检查SCAN域名解析结果和端口连通性。其他服务类型及多组地址的配置方法请参考高可用连接配置。
# 一次执行多条SQL时报错
问题现象
使用JDBC驱动执行SQL时,一次传入由分号(;)分隔的多条语句,驱动默认不支持该格式。例如:
INSERT INTO customer VALUES (1);
INSERT INTO customer VALUES (2);
INSERT INTO customer VALUES (3);
未开启多SQL功能时,实测错误信息如下:
异常类型:java.sql.SQLSyntaxErrorException
SQLSTATE:42000
错误码:4209
错误消息:[line: 1 column: 43] YAS-04209 unexpected word ;
可能原因
连接参数allowMultiStmt默认为FALSE,驱动将分号后的内容识别为非预期语法。
解决方法
确实需要一次执行多条SQL时,在JDBC URL中设置allowMultiStmt=true:
String url = "jdbc:yasdb://192.168.1.2:1688/yasdb"
+ "?allowMultiStmt=true";
启用后,多条SQL按照传入顺序依次执行;任一语句执行失败时,驱动终止执行并返回错误,JDBC接口只返回第一条SQL的执行结果。该功能仅支持不带参数且不包含autoGeneratedKey的SQL语句。仅在业务确实需要时开启,不要拼接未经校验的外部输入。参数详情请参考JDBC连接串配置参数。
# setQueryTimeout触发会话取消
问题现象
应用通过JDBC执行INSERT语句或调用存储过程时设置了较短的查询超时时间,例如setQueryTimeout(1)。实际执行时间超过1秒后,驱动发送取消请求,数据库中断当前操作并返回错误。
直接执行SQL发生超时时,实测错误信息如下:
异常类型:java.sql.SQLTimeoutException
SQLSTATE:57000
错误码:2094
错误消息:[line: 0 column: 0] YAS-02094 current session has been killed or canceled
涉及存储过程调试或触发器时,还可能出现以下错误:
YAS-02366 debug current session has been killed or canceled
YAS-02277 error during execution of trigger(错误详情中包含YAS-02366)
典型业务错误信息如下:
BISM.CORE.0116: [ERROR] "$XML_REQ$WRITE_DB$". Тип "SCRIPT_RULE".
[line: 0 column: 0] YAS-02094 current session has been killed or canceled!
可能原因
Statement.setQueryTimeout()和PreparedStatement.setQueryTimeout()的单位为秒。设置的时间小于SQL、存储过程或触发器的实际执行时间时,驱动会在超时后执行取消操作。YAS-02277是触发器执行失败的外层错误,应继续查看错误详情中的内部错误码。
解决方法
根据业务SQL的正常执行时间、锁等待情况和系统负载设置合理的超时时间。例如,将1秒调整为30秒:
// 调整前
pstmt.setQueryTimeout(1);
// 调整后
pstmt.setQueryTimeout(30);
不要仅通过无限增大超时时间掩盖慢SQL或锁等待问题。发生YAS-02094后,应关闭当前连接并重新获取连接,再根据JDBC日志和数据库日志检查实际执行耗时。
# BigInteger无法转换为BigDecimal
问题现象
应用从YashanDB迁移或适配Oracle类型映射时,将JDBC返回的BigInteger对象直接转换为BigDecimal,抛出以下异常:
java.lang.ClassCastException: java.math.BigInteger cannot be cast to java.math.BigDecimal
可能原因
数据库使用原生数据类型时,整数类型的JDBC返回对象可能与Oracle兼容类型的返回对象不同。Java对象之间不能通过强制类型转换完成BigInteger到BigDecimal的转换,因此应用按BigDecimal直接取值时抛出ClassCastException。
解决方法
需要保持Oracle兼容类型行为时,推荐在创建数据库时将数据库配置参数USE_NATIVE_TYPE设置为FALSE。此时,TINYINT、SMALLINT、INTEGER和BIGINT会按NUMBER(38,0)处理。
对于已经创建的数据库,应用不应直接强制转换getObject()返回值。可以优先调用ResultSet.getBigDecimal(),或者根据实际返回类型显式构造BigDecimal:
Object value = resultSet.getObject("amount");
BigDecimal amount;
if (value instanceof BigDecimal) {
amount = (BigDecimal) value;
} else if (value instanceof BigInteger) {
amount = new BigDecimal((BigInteger) value);
} else {
throw new SQLException("Unexpected numeric type: "
+ value.getClass().getName());
}
排查时可通过ResultSetMetaData.getColumnTypeName()和getColumnClassName()确认数据库类型及驱动返回的Java类型。
具体映射关系请参考JDBC数据类型映射关系。
# 数据库NULL被读取为0或false
问题现象
数据库列为NULL,但getInt()返回0、getLong()返回0L或getBoolean()返回false。读取CAST(NULL AS INTEGER)时不会抛出异常,实测结果如下:
getInt()返回值:0
wasNull()返回值:true
可能原因
Java基本类型不能表示NULL。JDBC基本类型Get方法在数据库值为NULL时返回对应的Java默认值。
解决方法
在读取后立即调用ResultSet.wasNull():
Class.forName("com.yashandb.jdbc.Driver");
try (Connection connection = DriverManager.getConnection(
"jdbc:yasdb://192.168.1.2:1688/yasdb",
"username",
"passwd");
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT CAST(NULL AS INTEGER) AS optional_count FROM dual")) {
if (resultSet.next()) {
int value = resultSet.getInt("optional_count");
if (resultSet.wasNull()) {
System.out.println("optional_count is NULL");
}
}
}
正常情况下,程序输出如下:
optional_count is NULL
绑定NULL参数时使用setNull()并指定正确的JDBC类型,避免驱动无法推断目标类型。
# 关闭ResultSet或Connection后无法继续读取LOB
问题现象
获取Blob、Clob或NClob后关闭结果集或连接,再读取LOB内容时发生异常。关闭产生Clob对象的连接后调用Clob.getSubString(),实测错误信息如下:
异常类型:com.yashandb.jdbc.exception.YasException
SQLSTATE:08003
错误码:0
错误消息:This connection has been closed.
可能原因
LOB对象可能引用驱动和数据库端资源,其有效期通常与产生它的连接和结果集相关。
解决方法
在连接和结果集有效期内完成LOB读取。小数据可以立即转换为byte[]或String;大数据应在资源关闭前通过流处理。LOB对象使用完成后调用free()。
错误码与异常类型的关系请参考YashanDB错误码与JDBC异常的对应关系。

