Spring Boot用OracleDataSource连LDAP时重复抛出InvalidSyntaxException问题
Spring Boot + Oracle LDAP连接:InvalidSyntaxException异常重复抛出后成功连接
问题描述
我们有一个Spring Boot应用,使用Spring Data调用Oracle数据库中的存储过程。当应用访问数据库时,Dynatrace会捕获到oracle.net.jdbc.nl.InvalidSyntaxException异常,提示信息为:
TNS-***** Invalid syntax error: Expected "(" before or at , cn =
数据库连接URL
使用的LDAP格式连接URL示例:
jdbc:oracle:thin:@ldap://mydomain.com:938/DAFPRXHSUSR,cn=OracleContext,dc=eusovd,dc=com
该URL符合Oracle LDAP连接字符串语法规范。
异常现象与推测
未编写任何重试逻辑的情况下,该异常会被抛出两次,第三次尝试时数据库调用成功。我们怀疑原因可能是:
- Oracle驱动Jar包内置了重试逻辑;
- 相关库在执行我们编写的调用前会发起2次请求,而这两次请求存在语法问题。
进一步推测:驱动误将LDAP URL识别为TNS URL,失败两次后才成功连接,但不确定是最终识别出LDAP格式、将URL转换为TNS格式,还是前两次请求使用错误语法,第三次才用正确语法执行。
异常堆栈信息
Exception: oracle.net.jdbc.nl.InvalidSyntaxException Message: TNS-***** Invalid syntax error: Expected "(" before or at , cn = Stacktrace: oracle.net.jdbc.nl.NVFactory._readTopLevelNVPair(NVFactory.java:97) oracle.net.jdbc.nl.NVFactory.createNVPair(NVFactory.java:65) oracle.jdbc.driver.PhysicalConnection.isDRCPConnection(PhysicalConnection.java:11325) oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:1199) oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:105) oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:886) oracle.jdbc.datasource.impl.OracleDataSource.getPhysicalConnection(OracleDataSource.java:707) oracle.jdbc.datasource.impl.OracleDataSource.getConnection(OracleDataSource.java:381) oracle.jdbc.datasource.impl.OracleDataSource.getConnectionInternal(OracleDataSource.java:2206) oracle.jdbc.datasource.impl.OracleDataSource.getConnection(OracleDataSource.java:354) oracle.jdbc.datasource.impl.OracleDataSource.getConnection(OracleDataSource.java:315) org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:157) org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:115) org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:78) org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:319) org.springframework.jdbc.core.metadata.CallMetaDataProviderFactory.createMetaDataProvider(CallMetaDataProviderFactory.java:74) org.springframework.jdbc.core.metadata.CallMetaDataContext.initializeMetaData(CallMetaDataContext.java:253) org.springframework.jdbc.core.simple.AbstractJdbcCall.compileInternal(AbstractJdbcCall.java:314) org.springframework.jdbc.core.simple.AbstractJdbcCall.compile(AbstractJdbcCall.java:297) org.springframework.jdbc.core.simple.AbstractJdbcCall.checkCompiled(AbstractJdbcCall.java:356) org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:383) org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:187) com.esrx.services.inventory.scanner.persistence.repository.impl.InventoryCountsRepositoryImpl.getSnapshotsList(InventoryCountsRepositoryImpl.java:70) com.esrx.services.inventory.scanner.persistence.repository.impl.InventoryCountsRepositoryImpl$$FastClassBySpringCGLIB$$970dd8c.invoke(<generated>) org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) com.esrx.services.inventory.scanner.persistence.repository.impl.InventoryCountsRepositoryImpl$$EnhancerBySpringCGLIB$$.getSnapshotsList com.esrx.services.inventory.scanner.core.service.impl.InventoryCountsServiceImpl.getSnapshotsList(InventoryCountsServiceImpl.java:35) com.esrx.services.inventory.scanner.web.controller.InventoryCountsController.getSnapshots(InventoryCountsController.java:56) jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java) jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
相关代码片段
存储过程调用代码
异常抛出在SimpleJdbcCall.execute()方法内:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall( dataSourcePool.getCurrentUserJdbcTemplate()) .withoutProcedureColumnMetaDataAccess() .withSchemaName(Constants.SCHEMA_NAME) .withCatalogName(Constants.CATALOG_NAME) .withProcedureName(Constants.ASSIGN_COUNTSHEET_ROW) .declareParameters(new SqlParameter(Constants.P_SNAPSHOT_ID, Types.NUMERIC), new SqlParameter(Constants.P_COUNT_SHEET_NUMBER, Types.NUMERIC), new SqlParameter(Constants.P_SEARCH_WHBIN, new SqlOutParameter("p_abbrv", Types.VARCHAR)); SqlParameterSource paramsIn = new MapSqlParameterSource() .addValue(Constants.P_SNAPSHOT_ID, pSnapshotId) .addValue(Constants.P_COUNT_SHEET_NUMBER, pCountSheetNumber) .addValue(Constants.P_SEARCH_WHBIN, searchWhbin); Map<String, Object> out = jdbcCall.execute(paramsIn);
数据源创建代码
异常仅抛出一次,第二次尝试成功,该数据源供所有数据库调用使用:
public OracleDataSource getDataSource(String username, String credential) throws SQLException { log.info("..:: --> Create DataSource <-- ::.."); OracleDataSource dataSource = new OracleDataSource(); dataSource.setUser(username); dataSource.setPassword(credential); dataSource.setURL(this.getUrl()); // dataSource.getFastConnectionFailoverEnabled(); // Try to get the connection, if credentials are not valid it will // not connect and throw an SQLException try(OracleConnection connection = (OracleConnection) dataSource.getConnection()){ } log.info("Succesful datasource creation for user {}", username); return dataSource; }
注:OracleDataSource是非池化的,这是应用的需求。
已测试环境
- Java版本:8、11
- Oracle数据库版本:19.0.0.0.0
- Oracle驱动版本:ojdbc8 19.3.0.0、ojdbc11 23.5.0.24.07(均存在该问题)
已尝试方案
- 升级Java和Oracle驱动版本,问题未解决;
- 改用HikariDataSource(池化数据源)后问题消失,但因应用需支持多Oracle用户独立登录,而HikariDataSource仅支持单用户连接池,无法使用。
内容的提问来源于stack exchange,提问作者Carlos Avila
相关产品推荐
相关产品推荐

