首页 > 其他分享 >简化属性拷贝插件 MapStructs 使用指北

简化属性拷贝插件 MapStructs 使用指北

时间:2023-12-26 16:24:41浏览次数:40  
标签:指北 插件 String tax Mapping private 123 MapStructs target

MapStruct 使用指南

1、安装与介绍

what?

mapstruct 是一个代码生成器,可以简化实现java bean 之间的转换的配置方法

生成的代码使用传统的方法实现get set属性,比起反射更快、更简单、更安全,易于理解

why?

基于多层的应用经常需要映射不同的对象模型 如VO -> TDO 等;属性转换的代码重复且容易出错。

与其他映射框架相比,MapStruct在编译时生成bean映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。

how?

MapStruct是一个注释处理器,插入Java编译器,通过在命令行构建(Maven,Gradle等)时使用。并且实现了默认的映射关系,故便于使用。

非spring环境下安装使用

参考官网设置:

MapStruct – Java bean mappings, the easy way!

spring环境下安装使用

pom.xml中增加mapstruct 的相关依赖

        <!-- mapstruct 实体转换 -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.5.5.Final</version>
        </dependency>

在pom文件的<plugins>标签中新增配置注解处理路径,项目中如果使用了lombok 需要注意其版本,如果版本高于 1.18.16,需要新增 lombok-mapstruct-binding 配置,兼容两者,低于 1.18.16 则不需要配置。

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.2.6.RELEASE</version>
            <configuration>
                <executable>true</executable>
                <mainClass>org.test.application</mainClass>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- depending on your project -->
                <target>1.8</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.5.5.Final</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.16</version>
                    </path>
                    <!-- This is needed when using Lombok 1.18.16 and above -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                    <!-- other annotation processors -->
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

最后是在IDEA 中安装mapstrut支持插件 mapStructSupport,该插件提供未映射字段提醒,写好映射文件后自动生成实现类,无需手动编译查看实现类的逻辑是否正确的功能

至此安装完成。

2、简单对象映射

简单对象映射定义为映射前后对象的基本成员都是基础数据类型,该类对象映射只需要映射转换前后对象的成员名称即可,详见以下示例:

现有转换之前的对象 OriginalOcrEntity

@Data
public class OriginalOcrEntity {
    private String bill_number;
    private String bill_code;
    private String total_words;
    private String payer;
    List<ItemOriginal> items = new ArrayList<>();
}

需要将其转换为以下对象 ConvertOcrEntity 并且 orientation 字段值需要通过其他字段计算得到

@Data
@ToString
public class ConvertOcrEntity {
    private String code;
    private String number;
    private String total_cn;
    private String buyer;
    /**
     * 待计算字段
     */
    private String orientation;

    List<ItemConvert> items = new ArrayList<>();

需要编写一个映射接口:

1、接口上增加 @Mapper(componentModel = "spring" ) 将其交于spring 进行管理,这样可以支持直接@Autowired 获取实例,否则需要手动获取该接口的实例使用;

2、写转换方法,mapstruct 默认会将转换前后字段名一致的字段 get 到并 set 进新的对象中,只需要配置字段名不同的字段值即可;

使用 @Mappings({ }) 标注转换信息,内填写具体映射信息 @Mapping(target = "xx", source = "xx") ;

3、如果对象的成员对象是实例,或者List 则需要实现 对应实体 -> 新的实体的映射接口 以及List转换到新的List的接口,如 List items 转换为 List items,则需要写两个映射接口:

List<ItemConvert> ITEM_CONVERT_LIST(List<ItemOriginal> itemOriginals);

上面实现类的会生成调用下面接口的实现类的方法去做转换。

@Mappings({
        @Mapping(target = "name", source = "project_name"),
        @Mapping(target = "unit", source = "uom"),
        @Mapping(target = "amount", source = "total")
})
ItemConvert ITEM_CONVERT(ItemOriginal itemOriginal);

最后完整的映射接口如下:

/**
 * @ClassName Converter
 * @Description 简单的对象转换
 * @Date 2023/10/11
 */
@Mapper(componentModel = "spring" )
public interface CommonConverter {

    @Mappings({
            @Mapping(target = "code", source = "bill_code"),
            @Mapping(target = "number", source = "bill_number"),
            @Mapping(target = "total_cn", source = "total_words"),
            @Mapping(target = "buyer", source = "payer"),
            @Mapping(target = "items", source = "items")

    })
    ConvertOcrEntity CONVERT_OCR_ENTITY(OriginalOcrEntity entity);


    List<ItemConvert> ITEM_CONVERT_LIST(List<ItemOriginal> itemOriginals);
    
    @Mappings({
            @Mapping(target = "name", source = "project_name"),
            @Mapping(target = "unit", source = "uom"),
            @Mapping(target = "amount", source = "total")
    })
    ItemConvert ITEM_CONVERT(ItemOriginal itemOriginal);

}

编写完成并保存后,idea如果安装了插件,会自动生成对应的代码,否则可能需要手动执行 maven complie 生成对应实现类;点击接口实现类图标,能够跳转到生成的实现类,该实现类的内容就是简单的get与 set 逻辑:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-10-12T17:32:27+0800",
    comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_45 (Oracle Corporation)"
)
@Component
public class CommonConverterImpl implements CommonConverter {

    @Override
    public ConvertOcrEntity CONVERT_OCR_ENTITY(OriginalOcrEntity entity) {
        if ( entity == null ) {
            return null;
        }

        ConvertOcrEntity convertOcrEntity = new ConvertOcrEntity();

        convertOcrEntity.setCode( entity.getBill_code() );
        convertOcrEntity.setNumber( entity.getBill_number() );
        convertOcrEntity.setTotal_cn( entity.getTotal_words() );
        convertOcrEntity.setBuyer( entity.getPayer() );
        // list对象转换 调用的 设置的 ITEM_CONVERT_LIST 接口的实现方法
        convertOcrEntity.setItems( ITEM_CONVERT_LIST( entity.getItems() ) );

        return convertOcrEntity;
    }

    @Override
    public List<ItemConvert> ITEM_CONVERT_LIST(List<ItemOriginal> itemOriginals) {
        if ( itemOriginals == null ) {
            return null;
        }

        List<ItemConvert> list = new ArrayList<ItemConvert>( itemOriginals.size() );
        for ( ItemOriginal itemOriginal : itemOriginals ) {
            // list 实际上 为单个转换方法 ITEM_CONVERT ,汇总后并返回
            list.add( ITEM_CONVERT( itemOriginal ) );
        }

        return list;
    }

    @Override
    public ItemConvert ITEM_CONVERT(ItemOriginal itemOriginal) {
        if ( itemOriginal == null ) {
            return null;
        }

        ItemConvert itemConvert = new ItemConvert();

        itemConvert.setName( itemOriginal.getProject_name() );
        itemConvert.setUnit( itemOriginal.getUom() );
        itemConvert.setAmount( itemOriginal.getTotal() );

        return itemConvert;
    }
}

可以看到,使用mapstruct 生成的实现类,无自己写的转换方法是一致的,能够减少大量写 get set方法的时间。

3、复杂对象映射

财务ocr识别结果转换的场景下存在复杂对象映射,主要针对,由于接口返回数据通常使用json 接收,并且部分字段值可能需要进行简单处理后才能保存到数据库中,json可以直接转为jsonObject ,jsonObject 底层使用的LinkedHashMap<String, Object> 存储的,故考虑直接传入LinkedHashMap<String,Object> 转换为所需对象,详见以下示例:

现通过接口返回的发票识别结果json串如下:

{
  "code": "6300161320",
  "number": "15064112",
  "code_confirm": "333",
  "number_confirm": "444",
  "date": "111",
  "pretax_amount": "222",
  "total": "444",
  "total_cn": "4123",
  "tax": "123",
  "check_code": "123",
  "machine_code": "123",
  "seller": "123",
  "seller_tax_id": "123",
  "seller_addr_tel": "123",
  "seller_bank_account": "123",
  "buyer": "123",
  "buyer_tax_id": "123123",
  "buyer_bank_account": "123123",
  "buyer_addr_tel": "31231",
  "company_seal": "123",
  "form_type": "12313",
  "form_name": "123",
  "kind": "123",
  "ciphertext": "123123",
  "travel_tax": "123123",
  "receiptor": "123",
  "reviewer": "12313",
  "issuer": "123",
  "place": "123",
  "province": "1231",
  "city": "123",
  "service_name": "132",
  "remark": "123",
  "item_names": "123",
  "agent_mark": "12312",
  "acquisition_mark": "123",
  "block_chain": "12313",
  "electronic_mark": "1231",
  "transit_mark": "1231",
  "oil_mark": "1231",
  "vehicle_mark": "12312",
  "title": "111",
  "items": [
    {
      "name": "222",
      "specification": "333",
      "unit": "1231",
      "quantity": "123",
      "price": "123",
      "total": "123",
      "tax_rate": "123",
      "tax": "1231"
    }
  ]
}

需要将该json串转换为以下实体

@Data
public class Invoice10101 {
    private String code;
    private String number;
    private String code_confirm;
    private String number_confirm;
    private String date;
    private String pretax_amount;
    private String total;
    private String total_cn;
    private String tax;
    private String check_code;
    private String machine_code;
    private String seller;
    private String seller_tax_id;
    private String seller_addr_tel;
    private String seller_bank_account;
    private String buyer;
    private String buyer_tax_id;
    private String buyer_bank_account;
    private String buyer_addr_tel;
    private String company_seal;/
    private String form_type;
    private String form_name;
    private String kind;
    private String ciphertext;
    private String travel_tax;
    private String receiptor;
    private String reviewer;
    private String issuer;
    private String place;
    private String province;
    private String city;
    private String service_name;
    private String remark;
    private String item_names;
    private String agent_mark;
    private String acquisition_mark;
    private String block_chain;
    private String electronic_mark;
    private String transit_mark;
    private String oil_mark;
    private String vehicle_mark;
    private String title;
    private List<Invoice10101Item> items;

}

Invoice10101Item 的结构如下:

@Data
public class Invoice10101Item {

    /** "*保险服务*保费", --货物或应税劳务、服务名称 */
    private String name;
    private String specification;
    private String unit;
    private String quantity;
    private String price;
    private String total;
    private String tax_rate;
    private String tax;
}

自定义converter内容如下:

使用 @Mapping(target = "total", expression = "java( xxx )" 指定该字段的调用java 函数处理,该处需要指定处理类的全限定名进行调用

如 ConversionUtil.removeRmbSymbol() 传入String ,去掉符号¥ ,将返回值设置成指定的target字段的值

/**
 * @InterfaceName Invoice10101Converter
 * @Description 增值税发票 转换器
 * @Date 2023/10/13
 */
@Mapper(componentModel = "spring")
public interface Invoice10101Converter {

    @Mappings({
            @Mapping(target = "pretax_amount", expression = "java( org.test.invoice.util.ConversionUtil.removeRmbSymbol((String) map.get(\"pretax_amount\")) )"),
            @Mapping(target = "total", expression = "java( org.test.invoice.util.ConversionUtil.removeRmbSymbol((String) map.get(\"total\")) )"),
            @Mapping(target = "tax", expression = "java( org.test.invoice.util.ConversionUtil.removeRmbSymbol((String) map.get(\"tax\")) )"),
            @Mapping(target = "items", source = "items")
    })
    Invoice10101 CONVERT_OCR_ENTITY (LinkedHashMap<String, Object> map);

    @Mappings({
            @Mapping(target = "name", expression = "java( org.test.invoice.util.ConversionUtil.removeRmbSymbol((String) map.get(\"total\")) )"),
            @Mapping(target = "tax", expression = "java( org.test.invoice.util.ConversionUtil.removeRmbSymbol((String) map.get(\"tax\")) )")
    })
    Invoice10101Item ITEM_CONVERT(Map<String, Object> map);

    //默认情况使用 String  直接返回
    default String map(Object o) {
        return String.valueOf(o);
    }

    // 设置 List<item> 对象 需要自定义逻辑
    default List<Invoice10101Item> CONVERTS(Object o) {
        List<Invoice10101Item> itemConverts = new ArrayList<>();
        JSONArray array =  (JSONArray) o;
        for (Object o1 : array) {
            Map map = ((JSONObject) o1).toJavaObject(Map.class);
            Invoice10101Item itemConvert = this.ITEM_CONVERT(map);
            itemConverts.add(itemConvert);
        }
        return itemConverts;
    }
}

测试转换结果:

--- 转换前
{
  "code": "111",
  "number": "222",
  "code_confirm": "333",
  "number_confirm": "444",
  "date": "111",
  ...
  "items": [
    {
      "name": "222",
      "specification": "333",
      "unit": "1231",
      "quantity": "123",
      "price": "123",
      "total": "123",
      "tax_rate": "123",
      "tax": "1231"
    }
  ]
}
--- 转换后
Invoice10101(code=111, number=222, code_confirm=333, number_confirm=444, date=111, pretax_amount=222, total=444, total_cn=4123, tax=123, check_code=123, machine_code=123, seller=123, seller_tax_id=123, seller_addr_tel=123, seller_bank_account=123, buyer=123, buyer_tax_id=123123, buyer_bank_account=123123, buyer_addr_tel=31231, company_seal=123, form_type=12313, form_name=123, kind=123, ciphertext=123123, travel_tax=123123, receiptor=123, reviewer=12313, issuer=123, place=123, province=1231, city=123, service_name=132, remark=123, item_names=123, agent_mark=12312, acquisition_mark=123, block_chain=12313, electronic_mark=1231, transit_mark=1231, oil_mark=1231, vehicle_mark=12312, title=111, items=[Invoice10101Item(name=123, specification=333, unit=1231, quantity=123, price=123, total=123, tax_rate=123, tax=1231)])

5、复用映射关系

有时部分映射关系可能被多个映射转换器使用,这时候可以提取该部分映射关系为一个新的converter,在其余使用到的接口文件引入提取的接口类,从而复用该部分映射关系。

提取item 转换关系到 ItemConverter ,并在CommonConverter 中uses 引入ItemConverter

/**
 * @InterfaceName ItemConverter
 * @Description ItemConverter
 * @Date 2023/10/13
 */
@Mapper(componentModel = "spring" )
public interface ItemConverter {
    @Mappings({
            @Mapping(target = "name", source = "project_name"),
            @Mapping(target = "unit", source = "uom"),
            @Mapping(target = "amount", source = "total")
    })
    ItemConvert ITEM_CONVERT(ItemOriginal itemOriginal);
}
/**
 * @ClassName Converter
 * @Description 简单的对象转换
 * @Date 2023/10/11
 */
@Mapper(componentModel = "spring" , uses = ItemConverter.class)
public interface CommonConverter {

    @Mappings({
            @Mapping(target = "code", source = "bill_code"),
            @Mapping(target = "number", source = "bill_number"),
            @Mapping(target = "total_cn", source = "total_words"),
            @Mapping(target = "buyer", source = "payer"),
            @Mapping(target = "items", source = "items")

    })
    ConvertOcrEntity CONVERT_OCR_ENTITY(OriginalOcrEntity entity);

    List<ItemConvert> ITEM_CONVERT_LIST(List<ItemOriginal> itemOriginals);

检查生成的class 文件,可以看到 自动autowired了所需的ItemConverter

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-10-13T15:44:29+0800",
    comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_45 (Oracle Corporation)"
)
@Component
public class CommonConverterImpl implements CommonConverter {

    @Autowired
    private ItemConverter itemConverter;

    @Override
    public ConvertOcrEntity CONVERT_OCR_ENTITY(OriginalOcrEntity entity) {
        if ( entity == null ) {
            return null;
        }

        ConvertOcrEntity convertOcrEntity = new ConvertOcrEntity();

        convertOcrEntity.setCode( entity.getBill_code() );
        convertOcrEntity.setNumber( entity.getBill_number() );
        convertOcrEntity.setTotal_cn( entity.getTotal_words() );
        convertOcrEntity.setBuyer( entity.getPayer() );
        convertOcrEntity.setItems( ITEM_CONVERT_LIST( entity.getItems() ) );

        return convertOcrEntity;
    }

    @Override
    public List<ItemConvert> ITEM_CONVERT_LIST(List<ItemOriginal> itemOriginals) {
        if ( itemOriginals == null ) {
            return null;
        }

        List<ItemConvert> list = new ArrayList<ItemConvert>( itemOriginals.size() );
        for ( ItemOriginal itemOriginal : itemOriginals ) {
            list.add( itemConverter.ITEM_CONVERT( itemOriginal ) );
        }

        return list;
    }
}

标签:指北,插件,String,tax,Mapping,private,123,MapStructs,target
From: https://www.cnblogs.com/charler/p/17928380.html

相关文章

  • Taurus .Net Core 微服务开源框架:Admin 插件【4-6】 - 配置管理-Mvc【Plugin-Doc 接口
    前言:继上篇:Taurus.NetCore微服务开源框架:Admin插件【4-5】-配置管理-Mvc【Plugin-Admin后台】本篇继续介绍下一个内容:系统配置节点:Mvc- Plugin- Doc 接口测试及文档:配置界面如下:  配置说明如下:1、Doc.IsEnable:配置当前接口测试文档插件是否可用这是一个......
  • 这款IDEA插件用着确实爽
    IDEA是一款功能强大的集成开发环境(IDE),它可以帮助开发人员更加高效地编写、调试和部署软件应用程序。我们在编写完接口代码后需要进行接口调试等操作,一般需要打开额外的调试工具。今天给大家介绍一款IDEA插件:Apipost-Helper-2.0。代码写完直接编辑器内调试、还支持生成接口文档、接......
  • 免费IDEA插件推荐-Apipost-Helper
    IDEA插件市场中的API调试插件不是收费(FastRequest)就是不好用(apidoc、apidocx等等)今天给大家介绍一款国产的API调试插件:Apipost-Helper,完全免费且好看好用!这款插件由Apipost团队开发的,其官方介绍是:用于IDEA项目快速生成API文档,快速查询接口、接口代码功能,并支持在IDEA中进行API调......
  • vite自动关闭端口号插件
    关闭端口号插件❓:有时候我们需要强制将我们的端口号进行固定,......
  • W3 Total Cache Pro v2.6.0 – WordPress 插件
    W3TotalCacheProv2.6.0:优化WordPress性能的专业解决方案一、引言在数字化的世界中,网站性能对于用户体验和搜索引擎排名至关重要。WordPress作为全球最受欢迎的内容管理系统之一,提供了大量的插件来帮助网站所有者优化其性能。其中,W3TotalCachePro以其出色的缓存功能和性能......
  • 抖音直播机器人浏览器插件
    这是我开发的一款浏览器插件,可以实现在直播间定时发送话术,以及监听直播间评论,对接AI客服系统进行自动回复。实现效果可以实时监听评论区内容,包括用户来了和点赞,以及用户发送的评论文本。调用我客服系统的接口,获取AI回复结果,再自动回复发送。 监听到的评论: 插件的配置......
  • Xmake v2.8.6 发布,新的打包插件:XPack
    Xmake是一个基于Lua的轻量级跨平台构建工具。它非常的轻量,没有任何依赖,因为它内置了Lua运行时。它使用xmake.lua维护项目构建,相比makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。我们......
  • Maven学习笔记 - git-commit-id-plugin插件
    转载自:https://blog.csdn.net/mytt_10566/article/details/100116670参考:插件GitHub地址:https://github.com/git-commit-id/maven-git-commit-id-plugingit-commit-id-plugin是一个类似于buildnumber-maven-plugin的插件,由于buildnumber-maven-plugin插件仅支持CVS和......
  • Maven War 插件详解
     MavenWar插件详解转载自:https://www.jianshu.com/p/0895de58c524WAR插件负责收集Web应用程序的所有依赖项、类和资源,并将它们打包到WAR包中,仅包含scope为compile+runtime的依赖项,默认绑定到package阶段。详情请参考:https://maven.apache.org/plugins/maven-w......
  • Maven打包插件之——maven-jar-plugin、maven-assembly-plugin、maven-shade-plugin
    转载自:https://blog.csdn.net/calm_encode/article/details/1039315371.打包插件的介绍   打包插件是把class文件,配置文件打包成一个jar(war或者其他格式)的包。而且可执行jar包中包含或者不包含相应的依赖包包,当不包含相应的依赖包时,我们需要建立lib目录,且jar和lib目......