Lombok builder reflection rowmapper
작업중 lombok builder를 바로 사용하지 못하고 reflection 을 통해 rowmapper를 구성해야 할 일이 생겨 작업했던 코드를 기록합니다.
여러 타입들에 대해 사용될 수 있는 공통적인 rowMapper 구성 필요시 유용 할 수 있습니다.
public class BuilderRowMapper<T, Builder> implements RowMapper<T> {
private final CustomEncryptorDecryptor customEncryptorDecryptor;
private final Class<T> tClazz;
public OracleRowMapper(Class<T> tClazz, CustomEncryptorDecryptor customEncryptorDecryptor) {
this.tClazz = tClazz;
this.customEncryptorDecryptor = customEncryptorDecryptor;
}
@SuppressWarnings("unchecked")
@SneakyThrows
@Override
public T mapRow(ResultSet rs, int rowNum) {
Builder builder = (Builder)tClazz.getDeclaredMethod("builder").invoke(null);
Field[] fields = tClazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(Column.class) || field.isAnnotationPresent(ReadOnlyProperty.class)) {
continue;
}
Method[] superBuilderMethods = builder.getClass().getSuperclass().getDeclaredMethods();
setBuilderProperty(rs, field, superBuilderMethods, builder);
}
Field[] superFields = tClazz.getSuperclass().getDeclaredFields();
for (Field field : superFields) {
if (!field.isAnnotationPresent(Column.class) || field.isAnnotationPresent(ReadOnlyProperty.class)) {
continue;
}
Method[] superSuperBuilderMethods =
builder.getClass().getSuperclass().getSuperclass().getDeclaredMethods();
setBuilderProperty(rs, field, superSuperBuilderMethods, builder);
}
return (T)builder.getClass().getSuperclass().getDeclaredMethod("build").invoke(builder);
}
private <T> void setBuilderProperty(
ResultSet rs,
Field field,
Method[] superBuilderMethods,
T builder
) throws SQLException, InvocationTargetException, IllegalAccessException {
for (Method builderMethod : superBuilderMethods) {
if (!builderMethod.getName().equals(field.getName())) {
continue;
}
Object rowValue = rs.getObject(field.getDeclaredAnnotation(Column.class).value());
if (rowValue == null) {
builderMethod.invoke(builder, rowValue);
continue;
}
if (Integer.class.isAssignableFrom(field.getType())
|| int.class.isAssignableFrom(field.getType())) {
builderMethod.invoke(builder, Integer.parseInt(rowValue.toString()));
} else if (Long.class.isAssignableFrom(field.getType())
|| long.class.isAssignableFrom(field.getType())) {
builderMethod.invoke(builder, Long.parseLong(rowValue.toString()));
} else if (String.class.isAssignableFrom(field.getType())) {
builderMethod.invoke(builder, rowValue.toString());
} else if (Boolean.class.isAssignableFrom(field.getType())
|| boolean.class.isAssignableFrom(field.getType())) {
builderMethod.invoke(builder,
"1".equals(rowValue.toString())
|| "TRUE".equalsIgnoreCase(rowValue.toString())
);
} else if (Instant.class.isAssignableFrom(field.getType())) {
Instant instant = Times.fromExtendedSimpleFormat(rowValue.toString());
builderMethod.invoke(builder, instant);
} else if (CustomEnum.class.isAssignableFrom(field.getType())) {
CustomEnum customEnum = getCustomEnum(field, rowValue);
builderMethod.invoke(builder, etcCodeEnum);
} else if (Encrypted.class.isAssignableFrom(field.getType())) {
builderMethod.invoke(builder,
Encrypted.from(customEncryptorDecryptor.decrypt(rowValue.toString()))
);
} else {
builderMethod.invoke(builder, rowValue);
}
}
}
private CustomEnum getCustomEnum(Field field, Object rowValue) {
return Arrays.stream(field.getType().getEnumConstants())
.map(it -> (CustomEnum)it)
.filter(it -> Objects.equals(it.getCustom(), rowValue))
.findFirst()
.orElseThrow(
() -> new FailedConversionException(
"No enum constant " + field.getType().getName() + "." + rowValue)
);
}
}