Skip to content

AseWhy/conversions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

53 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Conversions

НСбольшой ΠΌΠΎΠ΄ΡƒΠ»ΡŒ для ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ сущностСй Π² DTO. ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ происходит Π·Π° счСт рСфлСксии. Для Π½Π°Ρ‡Π°Π»Π° Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ сканированиС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ², для Ρ‡Π΅Π³ΠΎ Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π±ΠΈΠ½ ConversionConfiguration Π»ΡŽΠ±Ρ‹ΠΌ ΡƒΠ΄ΠΎΠ±Π½Ρ‹ΠΌ способом. Для автоматичСского сканирования ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² прилоТСния Π½ΡƒΠΆΠ½ΠΎ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ EnableConversions Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΡŽ Π² Π»ΡŽΠ±ΡƒΡŽ Ρ‚ΠΎΡ‡ΠΊΡƒ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ прилоТСния.

Π—Π°Ρ‡Π΅ΠΌ?

Для ΡƒΠ΄ΠΎΠ±Π½ΠΎΠΉ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½Π° DTO. ΠœΠΎΠ΄ΡƒΠ»ΡŒ позволяСт Π½Π΅ бСспокоится ΠΎΠ± Ρ€ΡƒΡ‡Π½ΠΎΠΌ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π΄Π°Π½Π½Ρ‹Ρ…, ΠΈ Π΄Π΅Π»Π°Π΅Ρ‚ это автоматичСски.

Когда ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ?

Когда Π΅ΡΡ‚ΡŒ достаточно слоТныС ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΠΈ Π΅ΡΡ‚ΡŒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Π΅ поля этого ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΌΡƒ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚Π΅Π»ΡŽ. Когда ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ DDD ΠΈ класс A ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ прСдставлСн ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΌΡƒ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚Π΅Π»ΡŽ ΠΊΠ°ΠΊ C ΠΈΠ»ΠΈ D Π² зависимости ΠΎΡ‚ контСкста.

Π˜Π·Π²Π΅ΡΡ‚Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹

  • ΠŸΡ€ΠΈ использовании ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ Π½Π° Π²Ρ‹Ρ…ΠΎΠ΄Π΅ ΠΈΠ· ΠΌΠ΅Ρ‚ΠΎΠ΄Π° ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€Π°, ΠΌΠΎΠΆΠ΅Ρ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ ошибка hibernate: "No Session".
    Для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ eager Π·Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ ΠΏΠΎΠ»Π΅ΠΉ, ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ join Π·Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ ΠΏΠΎΠ»Π΅ΠΉ JPA.
  • РСкурсивноС ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊ DTO Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… ΠΏΠΎΠ»Π΅ΠΉ.
    Для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ поля ΠΈ Π·Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Ρ‚ΡŒ ΠΈΡ… ΠΏΠΎ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ модуля

import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.asewhy.conversions.ConversionStore;
import io.github.asewhy.conversions.support.annotations.EnableConversions;
import io.github.asewhy.conversions.support.ConversionConfiguration;
import io.github.asewhy.conversions.support.naming.ConversionNamingStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConversions
public class ConversionConfig implements ConversionConfiguration {
    @Autowired
    protected ObjectMapper objectMapper;
    @Autowired
    protected ApplicationContext context;
    @Autowired
    protected ConversionSupportContext supportContext;

    @Override
    public ConversionStore conversionStore() {
        var store = new ConversionStore(context);

        store.from("com.example");

        return store;
    }

    @Override
    public ObjectMapper objectMapper() {
        return objectMapper;
    }

    @Override
    public Object context() {
        return supportContext;
    }
}

Как ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ Π²Ρ‹ΡˆΠ΅ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ provideStore Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ экзСмпляр Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π° конвСрсий. Π’ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ хранятся информация ΠΎ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌΡ‹Ρ… сущностях, ΠΈ ΠΌΠ°ΠΏΠΏΠΈΠ½Π³ΠΈ ΠΏΠΎΠ»Π΅ΠΉ ΠΊ Π½ΠΈΠΌ. Π”Π°Π»Π΅Π΅ поставщиком конвСрсий всС сущности Π±ΡƒΠ΄ΡƒΡ‚ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒΡΡ ΠΈΠ· экзСмпляра этого Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π°.

Если Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠ° собствСнная ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ° имСнования сущностСй, Ρ‚ΠΎ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Π΅Ρ‘ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ namingPolicy. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Π½ΠΎΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅:

public class ConversionConfig implements ConversionConfiguration {
    // ...
    @Autowired
    protected ConversionNamingStrategy conversionNamingStrategy;

    @Override
    public ConversionNamingStrategy namingStrategy() {
        return conversionNamingStrategy;
    }

    // ...
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠΈ имСнования ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π½ΠΈΠΆΠ΅:

import io.github.asewhy.conversions.support.CaseUtil;
import io.github.asewhy.conversions.support.naming.ExtrudableNamingStrategy;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import paa.coder.noodleCriteriaBuilder.restFilter.payloads.RestFilter;
import paa.coder.noodleCriteriaBuilder.restFilter.payloads.RestOrder;
import paa.coder.noodleCriteriaBuilder.restFilter.payloads.RestPage;
import java.util.Set;

@Component
public class ConversionNamingStrategy extends ExtrudableNamingStrategy {
    /**
     * ВсС сущности ΠΊΡ€ΠΎΠΌΠ΅ этих Π±ΡƒΠ΄ΡƒΡ‚ ΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ΅
     */
    private final static Set<Class<?>> EXCLUDED = Set.of(
        RestFilter.class,
        RestPage.class,
        RestOrder.class
    );

    @Override
    protected boolean isExcluded(@NotNull String defaultName, @NotNull Class<?> rawReturnType) {
        return EXCLUDED.contains(rawReturnType);
    }

    /**
     * БлСдуя этой ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ΅, ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€ Π΄ΡƒΠΌΠ°Π΅Ρ‚ Ρ‡Ρ‚ΠΎ всС поля сущностСй ΠΈΠΌΠ΅ΡŽΡ‚ snakeCase Π² запросС, ΠΈ ΠΎΡ‚Π²Π΅Ρ‚Π΅
     */
    @Override
    protected String convert(@NotNull String defaultName) {
        return CaseUtil.toLowerSnakeCase(defaultName);
    }
}

Π’ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΏΠΎΠΊΠ°Π·Π°Π½Π° Ρ€Π°Π±ΠΎΡ‚Π° с ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎ ExtrudableNamingStrategy классом, Π½ΠΎ Π½ΠΈΠΊΡ‚ΠΎ Π½Π΅ Π·Π°ΠΏΡ€Π΅Ρ‰Π°Π΅Ρ‚ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ свою Π»ΠΎΠ³ΠΈΠΊΡƒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ConversionNamingStrategy;

ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ сущностСй

ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€Ρ‹ Π² ΠΌΠΎΠ΄ΡƒΠ»Π΅ дСлятся Π½Π° ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€Ρ‹ ΠΎΡ‚Π²Π΅Ρ‚Π° ΠΈ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€Ρ‹ запроса.

ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ ΠΎΡ‚Π²Π΅Ρ‚Π°

Для указания Ρ‚ΠΎΠ³ΠΎ Ρ‡Ρ‚ΠΎ класс являСтся Ρ†Π΅Π»ΡŒΡŽ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ ΠΎΡ‚Π²Π΅Ρ‚Π° Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ Π΅Π³ΠΎ ΠΊΠ°ΠΊ @ResponseDTO, ΠΊΠ°ΠΊ Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.ConversionResponse;
import io.github.asewhy.conversions.support.annotations.ResponseDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
@ResponseDTO
public class SomeSourceObjectDTO extends ConversionResponse<SomeSourceObject> {
    private Long id;
}

Если Π΅ΡΡ‚ΡŒ ΠΊΠ°ΠΊΠΈΠ΅-Ρ‚ΠΎ поля, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΡ‚ΡŒ сам, ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄ fillInternal ΠΊΡƒΠ΄Π° ΠΏΠ΅Ρ€Π²Ρ‹ΠΌ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ΄Π°Π²Π°Ρ‚ΡŒΡΡ исходная ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.ConversionResponse;
import io.github.asewhy.conversions.support.annotations.ResponseDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
@ResponseDTO
public class SomeSourceObjectDTO extends ConversionResponse<SomeSourceObject> {
    private Long id;
    private String someUnfilledField;
    
    @Override
    protected void fillInternal(SomeSourceObject from, Object context) {
        this.someUnfilledField = from.someMethodWhoReturnsSomeUnfilledFieldValue();
    }
}

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΠΎΠ»Π΅ someUnfilledField Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠΌ выполнСния ΠΌΠ΅Ρ‚ΠΎΠ΄Π° someMethodWhoReturnsSomeUnfilledFieldValue Ρƒ SomeSourceObject.

ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ ΠΈΠ· ΠΎΠ±Ρ‰Π΅Π³ΠΎ интСрфСйса

Начиная с вСрсии 1.0.3 Π΅ΡΡ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ прСобразования сущностСй A ΠΈ B Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°ΡŽΡ‰ΠΈΡ… ΠΎΠ±Ρ‰ΠΈΠΉ интСрфСйс Π² ΠΎΠ΄Π½Ρƒ ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ ΠΎΡ‚Π²Π΅Ρ‚Π°. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π½ΠΈΠΆΠ΅:

public interface ExampleTestBook {
    String getName();
    String getIsbin();
}
@Getter
@Setter
@ToString
@AllArgsConstructor
public class ExampleTestBookInterfaceA implements ExampleTestBook {
    private String name;
    private String isbin;
    private String genre;
}
@Getter
@Setter
@ToString
@AllArgsConstructor
public class ExampleTestBookInterfaceB implements ExampleTestBook {
    private String name;
    private String isbin;
    private Integer pageCount;
}
@Getter
@Setter
@ToString
@ResponseDTO
public class ExampleTestBookInterfaceResponse extends ConversionResponse<ExampleTestBook> {
    private String name;
    private String isbin;
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π²Ρ‹ΡˆΠ΅ скаТСт ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€Ρƒ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ всС экзСмпляры ExampleTestBook Π² ExampleTestBookInterfaceResponse

ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ запроса

Для указания Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ исходный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ являСтся Ρ†Π΅Π»ΡŒΡŽ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ запроса Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ Π΅Π³ΠΎ Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠ΅ΠΉ @ConversionMutator, ΠΊΠ°ΠΊ Π² ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.ConversionMutator;
import io.github.asewhy.conversions.support.annotations.MutatorDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.validation.constraints.Min;

@Setter
@Getter
@ToString
@MutatorDTO
public class SomeSourceObjectMutatorDTO extends ConversionMutator<SomeSourceObjectDTO> {
    @Min(0)
    private int someInt;
}

Если Π΅ΡΡ‚ΡŒ ΠΊΠ°ΠΊΠΈΠ΅-Ρ‚ΠΎ поля, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΡ‚ΡŒ сам, ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄ fillInternal ΠΊΡƒΠ΄Π° ΠΏΠ΅Ρ€Π²Ρ‹ΠΌ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ΄Π°Π²Π°Ρ‚ΡŒΡΡ исходная ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.ConversionMutator;
import io.github.asewhy.conversions.support.annotations.MutatorDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.validation.constraints.Min;

@Setter
@Getter
@ToString
@MutatorDTO
public class SomeSourceObjectMutatorDTO extends ConversionMutator<SomeSourceObjectDTO> {
    @Min(0)
    private int someInt;
    private long p;

    @Override
    protected void fillInternal(SomeSourceObjectDTO fill, Object context) {
        if(context instanceof SomeService ss) {
            p = ss.doSomeWhoReturnsP();
        }
    }
}

На ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π²Ρ‹ΡˆΠ΅, Π² процСссС заполнСния сущности ΠΏΠΎΠ»Π΅ p Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΎ ΠΈΠ· сСрвиса SomeService. ΠŸΡ€ΠΈ этом экзСмпляр SomeService Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ ΠΊΠ°ΠΊ контСкст.

Если Π΅ΡΡ‚ΡŒ мутируСмая ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ являСтся Π²Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠΉ, Ρ‚ΠΎ Π² Π½Π΅ΠΉ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ поля ΠΈΠ· Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΎΠΉ сущности. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΌΠ΅Ρ‚ΠΎΠ΄ fillParentInternal ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.ConversionMutator;
import io.github.asewhy.conversions.support.annotations.MutatorDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.validation.constraints.Min;

@Setter
@Getter
@ToString
@MutatorDTO
public class SomeParentSourceObjectMutatorDTO extends ConversionMutator<SomeSourceObjectDTO> {
    private int id;
    private SomeSourceObjectMutatorDTO children;
}
import io.github.asewhy.conversions.ConversionMutator;
import io.github.asewhy.conversions.support.annotations.MutatorDTO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.validation.constraints.Min;

@Setter
@Getter
@ToString
@MutatorDTO
public class SomeSourceObjectMutatorDTO extends ConversionMutator<SomeSourceObjectDTO> {
    @Min(0)
    private int someInt;
    private int parentId;

    @Override
    protected void fillParentInternal(SomeSourceObjectDTO fill, Object parent, Object context) {
        if(parent instanceof SomeParentSourceObjectMutatorDTO p) {
            this.parentId = p.getId();
        }
    }
}

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΠΎΠ»Π΅ parentId Ρƒ ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€Π° SomeSourceObjectMutatorDTO Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΎ Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΈΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ, Π² случаС Ссли ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ Π±ΡƒΠ΄Π΅Ρ‚ Π²Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠΉ.

ΠœΠΎΠΆΠ΅Ρ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ ситуация ΠΊΠΎΠ³Π΄Π° ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€ Π±ΡƒΠ΄Π΅Ρ‚ Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ, Ρ‚ΠΎΠ³Π΄Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ кастомный рСсолвСр Ρ‚Π°ΠΊΠΎΠ³ΠΎ запроса. НапримСр, имССтся класс ExampleTestNonMutatorRequest ΠΎΠ΄Π½ΠΈΠΌ ΠΈΠ· ΠΏΠΎΠ»Π΅ΠΉ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€ ExampleTestMutatorRequest, для ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° это ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΠΎΠ»Π΅ request. Π’ΠΎΠ³Π΄Π° рСсолвСр для этого запроса ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Ρ‚Π°ΠΊΠΈΠΌ:

import com.fasterxml.jackson.databind.JsonNode;
import io.github.asewhy.conversions.ConversionProvider;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;

@Component
public class ExampleTestNonMutatorRequestResolver extends RequestResolver<ExampleTestNonMutatorRequest> {
    @Override
    protected ExampleTestNonMutatorRequest resolveInternalRequest(
            @NotNull JsonNode node,
            Class<? extends ExampleTestNonMutatorRequest> fromClass,
            Type generics,
            @NotNull ConversionProvider provider
    ) {
        var config = provider.getConfig();
        var objectMapper = config.getObjectMapper();

        try {
            var data = objectMapper.treeToValue(node, ExampleTestNonMutatorRequest.class);

            provider.createMutator(data.getRequest(), node.get("request"));

            return data;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected boolean canProcess(Class<?> from, Type generics, ConversionProvider provider) {
        return ExampleTestNonMutatorRequest.class.isAssignableFrom(from);
    }
}

ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ класс ExampleTestNonMutatorRequest Π½Π΅ являСтся ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€ΠΎΠΌ.

Π Π°Π±ΠΎΡ‚Π° с ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€Π°ΠΌΠΈ

На ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π²Ρ‹ΡˆΠ΅ ΠΏΠΎΠΊΠ°Π·Π°Π½ процСсс Π΄Π΅ΠΊΠ»Π°Ρ€Π°Ρ†ΠΈΠΈ ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€Π° запроса, ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΠΎΡ‚Π²Π΅Ρ‚Π°. ПослС Π΄Π΅ΠΊΠ»Π°Ρ€Π°Ρ†ΠΈΠΈ, Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€Π΅ просто ΡƒΠΊΠ°Π·Π°Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€ ΠΊΠ°ΠΊ @ShiftController ΠΈΠ»ΠΈ ΠΏΠΎΠΌΠ΅Ρ‚ΠΈΠ² ΠΌΠ΅Ρ‚ΠΎΠ΄ ΠΊΠ°ΠΊ @ConvertResponse, Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Ссли ΠΌΠ΅Ρ‚ΠΎΠ΄ ΡƒΠΆΠ΅ ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½, Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠ°ΠΊ @ResponseBody. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€Π° ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.support.annotations.ShiftController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@ShiftController
@RequestMapping("/comments")
public class CommentController {
    @GetMapping("/{id}")
    public SomeSourceObjectMutatorDTO get(@PathVariable("id") I id) {
        // ΠœΠ΅Ρ‚ΠΎΠ΄ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ экзСмпляр SomeSourceObject
        return (SomeSourceObjectMutatorDTO) provideService().restFindById(id);
    }
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ использования ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π΅Ρ€Π° запроса ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.support.annotations.ConvertMutator;
import io.github.asewhy.conversions.support.annotations.ShiftController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.validation.Valid;

@ShiftController
@RequestMapping("/comments")
public class CommentController {
    @PostMapping
    public void editSomeEntity(@ConvertMutator @Valid SomeSourceObjectMutatorDTO payload) {
        var foundEntity = null;// ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ экзСмпляр Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠΉ сущности
        
        // ΠœΠ΅Ρ‚ΠΎΠ΄ fill поставляСтся Π±Π°Π·ΠΎΠ²Ρ‹ΠΌ классом ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€Π° ΠΈ автоматичСски заполняСт поля с Ρ‚Π΅ΠΌΠΈ ΠΆΠ΅ названиями ΠΈ Ρ‚ΠΈΠΏΠΎΠΌ Π² Ρ†Π΅Π»Π΅Π²ΠΎΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π΅
        // Π²Π°ΠΆΠ½ΠΎ Ρ‡Ρ‚ΠΎΠ±Ρ‹ Ρ†Π΅Π»Π΅Π²ΠΎΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π±Ρ‹Π» ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΌ Π² generic ΠΏΡ€ΠΈ Π΄Π΅ΠΊΠ»Π°Ρ€Π°Ρ†ΠΈΠΈ ΠΌΡƒΡ‚Π°Ρ‚ΠΎΡ€Π°
        payload.fill(foundEntity);
    }
}

ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ²

Π£ вас ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‚ Π² сСбя сущности для ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ. ΠœΠΎΠ΄ΡƒΠ»ΡŒ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΡ… Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ автоматичСски, поэтому Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ рСсолвСры для Ρ‚Π°ΠΊΠΈΡ… ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΎΠ². ΠŸΡ€ΠΈΠΌΠ΅Ρ€ рСсолвСра ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.

import io.github.asewhy.conversions.ConversionProvider;
import io.github.asewhy.conversions.ResponseResolver;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import paa.coder.noodleCriteriaBuilder.restFilter.payloads.RestPage;

import java.lang.reflect.Type;

@Component
public class RestPageResponseResolver extends ResponseResolver<RestPage<?>> {
    @Override
    protected RestPage<?> resolveInternalResponse(
            @NotNull RestPage<?> restPage,
            Class<? extends RestPage<?>> aClass,
            @NotNull ConversionProvider conversionProvider,
            String mapping
    ) {
        return new RestPage<>(
                restPage.getFilter(),
                restPage.getContent().parallelStream().map(conversionProvider::createResponse).toList(),
                restPage.getTotalElements()
        );
    }

    @Override
    protected Class<?> extractInternalExample(@NotNull RestPage<?> from, String mapping, Object globalContextOrPassedContext) {
        return from.stream().map(Object::getClass).findFirst().orElse(null);
    }

    @Override
    protected boolean canProcess(Class<?> from, Type generics, ConversionProvider provider, String mapping) {
        return RestPage.class.isAssignableFrom(from);
    }
}

Π’ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π²Ρ‹ΡˆΠ΅ рСсолвится REST страница. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π²Ρ‹ΡˆΠ΅ позволяСт ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ содСрТимоС Rest страницы. ΠŸΡ€ΠΈ этом оставляя Ρ‚ΠΎΡ‚ ΠΆΠ΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚.