Java反射在模型中设置枚举



我正在尝试用反射更新我的DTO
问题是我的DTO中的一些字段是枚举,并且我收到一个错误,即我无法将enum字段设置为字符串

DTO:

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Table(name = "xxx")
public class Model {
@Id
@Column(name = "id")
private String runId;
@Column(name = "name")
private String name;

@Column(name = "status")
@Enumerated(EnumType.STRING)
private ExecutionStatus status;

}

控制器:

@PatchMapping(path = "/{id}", consumes = "application/json")
public ResponseEntity<Void> partialUpdateModel(@PathVariable String id, @RequestBody Map<Object, Object> fields)
throws Exception {
Optional<Model> model= service.getById(id);
if (model.isPresent()) {
fields.forEach((key, value) -> {
Field field = ReflectionUtils.findField(Model.class, (String) key);
field.setAccessible(true);
ReflectionUtils.setField(field, model.get(), value);
});

因此,当涉及到枚举字段时,无法设置该字段。上面写着

无法将ExecutionStatus设置为字符串

您要做的是:

model.status = "STATUS_1";
// incompatible types: java.lang.String cannot be converted to so.A.ExecutionStatus

您显然想要做的是找到具有指定字符串的指定枚举类型的枚举常量。这就是Enum.valueOf或YourEnum.valueOf方法的作用。

示例:

enum ExecutionStatus {
STATUS_1,
STATUS_2,
}
static class Model {
public String runId;
public String name;
public ExecutionStatus status;
@Override
public String toString() {
return "Model{" +
"runId='" + runId + ''' +
", name='" + name + ''' +
", status=" + status +
'}';
}
}
public static void main(String[] args) {
Map<Object, Object> fields = Map.of(
"runId", "MyRunId",
"name", "MyName",
"status", "STATUS_1"
);
Model model = new Model();
fields.forEach((key, value) -> {
Field field = null;
try {
field = Model.class.getDeclaredField((String) key);
field.setAccessible(true);
if (field.getType().isEnum()) {
// First variant (YourEnum.valueOf(String)
Method valueOf = field.getType().getMethod("valueOf", String.class);
Object enumConstant = valueOf.invoke(null, value);
field.set(model, enumConstant);
// Alternative (Enum.valueOf(Class, String) (cast is safe due to isEnum)
field.set(model, Enum.valueOf((Class<Enum>) field.getType(), (String) value));
} else {
field.set(model, value);
}
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
});
System.out.println(model);
}

你可以这样做,也要理解反射的成本很高,它涉及到正在动态解析的类型,我建议写setter或构造函数,而不是

if ("ExecutionStatus".equalsIgnoreCase(field.getType().getSimpleName())) {
ReflectionUtils.setField(field, model.get(), ExecutionStatus.valueOf(value));
} else {
ReflectionUtils.setField(field, model.get(), value);
}

这是因为源映射的类型是<Object, Object>

您要做的是设置ExecutionStatus类型的字段,读取Object类型的值。字段上的类型必须匹配。首先将一个值转换为ExecutionStatus,然后使用方法.setField(..)

最新更新