java MapStruct泛型Map和子对象的map组合列表

vaqhlq81  于 2023-01-07  发布在  Java
关注(0)|答案(2)|浏览(253)

我的父类是:User.java和2个类:FacebookUser.javaTwitterUser.java它们是使用DiscriminatorColumn返回的实体,取决于数据库中的类型列,我想编写正确的Map器来Map可能是FacebookUser或TwitterUser示例的用户。我有以下Map器,似乎没有按预期工作,仅MapUser父项而不是子项:

@Mapper
public interface UserMapper {
    public static UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    User map(UserDTO userDTO);

    @InheritInverseConfiguration
    UserDTO map(User user);

    List<UserDTO> map(List<User> users);

    FacebookUser map(FacebookUserDTO userDTO);

    @InheritInverseConfiguration
    FacebookUserDTO map(FacebookUser user);

    TwitterUser map(TwitterUserDTO userDTO);

    @InheritInverseConfiguration
    TwitterUserDTO map(TwitterUser user);

}

然后我使用:

UserDTO userDto = UserMapper.INSTANCE.map(user);

要Map的类:

@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING, length = 10)
@DiscriminatorValue(value = "Local")
public class User {
    @Column
    private String firstName;
    @Column
    private String lastName;
    ///... setters and getters
}

@Entity
@DiscriminatorValue(value = "Facebook")
public class FacebookUser extends User {
    @Column
    private String userId;
    ///... setters and getters
}

@Entity
@DiscriminatorValue(value = "Twitter")
public class TwitterUser extends User {
    @Column
    private String screenName; 
    ///... setters and getters
}

DTO:

public class UserDTO {
    private String firstName;
    private String lastName;
    ///... setters and getters
}

public class FacebookUserDTO extends UserDTO {
    private String userId;
    ///... setters and getters
}

public class TwitterUserDTO extends UserDTO {
    private String screenName; 
    ///... setters and getters
}

此外,如果我有混合Facebook用户和Twitter用户,或基本用户的用户列表:
假设我有以下用户:

User user = new User ("firstName","lastName");
User fbUser = new FacebookUser ("firstName","lastName","userId");
User twUser = new TwitterUser ("firstName","lastName","screenName");

List<User> users = new ArrayList<>();
users.add(user);
users.add(fbUser);
users.add(twUser);

//Then: 

List<UserDTO> dtos = UserMapper.INSTANCE.map(users);

我只得到了firstNamelastName,但没有screenNameuserId
有什么解决办法吗?

rsl1atfo

rsl1atfo1#

目前,它似乎还不能作为mapstruct的一个特性:Support for Type-Refinement mapping (or Downcast Mapping)
我在他们的谷歌群里问了一个问题:https://groups.google.com/forum/?fromgroups#!topic/mapstruct-users/PqB-g1SBTPg
并发现我需要使用接口内部的default方法进行手动Map(对于Java 8)。
并且得到了另一个几乎不适用的Map父类的问题,所以我再写一个空类,父类的子类叫做LocalUserDTO
因此代码如下所示:

@Mapper
public interface UserMapper {
    public static UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    LocalUser map(LocalUserDTO userDTO);

    @InheritInverseConfiguration
    LocalUserDTO map(LocalUser user);

    List<UserDTO> map(List<User> users);

    FacebookUser map(FacebookUserDTO userDTO);

    @InheritInverseConfiguration
    FacebookUserDTO map(FacebookUser user);

    TwitterUser map(TwitterUserDTO userDTO);

    @InheritInverseConfiguration
    TwitterUserDTO map(TwitterUser user);

    default UserDTO map(User user) {

        if (user instanceof FacebookUser) {
            return this.map((FacebookUser) user);
        } else if (user instanceof TwitterUser) {
            return this.map((TwitterUser) user);
        } else {
            return this.map((LocalUser) user);
        }
    }

    @InheritInverseConfiguration
    default User map(UserDTO userDTO) {
        if (userDTO instanceof FacebookUserDTO) {
            return this.map((FacebookUserDTO) userDTO);
        } else if (userDTO instanceof TwitterUserDTO) {
            return this.map((TwitterUserDTO) userDTO);
        } else {
            return this.map((LocalUserDTO) userDTO);
        }
    }

}
c9x0cxw0

c9x0cxw02#

我昨天在谷歌上搜索了这个问题,但是在网上找不到任何东西,所以让我们把这个留在这里。
由于前一个答案中提到的问题已经在今年早些时候得到解决,Mapstruct现在正式支持通过@SubclassMapping进行向下转换。

public interface UserMapper {
    @Named("UserToDTO")
    @SubclassMapping(source = FacebookUser.class, target = FacebookUserDTO.class)
    @SubclassMapping(source = TwitterUser.class, target = TwitterUserDTO.class)
    UserDTO toDTO(User source);
}

但是你不能把参数化类List<User>放到那个注解中,而且Mapstruct实际上并没有使用上面的List规范,因此,还需要一个函数。

public interface UserMapper {
    @Named("UserToDTOList")
    @IterableMapping(qualifiedByName = "UserToDTO")
    List<UserDTO> toDTO(List<User> source);

    @Named("UserToDTO")
    @SubclassMapping(source = FacebookUser.class, target = FacebookUserDTO.class)
    @SubclassMapping(source = TwitterUser.class, target = TwitterUserDTO.class)
    UserDTO toDTO(User source);
}

这也适用于类中的字段。对于我的情况,我们有一个类似于类中的列表字段。最初我只添加了@SubclassMapping,但在检查了Mapstruct生成的代码后,我发现它没有使用列表转换的方法。添加@IterableMapping修复了这一问题。
考虑一个包含列表字段的UsersWrapper类。

public class UsersWrapper {
  List<User> list;
}

和一个UsersWrapperDTO

public class UsersWrapperDTO {
  List<UserDTO> list;
}

然后,我们可以在Map器中使用另一种方法。

public interface UserMapper {

    @Mapping(source = "source.list", target = "list", qualifiedByName = "UserToDTOList")
    UsersWrapperDTO toDTO(UsersWrapper source);

    @Named("UserToDTOList")
    @IterableMapping(qualifiedByName = "UserToDTO")
    List<UserDTO> toDTO(List<User> source);

    @Named("UserToDTO")
    @SubclassMapping(source = FacebookUser.class, target = FacebookUserDTO.class)
    @SubclassMapping(source = TwitterUser.class, target = TwitterUserDTO.class)
    UserDTO toDTO(User source);
}

Mapstruct实际上生成了可读性很强的代码,所以如果您发现它的工作方式不如您想象的那样,首先检查一下也是一种选择。
我对Mapstruct不熟悉,所以花了一个下午的时间才把它们拼在一起!不过,我需要把这两个注解组合在一起,这看起来确实很简单,也很有意义。希望这篇文章能为人们节省一些时间。

相关问题