如何在Java 8中使用Builder模式动态为DTO赋值?
动态映射查询结果到MyDTO的实现方案
针对你不想硬编码索引赋值的需求,这里提供几种可行的实现方式:
方案一:基于字段名映射+反射(适合查询结果带列名的场景)
如果你的TableResult支持获取查询列名(比如通过result.getColumnNames()这类方法),可以利用列名与MyDTO属性名的对应关系,结合反射动态调用Builder方法:
TableResult result = queryJob.getQueryResults(); List<String> columnNames = result.getColumnNames(); // 假设能拿到列名列表 List<MyDTO> output = new ArrayList<>(); for (FieldValueList row : result.iterateAll()) { MyDTO.MyDTOBuilder builder = MyDTO.builder(); for (int i = 0; i < columnNames.size(); i++) { String columnName = columnNames.get(i); FieldValue fieldValue = row.get(i); // 处理空值逻辑 String value = null; if (fieldValue != null && fieldValue.getValue() != null) { value = fieldValue.getStringValue(); } // 反射调用对应的Builder方法 try { // Lombok生成的Builder方法和属性名一致,比如name(String)对应属性name Method method = builder.getClass().getMethod(columnName, String.class); method.invoke(builder, value); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { // 列名无对应属性时,打日志忽略即可 log.warn("No matching builder method for column: {}", columnName); } } output.add(builder.build()); }
方案二:自定义字段映射表(更可控,适配列名与属性名不一致的场景)
如果数据库列名和DTO属性名不匹配,或者不想用反射,可以提前定义列名到Builder方法的映射关系,手动指定对应规则:
// 提前定义列名到Builder方法的映射,灵活适配命名差异 Map<String, BiConsumer<MyDTO.MyDTOBuilder, String>> fieldMappings = new HashMap<>(); fieldMappings.put("user_name", MyDTO.MyDTOBuilder::name); // 列名user_name对应属性name fieldMappings.put("user_id", MyDTO.MyDTOBuilder::id); fieldMappings.put("user_place", MyDTO.MyDTOBuilder::place); fieldMappings.put("user_message", MyDTO.MyDTOBuilder::message); TableResult result = queryJob.getQueryResults(); List<String> columnNames = result.getColumnNames(); List<MyDTO> output = new ArrayList<>(); for (FieldValueList row : result.iterateAll()) { MyDTO.MyDTOBuilder builder = MyDTO.builder(); for (int i = 0; i < columnNames.size(); i++) { String columnName = columnNames.get(i); FieldValue fieldValue = row.get(i); String value = null; if (fieldValue != null && fieldValue.getValue() != null) { value = fieldValue.getStringValue(); } // 根据映射调用Builder方法 BiConsumer<MyDTO.MyDTOBuilder, String> mapper = fieldMappings.get(columnName); if (mapper != null) { mapper.accept(builder, value); } } output.add(builder.build()); }
这种方式不用反射,性能更好,也能精准控制列名和属性的对应关系。
方案三:基于DTO属性数量遍历(无列名时的妥协方案)
如果无法获取查询列名,只能通过索引访问,但想避免硬编码索引,可以利用反射获取MyDTO的属性数量,按顺序遍历赋值:
TableResult result = queryJob.getQueryResults(); List<MyDTO> output = new ArrayList<>(); // 获取MyDTO的所有字段(假设都是String类型属性) Field[] dtoFields = MyDTO.class.getDeclaredFields(); int fieldCount = dtoFields.length; for (FieldValueList row : result.iterateAll()) { MyDTO.MyDTOBuilder builder = MyDTO.builder(); // 遍历索引,不超过DTO属性数和当前行的字段数 for (int i = 0; i < fieldCount && i < row.size(); i++) { FieldValue fieldValue = row.get(i); String value = null; if (fieldValue != null && fieldValue.getValue() != null) { value = fieldValue.getStringValue(); } try { String fieldName = dtoFields[i].getName(); Method method = builder.getClass().getMethod(fieldName, String.class); method.invoke(builder, value); } catch (Exception e) { log.warn("Failed to set field at index {}: {}", i, e.getMessage()); } } output.add(builder.build()); }
注意:这种方式依赖查询结果的字段顺序和DTO属性顺序完全一致,否则会出现赋值错误。
关键注意点
- 空值处理:所有方案都保留了你原有的空值判断逻辑,避免空指针异常。
- 异常处理:对于找不到对应方法的情况,建议添加日志,方便后续排查问题。
- 性能考量:反射方案会有轻微性能损耗,高并发场景优先选方案二。
内容的提问来源于stack exchange,提问作者Andulos
相关产品推荐
相关产品推荐

