Я новичок в Mapstruct, и я пытаюсь понять это правильно.
Чего я хочу добиться, так это преобразовать параметр строки DTO (carModel) в его сущность, получить ее с помощью службы и репозитория.
Проблема в том, что класс Mapper, сгенерированный Mapstruct, пытается внедрить класс Service с аннотацией @Autowired, но это не работает. Услуга нулевая.
Вот мой @Mapper класс:
@Mapper(componentModel = "spring", uses = CarModelService.class)
public interface KitMapper extends EntityMapper<KitDTO, Kit> {
KitMapper INSTANCE = Mappers.getMapper(KitMapper.class);
@Mapping(source = "weight", target = "systemWeight")
@Mapping(source = "carModel", target = "carModel")
Kit toEntity(KitDTO kitDTO);
}
public interface EntityMapper<D, E> {
E toEntity(D dto);
List<E> toEntity(List<D> dtoList);
}
Класс @Service:
@Service
@Transactional
public class CarModelService {
private final CarModelRepository carModelRepository;
@Transactional(readOnly = true)
public CarModel findByName(String name) {
return carModelRepository.findByName(name).orElse(null);
}
}
Класс @Repository:
@Repository
public interface CarModelRepository extends JpaRepository<CarModel, Long> {
Optional<CarModel> findByName(String carModelName);
}
Классы DTO и Entity:
public class KitDTO {
private String id;
private String carModel; // e.g. "Ferrari Monza"
....
}
@Entity
@Table(name = "kit")
public class Kit implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(name = "sequenceGenerator")
@Column(name = "id")
private Long id;
@ManyToOne
private CarModel carModel;
...
}
@Entity
@Table(name = "car_model")
public class CarModel implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(name = "sequenceGenerator")
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
...
}
Сборка работает правильно, но приложение останавливается, когда я пытаюсь использовать Mapper. В нем говорится, что carModelService имеет значение null. Вот сгенерированный картографом класс реализации:
@Component
public class KitMapperImpl implements KitMapper {
@Autowired // <-- this seems not working
private CarModelService carModelService;
@Override
public Kit toEntity(KitDTO kitDTO) {
if ( kitDTO == null ) {
return null;
}
Kit kit = new Kit();
kit.setSystemWeight( String.valueOf( kitDTO.getWeight() ) );
kit.carModel( carModelService.findByName(kitDTO.getCarModel()) ); // <-- carModelService is null!
// other setters
return kit;
}
}
Я пробовал много вещей, используя Decorator, @Context, выражение, внедрял класс @Mapper в класс @Service.
Я нашел много вопросов, но на самом деле мне никто не помог:
Mapstruct - Как я могу внедрить пружинную зависимость в класс Generated Mapper
Класс @Service не привязан автоматически в org.mapstruct. Класс @Mapper
MapStruct mapper не инициализируется с автоподключением при отладке
Любая помощь будет оценена по достоинству! Заранее спасибо!
Видимо все сделано правильно! Вы реализовали, как рекомендуемые документы структуры карты. Не могли бы вы поделиться своим pom.xml?
@VladimirKrylov Я пробовал, но не получилось.
Не могли бы вы поделиться сообщением об ошибке?
Из информации, которой вы поделились, я вижу, что carModel в KitDto — это String, а в Entity — класс CarModel. Не уверен, как это реализовано в автоматически сгенерированном классе реализации mapstruct: kit.carModel( carModelService.findByName(kitDTO.getCarModel()) );.
Но я хотел бы поделиться другим подходом, не знаю, это лучшая практика или нет. В этом подходе вы можете создать абстрактный класс картографа, в котором вы можете вручную добавлять эти сопоставления в репозиторий @Autowired. Я поделился фрагментом для этого. Надеюсь, это поможет вам.
@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public abstract class ProductMapper {
@Autowired
private CarModelService carModelService;
public abstract Kit convertDTOToEntity(KitDTO kitDTO);
public Kit toEntity(KitDTO kitDTO);
{
Kit kit = convertDTOToEntity(kitDTO);
kit.setCarModel(carModelService.findByName(kitDTO.getCarModel()));
return kit;
}
}Любопытно узнать о других подходах, буду следить за этой темой. Можем обсудить лучшие практики
О том, как mapstruct использует carModelService.findByName внутри класса реализации. MapStruct автоматически пытается найти метод, который получает исходный тип в качестве параметра и возвращает целевой тип, а CarModelService был доступен в качестве опции с помощью «uses» над интерфейсом mapstruct ?
@ aakash-thomas Я попробовал предложенный вами подход, но не сработал. Сообщение об ошибке было Null Pointer Excpetion on carModelService. В любом случае, я нашел решение, я отправляю ответ как можно скорее :)
Решение найдено!
Вместо того, чтобы напрямую вызывать метод Mapper toEntity() из класса @RestController, я внедрил преобразователь в класс CarModelService и создал метод, который вызывает этот преобразователь.
Таким образом, поток:
Controller --> Service --> Mapper
@Service
@Transactional
public class KitService {
private final KitRepository kitRepository;
private final KitSearchRepository kitSearchRepository;
private final KitMapper kitMapper; // <-- mapper declaration
public KitService(KitRepository kitRepository, KitSearchRepository kitSearchRepository, KitMapper kitMapper) {
this.kitRepository = kitRepository;
this.kitSearchRepository = kitSearchRepository;
this.kitMapper = kitMapper; // <-- mapper initilization
}
// here the method which calls mapper
public Kit convertDTOToEntity(KitDTO kitDTO) {
return kitMapper.toEntity(kitDTO);
}
Таким образом, класс, сгенерированный Mapstruct, не выдает ошибку в CarModelService.
Кажется, что этот подход — единственный способ добиться этого, создать король «моста» между сервисами и картографами.
(Вы также можете использовать аннотацию @Autowired вместо конструктора)
Вы пробовали внедрение конструктора вместо внедрения поля? @Mapper (componentModel = «пружина», использует = CarModelService.class, инъекцияStrategy = InjectionStrategy.CONSTRUCTOR)