Coverage Summary for Class: Reader (net.sf.persism)
| Class | Method, % | Line, % |
|---|---|---|
| Reader | 100% (6/6) | 99.3% (139/140) |
| Reader$1 | 100% (1/1) | 100% (1/1) |
| Total | 100% (7/7) | 99.3% (140/141) |
1 package net.sf.persism; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.StringWriter; 7 import java.lang.reflect.InvocationTargetException; 8 import java.math.BigDecimal; 9 import java.math.BigInteger; 10 import java.sql.*; 11 import java.util.ArrayList; 12 import java.util.List; 13 import java.util.Map; 14 15 final class Reader { 16 17 private static final Log log = Log.getLogger(Reader.class); 18 private static final Log blog = Log.getLogger("net.sf.persism.Benchmarks"); 19 20 private final MetaData metaData; 21 private final Converter converter; 22 23 Reader(Session session) { 24 this.metaData = session.getMetaData(); 25 this.converter = session.getConverter(); 26 } 27 28 <T> T readObject(T object, Map<String, PropertyInfo> properties, ResultSet rs) throws SQLException, IOException { 29 Class<?> objectClass = object.getClass(); 30 31 // We should never call this method with a primitive type. 32 assert JavaType.getType(objectClass) == null; 33 34 ResultSetMetaData rsmd = rs.getMetaData(); 35 int columnCount = rsmd.getColumnCount(); 36 37 for (int j = 1; j <= columnCount; j++) { 38 39 String columnName = rsmd.getColumnLabel(j); 40 41 PropertyInfo columnProperty = getPropertyInfo(columnName, properties); 42 43 if (columnProperty != null) { 44 45 if (columnProperty.getter == null) { 46 throw new PersismException(Message.ClassHasNoGetterForProperty.message(object.getClass(), columnProperty.propertyName)); 47 } 48 49 Class<?> returnType = columnProperty.getter.getReturnType(); 50 51 Object value = readColumn(rs, j, rsmd.getColumnType(j), columnName, returnType); 52 53 if (value != null) { 54 try { 55 columnProperty.setValue(object, value); 56 } catch (IllegalArgumentException e) { 57 // A IllegalArgumentException (which is a RuntimeException) occurs if we're setting an unmatched ENUM 58 throw new PersismException(Message.IllegalArgumentReadingColumn.message(columnProperty.propertyName, objectClass, columnName, returnType, value.getClass(), value), e); 59 } 60 } 61 } 62 } 63 64 if (object instanceof Persistable<?> pojo) { 65 // Save this object initial state to later detect changed properties 66 pojo.saveReadState(); 67 } 68 return (T) object; 69 } 70 71 <T> T readRecord(RecordInfo<T> recordInfo, ResultSet rs) throws SQLException, IOException, InvocationTargetException, InstantiationException, IllegalAccessException { 72 73 long now; 74 now = System.nanoTime(); 75 76 ResultSetMetaData rsmd = rs.getMetaData(); 77 List<Object> constructorParams = new ArrayList<>(recordInfo.propertyInfoByConstructorOrder().keySet().size()); 78 79 for (String col : recordInfo.propertyInfoByConstructorOrder().keySet()) { 80 Class<?> returnType = recordInfo.propertyInfoByConstructorOrder().get(col).field.getType(); 81 82 int ncol = recordInfo.ordinals().get(col); 83 Object value = readColumn(rs, ncol, rsmd.getColumnType(ncol), rsmd.getColumnLabel(ncol), returnType); 84 if (value == null && returnType.isPrimitive()) { 85 // Set null primitives to their default, otherwise the constructor will not be found 86 value = JavaType.getDefaultValue(returnType); 87 } 88 89 constructorParams.add(value); 90 } 91 92 93 try { 94 //noinspection 95 return recordInfo.constructor().newInstance(constructorParams.toArray()); 96 } finally { 97 blog.debug("time to get readRecord: %s", (System.nanoTime() - now)); 98 } 99 } 100 101 102 Object readColumn(ResultSet rs, int column, int sqlColumnType, String columnName, Class<?> returnType) throws SQLException, IOException { 103 long now = System.nanoTime(); 104 105 if (returnType.isEnum()) { 106 // Some DBs may read an enum type as other 1111 - we can tell it here to read it as a string. 107 sqlColumnType = java.sql.Types.CHAR; 108 } 109 110 JavaType columnType = JavaType.convert(sqlColumnType); // note this could be null if we can't match a type 111 112 Object value = null; 113 114 if (columnType != null) { 115 116 switch (columnType) { 117 118 case BooleanType: 119 case booleanType: 120 if (returnType == Boolean.class || returnType == boolean.class) { 121 value = rs.getBoolean(column); 122 } else { 123 value = rs.getByte(column); 124 } 125 break; 126 127 case TimestampType: 128 if (returnType == String.class) { // JTDS 129 value = rs.getString(column); 130 } else { 131 // work around to Oracle reading a oracle.sql.TIMESTAMP class with getObject 132 value = rs.getTimestamp(column); 133 } 134 break; 135 136 case ByteArrayType: 137 case byteArrayType: 138 value = rs.getBytes(column); 139 break; 140 141 case ClobType: 142 if (metaData.getConnectionType().supportsReadingFromClobType()) { 143 Clob clob = rs.getClob(column); 144 if (clob != null) { 145 try (InputStream in = clob.getAsciiStream()) { 146 StringWriter writer = new StringWriter(); 147 148 int c = -1; 149 while ((c = in.read()) != -1) { 150 writer.write(c); 151 } 152 writer.flush(); 153 value = writer.toString(); 154 } 155 } 156 } else { 157 try (InputStream in = rs.getAsciiStream(column)) { 158 StringWriter writer = new StringWriter(); 159 160 int c = -1; 161 while ((c = in.read()) != -1) { 162 writer.write(c); 163 } 164 writer.flush(); 165 value = writer.toString(); 166 } 167 } 168 break; 169 170 case BlobType: 171 byte[] buffer = new byte[1024]; 172 if (metaData.getConnectionType().supportsReadingFromBlobType()) { 173 Blob blob = rs.getBlob(column); 174 if (blob != null) { 175 try (InputStream in = blob.getBinaryStream()) { 176 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 177 for (int len; (len = in.read(buffer)) != -1; ) { 178 bos.write(buffer, 0, len); 179 } 180 value = bos.toByteArray(); 181 } 182 } 183 } else { 184 try (InputStream in = rs.getBinaryStream(column)) { 185 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 186 for (int len; (len = in.read(buffer)) != -1; ) { 187 bos.write(buffer, 0, len); 188 } 189 value = bos.toByteArray(); 190 } 191 } 192 break; 193 194 case IntegerType: 195 // https://github.com/xerial/sqlite-jdbc/issues/604 196 // SQLite jdbc reports INT but the value is LONG for date types which can WRAP past Integer.MAX - Fixed in 3.39.3.0! Thanks! 197 if (metaData.getConnectionType() == ConnectionType.SQLite) { 198 value = rs.getObject(column); 199 if (value != null) { 200 if (value instanceof Long) { 201 value = rs.getLong(column); 202 } else { 203 value = rs.getInt(column); 204 } 205 } 206 } else { 207 value = rs.getObject(column) == null ? null : rs.getInt(column); 208 } 209 break; 210 211 case LongType: 212 value = rs.getObject(column) == null ? null : rs.getLong(column); 213 break; 214 215 case FloatType: 216 value = rs.getObject(column) == null ? null : rs.getFloat(column); 217 break; 218 219 case DoubleType: 220 value = rs.getObject(column) == null ? null : rs.getDouble(column); 221 break; 222 223 case BigIntegerType: 224 case BigDecimalType: 225 if (returnType == BigInteger.class) { 226 BigDecimal bd = rs.getBigDecimal(column); 227 if (bd != null) { 228 value = bd.toBigInteger(); 229 } 230 } else { 231 value = rs.getBigDecimal(column); 232 } 233 break; 234 235 case TimeType: 236 value = rs.getTime(column); 237 break; 238 239 // We can't assume rs.getDate will work. SQLITE actually has a long value in here. 240 // We can live with rs.getObject and the convert method will handle it. 241 // case SQLDateType: 242 // case UtilDateType: 243 // value = rs.getDate(column); 244 // break; 245 246 case StringType: 247 if (returnType == Character.class || returnType == char.class) { 248 String s = rs.getString(column); 249 if (s != null && s.length() > 0) { 250 value = s.charAt(0); 251 } 252 break; 253 } 254 255 value = rs.getString(column); 256 break; 257 258 default: 259 value = rs.getObject(column); 260 } 261 262 } else { 263 value = rs.getObject(column); 264 String objType = "Unknown"; 265 if (value != null) { 266 objType = value.getClass().getName(); 267 } 268 log.warnNoDuplicates(Message.ColumnTypeNotKnownForSQLType.message(sqlColumnType, columnName, objType)); 269 } 270 271 // If value is null or column type is unknown - no need to try to convert anything. 272 if (value != null && columnType != null) { 273 value = converter.convert(value, returnType, columnName); 274 } 275 276 blog.debug("time to readColumn: %s", (System.nanoTime() - now)); 277 return value; 278 } 279 280 281 // Poor man's case-insensitive linked hash map ;) 282 PropertyInfo getPropertyInfo(String col, Map<String, PropertyInfo> properties) { 283 for (String key : properties.keySet()) { 284 if (key.equalsIgnoreCase(col)) { 285 return properties.get(key); 286 } 287 } 288 return null; 289 } 290 291 }