Как я могу использовать записи 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());