Mapstruct针对domain实体与Entity模型之间的映射操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhou_fan_xi/article/details/84142878

概述

MapStruct是一种类型安全的bean映射类生成java注释处理器,是一种java对象转换框架。
我们要做的就是定义一个映射器接口,声明任何必需的映射方法。在编译的过程中,MapStruct会生成此接口的实现。该实现使用纯java方法调用的源和目标对象之间的映射,MapStruct节省了时间,通过生成代码完成繁琐和容易出错的代码逻辑。

  • MapStruct 可以将某几种类型的对象映射为另外一种类型,如将多个 DO(业务实体对象) 对象转换为 DTO(数据传输对象)
  • 使用方式也很简单,定义一个映射接口,声明映射方法,配上注解,MapSturct 就会实现此接口

定义映射可以使用接口也可以使用静态类。

<1>使用接口映射:

实例如下:

  • 添加依赖包
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>1.2.0.CR1</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.CR1</version>
            <scope>provided</scope>
        </dependency>
  • 创建两个实体类
@Entity
@Table(name = "case_info")
@Data
public class CaseInfo {
    private String batchNumber;
    private String caseNumber;
}
@Entity
@Table(name = "Personal")
@Data
public class Personal extends BaseEntity {
    private String name;
    private Integer sex;
    private String mobileNo;
}
  •  创建接收映射类型
@Data
public class CaseInfoPersonalModel {
    private String batchNumber1;
    private String caseNumber1;
    private String name1;
    private Integer sex1;
    private String mobileNo1;

}
  • 创建 Mapper(映射)接口,MapStruct 就会自动实现该接口
@Mapper(componentModel = "spring")
public interface CasePersonalMapper {

    @Mappings({
            @Mapping(source = "caseInfo.batchNumber",target = "batchNumber1"),
            @Mapping(source = "caseInfo.caseNumber",target = "caseNumber1"),
            @Mapping(source = "personal.name",target = "name1"),
            @Mapping(source = "personal.sex",target = "sex1"),
            @Mapping(source = "personal.mobileNo",target = "mobileNo1")
    })
    CaseInfoPersonalModel fromCaseInfoPersonal(CaseInfo caseInfo, Personal personal);
}
  • 测试:将caseInfo和personal通过 Mapper 映射成 caseInfoPersonalModel
    @GetMapping("/getCaseInfoPersonal")
    @ApiOperation(value = "测试数据", notes = "测试数据")
    public ResponseEntity<CaseInfoPersonalModel> getCaseInfoPersonal(@RequestParam @ApiParam("案件id") String caseId,
                                                                     @RequestHeader(value = "X-UserToken") String token){
        try {
            User user = getUserByToken(token);
            QCaseInfo caseInfo = QCaseInfo.caseInfo;
            CaseInfo caseInfo1 = caseInfoRepository.findOne(caseInfo.id.eq(caseId).and(caseInfo.companyCode.eq(user.getCompanyCode())));
            Personal personal = personalRepository.findOne(QPersonal.personal.id.eq(caseInfo1.getPersonalInfo().getId()));
            CaseInfoPersonalModel caseInfoPersonalModel = casePersonalMapper.fromCaseInfoPersonal(caseInfo1, personal);
            return ResponseEntity.ok().body(caseInfoPersonalModel);
        } catch (Exception e) {
            log.debug(e.getMessage(),e);
            return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("","","插入失败")).body(null);
        }
    }

测试结果成功将两张表的需要的字段映射到返回对象字段中。

MapStruct注解

  • @Mapper:注解在接口、类上,这样 MapStruct 才会去实现该接口
    • componentModel:该属性用于指定实现类的类型,有几个属性:
      • default:默认,不使用任何组建类型,可以通过Mappers.getMapper(Class) 方式获取实例对象
      • spring:在实现类上注解 @Component,可通过 @Autowired 方式注入
      • cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
      • jsr330:实现类上添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取。
  • @Mappings:配置多个@Mapping
  • @Mapping:配置属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
    • source:源属性、target:目标属性
    • dateFormat:可将 String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat 的日期格式

 注意事项:

    在同时使用swagger和mapstruct-jdk8的时候会报错:Couldn't retrieve @Mapper annotation

Error:(19, 1) java: Couldn't retrieve @Mapper annotation

    错误解释:

This actually is not a problem on our side. We have 2 packages, mapstruct and mapstruct-jdk8. The problem seems to occur when you have both on the classpath.

You don't need to exclude the entire swagger for this. You can do this to only exclude the Mapstruct dependency that swagger2 brings with itself:

只需排除 swagger2 本身带来的 Mapstruct 依赖关系:

       <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mapstruct</groupId>
                    <artifactId>mapstruct</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

简单映射:

  •    定义返回模型:
@Data
public class CaseInfoPersonalModel {
    private String name;
    private Integer sex;
    private String mobileNo;
}
  •  定义映射接口:
@Mapper
public interface CasePersonalMapper {
    CasePersonalMapper INSTANCE = Mappers.getMapper(CasePersonalMapper.class);
    CaseInfoPersonalModel fromPersonal(Personal personal);
}
  • 测试类:
    @GetMapping("/getCaseInfoPersonal")
    @ApiOperation(value = "测试数据", notes = "测试数据")
    public ResponseEntity<CaseInfoPersonalModel> getCaseInfoPersonal(@RequestParam @ApiParam("案件id") String caseId,
                                                                     @RequestHeader(value = "X-UserToken") String token){
        try {
            User user = getUserByToken(token);
            QCaseInfo caseInfo = QCaseInfo.caseInfo;
            CaseInfo caseInfo1 = caseInfoRepository.findOne(caseInfo.id.eq(caseId).and(caseInfo.companyCode.eq(user.getCompanyCode())));
            Personal personal = personalRepository.findOne(QPersonal.personal.id.eq(caseInfo1.getPersonalInfo().getId()));
            CaseInfoPersonalModel caseInfoPersonalModel = CasePersonalMapper.INSTANCE.fromPersonal(personal);
            return ResponseEntity.ok().body(caseInfoPersonalModel);
        } catch (Exception e) {
            log.debug(e.getMessage(),e);
            return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert("","","插入失败")).body(null);
        }
    }

运行结果 成功将实体类中需要的字段映射到返回对象字段中。

注意事项:当返回对象和实体类对象中有字段名称不一致时  不会报错 但对应的返回对象中字段为null。

如果model定义了的实体没有可以映射的属性时,就可以使用@Mapping(target = "模型属性", ignore = true)来跳过不需要映射的模型属性了。

@Mapper
public interface CasePersonalMapper {
    CasePersonalMapper INSTANCE = Mappers.getMapper(CasePersonalMapper.class);
    @Mapping(source = "mobileNo",target = "mobileNo1")
    @Mapping(target = "name",ignore = true)
    CaseInfoPersonalModel fromPersonal(Personal personal);
}

<2>使用静态类映射实体 

@Mapper(componentModel = "spring")
public abstract class CasePersonalMapper {
    public CaseInfoPersonalModel mapEighteen(Personal entity) {
        if (entity.getName().equals("zhangsan")) {
            return fromPersonal(entity);
        }
        return null;
    };
    @Mapping(source = "mobileNo", target = "mobileNo1")
    @Mapping(target = "name", ignore = true)
    protected abstract CaseInfoPersonalModel fromPersonal(Personal personal);
}

用静态类来映射有他的好处,最起码接口只能定义方法,无法写方法体,但是使用了静态类,就可以写上方法体了。调用mapEighteen()就可以只映射实体属性name为zhangsan的实体了。 

 

猜你喜欢

转载自blog.csdn.net/zhou_fan_xi/article/details/84142878