我有两个简单的实体:
@Entity
public class User {
@Id
@GeneratedValue
private Long Id;
private String username;
private String firstName;
@ManyToOne
private Role role;
//Getters and setters...
}
@Entity
public class Role {
@Id
@GeneratedValue
private Long Id;
private String name;
@OneToMany(mappedBy="role")
Set<User> users = new HashSet<>();
//Getters and setters...
}
在我的控制器中,我需要使用以下HTTP请求更新用户实体
curl -X PATCH \
-H "Content-Type: application/json" \
-d \
'{
"username": null,
"role": 1
}' http://localhost:8080/users/1
正如您所见,我希望字段可以设置为空,请求中不存在的字段 和关系可以使用相关的型号ID 设置。
这是我在控制器中实现此目的的尝试
@RestController
@RequestMapping("/users")
public class UserRestController {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
@Autowired
UserRestController (UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
@RequestMapping(value = "/{userId}", method = {RequestMethod.PATCH, RequestMethod.PUT})
ResponseEntity<?> updateUser(@PathVariable Long userId, HttpServletRequest request) {
User userToUpdate = userRepository.findOne(userId);
ObjectMapper mapper = new ObjectMapper();
User updatedUser = mapper.readerForUpdating(userToUpdate ).readValue(request.getReader());
User user = userRepository.save(updatedUser);
return new ResponseEntity<>(user, new HttpHeaders(), HttpStatus.CREATED);
}
}
我唯一能够实现的是部分更新和无效更新的字段。但是,这样,我无法仅通过ID更新关系。 请帮忙吗?
感谢@Naros更新的帖子,我重写了整个控制器来处理角色ID更新
@RestController
@RequestMapping("/users")
public class UserRestController {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private UserService userService;
@RequestMapping(value = "/{userId}", method = {RequestMethod.PATCH, RequestMethod.PUT})
ResponseEntity<?> updateUser(@PathVariable Long userId, @RequestBody ObjectNode requestJsonNode) throws IOException {
User userToUpdate = userRepository.findOne(userId);
RestMerger restMerger = new RestMerger();
restMerger.getAssociations().put("role", userService);
User user = (User) restMerger.merge(userToUpdate, requestJsonNode);
userRepository.save(user);
return new ResponseEntity<>(user, new HttpHeaders(), HttpStatus.CREATED);
}
}
我生成了一个 UserService 类来处理id-entity合并功能。
@Service
public class UserService implements AssociationResolver {
public static final String ROLE_FIELD = "role";
@Autowired
private RoleRepository roleRepository;
@Override
public void resolveAssociation(String name, JsonNode objectNode, ObjectNode mainNode) throws JsonProcessingException {
if(name.equals(ROLE_FIELD))
resolveRoleAssociation(name, objectNode.get(name).asLong(), mainNode);
}
private void resolveRoleAssociation(String name, Long id, ObjectNode mainNode) {
Role role = roleRepository.findOne(id);
ObjectMapper mapper = new ObjectMapper();
mainNode.replace(name, mapper.convertValue(role, JsonNode.class));
}
}
此服务可能包括完整控制器 updateUser 功能和字段验证。 RestMerger 类更新实体,就像ObjectMapper一样。
public class RestMerger {
private HashMap<String, AssociationResolver> associations = new HashMap<>();
public Object merge(Object mainObject, ObjectNode updateNode) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
ObjectNode mainNode = mapper.convertValue(mainObject, ObjectNode.class);
Iterator<String> fieldNames = updateNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode jsonNode = mainNode.get(fieldName);
if(associations.keySet().contains(fieldName)) {
associations.get(fieldName).resolveAssociation(fieldName, updateNode, mainNode);
} else if (jsonNode.isObject())
merge(jsonNode, updateNode);
else {
JsonNode value = updateNode.get(fieldName);
mainNode.replace(fieldName, value);
}
}
return mapper.treeToValue(mainNode, mainObject.getClass());
}
public HashMap<String, AssociationResolver> getAssociations() {
return associations;
}
}
最后 AssociationResolver 接口使通用 RestMerger 类与自定义实体服务进行交互
public interface AssociationResolver {
void resolveAssociation(String name, JsonNode value, ObjectNode mainNode) throws JsonProcessingException;
}
答案 0 :(得分:0)
如果您不愿意使用ObjectMapper
,则可以使用声明性更改重新编码控制器。我在ObjectMapper
上看到的好处是,您可以精确控制RESTful接口可以修改哪些字段,以及哪些字段可以被静默忽略或发出警告/错误。
注意:以下代码段假设一个名为UserAttributes
的值对象,它基本上公开了控制器将接受和操作的有效值,而不是基于原始代码的动态输入
@RestController
@RequestMapping("/users")
public class UserRestController {
/* other cruff */
@RequestMapping(
value = "/{userId}",
method = {
RequestMethod.PATCH,
RequestMethod.PUT
})
public ResponseEntity<?> updateUser(
@PathVariable Long userId,
@RequestBody UserAttributes userAttributes) {
User userToUpdate = userRepository.findOne(userId);
if(userToUpdate == null) {
/* handle cannot find user by userid */
}
// update the username from the user object
userToUpdate.setUsername(userAttributes.getUserName());
if(userAttributes.getRole() != null) {
Role roleToUpdate = roleRepository.findOne(userAttributes.getRole());
if(roleToUpdate == null) {
/* handle cannot find the role */
}
roleToUpdate.getUsers().add(userToUpdate);
userToUpdate.setRole(roleToUpdate);
roleRepository.save(roleToUpdate);
}
else {
userToUpdate.setRole(null);
}
userToUpdate = userRepository.save(userToUpdate);
return new ResponseEntity<>(
userToUpdate,
new HttpHeaders(),
HttpStatus.CREATED);
}
}
<强>更新强>
可能有用的特殊JsonNode
合并功能:
public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
Iterator<String> fieldNames = updateNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode jsonNode = mainNode.get(fieldName);
// if field exists and is an embedded object
if (jsonNode != null && jsonNode.isObject()) {
merge(jsonNode, updateNode.get(fieldName));
}
else {
if (mainNode instanceof ObjectNode) {
// Overwrite field
JsonNode value = updateNode.get(fieldName);
((ObjectNode) mainNode).put(fieldName, value);
}
}
}
return mainNode;
}