java - etiquetas - cachedrowsetimpl getString basado en la etiqueta de la columna
libreria de etiquetas jsp (5)
En el nombre de Dios
Zalema,
Tengo un problema. Cuando ejecuto un código de seguimiento, todo está bien:
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"));
y el resultado será:
R
23
pero cuando ejecuto el código siguiente, veo algunas cosas diferentes:
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"));
y el resultado será:
R
java.sql.SQLException: Invalid column name
¿Alguien puede ayudarme, por favor?
¡el último JDK 1.7 ya implementó CachedRowSet y se ha corregido el error del nombre de la etiqueta!
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);
Aquí mi versión mejorada de getColIdxByName para soportar nombres de MySQL 5.x como "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());
}
El problema es que la implementación de referencia de CachedRowSet
( com.sun.rowset.CachedRowSetImpl
) contiene un error: cuando recupera una columna por nombre, utiliza el columnName
, y no el columnLabel
, por lo que va contra el resto de la especificación JDBC que usa columnLabel
para recuperar valores. Este error hace que sea imposible recuperar valores del conjunto de filas mediante columnLabel
.
El error en Oracle es http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7046875 , pero (sorpresa, sorpresa) lo han hecho no disponible para su consulta pública.
Hay dos posibles soluciones. Una de ellas es comprobar si el controlador proporciona una propiedad para que también el método ResultSetMetaData.getColumnName(..)
devuelva el valor de columnLabel
, la segunda solución sería crear una subclase de CachedRowSetImpl
(que, lamentablemente, requiere muchos métodos de anulación).
La siguiente versión se copia de este mensaje: 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));
}
}
También puede consultar org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet
, que también dice:
Nota: desde JDBC 4.0, se ha aclarado que cualquier método que use una cadena para identificar la columna debe usar la etiqueta de la columna. La etiqueta de la columna se asigna con la palabra clave ALIAS en la cadena de consulta SQL. Cuando la consulta no utiliza un ALIAS, la etiqueta predeterminada es el nombre de la columna. La mayoría de las implementaciones de ResultSet de JDBC siguen este nuevo patrón, pero existen excepciones como la clase com.sun.rowset.CachedRowSetImpl que solo utiliza el nombre de columna, ignorando las etiquetas de columna. A partir de Spring 3.0.5,
ResultSetWrappingSqlRowSet
traducirá las etiquetas de las columnas al índice de columnas correcto para proporcionar una mejor compatibilidad concom.sun.rowset.CachedRowSetImpl
que es la implementación predeterminada utilizada porJdbcTemplate
cuando se trabaja con RowSets.
Puede usar la selección interna:
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"));
Una solución alternativa parece ser envolver sus columnas en una función u operación matemática. Luego puede usar el alias en CachedRowSetImpl
.
Si tu SQL es este:
SELECT
id AS student_id,
cost - discount AS total_cost,
first_name AS name
FROM
students
Podrá referirse a studentRow.getBigDecimal("total_cost")
, pero studentRow.getLong("student_id")
y studentRow.getString("name")
fallará con una SQLException
"Invalid column name".
Pero si tu SQL fue así:
SELECT
id + 0 AS student_id,
cost - discount AS total_cost,
CONCAT(first_name) AS name
FROM
students
Entonces funciona como era de esperar.
No estoy seguro de cuál sería la penalización de rendimiento para esto, pero funcionará en caso de apuro.