Last active
March 14, 2020 04:14
-
-
Save joshlong/307e4a4fc3bd509da12fdaf25e55df11 to your computer and use it in GitHub Desktop.
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
| /** | |
| * Maps data from a JDBC {@code ResultSet} into Java 14 record objects of type T. | |
| * | |
| * @author <a href="mailto:josh@joshlong.com">Josh Long</a> | |
| */ | |
| class RecordRowMapper<T> implements RowMapper<T> { | |
| private final Log logger = LogFactory.getLog(getClass()); | |
| private final Map<String, RecordComponent> mappedFields = new HashMap<>(); | |
| private final List<String> parameterNames = new ArrayList<>(); | |
| private final Constructor<?> constructor; | |
| /** | |
| * Instantiate a new instance using the default constructor for a class. | |
| */ | |
| public RecordRowMapper(Class<T> tClass) { | |
| this(tClass.getConstructors()[0]); | |
| } | |
| /** | |
| * If there's ambiguity as to which constructor to invoke, provide one explicitly. | |
| */ | |
| public RecordRowMapper(Constructor<?> constructor) { | |
| Assert.notNull(constructor, "the Constructor reference should not be null"); | |
| this.constructor = constructor; | |
| for (RecordComponent recordComponent : constructor.getDeclaringClass().getRecordComponents()) { | |
| this.mappedFields.put(normalizeName(recordComponent.getName()), recordComponent); | |
| } | |
| for (Parameter parameter : constructor.getParameters()) { | |
| parameterNames.add(parameter.getName()); | |
| } | |
| } | |
| @Override | |
| public T mapRow(ResultSet rs, int rowNum) throws SQLException { | |
| ResultSetMetaData metaData = rs.getMetaData(); | |
| Map<String, Object> objectProperties = new HashMap<>(); | |
| int columnCount = metaData.getColumnCount(); | |
| for (int index = 1; index <= columnCount; index++) { | |
| String column = JdbcUtils.lookupColumnName(metaData, index); | |
| RecordComponent recordComponent = this.mappedFields.get(normalizeName(column)); | |
| if (null != recordComponent) { | |
| if (rowNum == 0 && logger.isDebugEnabled()) { | |
| logger.debug("Mapping column '" + column + "' to property '" + recordComponent.getName() + | |
| "' of type '" + ClassUtils.getQualifiedName(recordComponent.getDeclaringRecord()) + "'"); | |
| } | |
| objectProperties.put(recordComponent.getName(), getColumnValue(rs, index, recordComponent.getType())); | |
| } | |
| } | |
| try { | |
| return (T) this.constructor.newInstance( | |
| this.parameterNames.stream().map(objectProperties::get).toArray() | |
| ); | |
| } | |
| catch (Exception e) { | |
| ReflectionUtils.rethrowRuntimeException(e); | |
| } | |
| return null; | |
| } | |
| private String normalizeName(String name) { | |
| return name.toLowerCase().replaceAll("_", ""); | |
| } | |
| private Object getColumnValue(ResultSet rs, int index, Class<?> clzz) throws SQLException { | |
| return JdbcUtils.getResultSetValue(rs, index, clzz); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment