2013-03-03 66 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 

然後它如你所願。

我不確定這會對性能造成怎樣的影響,但是它會在一個小問題中起作用。