Как я могу использовать записи Java в качестве DTO с ModelMapper?

Я занимаюсь рефакторингом своего кода. Я хочу использовать записи java вместо класса java в моем DTO. Чтобы преобразовать DTO в Entity, я использую ModelMapper (версия 2.3.5). Когда я пытаюсь получить информацию о пользователе (метод вызова co convert Entity to DTO), я получаю эту ошибку.

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

Это мой код.

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;
    }
}

Изменить: обновление до версии2.3.8 не помогает!

3 ответа

Решение

Поля записи являются окончательными, поэтому они должны быть установлены через конструктор. Многие фреймворки в любом случае обманывают и используют различные уловки для изменения конечных полей, но это не будет работать с записями. Если вы хотите создать экземпляр записи, вы должны предоставить все значения полей во время создания.

Платформам может потребоваться некоторое время, чтобы узнать о записях. Старая модель "вызвать конструктор без аргументов и затем установить поля" не будет работать для записей. Некоторые фреймворки уже могут справиться с этим (например, "внедрение конструктора"), в то время как других еще нет. Но мы ожидаем, что фреймворки появятся в ближайшее время.

Как сказали комментаторы, вы должны поощрять своего поставщика фреймворка поддерживать их. Это не сложно.

record- это функция предварительного просмотра в Java 14, поэтому я бы рекомендовал вам не использовать ее в производственной среде. Во-вторых, он не имитирует java bean.

recordне имеет конструктора по умолчанию no arg неявно, если есть поля. Если вы написали конструктор без аргументов, вам придется делегировать вызов всем конструкторам аргументов, и поскольку все поля являютсяfinalвы можете установить их только один раз. Так что вы как бы застряли там. См. JEP 359:

Это не цель объявить "войну шаблону"; в частности, не ставится цель решить проблемы изменяемых классов с использованием соглашений об именах JavaBean.

Альтернативой, которая работает сегодня, было бы использование Lombok. ПримерUserDto используя Ломбок:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserDto {
    private String firstName;
    private String lastName;
    private String email;
    private String imageUrl;
}

Наконец-то вышла первоначальная версия modelmapper с поддержкой записей . Новая зависимость для этого:

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

Затем вам необходимо зарегистрироватьRecordModuleвот так:

      private ModelMapper modelMapper = new ModelMapper().registerModule(new RecordModule());
Другие вопросы по тегам