You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MapStruct报错:MetaData中firstProperty无写入访问器的解决咨询

MapStruct映射嵌套不可变Kotlin数据类报错解决方案

问题背景

需要将自定义FlatDTO映射到OpenAPI自动生成的嵌套Response类(生成代码不可修改),执行mvn clean install时出现如下错误:

error: Property "firstProperty" has no write accessor in MetaData for target name "metaData.firstProperty".
[ERROR]     @org.mapstruct.Mappings(value = {@org.mapstruct.Mapping(target = "infoData.id", source = "id"), @org.mapstruct.Mapping(target = "metaData.firstProperty", source = "firstProperty")})

原因是OpenAPI生成的MetaData类中firstProperty为val修饰的不可变属性,没有setter方法,默认MapStruct会尝试通过setter赋值导致报错。

相关代码

OpenAPI生成的Response类

package org.mapstruct.example.kotlin.openapi

import com.fasterxml.jackson.annotation.JsonProperty
import javax.validation.Valid

data class Response(
    @field:Valid @field:JsonProperty("infoData", required = true) val infoData: InfoData,
    @field:Valid @field:JsonProperty("metaData", required = true) val metaData: MetaData
)

data class InfoData(
    @field:JsonProperty("id", required = true) val id: kotlin.String,
)

data class MetaData(
    @field:JsonProperty("firstProperty") val firstProperty: String? = null,
)

自定义FlatDTO类

package org.mapstruct.example.kotlin.models

data class FlatDTO(
    var id: String? = null,
    var firstProperty: String,
)

原MapStruct映射器

package org.mapstruct.example.kotlin.mapper

import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.Mappings
import org.mapstruct.example.kotlin.models.FlatDTO
import org.mapstruct.example.kotlin.openapi.Response

@Mapper
interface DataMapper {
    @Mappings(
        Mapping(target = "infoData.id", source = "id"),
        Mapping(target = "metaData.firstProperty", source = "firstProperty")
    )
    fun flatToResponse(flatDTO: FlatDTO): Response
}

解决方案

方法1:显式调用嵌套类构造器

利用Kotlin数据类的构造器初始化嵌套对象,绕过setter需求。修改映射器如下:

@Mapper
interface DataMapper {
    @Mappings(
        Mapping(target = "infoData", expression = "java(new org.mapstruct.example.kotlin.openapi.InfoData(flatDTO.getId()))"),
        Mapping(target = "metaData", expression = "java(new org.mapstruct.example.kotlin.openapi.MetaData(flatDTO.getFirstProperty()))")
    )
    fun flatToResponse(flatDTO: FlatDTO): Response
}

或者用更符合Kotlin风格的自定义方法:

import org.mapstruct.Named
import org.mapstruct.Mapper
import org.mapstruct.Mapping

@Mapper
interface DataMapper {
    @Mapping(target = "infoData", source = ".", qualifiedByName = ["toInfoData"])
    @Mapping(target = "metaData", source = ".", qualifiedByName = ["toMetaData"])
    fun flatToResponse(flatDTO: FlatDTO): Response

    @Named("toInfoData")
    fun toInfoData(flatDTO: FlatDTO): InfoData {
        return InfoData(flatDTO.id ?: throw IllegalArgumentException("id cannot be null"))
    }

    @Named("toMetaData")
    fun toMetaData(flatDTO: FlatDTO): MetaData {
        return MetaData(flatDTO.firstProperty)
    }
}

方法2:启用构造器优先映射模式

在映射器上配置componentModel并指定构造器注入策略,让MapStruct优先使用构造器而非setter:

import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.Mappings
import org.mapstruct.InjectionStrategy

@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
interface DataMapper {
    @Mappings(
        Mapping(target = "infoData.id", source = "id"),
        Mapping(target = "metaData.firstProperty", source = "firstProperty")
    )
    fun flatToResponse(flatDTO: FlatDTO): Response
}

注意:此方法要求MapStruct版本≥1.4.0,确保能正确识别Kotlin数据类的构造器参数。

核心原理

Kotlin数据类的val属性虽无setter,但会生成包含所有属性的主构造器。MapStruct支持通过构造器初始化对象,只要显式指定构造调用或启用构造器映射模式,就能避开无setter的限制。

内容的提问来源于stack exchange,提问作者Sam Si

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:31:07