All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.executor.jvm.SingleColumnRowMapper Maven / Gradle / Ivy

There is a newer version: 4.30.0
Show newest version
package liquibase.executor.jvm;

import liquibase.util.JdbcUtil;
import liquibase.util.NumberUtil;
import liquibase.util.ObjectUtil;

import java.math.BigDecimal;
import java.sql.*;

/**
 * RowMapper implementation that converts a single column into
 * a single result value per row. Expects to work on a ResultSet
 * that just contains a single column.
 * 

*

The type of the result value for each row can be specified. * The value for the single column will be extracted from the ResultSet * and converted into the specified target type. * * @author Spring Framework */ 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. *

If not specified, the column value will be exposed as * returned by the JDBC driver. */ public void setRequiredType(Class requiredType) { this.requiredType = requiredType; } /** * Extract a value for the single column in the current row. *

Validates that there is only one column selected, * then delegates to getColumnValue() and also * convertValueToRequiredType, if necessary. * * @see java.sql.ResultSetMetaData#getColumnCount() * @see #getColumnValue(java.sql.ResultSet,int,Class) * @see #convertValueToRequiredType(Object,Class) */ @Override public Object mapRow(ResultSet rs, int rowNum) throws SQLException { // Validate column count. ResultSetMetaData rsmd = rs.getMetaData(); int nrOfColumns = rsmd.getColumnCount(); if (nrOfColumns == 0) { throw new SQLException("Returned no columns!"); } else if (nrOfColumns != 1) { throw new SQLException("Returned too many columns: "+ 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 SQLException( "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. *

The default implementation calls ResultSet.getString(index) etc * for all standard value types (String, Boolean, number types, date types, etc). * It calls ResultSet.getObject(index) else. *

If no required type has been specified, this method delegates to * getColumnValue(rs, index), which basically calls * ResultSet.getObject(index) but applies some additional * default conversion to appropriate value types. *

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 null if none specified) * @return the Object value * @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; 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 = rs.getByte(index); wasNullCheck = true; } else if (Short.class.equals(requiredType)) { value = rs.getShort(index); wasNullCheck = true; } else if (Integer.class.equals(requiredType)) { value = rs.getInt(index); wasNullCheck = true; } else if (Long.class.equals(requiredType)) { value = rs.getLong(index); wasNullCheck = true; } else if (Float.class.equals(requiredType)) { value = rs.getFloat(index); wasNullCheck = true; } else if (Double.class.equals(requiredType) || Number.class.equals(requiredType)) { value = 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. *

The default implementation delegates to JdbcUtils.getResultSetValue(), * which uses the ResultSet.getObject(index) method. Additionally, * it includes a "hack" to get around Oracle returning a non-standard object for * their TIMESTAMP datatype. See the JdbcUtils#getResultSetValue() * javadoc for details. * * @param rs is the ResultSet holding the data * @param index is the column index * @return the Object value */ protected Object getColumnValue(ResultSet rs, int index) throws SQLException { return JdbcUtil.getResultSetValue(rs, index); } /** * Convert the given column value to the specified required type. * Only called if the extracted column value does not match already. *

If the required type is String, the value will simply get stringified * via toString(). 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 getColumnValue() * (never null) * @param requiredType the type that each result object is expected to match * (never null) * @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 ObjectUtil.convert(value, this.requiredType); } else { // Convert stringified value to target Number class. return NumberUtil.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() + "]"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy