Created
October 11, 2018 18:55
-
-
Save William-Lake/97102142112598d77368f024d9ccb6f7 to your computer and use it in GitHub Desktop.
Generic ResultSet to POJO Converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.math.BigDecimal; | |
import java.sql.ResultSet; | |
import java.sql.SQLException; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
/** | |
* RSToPOJOConverter | |
* | |
* Converts a ResultSet to a List of Objects. | |
* | |
* Built this Class to avoid having to rewrite | |
* different methods for building various objects | |
* individually. | |
* | |
* Only works if the following is true: | |
* | |
* 1. The ResultSet field names match up with the names | |
* of the associated target Class fields like so: | |
* EXAMPLE_VALUE -> exampleValue | |
* | |
* 2. The target class fields are primitive values. | |
* | |
* You can overcome #1 via SQL aliases: http://www.geeksengine.com/database/basic-select/column-alias.php | |
* | |
* You can overcome #2 by only passing in ResultSets whose | |
* field values match with only the primitive fields in the | |
* target Class. E.g. If the target class has a custom object | |
* for a field value, pass a ResultSet who doesn't contain it. | |
* | |
* ======================================================================= | |
* | |
* I realize there are libraries who can do a lot | |
* of this for me, but this was for a very | |
* small project that didn't justify a large | |
* framework. This lightweight class met my needs. | |
*/ | |
public class RSToPOJOConverter | |
{ | |
/** | |
* Converts the given ResultSet to a List of objects, | |
* using the provided target Class to determine what | |
* objects to create from the provided ResultSet. | |
* | |
* @param resultSet | |
* The ResultSet to pull the data out of. | |
* @param targetClass | |
* The class the ResultSet should be converted into. | |
* @return The List of objects converted from the ResultSet. | |
* @throws SQLException | |
* @throws InstantiationException | |
* @throws IllegalAccessException | |
* @throws IllegalArgumentException | |
* @throws InvocationTargetException | |
*/ | |
public static List<?> convertResultSetToObjectList(ResultSet resultSet, Class<?> targetClass) throws SQLException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException | |
{ | |
List<Object> createdObjects = new ArrayList<>(); | |
/* | |
* Maps the name of a field in the ResultSet to | |
* the name of the Java method that should be | |
* used to set the gathered value in an instance | |
* of the target Class. | |
* | |
* E.g. If the field name = "ITEM_ID", the expected setter name is setItemId() | |
*/ | |
Map<String,Method> dbFieldNameSetterMap = new HashMap<>(); | |
/* | |
* Gather all the methods for the given class, | |
* filter out all but the setters, | |
* for each method, | |
* take the setter's method name and | |
* create the expected field name for the value the DB (E.g. EXAMPLE_VALUE), | |
* | |
* map the field name to the setter method. | |
*/ | |
Arrays | |
.asList(targetClass.getMethods()) | |
.stream() | |
.filter(method -> method.getName().startsWith("set")) | |
.forEach(setter -> | |
{ | |
// setExampleValue -> EXAMPLE_VALUE | |
String fieldName = convertSetterNameToFieldName(setter.getName()); | |
dbFieldNameSetterMap.put(fieldName, setter); | |
}); | |
// Process the ResultSet | |
// While there are still results to process, | |
while(resultSet.next()) | |
{ | |
// Create a new instance of the target class, | |
Object targetObject = targetClass.newInstance(); | |
/* | |
* Loop through the field names you've created, | |
* Get the associated setter, | |
* Gather the value from the ResultSet, | |
* Review/Verify the datatype you're saving, | |
* Use the target setter to save the gathered value | |
* in the instance of the target object. | |
*/ | |
for (String dbFieldName : dbFieldNameSetterMap.keySet()) | |
{ | |
Method setter = dbFieldNameSetterMap.get(dbFieldName); | |
Object dbFieldValue = resultSet.getObject(dbFieldName); | |
dbFieldValue = reviewValue(dbFieldValue); | |
setter.invoke(targetObject, dbFieldValue); | |
} | |
createdObjects.add(targetObject); | |
} | |
return createdObjects; | |
} | |
/** | |
* Converts a setter method's name to | |
* the associated Database field name. | |
* | |
* @param setterName | |
* The name of the setter method. | |
* @return The name of the associated Database field. | |
*/ | |
private static String convertSetterNameToFieldName(String setterName) | |
{ | |
String fieldName = setterName.replaceAll("([A-Z])", "_$1"); | |
fieldName = fieldName.substring(4); | |
return fieldName.toUpperCase(); | |
} | |
/** | |
* Required for a number of reasons: | |
* | |
* 1. ResultSet.getObject(String columnName) returns a BigDecimal for the id columns where I expected a Long. This is an issue | |
* because the equivalent POJO Instance Ids were using Longs for their ids, not BigDecimals. Using the dataType object to | |
* cast it (Class.cast()) doesn't work because you can't cast a BigDecimal to a Long. | |
* 2. ResultSet.getObject(String columnName, Class<?> type) is returning AbstractMethodError, even for a STRING. | |
* | |
* After way too much wasted time trying to figure this out, instead wrote this method which works. | |
* | |
* The goal is to review and change what jdbc returns from the ResultSet, when necessary. | |
* | |
* At the moment only converts a BigDecimal to a Long. | |
* | |
* Other values are returned as is. | |
* | |
* @param databaseValue | |
* The value to review. | |
* @return The reviewed and potentially correctly casted value. | |
*/ | |
private static Object reviewValue(Object databaseValue) | |
{ | |
if (databaseValue instanceof BigDecimal) return ((BigDecimal) databaseValue).longValue(); | |
return databaseValue; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment