我有這兩個簡單的實體:如何部分更新實體及其在了Spring Web +關係JPA
@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
更新用戶實體
正如你可以看到我想要的一個字段可以設置爲null,即從請求缺少某個字段不應更新和一個關係式C可以使用相關型號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更新關係。 請問有人可以幫忙嗎?
UPDATE
由於@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實體合併功能。
@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 inteface使得通用RestMerger類的自定義實體服務
public interface AssociationResolver {
void resolveAssociation(String name, JsonNode value, ObjectNode mainNode) throws JsonProcessingException;
}
我不想更新未設置字段,如果它沒有被要求發送,但我想更新只要該字段在請求中設置,即使它設置爲空,也使用ObjectMapper。在你的代碼片段中,我似乎明白'UserAttribute' _role_字段被設置爲空,我的請求字段爲空(正確),我的請求字段不存在(不正確)。糾正我,如果我錯了。 –
@RuggeroRusso看來如果你想使用'ObjectMapper',你可能需要使用'JsonNode'手動執行合併。否則,使用我的原始代碼,您需要發送角色:[special null me value]來觸發刪除操作,空值跳過,設置值。 – Naros