Реализация интерфейса Mapstruct Kotlin Gradle не завершена

#kotlin #mapstruct #gradle-kotlin-dsl

Вопрос:

-Определение сущности

 @entity
data class Customer(
@id @GeneratedValue
var id: UUID? = null,
@column(columnDefinition = "text")
val name: String,
@column(columnDefinition = "text")
val phoneNo: String,
val dateOfBirth: LocalDate,
val customerSince: LocalDate,
@column(columnDefinition = "timestamp default current timestamp")
val lastUpdatedAt: LocalDateTime,
@OnetoOne
val address: Address,
@OnetoOne
val paymentInfo: PaymentInfo
)
 

-Определение карты

 @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
interface CustomerMapper {
@mapping(source = "id", target = "customerId")
fun toCustomerDto(customer: Customer): CustomerDto

@InheritInverseConfiguration
fun toCustomer(customerDto: CustomerDto): Customer
}
 

-Сгенерированный Код

 @generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-07-13T21:56:38-0600",
comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from kotlin-annotation-processing-gradle-1.5.20.jar, environment: Java 11.0.10 (Oracle Corporation)"
)
@component
public class CustomerMapperImpl implements CustomerMapper {

@Override
public CustomerDto toCustomerDto(Customer customer) {
    if ( customer == null ) {
        return null;
    }

    UUID customerId = null;
    String name = null;
    String phoneNo = null;
    LocalDate dateOfBirth = null;
    LocalDate customerSince = null;
    LocalDateTime lastUpdatedAt = null;
    AddressDto address = null;
    PaymentInfoDto paymentInfo = null;

    customerId = customer.getId();
    name = customer.getName();
    phoneNo = customer.getPhoneNo();
    dateOfBirth = customer.getDateOfBirth();
    customerSince = customer.getCustomerSince();
    lastUpdatedAt = customer.getLastUpdatedAt();
    address = addressToAddressDto( customer.getAddress() );
    paymentInfo = paymentInfoToPaymentInfoDto( customer.getPaymentInfo() );

    CustomerDto customerDto = new CustomerDto( customerId, name, phoneNo, dateOfBirth, customerSince, lastUpdatedAt, address, paymentInfo );

    return customerDto;
}

@Override
public Customer toCustomer(CustomerDto customerDto) {
    if ( customerDto == null ) {
        return null;
    }

    Customer customer = new Customer();

    customer.setId( customerDto.getCustomerId() );

    return customer;
}

protected AddressDto addressToAddressDto(Address address) {
    if ( address == null ) {
        return null;
    }

    int streetNo = 0;
    String streetName = null;
    String city = null;
    String state = null;
    int zip = 0;

    streetNo = address.getStreetNo();
    streetName = address.getStreetName();
    city = address.getCity();
    state = address.getState();
    zip = address.getZip();

    AddressDto addressDto = new AddressDto( streetNo, streetName, city, state, zip );

    return addressDto;
}

protected PaymentInfoDto paymentInfoToPaymentInfoDto(PaymentInfo paymentInfo) {
    if ( paymentInfo == null ) {
        return null;
    }

    String creditCard = null;
    int expiryMonth = 0;
    int expiryYear = 0;
    int cvv = 0;

    creditCard = paymentInfo.getCreditCard();
    expiryMonth = paymentInfo.getExpiryMonth();
    expiryYear = paymentInfo.getExpiryYear();
    cvv = paymentInfo.getCvv();

    PaymentInfoDto paymentInfoDto = new PaymentInfoDto( creditCard, expiryMonth, expiryYear, cvv );

    return paymentInfoDto;
}
}
 

-Импорт скрипта сборки Gradle
в org.jetbrains.котлин.градл.задачи.Котлинкомпилировать

 plugins {
id("org.springframework.boot") version "2.5.2"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.5.20"
kotlin("plugin.spring") version "1.5.20"
kotlin("kapt") version "1.5.20"
kotlin("plugin.jpa") version "1.5.20"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
mavenCentral()
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.mapstruct:mapstruct:1.4.2.Final")
kapt("org.mapstruct:mapstruct-processor:1.4.2.Final")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}

tasks.withType {
useJUnitPlatform()
}

kapt {
arguments {

arg("mapstruct.defaultComponentModel", "spring")
}
}
 

-Проблема:
Хотя Mapstruct генерирует код для функции toCustomer, реализация нарушена, поскольку свойства клиента не заданы, за исключением поля id. Что нужно сделать, чтобы все интерфейсы были реализованы должным образом. В mapstruct с Kotlin и Gradle есть связанная проблема, связанная с тем, что результаты не согласуются, иногда генерируется код, а иногда его нет, что я тоже испытал https://github.com/mapstruct/mapstruct/issues/2499

Ответ №1:

Проблема здесь не имеет ничего общего с Gradle, а все связано с тем, как определен класс данных Kotlin.

Я не эксперт в Котлине.

Однако при использовании val в классах данных Kotlin создаст несколько конструкторов, включая пустой конструктор по умолчанию. Поскольку в настоящее время мы не можем легко определить, является ли класс классом данных Kotlin, во время обработки аннотаций MapStruct выбирает неправильный конструктор для выполнения сопоставления.

Об этой проблеме уже сообщалось, и ее можно найти здесь

Комментарии:

1. Существуют ли какие-либо обходные пути/хаки для решения этой проблемы на данный момент? Я не вижу, чтобы этот вопрос был еще приоритетным.

2. Если вы можете принудительно добавить аннотацию к одному из конструкторов, то аннотирование @Default с помощью любого пакета должно сработать

3. @SarangKanabargi удалось ли вам найти решение для этого? @Филип, использование @Default аннотации в конструкторе работало до версии Kotlin v1.5 , но больше не работает