最近遇到一个奇怪的异常,使用Mybatis调用一个oracle的package时出现一个异常:
Mybatis中xxDAO.xml
异常信息如下:
16:09:32.399 [http-apr-8080-exec-6] ERROR c.xx.xx.xxService - Error occur when getting xx By xx. org.springframework.jdbc.UncategorizedSQLException: ### Error querying database. Cause: java.sql.SQLException: Non supported SQL92 token at position: 1: ### The error may exist in com/xx/xx/dao/xxDAO.xml ### The error may involve com.xx.xx.dao.xxDAO.getxxByUserId-Inline ### The error occurred while setting parameters ### SQL: { call ? := XX.do_xxSearch( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) } ### Cause: java.sql.SQLException: Non supported SQL92 token at position: 1: ; uncategorized SQLException for SQL []; SQL state [99999]; error code [17034]; Non supported SQL92 token at position: 1: ; nested exception is java.sql.SQLException: Non supported SQL92 token at position: 1: at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE] at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73) ~[mybatis-spring-1.2.1.jar:1.2.1] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368) ~[mybatis-spring-1.2.1.jar:1.2.1] at com.sun.proxy.$Proxy478.selectList(Unknown Source) ~[na:na] at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:198) ~[mybatis-spring-1.2.1.jar:1.2.1] at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:114) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:58) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:43) ~[mybatis-3.2.3.jar:3.2.3] at com.sun.proxy.$Proxy532.getAppWorkQueueByUserId(Unknown Source) ~[na:na] .... at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_31] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_31] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_31] at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_31] at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:205) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) [jersey-common-2.22.1.jar:na] at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) [jersey-common-2.22.1.jar:na] at org.glassfish.jersey.internal.Errors.process(Errors.java:315) [jersey-common-2.22.1.jar:na] at org.glassfish.jersey.internal.Errors.process(Errors.java:297) [jersey-common-2.22.1.jar:na] at org.glassfish.jersey.internal.Errors.process(Errors.java:267) [jersey-common-2.22.1.jar:na] at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317) [jersey-common-2.22.1.jar:na] at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154) [jersey-server-2.22.1.jar:na] at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:471) [jersey-container-servlet-core-2.22.1.jar:na] at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:425) [jersey-container-servlet-core-2.22.1.jar:na] at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:383) [jersey-container-servlet-core-2.22.1.jar:na] at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:336) [jersey-container-servlet-core-2.22.1.jar:na] at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:223) [jersey-container-servlet-core-2.22.1.jar:na] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) [catalina.jar:7.0.42] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) [catalina.jar:7.0.42] xx at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) [catalina.jar:7.0.42] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) [catalina.jar:7.0.42] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) [catalina.jar:7.0.42] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) [catalina.jar:7.0.42] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) [catalina.jar:7.0.42] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) [catalina.jar:7.0.42] at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) [catalina.jar:7.0.42] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) [catalina.jar:7.0.42] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) [catalina.jar:7.0.42] at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023) [tomcat-coyote.jar:7.0.42] at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) [tomcat-coyote.jar:7.0.42] at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1852) [tomcat-coyote.jar:7.0.42] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_31] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_31] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_31] Caused by: java.sql.SQLException: Non supported SQL92 token at position: 1: at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:133) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:199) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:263) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:271) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleSql.handleToken(OracleSql.java:1245) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleSql.handleODBC(OracleSql.java:1136) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleSql.parse(OracleSql.java:1053) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleSql.getSql(OracleSql.java:310) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleSql.getSqlBytes(OracleSql.java:604) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.T4CCallableStatement.doOall8(T4CCallableStatement.java:177) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.T4CCallableStatement.executeForRows(T4CCallableStatement.java:950) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1222) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3387) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3488) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OracleCallableStatement.execute(OracleCallableStatement.java:3857) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1374) ~[ojdbc6.jar:Oracle JDBC Driver version - "11.1.0.7.0-Production"] at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:172) ~[tomcat-dbcp.jar:7.0.42] at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:172) ~[tomcat-dbcp.jar:7.0.42] at org.apache.ibatis.executor.statement.CallableStatementHandler.query(CallableStatementHandler.java:63) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:70) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:57) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:259) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:132) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:115) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104) ~[mybatis-3.2.3.jar:3.2.3] at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98) ~[mybatis-3.2.3.jar:3.2.3] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_31] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_31] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_31] at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_31] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) ~[mybatis-spring-1.2.1.jar:1.2.1] ... 53 common frames omitted Feb 21, 2017 4:09:32 PM org.glassfish.jersey.filter.LoggingFilter log
这个异常实际上原因很简单,是因为xxDAO.xml中的大括号换行引起,解决方法也简单,升级oracle驱动,或是去掉换行。
因为这段代码在其他环境都是运行正常,就只有我电脑会有问题,更奇怪的是在我电脑上通过unit testing调用又是正常,一开始我是怀疑是代码不一致引起:unit testing和我部署到tomcat的代码是不一样的,但是通过我再三确认调查,代码肯定是同一份。
所以猜想是不是Mybatis的问题,由于Mybatis只是对jdbc进行封装,最终的底层还是通过jdbc执行数据库操作,会不会是在调用jdbc时传入的参数由于某种原因不一致导致?
public class ProcedureCallTest { public static void main(String args[]) throws Exception { DriverManager.registerDriver(new sun.jdbc.odbc.JdbcOdbcDriver()); Connection conn = DriverManager.getConnection("jdbc:odbc:mydata", "sa", ""); CallableStatement c = conn.prepareCall("{call getsum(?)}"); c.setInt(1, 100); c.execute(); conn.close(); } }
所以有可能不一致的地方有两个地方,conn.prepareCall参数不一样或是c.setInt设置参数有问题,跟踪Mybatis源码:
org.apache.ibatis.executor.statement.CallableStatementHandler.java
protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getResultSetType() != null) { return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.prepareCall(sql); } }
c.setInt设置参数是代码是:
public void parameterize(Statement statement) throws SQLException { registerOutputParameters((CallableStatement) statement); parameterHandler.setParameters((CallableStatement) statement); }
会根据参数类型调用不同的xxxhander.java设置参数,比如如果是参数类型是String,则调用org.apache.ibatis.type.StringTypeHandler中的ps.setString(i, parameter);设置
最终分析结果让我很困惑,unit testing和tomcat项目传入的参数值都是一样的,所以问题就不应该是Mybatis,而是更底层,就可能是oracle的jdbc驱动不一样引起,这回找到了问题:
unit testing使用的oracle驱动是通过maven配置的:
com.oracle ojdbc7 12.1.0.2.0 provided
scope是provided,所以jar是不会加入部署后的项目中,找了一下项目并没有发现有加入oracle驱动,最后是在tomcat的lib中找到,发现竟然是用ojdbc6,和其他运行环境对比了一下,原来他们都用了ojdbc7,升级一下这个包,异常消失,进一步分析结论如下:
是由于xxDAO.xml中的大括号换行引起(高亮的括号),在ojdbc7是没问题,但是在ojdbc6会引起上述异常,如果把换行去掉,改为:
这样在ojdbc6也正常,所以解决方案有两种:
1. 升级驱动
2. 大括号不要换行
我的博客:http://itart.top/article/8198299e2cd443a29feb23a6d7c92553