복붙노트

[SPRING] 스프링 부분 업데이트 개체 데이터 바인딩

SPRING

스프링 부분 업데이트 개체 데이터 바인딩

Spring 3.2에서 특별한 부분 업데이트 기능을 구현하려고합니다. 우리는 백엔드를 위해 Spring을 사용하고 있으며 간단한 Javascript 프론트 엔드를 가지고 있습니다. 저는 update () 함수가 값의 수를 취하고 그에 따라 지속성 모델을 업데이트해야한다는 요구 사항에 대한 솔직한 해결책을 찾을 수 없었습니다.

우리는 모든 필드에 대해 인라인 편집을하므로 사용자가 필드를 편집하고 확인하면 id와 수정 된 필드가 json으로 컨트롤러에 전달됩니다. 컨트롤러는 클라이언트 (1에서 n까지)에서 임의의 수의 필드를 가져 와서 해당 필드 만 업데이트 할 수 있어야합니다.

예를 들어 id == 1 인 사용자가 displayName을 편집하면 서버에 게시 된 데이터는 다음과 같이 표시됩니다.

{"id":"1", "displayName":"jim"}

현재 우리는 UserController에 다음과 같이 불완전한 솔루션을 가지고 있습니다 :

@RequestMapping(value = "/{id}", method = RequestMethod.POST )
public @ResponseBody ResponseEntity<User> update(@RequestBody User updateUser) {
    dbUser = userRepository.findOne(updateUser.getId());
    customObjectMerger(updateUser, dbUser);
    userRepository.saveAndFlush(updateUuser);
    ...
}

이 코드는 작동하지만 몇 가지 문제가 있습니다. @RequestBody는 새 updateUser를 만들고 id와 displayName을 채 웁니다. CustomObjectMerger는이 updateUser를 데이터베이스의 해당 dbUser와 병합하여 updateUser에 포함 된 유일한 필드를 업데이트합니다.

문제는 updateUser의 일부 필드에 기본값 및 기타 자동 생성 필드 값을 채 웁니다.이 값은 병합시 dbUser에있는 유효한 데이터를 덮어 씁니다. 명시 적으로이 필드를 무시해야한다고 선언하는 것은 옵션이 아닙니다. 업데이트를 통해이 필드도 설정할 수 있기를 바랍니다.

나는 스프링이 자동으로 dbUser에 update () 함수로 명시 적으로 전달 된 정보만을 병합하도록하는 방법을 모색 중이다 (디폴트 / 자동 필드 값을 재설정하지 않음). 이 작업을 수행하는 간단한 방법이 있습니까?

업데이트 : 나는 거의 내가 다음과 같은 옵션을 고려했다. 문제는 그것이 @RequestParam과 (AFAIK) JSON 문자열을하지 않는 것처럼 업데이트 데이터를 필요로한다는 것입니다.

//load the existing user into the model for injecting into the update function
@ModelAttribute("user")
public User addUser(@RequestParam(required=false) Long id){
    if (id != null) return userRepository.findOne(id);
    return null;
}
....
//method declaration for using @MethodAttribute to pre-populate the template object
@RequestMapping(value = "/{id}", method = RequestMethod.POST )
public @ResponseBody ResponseEntity<User> update(@ModelAttribute("user") User updateUser){
....
}

필자는 customObjectMerger ()를 다시 작성하여 JSON으로보다 적절하게 작업하고 계산하여 HttpServletRequest에서 오는 필드 만 고려하도록했습니다. 하지만 첫 번째 장소에서 customObjectMerger ()를 사용해야하는 경우에도 Spring에서 내가 본 것만 큼 JSON 기능이 부족할 때 hacky를 느낄 수 있습니다. 누군가가 Spring에게이 일을하도록하는 방법을 알고 있다면, 나는 그것을 크게 고맙게 생각합니다!

해결법

  1. ==============================

    1.나는이 같은 문제에 봉착했다. 내 현재 솔루션은 다음과 같습니다. 아직 많은 테스트를 수행하지는 않았지만 초기 검사에서 상당히 잘 작동하는 것처럼 보입니다.

    나는이 같은 문제에 봉착했다. 내 현재 솔루션은 다음과 같습니다. 아직 많은 테스트를 수행하지는 않았지만 초기 검사에서 상당히 잘 작동하는 것처럼 보입니다.

    @Autowired ObjectMapper objectMapper;
    @Autowired UserRepository userRepository;
    
    @RequestMapping(value = "/{id}", method = RequestMethod.POST )
    public @ResponseBody ResponseEntity<User> update(@PathVariable Long id, HttpServletRequest request) throws IOException
    {
        User user = userRepository.findOne(id);
        User updatedUser = objectMapper.readerForUpdating(user).readValue(request.getReader());
        userRepository.saveAndFlush(updatedUser);
        return new ResponseEntity<>(updatedUser, HttpStatus.ACCEPTED);
    }
    

    ObjectMapper는 org.codehaus.jackson.map.ObjectMapper 유형의 bean입니다.

    희망이 도움이 누군가,

    편집하다:

    하위 개체에 문제가 발생했습니다. 자식 개체가 부분적으로 업데이트 할 속성을 받으면 새로운 개체를 만들고 해당 속성을 업데이트 한 다음 설정합니다. 그러면 해당 객체의 다른 모든 속성이 지워집니다. 나는 깨끗한 해결책을 찾으면 업데이트 할 것이다.

  2. ==============================

    2.우리는 당신이하고 싶은 것을 성취하기 위해 @ModelAttribute를 사용하고 있습니다.

    우리는 당신이하고 싶은 것을 성취하기 위해 @ModelAttribute를 사용하고 있습니다.

    여기에서 요점은 @modelattribute 메소드가 모델의 이니셜 라이저라는 것입니다. 그런 다음 Spring은 요청을이 모델과 병합합니다. @requestmapping 메소드에서이를 선언하기 때문입니다.

    이렇게하면 부분 업데이트 기능이 제공됩니다.

    일부 또는 심지어 많이? ;) 우리가 DAO를 컨트롤러에서 직접 사용하고 전용 서비스 레이어에서이 병합을 수행하지 않았기 때문에 이것이 나쁜 습관이라고 주장 할 것입니다. 그러나 현재 우리는이 문제로 인해 문제에 부딪치지 않았습니다.

  3. ==============================

    3.호출을 지속하거나 병합 또는 업데이트하기 전에 엔티티와 뷰 객체를 병합하는 API를 작성합니다.

    호출을 지속하거나 병합 또는 업데이트하기 전에 엔티티와 뷰 객체를 병합하는 API를 작성합니다.

    첫 번째 버전이지만 시작이라고 생각합니다.

    POJO`S 필드에서 주석 UIAttribute를 사용하면됩니다.

    MergerProcessor.merge (pojoUi, pojoDb);

    네이티브 애트리뷰트 및 컬렉션과 함께 작동합니다.

    https://github.com/nfrpaiva/ui-merge로 이동하십시오.

  4. ==============================

    4.다음 접근법을 사용할 수 있습니다.

    다음 접근법을 사용할 수 있습니다.

    이 시나리오의 경우 엔티티가 부분적으로 갱신되므로 PATCH 메소드가 더 적절합니다.

    컨트롤러 메소드에서 요청 본문을 문자열로 취하십시오.

    해당 문자열을 JSONObject로 변환하십시오. 그런 다음 키를 반복하고 일치하는 변수를 들어오는 데이터로 업데이트합니다.

    import org.json.JSONObject;
    
    @RequestMapping(value = "/{id}", method = RequestMethod.PATCH )
    public ResponseEntity<?> updateUserPartially(@RequestBody String rawJson, @PathVariable long id){
    
        dbUser = userRepository.findOne(id);
    
        JSONObject json = new JSONObject(rawJson);
    
        Iterator<String> it = json.keySet().iterator();
        while(it.hasNext()){
            String key = it.next();
            switch(key){
                case "displayName":
                    dbUser.setDisplayName(json.get(key));
                    break;
                case "....":
                    ....
            }
        }
        userRepository.save(dbUser);
        ...
    }
    

    이 접근 방식의 단점은 들어오는 값의 유효성을 수동으로 확인해야한다는 것입니다.

  5. ==============================

    5.주요 문제는 다음 코드에 있습니다.

    주요 문제는 다음 코드에 있습니다.

    @RequestMapping(value = "/{id}", method = RequestMethod.POST )
    public @ResponseBody ResponseEntity<User> update(@RequestBody User updateUser) {
        dbUser = userRepository.findOne(updateUser.getId());
        customObjectMerger(updateUser, dbUser);
        userRepository.saveAndFlush(updateUuser);
        ...
    }
    

    위의 함수에서는 private 함수 및 클래스 (userRepository, customObjectMerger, ...) 중 일부를 호출하지만 작동 방식이나 함수의 모양을 설명하지 않습니다. 그래서 나는 추측 할 수 있습니다.

    여기서 우리는 CustomObjectMerger에서 어떤 일이 일어 났는지 알지 못합니다 (귀하의 직능이며 귀하는 그것을 보여주지 않습니다). 그러나 당신이 설명하는 것에서 추측 할 수 있습니다 : updateUser의 모든 속성을 데이터베이스의 개체로 복사합니다. Spring이 객체를 매핑 할 때 모든 데이터를 채울 것이기 때문에 이것은 절대적으로 잘못된 방법입니다. 특정 속성 만 업데이트하려고합니다.

    귀하의 경우에는 2 가지 옵션이 있습니다.

    1) 모든 등록 정보 (변경되지 않은 등록 정보 포함)를 서버에 보냄. 대역폭이 약간 더 들지만, 여전히 길을 계속 지켜야합니다.

    2) User 객체의 기본값으로 일부 특수 값을 설정해야합니다 (예 : id = -1, age = -1 ...). 그런 다음 customObjectMerger에서 -1이 아닌 값을 설정합니다.

    위의 2 가지 해결 방법이 만족스럽지 않다고 생각되면 json 요청을 직접 구문 분석하고 Spring 객체 매핑 메커니즘을 고민하지 마십시오. 때로는 혼란 스러울 때도 있습니다.

  6. ==============================

    6.부분 업데이트는 @SessionAttributes 기능을 사용하여 해결할 수 있습니다.이 기능은 customObjectMerger로 직접 해낸 작업을 수행하도록 만들어졌습니다.

    부분 업데이트는 @SessionAttributes 기능을 사용하여 해결할 수 있습니다.이 기능은 customObjectMerger로 직접 해낸 작업을 수행하도록 만들어졌습니다.

    당신이 시작하도록 내 대답, 특히 편집을보세요.

    https://stackoverflow.com/a/14702971/272180

  7. from https://stackoverflow.com/questions/15124858/spring-partial-update-object-data-binding by cc-by-sa and MIT license