Coverage Report - liquibase.executor.jvm.SingleColumnRowMapper
 
Classes in this File Line Coverage Branch Coverage Complexity
SingleColumnRowMapper
0%
0/69
0%
0/58
6.429
 
 1  
 package liquibase.executor.jvm;
 2  
 
 3  
 import java.math.BigDecimal;
 4  
 import java.sql.Blob;
 5  
 import java.sql.Clob;
 6  
 import java.sql.ResultSet;
 7  
 import java.sql.ResultSetMetaData;
 8  
 import java.sql.SQLException;
 9  
 
 10  
 import liquibase.util.JdbcUtils;
 11  
 import liquibase.util.NumberUtils;
 12  
 
 13  
 /**
 14  
  * RowMapper implementation that converts a single column into a single result value per row. Expects to work on a
 15  
  * ResultSet that just contains a single column.
 16  
  * <p/>
 17  
  * <p>
 18  
  * The type of the result value for each row can be specified. The value for the single column will be extracted from
 19  
  * the ResultSet and converted into the specified target type.
 20  
  * 
 21  
  * @author Spring Framework
 22  
  */
 23  
 class SingleColumnRowMapper implements RowMapper {
 24  
 
 25  
     private Class requiredType;
 26  
 
 27  
     /**
 28  
      * Create a new SingleColumnRowMapper.
 29  
      * 
 30  
      * @see #setRequiredType
 31  
      */
 32  0
     public SingleColumnRowMapper() {
 33  0
     }
 34  
 
 35  
     /**
 36  
      * Create a new SingleColumnRowMapper.
 37  
      * 
 38  
      * @param requiredType
 39  
      *            the type that each result object is expected to match
 40  
      */
 41  0
     public SingleColumnRowMapper(Class requiredType) {
 42  0
         this.requiredType = requiredType;
 43  0
     }
 44  
 
 45  
     /**
 46  
      * Set the type that each result object is expected to match.
 47  
      * <p>
 48  
      * If not specified, the column value will be exposed as returned by the JDBC driver.
 49  
      */
 50  
     public void setRequiredType(Class requiredType) {
 51  0
         this.requiredType = requiredType;
 52  0
     }
 53  
 
 54  
     /**
 55  
      * Extract a value for the single column in the current row.
 56  
      * <p>
 57  
      * Validates that there is only one column selected, then delegates to <code>getColumnValue()</code> and also
 58  
      * <code>convertValueToRequiredType</code>, if necessary.
 59  
      * 
 60  
      * @see java.sql.ResultSetMetaData#getColumnCount()
 61  
      * @see #getColumnValue(java.sql.ResultSet,int,Class)
 62  
      * @see #convertValueToRequiredType(Object,Class)
 63  
      */
 64  
     @Override
 65  
     public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
 66  
         // Validate column count.
 67  0
         ResultSetMetaData rsmd = rs.getMetaData();
 68  0
         int nrOfColumns = rsmd.getColumnCount();
 69  0
         if (nrOfColumns != 1) {
 70  0
             throw new SQLException("Returned too many rows");
 71  
         }
 72  
 
 73  
         // Extract column value from JDBC ResultSet
 74  0
         Object result = getColumnValue(rs, 1, this.requiredType);
 75  0
         if (result != null && this.requiredType != null && !this.requiredType.isInstance(result)) {
 76  
             // Extracted value does not match already: try to convert it.
 77  
             try {
 78  0
                 return convertValueToRequiredType(result, this.requiredType);
 79  0
             } catch (IllegalArgumentException ex) {
 80  0
                 throw new SQLException("Type mismatch affecting row number " + rowNum + " and column type '"
 81  
                         + rsmd.getColumnTypeName(1) + "': " + ex.getMessage());
 82  
             }
 83  
         }
 84  0
         return result;
 85  
     }
 86  
 
 87  
     /**
 88  
      * Retrieve a JDBC object value for the specified column.
 89  
      * <p>
 90  
      * The default implementation calls <code>ResultSet.getString(index)</code> etc for all standard value types
 91  
      * (String, Boolean, number types, date types, etc). It calls <code>ResultSet.getObject(index)</code> else.
 92  
      * <p>
 93  
      * If no required type has been specified, this method delegates to <code>getColumnValue(rs, index)</code>, which
 94  
      * basically calls <code>ResultSet.getObject(index)</code> but applies some additional default conversion to
 95  
      * appropriate value types.
 96  
      * <p>
 97  
      * Explicit extraction of a String is necessary to properly extract an Oracle RAW value as a String, for example.
 98  
      * For the other given types, it is also recommendable to extract the desired types explicitly, to let the JDBC
 99  
      * driver perform appropriate (potentially database-specific) conversion.
 100  
      * 
 101  
      * @param rs
 102  
      *            is the ResultSet holding the data
 103  
      * @param index
 104  
      *            is the column index
 105  
      * @param requiredType
 106  
      *            the type that each result object is expected to match (or <code>null</code> if none specified)
 107  
      * @return the Object value
 108  
      * @see java.sql.ResultSet#getString(int)
 109  
      * @see java.sql.ResultSet#getObject(int)
 110  
      * @see #getColumnValue(java.sql.ResultSet,int)
 111  
      */
 112  
     protected Object getColumnValue(ResultSet rs, int index, Class requiredType) throws SQLException {
 113  0
         if (requiredType != null) {
 114  
             Object value;
 115  0
             boolean wasNullCheck = false;
 116  
 
 117  
             // Explicitly extract typed value, as far as possible.
 118  0
             if (String.class.equals(requiredType)) {
 119  0
                 value = rs.getString(index);
 120  0
             } else if (Boolean.class.equals(requiredType)) {
 121  0
                 value = (rs.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE);
 122  0
                 wasNullCheck = true;
 123  0
             } else if (Byte.class.equals(requiredType)) {
 124  0
                 value = rs.getByte(index);
 125  0
                 wasNullCheck = true;
 126  0
             } else if (Short.class.equals(requiredType)) {
 127  0
                 value = rs.getShort(index);
 128  0
                 wasNullCheck = true;
 129  0
             } else if (Integer.class.equals(requiredType)) {
 130  0
                 value = rs.getInt(index);
 131  0
                 wasNullCheck = true;
 132  0
             } else if (Long.class.equals(requiredType)) {
 133  0
                 value = rs.getLong(index);
 134  0
                 wasNullCheck = true;
 135  0
             } else if (Float.class.equals(requiredType)) {
 136  0
                 value = rs.getFloat(index);
 137  0
                 wasNullCheck = true;
 138  0
             } else if (Double.class.equals(requiredType) || Number.class.equals(requiredType)) {
 139  0
                 value = rs.getDouble(index);
 140  0
                 wasNullCheck = true;
 141  0
             } else if (byte[].class.equals(requiredType)) {
 142  0
                 value = rs.getBytes(index);
 143  0
             } else if (java.sql.Date.class.equals(requiredType)) {
 144  0
                 value = rs.getDate(index);
 145  0
             } else if (java.sql.Time.class.equals(requiredType)) {
 146  0
                 value = rs.getTime(index);
 147  0
             } else if (java.sql.Timestamp.class.equals(requiredType) || java.util.Date.class.equals(requiredType)) {
 148  0
                 value = rs.getTimestamp(index);
 149  0
             } else if (BigDecimal.class.equals(requiredType)) {
 150  0
                 value = rs.getBigDecimal(index);
 151  0
             } else if (Blob.class.equals(requiredType)) {
 152  0
                 value = rs.getBlob(index);
 153  0
             } else if (Clob.class.equals(requiredType)) {
 154  0
                 value = rs.getClob(index);
 155  
             } else {
 156  
                 // Some unknown type desired -> rely on getObject.
 157  0
                 value = rs.getObject(index);
 158  
             }
 159  
 
 160  
             // Perform was-null check if demanded (for results that the
 161  
             // JDBC driver returns as primitives).
 162  0
             if (wasNullCheck && value != null && rs.wasNull()) {
 163  0
                 value = null;
 164  
             }
 165  0
             return value;
 166  
         } else {
 167  
             // No required type specified -> perform default extraction.
 168  0
             return getColumnValue(rs, index);
 169  
         }
 170  
     }
 171  
 
 172  
     /**
 173  
      * Retrieve a JDBC object value for the specified column, using the most appropriate value type. Called if no
 174  
      * required type has been specified.
 175  
      * <p>
 176  
      * The default implementation delegates to <code>JdbcUtils.getResultSetValue()</code>, which uses the
 177  
      * <code>ResultSet.getObject(index)</code> method. Additionally, it includes a "hack" to get around Oracle returning
 178  
      * a non-standard object for their TIMESTAMP datatype. See the <code>JdbcUtils#getResultSetValue()</code> javadoc
 179  
      * for details.
 180  
      * 
 181  
      * @param rs
 182  
      *            is the ResultSet holding the data
 183  
      * @param index
 184  
      *            is the column index
 185  
      * @return the Object value
 186  
      */
 187  
     protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
 188  0
         return JdbcUtils.getResultSetValue(rs, index);
 189  
     }
 190  
 
 191  
     /**
 192  
      * Convert the given column value to the specified required type. Only called if the extracted column value does not
 193  
      * match already.
 194  
      * <p>
 195  
      * If the required type is String, the value will simply get stringified via <code>toString()</code>. In case of a
 196  
      * Number, the value will be converted into a Number, either through number conversion or through String parsing
 197  
      * (depending on the value type).
 198  
      * 
 199  
      * @param value
 200  
      *            the column value as extracted from <code>getColumnValue()</code> (never <code>null</code>)
 201  
      * @param requiredType
 202  
      *            the type that each result object is expected to match (never <code>null</code>)
 203  
      * @return the converted value
 204  
      * @see #getColumnValue(java.sql.ResultSet,int,Class)
 205  
      */
 206  
     protected Object convertValueToRequiredType(Object value, Class requiredType) {
 207  0
         if (String.class.equals(this.requiredType)) {
 208  0
             return value.toString();
 209  0
         } else if (Number.class.isAssignableFrom(this.requiredType)) {
 210  0
             if (value instanceof Number) {
 211  
                 // Convert original Number to target Number class.
 212  0
                 return NumberUtils.convertNumberToTargetClass(((Number) value), this.requiredType);
 213  
             } else {
 214  
                 // Convert stringified value to target Number class.
 215  0
                 return NumberUtils.parseNumber(value.toString(), this.requiredType);
 216  
             }
 217  
         } else {
 218  0
             throw new IllegalArgumentException("Value [" + value + "] is of type [" + value.getClass().getName()
 219  
                     + "] and cannot be converted to required type [" + this.requiredType.getName() + "]");
 220  
         }
 221  
     }
 222  
 }