#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 , но больше не работает