2

I want something very basic.

I just want to update the User. Front end sends a json for the user. I wanted to avoid set every value of currentUser (it has like 50 fields)

@PutMapping("user/{id}")
public boolean updateUser(@PathVariable id, @RequestBody User user) {
   User currentUser = userRepo.findOne(id);
   // What now??

}
Joe Clay
  • 33,401
  • 4
  • 85
  • 85
user10302013
  • 125
  • 2
  • 8
  • It would be helpful if you specified exactly what library you're using to communicate with your database - I'd guess it's Spring Data, but without knowing for sure it's hard to answer your question. – Joe Clay Sep 20 '18 at 12:03
  • @JoeClay JpaRepository, I have it autowired to the controller – user10302013 Sep 20 '18 at 12:05
  • Spring Data Rest automatically create all the REST endpoints. You can use PATCH requests for changing only provided properties. – Selindek Sep 20 '18 at 12:07
  • any code help?? – user10302013 Sep 20 '18 at 12:15
  • `PUT` should replace the entire resource with the new representation. Your DAO/Repository should have a method to set the `User` object for a given id. If you just want to do update a subset of the `User` fields, you need to use HTTP `PATCH` method, for which there are various ways to implement in Spring: https://www.baeldung.com/http-put-patch-difference-spring – Mike Sep 20 '18 at 13:01
  • if you get User type from request why you again try to find it ? try update the send user itself – MohammadReza Alagheband Sep 20 '18 at 13:30

3 Answers3

2

you need to do something like that. Please keep in mind that this approach is helpful for partial object update. That means if your object(in RequestBody) doesn't contains some fields(field==null) then this field will remain unchanged.

@PutMapping("user/{id}")
public boolean updateUser(@PathVariable id, @RequestBody User user) {
   User currentUser = userRepo.findOne(id);
   user = (User) PersistenceUtils.partialUpdate(currentUser, user);
   return userRepo.save(user);

}

public class PersistenceUtils {

    public static Object partialUpdate(Object dbObject, Object partialUpdateObject){
        String[] ignoredProperties = getNullPropertyNames(partialUpdateObject);
        BeanUtils.copyProperties(partialUpdateObject, dbObject, ignoredProperties);
        return dbObject;
    }

    private static String[] getNullPropertyNames(Object object) {
        final BeanWrapper wrappedSource = new BeanWrapperImpl(object);
        return Stream.of(wrappedSource.getPropertyDescriptors())
                .map(FeatureDescriptor::getName)
                .filter(propertyName -> wrappedSource.getPropertyValue(propertyName) == null)
                .toArray(String[]::new);
    }


}
Alexander Petrov
  • 951
  • 6
  • 11
1

First Approach:

  • If User type it sent from the front-end you don't need to set everything again, you can use the object itself to update the values.

So, you can take these steps:

  1. remove {id} from url mapping as id already included in User class
  2. check if id has a value, otherwise throw an execption that updating not possible
  3. if id is available just execute userRepository.save(user) for updating
  4. return body back to front

    @PutMapping("/user")
    public ResponseEntity < User > updateUser(@RequestBody User user) throws 
    URISyntaxException {
        log.debug("REST request to update User : {}", user);
        if (user.getId() == null) {
            throw new ResourceNotFoundException("User id should not be null ")
        }
        User result = userRepository.save(user);
        return ResponseEntity.ok().body(result);
    }
    

And here is the custom Exception definition when id is null:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }

    public ResourceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

Second Approach:

  • If you are still insisting to set huge amount of properties you can use org.springframework.beans.BeanUtils copyProperties as BeanUtils.copyProperties(sourceItem, targetItem)
@PutMapping("/{id}")
public ResponseEntity<User> update(@PathVariable("id") id,  @RequestBody User user) {
    User currentUser = userRepo.findOne(id);
    BeanUtils.copyProperties(user, currentUser);
    return ResponseEntity.ok(repo.save(targetItem));
}
0

You can refer to this question about PATCH vs PUT.

Let's say you are just changing the online status of the user. In that case it might be better to go with PATCH and have the change reflected on a path variable.

For example:

@PatchMapping("user/{id}/{status}")
public boolean setStatus(@PathVariable id, @PathVariable status) {
   User currentUser = userRepo.findOne(id);
   currentUser.setStatus(status);
   userRepo.save(currentUser);
   // ...
}

If the intention is to make changes to an undefined number of fields, you can go with PUT, have the data in the request body and use the DTO pattern. You can find a lot of examples on the web about the DTO pattern. In this case, the code would be as below.

@PatchMapping("user/{id}")
public boolean updateUser(@PathVariable id, @RequestBody UserDTO userDTO) {

   // UserMapper is the mapper class which returns you a new User
   // populated with the data provided in the data transfer object.
   User user = UserMapper.makeUser(userDTO);
   userRepo.update(id, user);
   // ...
}
McMutton
  • 905
  • 13
  • 25