import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.springframework.jdbc.support.JdbcUtils;
import com.huawei.widget.commons.dao.DBException;
import com.huawei.widget.commons.dao.RowMapper;
import com.huawei.widget.commons.dao.util.NumberUtils;
/**
* 返回单列的RowMapper实现
*
* @author g00106664
* @version C02 2009-4-27
* @since OpenEye WIDGET_SRV V100R001C02
*/
@SuppressWarnings("unchecked")
public class SingleColumnRowMapper implements RowMapper
{
/**
* 需转换的类型
*/
private Class requiredType;
/**
* Create a new SingleColumnRowMapper.
*
* @see #setRequiredType
*/
public SingleColumnRowMapper()
{
}
/**
* Create a new SingleColumnRowMapper.
*
* @param requiredType
* the type that each result object is expected to match
*/
public SingleColumnRowMapper(Class requiredType)
{
this.requiredType = requiredType;
}
/**
* Set the type that each result object is expected to match.
* <p>
* If not specified, the column value will be exposed as returned by the
* JDBC driver.
*
* @param requiredType
* 所需类型
*/
public void setRequiredType(Class requiredType)
{
this.requiredType = requiredType;
}
/**
* 将每行数据映射为实体类
*
* @param rs
* ResultSet
* @param rowNum
* the row num
* @return the t
* @throws java.sql.SQLException
* the SQL exception
* @see com.huawei.widget.db.RowMapper#mapRow(ResultSet, int)
*/
public Object mapRow(ResultSet rs, int rowNum) throws SQLException
{
// Validate column count.
ResultSetMetaData rsmd = rs.getMetaData();
int nrOfColumns = rsmd.getColumnCount();
if (nrOfColumns != 1)
{
throw new DBException("Incorrect column count: expected " + 1
+ ", actual " + nrOfColumns);
}
// Extract column value from JDBC ResultSet
Object result = getColumnValue(rs, 1, this.requiredType);
if (result != null && this.requiredType != null
&& !this.requiredType.isInstance(result))
{
// Extracted value does not match already: try to convert it.
try
{
return convertValueToRequiredType(result, this.requiredType);
}
catch (IllegalArgumentException ex)
{
throw new DBException("Type mismatch affecting row number "
+ rowNum + " and column type '"
+ rsmd.getColumnTypeName(1) + "': " + ex.getMessage());
}
}
return result;
}
/**
* Retrieve a JDBC object value for the specified column.
* <p>
* The default implementation calls <code>ResultSet.getString(index)</code>
* etc for all standard value types (String, Boolean, number types, date
* types, etc). It calls <code>ResultSet.getObject(index)</code> else.
* <p>
* If no required type has been specified, this method delegates to
* <code>getColumnValue(rs, index)</code>, which basically calls
* <code>ResultSet.getObject(index)</code> but applies some additional
* default conversion to appropriate value types.
* <p>
* Explicit extraction of a String is necessary to properly extract an
* Oracle RAW value as a String, for example. For the other given types, it
* is also recommendable to extract the desired types explicitly, to let the
* JDBC driver perform appropriate (potentially database-specific)
* conversion.
*
* @param rs
* is the ResultSet holding the data
* @param index
* is the column index
* @param requiredType
* the type that each result object is expected to match (or
* <code>null</code> if none specified)
* @return the Object value
* @throws java.sql.SQLException
* 抛出异常
* @see java.sql.ResultSet#getString(int)
* @see java.sql.ResultSet#getObject(int)
* @see #getColumnValue(java.sql.ResultSet, int)
*/
protected Object getColumnValue(ResultSet rs, int index, Class requiredType)
throws SQLException
{
if (requiredType != null)
{
Object value = null;
boolean wasNullCheck = false;
// Explicitly extract typed value, as far as possible.
if (String.class.equals(requiredType))
{
value = rs.getString(index);
}
else if (Boolean.class.equals(requiredType))
{
value = (rs.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE);
wasNullCheck = true;
}
else if (Byte.class.equals(requiredType))
{
value = Byte.valueOf(rs.getByte(index));
wasNullCheck = true;
}
else if (Short.class.equals(requiredType))
{
value = Short.valueOf(rs.getShort(index));
wasNullCheck = true;
}
else if (Integer.class.equals(requiredType))
{
value = Integer.valueOf(rs.getInt(index));
wasNullCheck = true;
}
else if (Long.class.equals(requiredType))
{
value = Long.valueOf(rs.getLong(index));
wasNullCheck = true;
}
else if (Float.class.equals(requiredType))
{
value = Float.valueOf(rs.getFloat(index));
wasNullCheck = true;
}
else if (Double.class.equals(requiredType)
|| Number.class.equals(requiredType))
{
value = Double.valueOf(rs.getDouble(index));
wasNullCheck = true;
}
else if (byte[].class.equals(requiredType))
{
value = rs.getBytes(index);
}
else if (java.sql.Date.class.equals(requiredType))
{
value = rs.getDate(index);
}
else if (java.sql.Time.class.equals(requiredType))
{
value = rs.getTime(index);
}
else if (java.sql.Timestamp.class.equals(requiredType)
|| java.util.Date.class.equals(requiredType))
{
value = rs.getTimestamp(index);
}
else if (BigDecimal.class.equals(requiredType))
{
value = rs.getBigDecimal(index);
}
else if (Blob.class.equals(requiredType))
{
value = rs.getBlob(index);
}
else if (Clob.class.equals(requiredType))
{
value = rs.getClob(index);
}
else
{
// Some unknown type desired -> rely on getObject.
value = rs.getObject(index);
}
// Perform was-null check if demanded (for results that the
// JDBC driver returns as primitives).
if (wasNullCheck && value != null && rs.wasNull())
{
value = null;
}
return value;
}
else
{
// No required type specified -> perform default extraction.
return getColumnValue(rs, index);
}
}
/**
* Retrieve a JDBC object value for the specified column, using the most
* appropriate value type. Called if no required type has been specified.
* <p>
* The default implementation delegates to
* <code>JdbcUtils.getResultSetValue()</code>, which uses the
* <code>ResultSet.getObject(index)</code> method. Additionally, it
* includes a "hack" to get around Oracle returning a non-standard object
* for their TIMESTAMP datatype. See the
* <code>JdbcUtils#getResultSetValue()</code> javadoc for details.
*
* @param rs
* is the ResultSet holding the data
* @param index
* is the column index
* @return the Object value
* @throws java.sql.SQLException
* throw
* @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet,
* int)
*/
protected Object getColumnValue(ResultSet rs, int index)
throws SQLException
{
return JdbcUtils.getResultSetValue(rs, index);
}
/**
* Convert the given column value to the specified required type. Only
* called if the extracted column value does not match already.
* <p>
* If the required type is String, the value will simply get stringified via
* <code>toString()</code>. In case of a Number, the value will be
* converted into a Number, either through number conversion or through
* String parsing (depending on the value type).
*
* @param value
* the column value as extracted from
* <code>getColumnValue()</code> (never <code>null</code>)
* @param requiredType
* the type that each result object is expected to match (never
* <code>null</code>)
* @return the converted value
* @see #getColumnValue(java.sql.ResultSet, int, Class)
*/
protected Object convertValueToRequiredType(Object value, Class requiredType)
{
if (String.class.equals(this.requiredType))
{
return value.toString();
}
else if (Number.class.isAssignableFrom(this.requiredType))
{
if (value instanceof Number)
{
// Convert original Number to target Number class.
return NumberUtils.convertNumberToTargetClass(((Number) value),
this.requiredType);
}
else
{
// Convert stringified value to target Number class.
return NumberUtils.parseNumber(value.toString(),
this.requiredType);
}
}
else
{
throw new IllegalArgumentException("Value [" + value
+ "] is of type [" + value.getClass().getName()
+ "] and cannot be converted to required type ["
+ this.requiredType.getName() + "]");
}
}
}