Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use Java records as DTO with ModelMapper?

I'm refactoring my code. I want to use java records instead of java class in my DTO. To convert DTO to Entity, I'm using ModelMapper (version 2.3.5). When I try to get info about user (call method co convert Entity to DTO) I get this error.

Failed to instantiate instance of destination xxx.UserDto. Ensure that xxx.UserDto has a non-private no-argument constructor.

This is my code.

public record UserDto(String firstName,
                      String lastName,
                      String email,
                      String imageUrl) {}

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ModelMapper modelMapper;


    @GetMapping("/user/me")
    @PreAuthorize("hasRole('USER')")
    public UserDto getCurrentUser(@CurrentUser UserPrincipal userPrincipal) {
        return convertToDto(userRepository.findById(userPrincipal.getId())
                .orElseThrow(() -> new ResourceNotFoundException("User", "id", userPrincipal.getId())));
    }


    private UserDto convertToDto(User user) {
        UserDto userDto = modelMapper.map(user, UserDto.class);
        return userDto;
    }

    private User convertToEntity(UserDto userDto) throws Exception {
        User post = modelMapper.map(userDto, User.class);
        return post;
    }
}

Edit: Updating to version 2.3.8 doesn't help!

like image 724
Pawel Avatar asked Dec 01 '25 06:12

Pawel


2 Answers

The fields of a record are final, so they must be set through the constructor. Many frameworks will cheat and use various tricks to modify final fields after the fact anyway, but these will not work on records. If you want to instantiate a record, you have to provide all the field values at construction time.

It may take a little time for frameworks to learn about records. The old model of "call a no-arg constructor, then set the fields" will not work for records. Some frameworks are already able to deal with this (e.g., "constructor injection"), while others are not yet there. But, we expect that frameworks will get there soon enough.

As the commenters said, you should encourage your framework provider to support them. It isn't hard.

like image 198
Brian Goetz Avatar answered Dec 06 '25 00:12

Brian Goetz


The initial version for modelmapper with support for records is finally out. It supports Record -> POJO mapping.

The new dependency for this is:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper-module-record</artifactId>
    <version>1.0.0</version>
</dependency>

You then need to register the RecordModule like so:

private ModelMapper modelMapper = new ModelMapper().registerModule(new RecordModule());
like image 36
mr nooby noob Avatar answered Dec 06 '25 00:12

mr nooby noob