MapStruct 未检测到构建器中的设置器

Posted

技术标签:

【中文标题】MapStruct 未检测到构建器中的设置器【英文标题】:MapStruct does not detect setters in builder 【发布时间】:2019-09-17 05:28:24 【问题描述】:

我正在使用 spring 构建一个简单的 REST 服务。我将我的实体与 DTO 分开,并使用 Immutables 使 DTO 不可变。我需要 DTO 和 DAO 之间的映射,所以我选择了MapStruct。 Mapper 无法检测到我在 DAO 中定义的设置器。

问题与this question 完全相同。这个问题没有公认的答案,我已经尝试了该问题中的所有建议,但它们不起作用。我不想尝试this answer,因为我觉得它违背了我使用 Immutables 的目的。 @marc-von-renteln 在评论 here 中很好地总结了这个原因

我尝试了@tobias-schulte 提供的answer。但这导致了一个不同的问题。在答案中的 Mapper 类中,尝试从映射方法返回 Immutable*.Builder 会引发错误,指出找不到 Immutable 类型。

我已经详尽搜索了针对 MapStruct 和 Immutables 记录的问题,但我一直无法找到解决方案。不幸的是,很少有例子或人使用 MapStruct 和 Immutables 的组合。 mapstruct-examples 存储库也没有使用 Immutables 的示例。

我什至尝试为每个 DtTO 定义单独的 Mapper 接口(如 UserStatusMapper)。我只是让它变得更复杂,出现更多错误。

我创建了一个示例 spring 项目来演示该问题。 GitHub Repo Link。这个演示应用程序与我正在创建的 REST 服务几乎相同。所有数据库(spring-data-jpa,hibernate)的东西都被删除了,我正在使用模拟数据。 如果您签出项目并运行演示应用程序,则可以进行两次 API 调用。

获取用户: 要求: http://localhost:8080/user/api/v1/users/1 回应:


    "id": 0,
    "username": "TestUser",
    "email": "TestUser@demo.com",
    "userStatus": 
        "id": 1,
        "status": 1,
        "statusName": "Active"
    

Createuser:这里有问题 http://localhost:8080/user/api/v1/users/create 样本输入:


    "username": "TestUser",
    "email": "TestUser@demo.com",
    "userStatus": 
        "id": 1,
        "status": 1,
        "statusName": "Active"
    

回复:


    "timestamp": "2019-04-28T09:29:24.933+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Type definition error: [simple type, class com.immutablesmapstruct.demo.dto.model.ImmutableUserDto$Builder]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.immutablesmapstruct.demo.dto.model.ImmutableUserDto$Builder`, problem: Cannot build UserDto, some of required attributes are not set [username, email, userStatus]\n at [Source: (PushbackInputStream); line: 9, column: 1]",
    "path": "/user/api/v1/users/create"

以下是与问题相关的重要代码:

道: 1。用户道

public class User 

    // Primary Key. Something that is annotated with @Id
    private int id;
    private String username;
    private String email;
    private UserStatus userStatus;

    private User(Builder builder) 
        id = builder.id;
        username = builder.username;
        email = builder.email;
        userStatus = builder.userStatus;
    

    public static Builder builder() 
        return new Builder();
    

    public int getId() 
        return id;
    

    public String getUsername() 
        return username;
    

    public String getEmail() 
        return email;
    

    public UserStatus getUserStatus() 
        return userStatus;
    

    public static final class Builder 
        private int id;
        private String username;
        private String email;
        private UserStatus userStatus;

        private Builder() 
        

        public Builder setId(int id) 
            this.id = id;
            return this;
        

        public Builder setUsername(String username) 
            this.username = username;
            return this;
        

        public Builder setEmail(String email) 
            this.email = email;
            return this;
        

        public Builder setUserStatus(UserStatus userStatus) 
            this.userStatus = userStatus;
            return this;
        

        public User build() 
            return new User(this);
        

2.用户状态道:

package com.immutablesmapstruct.demo.dao.model;

/**
 * Status of user.
 * Example: Active or Inactive
 */
public class UserStatus 
    // Primary Key. Something that is annotated with @Id
    private int id;
    // A value of 1 or 0
    private int status;
    // Active , InActive
    private String statusName;

    private UserStatus(Builder builder) 
        id = builder.id;
        status = builder.status;
        statusName = builder.statusName;
    

    public static Builder builder() 
        return new Builder();
    

    public int getId() 
        return id;
    

    public int getStatus() 
        return status;
    

    public String getStatusName() 
        return statusName;
    

    public static final class Builder 
        private int id;
        private int status;
        private String statusName;

        private Builder() 
        

        public Builder setId(int id) 
            this.id = id;
            return this;
        

        public Builder setStatus(int status) 
            this.status = status;
            return this;
        

        public Builder setStatusName(String statusName) 
            this.statusName = statusName;
            return this;
        

        public UserStatus build() 
            return new UserStatus(this);
        
    

DTO 1。 UserDto:

package com.immutablesmapstruct.demo.dto.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSerialize(as = ImmutableUserDto.class)
@JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto 

    @Value.Default
    @JsonProperty
    public int id() 
        return 0;
    

    @JsonProperty
    public abstract String username();

    @JsonProperty
    public abstract String email();

    @JsonProperty
    public abstract UserStatusDto userStatus();

2. UserStatusDto:

package com.immutablesmapstruct.demo.dto.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSerialize(as = ImmutableUserStatusDto.class)
@JsonDeserialize(builder = ImmutableUserStatusDto.Builder.class)
public abstract class UserStatusDto 

    @JsonProperty
    public abstract int id();

    @JsonProperty
    public abstract int status();

    @JsonProperty
    public abstract String statusName();


MapStruct 用户映射器:

package com.immutablesmapstruct.demo.dto.mapper;

import com.immutablesmapstruct.demo.dao.model.User;
import com.immutablesmapstruct.demo.dao.model.UserStatus;
import com.immutablesmapstruct.demo.dto.model.UserDto;
import com.immutablesmapstruct.demo.dto.model.UserStatusDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper(componentModel = "spring")
public interface UserMapper 

    UserMapper USER_MAPPER_INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDto userDaoToDto(User user);

    //Problem here.
    User userDtoToDao(UserDto userDto);

    UserStatusDto userStatusDaoToDto(UserStatus userStatusDao);
    UserStatus userStatusDtoToDao(UserStatusDto userStatusDto);


如果我查看 MapStruct 为userDtoToDao 生成的具体方法,我可以清楚地看到设置器没有被识别。

package com.immutablesmapstruct.demo.dto.mapper;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-04-28T02:29:03-0700",
    comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_191 (Oracle Corporation)"
)
@Component
public class UserMapperImpl implements UserMapper 
...
...
    @Override
    public User userDtoToDao(UserDto userDto) 
        if ( userDto == null ) 
            return null;
        

        com.immutablesmapstruct.demo.dao.model.User.Builder user = User.builder();

        return user.build();
    
....
....

【问题讨论】:

【参考方案1】:

Mapstruct 无法识别 UserDtoUserStatusDto 中的 getter。

当您将这些抽象类中的现有方法(如 public abstract String username())更改为经典的 getter 时

@JsonProperty("username")
public abstract String getUsername();

MapperImpl 将包含所需的调用。请注意,@JsonProperty 之后需要有属性名称本身(因为更改了方法名称)。

这里是完整的类 UserDtoUserStatusDto 有上述变化:

package com.immutablesmapstruct.demo.dto.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSerialize(as = ImmutableUserDto.class)
@JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto 

    @Value.Default
    @JsonProperty("id")
    public int getId() 
        return 0;
    

    @JsonProperty("username")
    public abstract String getUsername();

    @JsonProperty("email")
    public abstract String getEmail();

    @JsonProperty("userStatus")
    public abstract UserStatusDto getUserStatus();


 

package com.immutablesmapstruct.demo.dto.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(defaults = @Value.Immutable(copy = false), init = "set*")
@JsonSerialize(as = ImmutableUserStatusDto.class)
@JsonDeserialize(builder = ImmutableUserStatusDto.Builder.class)
public abstract class UserStatusDto 

    @JsonProperty("id")
    public abstract int getId();

    @JsonProperty("status")
    public abstract int getStatus();

    @JsonProperty("statusName")
    public abstract String getStatusName();



【讨论】:

感谢@dersvenhesse 的回复。这解决了我的问题:)

以上是关于MapStruct 未检测到构建器中的设置器的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Luis 集成到机器人构建器中

将图像添加到界面构建器中的按钮

带有 AWS SDK V2 构建器的 MapStruct

在 Flutter 的流构建器中使用 Future 构建器是正确的

向架构构建器中的枚举类型字段添加默认值

DbContext模型构建器中的用户详细信息