MapStruct не обнаруживает сеттеры в компоновщике - PullRequest
2 голосов
/ 28 апреля 2019

Я создаю простой сервис REST, используя пружину. Я отделил свои сущности от DTO и сделал DTO неизменными, используя Immutables . Мне нужно было сопоставить DTO и DAO, поэтому я выбрал MapStruct . Mapper не может обнаружить сеттеры, которые я определил в моих DAO.

Проблема в точности похожа на этот вопрос . На этот вопрос нет принятого ответа, и я попробовал все предложения в этом вопросе, и они не работают. Я не хочу пробовать этот ответ , потому что я чувствую, что он побеждает цель, для которой я использую неизменные. @ marc-von-renteln хорошо описывает эту причину в комментарии здесь

Я попробовал ответ , предоставленный @ tobias-schulte. Но это вызвало другую проблему. В классе Mapper в ответе при попытке вернуть Immutable * .Builder из метода сопоставления выдается сообщение о том, что тип Immutable не найден.

Я провел исчерпывающий поиск проблем, зарегистрированных в MapStruct и Immutables, и не смог найти решение. К сожалению, вряд ли есть примеры или люди, использующие комбинацию MapStruct и Immutables В репозитории mapstruct-examples также нет примера для работы с Immutables.

Я даже пытался определить отдельные интерфейсы Mapper для каждого из DtTO (например, UserStatusMapper). Я только усложнял с большим количеством ошибок.

Я создал образец весеннего проекта, чтобы продемонстрировать проблему. Ссылка на GitHub Repo . Это демонстрационное приложение почти такое же, как REST-сервис, который я создаю. Вся база данных (spring-data-jpa, hibernate) удалена, и я использую фиктивные данные. Если вы извлекаете проект и запускаете демо-приложение, вы можете сделать два вызова API.

GetUser: Запрос: 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"
}

Ниже приведены важные части кода, связанные с проблемой:

Daos: 1. UserDao

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. UserStatusDao:

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);
        }
    }
}

DTOS 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 UserMapper:

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 Ответ

2 голосов
/ 28 апреля 2019

Mapstruct не распознает ваши получатели в UserDto и UserStatusDto.

Когда вы меняете существующие методы (например, public abstract String username()) в этих абстрактных классах на классические методы получения, такие как

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

, MapperImpl будет содержать необходимые вызовы.Обратите внимание, что @JsonProperty должен впоследствии иметь само имя атрибута (из-за измененного имени метода).

Вот полные классы UserDto и 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 = 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();

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...