Home » Java » java – How to patch Dto to Entity?-Exceptionshub

java – How to patch Dto to Entity?-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

I have a controller that accepts a dto object. I need to change the fields that are present in the dto object.

@PatchMapping(value = "/update/{uuid}")
    public ResponseEntity<UserDto> update(
            @RequestBody UserDto userDto,
            @PathVariable("uuid")UUID uuid) throws UserNotFoundException {           
        User updatedUser = userService.update(
                userMapper.userDtoToUser(userDto),
                uuid
        );           
        return .....
    }

But a userService can only accept entities. I need to use mapper dto -> entity. But the entity cannot have empty fields that come in dto (Let’s say that you need to change only one field). What to do in this situation? I know that the controller should not contain logic

How to&Answers:

Two possible ways to resolve this problem. You have to either change the service method to accept the dto and not the entity or you have to create a @Component class which implements Converter, override the convert method and do the necessary field changes there and then @Autowire GenericConversionService in your controller and call genericConversionService.convert(userDto, User.class);

The converter should look like this:

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class UserDtoToUser implements Converter<UserDto, User> {

    @Override
    public User convert(UserDto source) {
        User user = new User();
        // user.set ..... for all necessary fields
        return user;
    }

}

EDIT

In case you want to check the validity of the fields you’re receiving you can simply use the following annotations to ensure your data is correct: @NotBlank – this checks if a field is null or an empty string, @NotNull – this checks if a field is null, @NotEmpty – this checks if a field is null or an empty collection. Important to remember – you must add the @Valid annotation before the object you want to validate (side note – in case of nested objects you also need to add it to your object fields) so it looks like this @RequestBody @Valid UserDto userDto,

And then for the dto it should look something like this:

import java.util.List;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

public class UserDto {
    @NotNull
    private Integer id;

    @NotBlank
    private String username;

    @NotBlank
    private String password;

    @NotEmpty
    private List<String> roles;
}

Change to fields to whatever is in your dto of course. Also in case you need to do more validations there are a number of other validation annotations you can add.

Answer:

You could use reflection to check the null properties and BeanUtils for the copying

In Spring that would be the way that I’d to check the empty properties

public static String[] getNullPropertyNames (Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    PropertyDescriptor[] pds = src.getPropertyDescriptors();

    Set<String> emptyNames = new HashSet<>();
    for(PropertyDescriptor pd : pds) {
        Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) emptyNames.add(pd.getName());
    }
    return emptyNames.toArray(new String[0]);
}

And then for the copying

User updatedUser = new User();
BeanUtils.copyProperties(userDto, updatedUser, getNullPropertyNames(userDto));

Answer:

As you say the controller should not contain logic, so for this Spring has an interface, the interface is Validator

There are pros and cons for considering validation as business logic, and Spring offers a design for validation (and data binding) that does not exclude either one of them. Specifically validation should not be tied to the web tier, should be easy to localize and it should be possible to plug in any validator available. Considering the above, Spring has come up with a Validator interface that is both basic ands eminently usable in every layer of an application.

This is what you need to do:

Spring features a Validator interface that you can use to validate objects. The Validator interface works using an Errors object so that while validating, validators can report validation failures to the Errors object.

we have a DTO to which we will validate the fields:

public class Person {
    private String name;
    private int age;

    // the usual getters and setters...
}

To do validations we must implement Validator interface:

    public class PersonValidator implements Validator {

    /**
     * This Validator validates *just* Person instances
     */
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        if (supports(obj.getClass())) {
            ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
            Person p = (Person) obj;
            if (p.getAge() < 0) {
                e.rejectValue("age", "negativevalue");
            } else if (p.getAge() > 110) {
                e.rejectValue("age", "too.darn.old");
            }
        }
    }
}

If the DTO passes all validations you can map the DTO to an Entity object

You can find more information here Spring Validator