基于列标签的CachedRowSetImpl getString抛出“无效的列名”

时间:2013-03-03 10:28:39

标签: java sql jdbc resultset

当我执行以下代码时,一切都很好:

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

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

5 个答案:

答案 0 :(得分:6)

问题是CachedRowSetcom.sun.rowset.CachedRowSetImpl)的参考实现包含一个错误:当您按名称检索列时,它使用columnName不< / strong> columnLabel,因此违反了使用columnLabel检索值的JDBC规范的其余部分。此错误使得无法通过columnLabel从行集中检索值。

Oracle的错误是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提供更好的支持,JdbcTemplate是{{1}}在使用RowSet时使用的默认实现。< / p>

答案 1 :(得分: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"));

答案 2 :(得分:1)

一种解决方法似乎是将列包装在函数或数学运算中。然后,您可以使用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&#34;无效的列名称&#34;。

但如果您的SQL是这样的:

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

然后它就像你期望的那样工作。

我不确定性能损失会对此产生什么影响,但它会起作用。

答案 3 :(得分: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());
}

答案 4 :(得分:0)

最新的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);