【问题标题】:CachedRowSetImpl getString based on column label throws "Invalid column name"CachedRowSetImpl getString 基于列标签抛出“无效的列名”
【发布时间】:2013-02-17 13:07:04
【问题描述】:

当我执行以下代码时一切正常:

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

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

【问题讨论】:

    标签: java sql jdbc resultset


    【解决方案1】:

    问题在于CachedRowSet (com.sun.rowset.CachedRowSetImpl) 的参考实现包含一个错误:当您按名称检索列时,它使用columnName,而不是 @987654327 @,因此违反了使用 columnLabel 检索值的 JDBC 规范的其余部分。此错误导致无法从 columnLabel 的行集中检索值。

    Oracle 的错误是 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7046875,但是(令人惊讶,惊讶)他们已使其无法供公众查看。

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

    以下版本复制自此消息:Re: Regression from 2.1.6

    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 起,已明确任何使用字符串标识列的方法都应使用列标签。列标签是使用 SQL 查询字符串中的 ALIAS 关键字分配的。当查询不使用 ALIAS 时,默认标签是列名。大多数 JDBC ResultSet 实现都遵循这种新模式,但也有例外,例如 com.sun.rowset.CachedRowSetImpl 类,它只使用列名,而忽略任何列标签。从 Spring 3.0.5 开始,ResultSetWrappingSqlRowSet 会将列标签转换为正确的列索引,以更好地支持 com.sun.rowset.CachedRowSetImpl,这是 JdbcTemplate 在使用 RowSets 时使用的默认实现。

    【讨论】:

    • 在 Netbeans 中,当我尝试创建新的 ResultSetWrappingSqlRowSet(crs) 时,我得到 InvalidResultSetAccessException cannot be convertable to throwable,其中 crs 是一个 CachedRowSetIml。 throws 子句和 try-catch 都失败。你能提出一些建议吗?
    • 至于第一个变种——crs.populate(rs);如果没有看到填充方法,尽管它从 CachedRowSetImpl 扩展?
    • @Zon ResultSetWrappingSqlRowSet 可以——据我所知——只包装一个普通的ResultSet。您应该使用它而不是 CachedRowSetImpl,而不是包装 CachedRowSetImpl(这也会破坏包装的目的,因为它已经太晚了)。至于你的第二个问题:该方法存在于超类中,所以你应该可以调用它。
    【解决方案2】:

    您可以使用内部选择:

    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"));
    

    【讨论】:

    • 这只是给了我一个错误Table '&lt;database_name&gt;.AA' doesn't exist。我的PreparedStatementResultSet 工作正常。
    【解决方案3】:

    一种解决方法似乎是将列包装在函数或数学运算中。然后就可以使用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
    

    然后它会按您的预期工作。

    我不确定这会对性能造成什么影响,但它会在紧要关头起作用。

    【讨论】:

      【解决方案4】:

      这是我改进的 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());
      }
      

      【讨论】:

        【解决方案5】:

        最新的 JDK 1.7 已经实现了 CachedRowSet 并且标签名称的 bug 已经修复!

        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);
        

        【讨论】:

        • 不适用于 Java 7 - 仍然是列名而不是列标签。
        猜你喜欢
        • 2016-07-29
        • 1970-01-01
        • 1970-01-01
        • 2019-09-05
        • 2013-11-29
        • 2023-02-22
        • 1970-01-01
        • 2020-06-12
        相关资源
        最近更新 更多