使用Spring JDBC对Oracle10g进行数据库分页的时候出现了异常,错误信息如下:
[ERROR]2971656-2009-05-1815:38:24- [com.ieslab.idp.datamaint.service.impl.DataGridServiceImpl. buildGrid(DataGridServiceImpl.java: 171)]-[]-org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL [select * from (select temp.* ,ROWNUM num from ( select * from idp_pub_m.厂站_VW) temp where ROWNUM <= 4 ) where num > 0]; SQL state [null]; error code [0]; Invalid scale size. Cannot be less than zero; nested exception is java.sql.SQLException: Invalid scale size. Cannot be less than zero
上面错误中,进行的操作是从一个视图中查询数据并进行分页操作,当单独执行:
select temp.* ,ROWNUM num from ( select * from idp_pub_m.厂站_VW) temp where ROWNUM <= 4
时,是没有错误的,可以成功执行。有网友说是Oracle10g中对返回的结果集中数据精度的定义和9i中不同;例如同样是
select count(*) from ......
语句,在oracle 10g中使用Spring JDBC操作时就会有问题,异常信息同样是 java.sql.SQLException: Invalid scale size. Cannot be less than zero;将SQL语句写为
select count(*)+0 from ......
就可以解决。
这是另一个网友aggie2000 给出的解答,贴于此处,留作备忘。呵呵!
原帖地址是http://forum.springsource.org/showthread.php?t=19848
I have been dealing with the same problem (SQLException - "Invalid scale size. Cannot be less than zero") and believe I have arrived at a better solution for those who wish to use the Spring API as much as possible.
The basic problem, as I understand it, is that there is an incompatibility between Oracle and the standard CachedRowSet implementation (CachedRowSetImpl) of Java 1.5. Spring uses this implementation by default when you call queryForRowSet(...). However, this does not mean that you cannot use SqlRowSet. The SqlRowSet class doesn't know anything about the implementation of the CachedRowSet interface that you're using. The class that is actually utilizing the CachedRowSetImpl class is the ResultSetExtractor... more specifically, the SqlRowSetResultSetExtractor (this is used by Spring when you call queryForRowSet).
In order to achieve the same result (returning a Spring SqlRowSet), you can pass in your own ResultSetExtractor to the query(...) methods (*not* queryForRowSet) that take a ResultSetExtractor as a parameter. What I did was just clone the SqlRowSetResultSetExtractor and instead of using the standard CachedRowSetImpl class, I replaced it with Oracle's CachedRowSet implementation. This way, when the ResultSet is mapped to a CachedRowSet, it uses Oracle's implementation to do so and thus the incompatibility is eliminated. Here is my ResultSetExtractor class that does just that...
-------------------------
package com.yada.yada.yada; import java.sql.ResultSet; import java.sql.SQLException; import javax.sql.rowset.CachedRowSet; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.support.rowset.ResultSetW rappingSqlRowSet; import org.springframework.jdbc.support.rowset.SqlRowSet; import oracle.jdbc.rowset.OracleCachedRowSet; public class SqlRowSetOracleResultSetExtractor implements ResultSetExtractor { public Object extractData(ResultSet rs) throws SQLException { return createSqlRowSet(rs); } /** * Create a SqlRowSet that wraps the given ResultSet, * representing its data in a disconnected fashion. *This implementation creates a Spring ResultSetWrappingSqlRowSet * instance that wraps a standard JDBC CachedRowSet instance. * Can be overridden to use a different implementation. * @param rs the original ResultSet (connected) * @return the disconnected SqlRowSet * @throws SQLException if thrown by JDBC methods * @see #newCachedRowSet * @see org.springframework.jdbc.support.rowset.ResultSetW rappingSqlRowSet */ protected SqlRowSet createSqlRowSet(ResultSet rs) throws SQLException { CachedRowSet rowSet = newCachedRowSet(); rowSet.populate(rs); return new ResultSetWrappingSqlRowSet(rowSet); } /** * Create a new CachedRowSet instance, to be populated by * the
createSqlRowSet
implementation. *This implementation creates a new instance of * Oracle's
oracle.jdbc.rowset.OracleCachedRowSet
class, * which is their implementation of the Java 1.5 CachedRowSet interface. * @return a new CachedRowSet instance * @throws SQLException if thrown by JDBC methods * @see #createSqlRowSet * @see oracle.jdbc.rowset.OracleCachedRowSet */ protected CachedRowSet newCachedRowSet() throws SQLException { return new OracleCachedRowSet(); } }
-------------------------
You can pass this to the various query methods like so:
SqlRowSet sqlRowSet = (SqlRowSet)jdbcTemplate.query(sql, new SqlRowSetOracleResultSetExtractor());
Hope that helps! Seems to be working for me.
我按照aggie2000 给出的回复解决了这个问题,上面的代码中引用的几个类在Oracle10g的驱动ojdbc14.jar中。至于具体原因,aggie2000 已经给出了答案,欢迎对这个问题熟悉的朋友,回复讨论。