2013-03-03 73 views
4

对CachedRowSetImpl的getString抛出“无效列名”

ResultSet rs = con.prepareStatement("SELECT r.UID AS R FROM r").executeQuery(); 
System.out.println(rs.getMetaData().getColumnLabel(1)); 
rs.next(); 
System.out.println(rs.getString("R")); 

结果是:

R 
23 

但是,当我执行下面的代码:

ResultSet rs = con.prepareStatement("SELECT r.UID AS R FROM r").executeQuery(); 
CachedRowSetImpl rslt = new CachedRowSetImpl(); 
rslt.populate(rs); 
System.out.println(rslt.getMetaData().getColumnLabel(1)); 
rslt.next(); 
System.out.println(rslt.getString("R")); 

结果是:

R 
java.sql.SQLException: Invalid column name 

为什么它在这里抛出异常?

回答

4

的问题是,CachedRowSetcom.sun.rowset.CachedRowSetImpl)的参考实现包含一个错误:当您检索通过名称的列,它使用columnName,而columnLabel,为此逆着JDBC规范的其余部分,其使用columnLabel来检索值。此错误使得不可能通过columnLabel从行集检索值。

甲骨文的错误是http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7046875,但是(令人惊讶,令人意外)他们让它无法公开观看。

有两种潜在的解决方法。一个是检查你的驱动程序是否提供了一个属性,以使ResultSetMetaData.getColumnName(..)方法返回columnLabel的值,第二个解决方法是创建CachedRowSetImpl的子类(这不幸需要很多覆盖方法)。

版本低于从该消息复制:http://tech.groups.yahoo.com/group/Firebird-Java/message/10715

import java.math.BigDecimal; 
import java.sql.Array; 
import java.sql.Blob; 
import java.sql.Clob; 
import java.sql.Ref; 
import java.sql.SQLException; 
import java.util.Calendar; 
import java.util.Collection; 
import java.util.Hashtable; 

import javax.sql.rowset.RowSetMetaDataImpl; 

import com.sun.rowset.CachedRowSetImpl; 

public class FixedCachedRowSetImpl extends CachedRowSetImpl { 

    private static final long serialVersionUID = -9067504047398250113L; 
    private RowSetMetaDataImpl RowSetMD; 

    public FixedCachedRowSetImpl() throws SQLException { 
     super(); 
    } 

    public FixedCachedRowSetImpl(Hashtable env) throws SQLException { 
     super(env); 
    } 

    private int getColIdxByName(String name) throws SQLException { 
     RowSetMD = (RowSetMetaDataImpl) this.getMetaData(); 
     int cols = RowSetMD.getColumnCount(); 

     for (int i = 1; i <= cols; ++i) { 
      String colName = RowSetMD.getColumnLabel(i); 
      if (colName != null) if (name.equalsIgnoreCase(colName)) 
       return (i); 
      else 
       continue; 
     } 
     throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalcolnm").toString()); 
    } 

    @Override 
    public Collection<?> toCollection(String column) throws SQLException { 
     return toCollection(getColIdxByName(column)); 
    } 

    @Override 
    public String getString(String columnName) throws SQLException { 
     return getString(getColIdxByName(columnName)); 
    } 

    @Override 
    public boolean getBoolean(String columnName) throws SQLException { 
     return getBoolean(getColIdxByName(columnName)); 
    } 

    @Override 
    public byte getByte(String columnName) throws SQLException { 
     return getByte(getColIdxByName(columnName)); 
    } 

    @Override 
    public short getShort(String columnName) throws SQLException { 
     return getShort(getColIdxByName(columnName)); 
    } 

    @Override 
    public int getInt(String columnName) throws SQLException { 
     return getInt(getColIdxByName(columnName)); 
    } 

    @Override 
    public long getLong(String columnName) throws SQLException { 
     return getLong(getColIdxByName(columnName)); 
    } 

    @Override 
    public float getFloat(String columnName) throws SQLException { 
     return getFloat(getColIdxByName(columnName)); 
    } 

    @Override 
    public double getDouble(String columnName) throws SQLException { 
     return getDouble(getColIdxByName(columnName)); 
    } 

    @Override 
    public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { 
     return getBigDecimal(getColIdxByName(columnName), scale); 
    } 

    @Override 
    public byte[] getBytes(String columnName) throws SQLException { 
     return getBytes(getColIdxByName(columnName)); 
    } 

    @Override 
    public java.sql.Date getDate(String columnName) throws SQLException { 
     return getDate(getColIdxByName(columnName)); 
    } 

    @Override 
    public java.sql.Time getTime(String columnName) throws SQLException { 
     return getTime(getColIdxByName(columnName)); 
    } 

    @Override 
    public java.sql.Timestamp getTimestamp(String columnName) throws SQLException { 
     return getTimestamp(getColIdxByName(columnName)); 
    } 

    @Override 
    public java.io.InputStream getAsciiStream(String columnName) throws SQLException { 
     return getAsciiStream(getColIdxByName(columnName)); 

    } 

    @Override 
    public java.io.InputStream getUnicodeStream(String columnName) throws SQLException { 
     return getUnicodeStream(getColIdxByName(columnName)); 
    } 

    @Override 
    public java.io.InputStream getBinaryStream(String columnName) throws SQLException { 
     return getBinaryStream(getColIdxByName(columnName)); 
    } 

    @Override 
    public Object getObject(String columnName) throws SQLException { 
     return getObject(getColIdxByName(columnName)); 
    } 

    @Override 
    public int findColumn(String columnName) throws SQLException { 
     return getColIdxByName(columnName); 
    } 

    @Override 
    public java.io.Reader getCharacterStream(String columnName) throws SQLException { 
     return getCharacterStream(getColIdxByName(columnName)); 
    } 

    @Override 
    public BigDecimal getBigDecimal(String columnName) throws SQLException { 
     return getBigDecimal(getColIdxByName(columnName)); 
    } 

    @Override 
    public boolean columnUpdated(String columnName) throws SQLException { 
     return columnUpdated(getColIdxByName(columnName)); 
    } 

    @Override 
    public void updateNull(String columnName) throws SQLException { 
     updateNull(getColIdxByName(columnName)); 
    } 

    @Override 
    public void updateBoolean(String columnName, boolean x) throws SQLException { 
     updateBoolean(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateByte(String columnName, byte x) throws SQLException { 
     updateByte(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateShort(String columnName, short x) throws SQLException { 
     updateShort(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateInt(String columnName, int x) throws SQLException { 
     updateInt(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateLong(String columnName, long x) throws SQLException { 
     updateLong(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateFloat(String columnName, float x) throws SQLException { 
     updateFloat(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateDouble(String columnName, double x) throws SQLException { 
     updateDouble(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { 
     updateBigDecimal(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateString(String columnName, String x) throws SQLException { 
     updateString(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateBytes(String columnName, byte x[]) throws SQLException { 
     updateBytes(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateDate(String columnName, java.sql.Date x) throws SQLException { 
     updateDate(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateTime(String columnName, java.sql.Time x) throws SQLException { 
     updateTime(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException { 
     updateTimestamp(getColIdxByName(columnName), x); 
    } 

    @Override 
    public void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException { 
     updateAsciiStream(getColIdxByName(columnName), x, length); 
    } 

    @Override 
    public void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException { 
     updateBinaryStream(getColIdxByName(columnName), x, length); 
    } 

    @Override 
    public void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException { 
     updateCharacterStream(getColIdxByName(columnName), reader, length); 
    } 

    @Override 
    public void updateObject(String columnName, Object x, int scale) throws SQLException { 
     updateObject(getColIdxByName(columnName), x, scale); 
    } 

    @Override 
    public void updateObject(String columnName, Object x) throws SQLException { 
     updateObject(getColIdxByName(columnName), x); 
    } 

    @Override 
    public Object getObject(String columnName, java.util.Map<String, Class<?>> map) throws SQLException { 
     return getObject(getColIdxByName(columnName), map); 
    } 

    @Override 
    public Ref getRef(String colName) throws SQLException { 
     return getRef(getColIdxByName(colName)); 
    } 

    @Override 
    public Blob getBlob(String colName) throws SQLException { 
     return getBlob(getColIdxByName(colName)); 
    } 

    @Override 
    public Clob getClob(String colName) throws SQLException { 
     return getClob(getColIdxByName(colName)); 
    } 

    @Override 
    public Array getArray(String colName) throws SQLException { 
     return getArray(getColIdxByName(colName)); 
    } 

    @Override 
    public java.sql.Date getDate(String columnName, Calendar cal) throws SQLException { 
     return getDate(getColIdxByName(columnName), cal); 
    } 

    @Override 
    public java.sql.Time getTime(String columnName, Calendar cal) throws SQLException { 
     return getTime(getColIdxByName(columnName), cal); 
    } 

    @Override 
    public java.sql.Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { 
     return getTimestamp(getColIdxByName(columnName), cal); 
    } 

    @Override 
    public void updateRef(String columnName, java.sql.Ref ref) throws SQLException { 
     updateRef(getColIdxByName(columnName), ref); 
    } 

    @Override 
    public void updateClob(String columnName, Clob c) throws SQLException { 
     updateClob(getColIdxByName(columnName), c); 
    } 

    @Override 
    public void updateBlob(String columnName, Blob b) throws SQLException { 
     updateBlob(getColIdxByName(columnName), b); 
    } 

    @Override 
    public void updateArray(String columnName, Array a) throws SQLException { 
     updateArray(getColIdxByName(columnName), a); 
    } 

    @Override 
    public java.net.URL getURL(String columnName) throws SQLException { 
     return getURL(getColIdxByName(columnName)); 
    } 
} 

你也可以看看org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet,这也说:

注:由于JDBC 4.0,已阐明,任何使用String来标识列的方法应该使用列标签。列标签是使用SQL查询字符串中的ALIAS关键字分配的。当查询不使用ALIAS时,默认标签是列名称。大多数JDBC ResultSet实现遵循这种新模式,但也有例外,例如仅使用列名的com.sun.rowset.CachedRowSetImpl类,忽略任何列标签。从Spring 3.0.5开始,ResultSetWrappingSqlRowSet会将列标签转换为正确的列索引,以更好地支持com.sun.rowset.CachedRowSetImpl,这是使用RowSets时JdbcTemplate使用的默认实现。

+0

在Netbeans中,当尝试创建新的ResultSetWrappingSqlRowSet(crs)时,InvalidResultSetAccessException无法转换为throwable,其中crs是CachedRowSetIml。同时抛出子句和try-catch失败。你能提出一些建议吗? – Zon 2014-08-05 19:33:53

+0

至于第一个变种 - 你如何做crs.populate(rs);如果没有看到填充方法,尽管它从CachedRowSetImpl扩展而来? – Zon 2014-08-05 20:24:25

+1

@Zon'ResultSetWrappingSqlRowSet'可以 - 据我所知 - 只包装一个普通的'ResultSet'。你应该使用它来代替'CachedRowSetImpl',而不是包装一个'CachedRowSetImpl'(这也会破坏包装的目的,因为它已经太晚了)。至于你的第二个问题:方法存在于超类中,所以你应该可以调用它。 – 2014-08-05 20:29:23

0

这里我getColIdxByName的改进版本,以支持MySQL 5.x的名称,如 “tbl.column”:

private int getColIdxByName(String name) throws SQLException { 
    RowSetMD = (RowSetMetaDataImpl) this.getMetaData(); 
    int cols = RowSetMD.getColumnCount(); 

    for (int i = 1; i <= cols; ++i) { 
     String colLabel = RowSetMD.getColumnLabel(i); 
     String colName = RowSetMD.getColumnName(i); 
     if (colName != null) if (name.equalsIgnoreCase(colName) || name.equalsIgnoreCase(RowSetMD.getTableName(i) + "." + colName)) {    
      return (i); 
     } 
     else if (colLabel != null) if (name.equalsIgnoreCase(colLabel)) { 
      return (i); 
     } 
     else 
      continue; 
    } 
    throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.invalcolnm").toString()); 
} 
0

最新的JDK 1.7已经实现了CachedRowSet的和标签名称错误已得到修复!

import java.sql.ResultSet; 
import javax.sql.rowset.CachedRowSet; 
import javax.sql.rowset.RowSetFactory; 
import javax.sql.rowset.RowSetProvider; 

ResultSet rs = con.prepareStatement("SELECT r.UID AS R FROM r").executeQuery(); 
RowSetFactory rowSetFactory = RowSetProvider.newFactory(); 
CachedRowSet crs = rowSetFactory.createCachedRowSet(); 
crs.populate(rs); 
+1

不适用于Java 7 - 静态列名称不是列标签。 – Zon 2014-08-05 19:54:53

+0

也不适用于Java 8。 – Dave 2017-09-12 15:01:38

1

您可以使用内部的选择:

ResultSet rs = con.prepareStatement("SELECT * FROM (SELECT r.UID AS R FROM r) AA").executeQuery(); 
CachedRowSetImpl rslt = new CachedRowSetImpl(); 
rslt.populate(rs); 
System.out.println(rslt.getMetaData().getColumnLabel(1)); 
rslt.next(); 
System.out.println(rslt.getString("R")); 
+0

这只是给了我一个错误'表' .AA'不存在'。我的'PreparedStatement'和'ResultSet'工作正常。 – Dave 2017-09-12 15:35:00

0

一个解决办法似乎是包裹在一个函数或数学运算的列。然后您可以使用CachedRowSetImpl中的别名。

如果你的SQL是这样的:

SELECT 
    id AS student_id, 
    cost - discount AS total_cost, 
    first_name AS name 
FROM 
    students 

您可以指studentRow.getBigDecimal("total_cost"),但studentRow.getLong("student_id")studentRow.getString("name")将失败,并SQLException“无效列名”。

但如果你的SQL是这样的:

SELECT 
    id + 0 AS student_id, 
    cost - discount AS total_cost, 
    CONCAT(first_name) AS name 
FROM 
    students 

然后它如你所愿。

我不确定这会对性能造成怎样的影响,但是它会在一个小问题中起作用。