Возвращаемый объект для статических справочных таблиц через Mapstruct

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 появляется несколько раз в моем родительском объекте

Что должно возвращать этот сопоставитель, если Entity отсутствует в репозитории для данного кода состояния?

Alex 24.05.2024 08:03

@Alex Спасибо, что изучили это. Он должен вернуть IntegrityVoilationException, если объект не найден, как если бы мы выполнили JPARepository findById(status_code). Проблема, с которой я столкнулся при использовании метода по умолчанию с использованием JPARepository, заключалась в том, что интерфейс не мог автоматически связать его, и при компиляции maven его реализация становилась пустой.

user2176576 24.05.2024 08:12

Напишите картограф как абстрактный класс. Это позволяет автоматически подключать репозиторий

Alex 24.05.2024 08:30

@Alex обновил вопрос с изменениями пробного варианта предложения

user2176576 24.05.2024 09:40

Если у вас есть уровень обслуживания, вам следует использовать репозиторий только на уровне обслуживания, а не в картографе с архитектурной точки зрения. Реализуйте желаемый метод получения объекта из dto на уровне сервиса. Используйте наличные для объектов, найденных в базе данных, чтобы предотвратить повторный вход в базу данных. Я могу предоставить пример, если он вам нужен.

Alex 24.05.2024 10:16

Да, у нас есть архитектурная настройка для промежуточного уровня обслуживания, я хотел избежать переделки: сначала создавать dto с использованием кода состояния из запроса json, а затем использовать картограф для преобразования в сущность (для справочных таблиц это похоже на создание новый заполнитель объекта), тогда как на уровне сервиса мне теперь нужно будет найти findById, используя объект-заполнитель сущности, созданный картографической структурой. Поэтому я подумал, можно ли это сделать единоразово для справочных таблиц, которые не так часто меняются. Однако абстрактный класс допустил ошибку. Спасибо. Пример кэширования приветствуется.

user2176576 24.05.2024 11:55
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сопоставитель также можно определить в форме абстрактного класса вместо интерфейса и реализовать пользовательские методы непосредственно в классе сопоставителя. В этом случае 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 24.05.2024 09:12

@daniu одним из требований является использование компонента репозитория с автоматическим подключением. Как было сказано, интерфейс этого не позволяет.

Alex 24.05.2024 09:18

Попробовал абстрактный класс и обновил проблему, с которой я столкнулся выше. Моя цель в первую очередь состоит в том, чтобы использовать код состояния, присутствующий в поле json в теле запроса, и сопоставить его с объектом кода состояния, который появляется в поле моего родительского объекта при сохранении это как единое целое.

user2176576 24.05.2024 09:43
Ответ принят как подходящий

Вместо использования репозитория в картографе мы можем переместить метод

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 с пользовательским исключением.

user2176576 29.05.2024 13:34

Помимо try-catch, существует несколько методов перехвата и обработки исключений. Полное руководство смотрите на странице reflectoring.io/spring-boot-Exception-handling Я использую @ControllerAdvice оттуда, а также @Aspect описано на docs.spring.io/spring-framework/referenhttps://docs.spring.i‌ ​о/…

Alex 29.05.2024 15:12

Извините, настоящая ссылка на аспекты — docs.spring.io/spring-framework/reference/core/aop.html

Alex 29.05.2024 15:21

спасибо @Alex посмотрю на это.

user2176576 29.05.2024 15:44

Другие вопросы по теме