3 таблицы A, B и статус справочной таблицы со столбцом status_code, который извлекает такие детали, как описание и тип. Использование Springboot 3.2.5 и JPA и
<mapstruct.version>1.5.5.Final</mapstruct.version>
<lombok-mapstruct-binding>0.2.0</lombok-mapstruct-binding>
Определенный преобразователь для A и B с использованием AEntity и BEntity с соответствующими DTO. Каждый из этих классов содержит поле статуса, которое представляет собой FK из таблицы состояния. Как определить преобразователь состояния так, чтобы преобразователь возвращал объект, который присутствует в репозитории для данного кода состояния в DTO для объекта состояния?
Как предложил @Alex, я написал это как абстрактный класс, например:
@Mapper(componentModel = "spring")
public abstract class StatusTypeMapper {
@Autowired
private StatusTypeRepository StatusTypeRepository;
StatusTypeMapper INSTANCE = Mappers.getMapper(StatusTypeMapper.class);
StatusType toEntity(StatusTypeDTO StatusTypeDTO){
return StatusTypeRepository.findById(StatusTypeDTO.getStatusCode()).get();
}
}
а затем после компиляции я получаю следующую ошибку:
StatusTypeMapperImpl.<init>(StatusTypeMapperImpl.java:12)
at jdk.internal.reflect.GeneratedConstructorAccessor57.newInstance(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.mapstruct.factory.Mappers.doGetMapper(Mappers.java:85)
at org.mapstruct.factory.Mappers.getMapper(Mappers.java:69)
at org.mapstruct.factory.Mappers.getMapper(Mappers.java:58)
at myPackage.mapper.StatusTypeMapper.<init>(StatusTypeMapper.java:15)
at myPackage.mapper.StatusTypeMapperImpl.<init>(StatusTypeMapperImpl.java:12)
Поскольку я уже выполняю сопоставление с помощью Mapstruct, я надеялся, что это позволит мне каким-то образом сопоставить DTO с Enity, уже присутствующим в базе данных. В противном случае мне нужно снова получить его на моем уровне службы, и это будет переработано для вложенных объекты, где этот status_code появляется несколько раз в моем родительском объекте
@Alex Спасибо, что изучили это. Он должен вернуть IntegrityVoilationException, если объект не найден, как если бы мы выполнили JPARepository findById(status_code). Проблема, с которой я столкнулся при использовании метода по умолчанию с использованием JPARepository, заключалась в том, что интерфейс не мог автоматически связать его, и при компиляции maven его реализация становилась пустой.
Напишите картограф как абстрактный класс. Это позволяет автоматически подключать репозиторий
@Alex обновил вопрос с изменениями пробного варианта предложения
Если у вас есть уровень обслуживания, вам следует использовать репозиторий только на уровне обслуживания, а не в картографе с архитектурной точки зрения. Реализуйте желаемый метод получения объекта из dto на уровне сервиса. Используйте наличные для объектов, найденных в базе данных, чтобы предотвратить повторный вход в базу данных. Я могу предоставить пример, если он вам нужен.
Да, у нас есть архитектурная настройка для промежуточного уровня обслуживания, я хотел избежать переделки: сначала создавать dto с использованием кода состояния из запроса json, а затем использовать картограф для преобразования в сущность (для справочных таблиц это похоже на создание новый заполнитель объекта), тогда как на уровне сервиса мне теперь нужно будет найти findById, используя объект-заполнитель сущности, созданный картографической структурой. Поэтому я подумал, можно ли это сделать единоразово для справочных таблиц, которые не так часто меняются. Однако абстрактный класс допустил ошибку. Спасибо. Пример кэширования приветствуется.




Сопоставитель также можно определить в форме абстрактного класса вместо интерфейса и реализовать пользовательские методы непосредственно в классе сопоставителя. В этом случае MapStruct сгенерирует расширение абстрактного класса с реализацией всех абстрактных методов, см. https://mapstruct.org/documentation/stable/reference/html/#sub-class-mappings
Таким образом, грубый код может быть
@Mapper(componentModel = "spring)
public abstract class EntityToModelMapper {
@Autowired EntityRepository entityRepository;
@Mapping(target = "status", expression = "java(map(arg))")
public abstract Model convert(Entity arg);
StatusType map(Entity entity) {
// use repository here and return status or throw exception
}
}
Или вы оставляете его в качестве интерфейса и либо определяете метод default, либо метод static (который я считаю более подходящим, но слишком загромождает выражение аннотации, поскольку для метода необходимо использовать полное имя класса).
@daniu одним из требований является использование компонента репозитория с автоматическим подключением. Как было сказано, интерфейс этого не позволяет.
Попробовал абстрактный класс и обновил проблему, с которой я столкнулся выше. Моя цель в первую очередь состоит в том, чтобы использовать код состояния, присутствующий в поле json в теле запроса, и сопоставить его с объектом кода состояния, который появляется в поле моего родительского объекта при сохранении это как единое целое.
Вместо использования репозитория в картографе мы можем переместить метод
public StatusType toEntity(StatusTypeDTO StatusTypeDTO){
return StatusTypeRepository.findById(StatusTypeDTO.getStatusCode()).get();
}
на уровень обслуживания. Давайте перепишем его для использования кеша:
@Service
public StatusTypeService {
@Autowired
StatusTypeRepository statusTypeRepository;
@Cacheable("status_code")
public StatusType findStatusTypeById(Long id) {
return statusTypeRepository.findById(id);
}
public StatusType toEntity(StatusTypeDTO StatusTypeDTO) {
return findStatusTypeById(statusTypeDTO.getStatusType.get());
}
}
где первая функция найдет объект в базе данных по идентификатору и сохранит его в кеше с именем «status_code». Это похоже на сохранение его на карте.
Map <Long, StatusCodeEntity> status_code;
Вторая функция использует первую, поэтому, когда объект запросит второй раз, он будет найден в кеше.
Обратите внимание: в примере я использую Long как тип идентификатора StatusEntity, но в вашем случае он может быть другим.
еще одно сомнение - использование этого statusTypeRepository.findById(id).get() для возврата объекта справочной таблицы, но не в состоянии уловить распознавание целостности, тогда вы не знаете, как конкретно перехватывать исключения и сопоставлять их с пользовательскими исключениями, например, нужно сопоставьте SQLState: 23000 с пользовательским исключением.
Помимо try-catch, существует несколько методов перехвата и обработки исключений. Полное руководство смотрите на странице reflectoring.io/spring-boot-Exception-handling Я использую @ControllerAdvice оттуда, а также @Aspect описано на docs.spring.io/spring-framework/referenhttps://docs.spring.i о/…
Извините, настоящая ссылка на аспекты — docs.spring.io/spring-framework/reference/core/aop.html
спасибо @Alex посмотрю на это.
Что должно возвращать этот сопоставитель, если Entity отсутствует в репозитории для данного кода состояния?