POJO转VO,让你不再痛苦

在开始之前,我们来说一说传统的代码开发风格:
        首先,我们要有dao、service、controller三层结构。dao用来操作DB,service层封装具体的业务逻辑,Controller层面向前端页面。复杂点的项目会引入一个bo层,也就是dao和service的中间层。比如单表的一些相对复杂的操作封装在bo里,service层只处理实体与实体之间的关系。
        当我们需要在前端页面展示多个实体数据的时候,我们是这样做的:
1.新建一个vo对象
2.vo对象包含我们所需要返回的实体属性
3.然后通过各类properties copy工具来复制属性
过程非常的无聊、甚至痛苦,但是又毫无办法。
得益于有了mapStruct这个组件,让pojo转vo不在痛苦。

一、mapStruct环境准备

导入依赖,一般是结合lombok一起使用,不然略显单调(lombok的使用说明可以查看之前文章):

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>

其实还有另外一种方式,就是基于maven插件的做法,就可以不用导入mapstruct-processor了。

     <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.5.1</version>
           <configuration>
               <source>${java.version}</source>
               <target>${java.version}</target>
               <encoding>${java.encoding}</encoding>
               <annotationProcessorPaths>
                   <path>
                       <groupId>org.mapstruct</groupId>
                       <artifactId>mapstruct-processor</artifactId>
                       <version>${org.mapstruct.version}</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
    </plugin>

但是一般还是使用的第一种方式。
然后在idea->settings->annotation processors->启用注解处理


二、使用

准备一个Goods 对象和User对象:

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Goods {
    private Integer id;
    private String descs;
    private double prices;
    private String name;
    private String about;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class User {
    private int userId;
    private String userName;
    private int age;
    private String description;
}

现在我需要拿到商品和用户信息,只需要新建一个DTO--GoodsInfoDTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class GoodsInfoDto {
    private Integer goodsId;
    private String descs;
    private double goodsPrices;
    private String goodsName;
    private String about;
    private int userId;
    private String userName;
    private int age;
    private String userDescs;
}

然后一个一个的set属性吗?NO,来看mapStruct怎么做的。
新建一个converter类,名字随意,我这里叫GoodsInfoConverter

@Mapper
public interface GoodsInfoConverter {
    GoodsInfoConverter INSTANCE = Mappers.getMapper(GoodsInfoConverter.class);
    @Mappings({@Mapping(source = "goods.id", target = "goodsId"),
            @Mapping(source = "goods.prices", target = "goodsPrices"),
        @Mapping(source = "goods.name",target = "goodsName"),
        @Mapping(source = "user.description",target = "userDescs"),
    }
    )
    public GoodsInfoDto goods2GoodsInfoDto(Goods goods, User user);
}

其中的GoodsInfoConverter INSTANCE = Mappers.getMapper(GoodsInfoConverter.class);必不可少,类似于一个SPI接口,就可以让你再客户端通过GoodsInfoConverter.INSTANCE获取到它的实例。
下面的@Mappings就是对象-DTO之间的映射关系,这里要转换goodsuser对象到DTO,所以传入了两个参数。其中的source是需要转换的对象属性,如果有多个对象需要转换,则需要在前面加入对应的对象名,例如goods.id,用过hibernate的同学会觉得格外小清新~.其中的target是DTO中的属性名,如果需要转换的对象和DTO属性名相同,则不需要配置。还有一些其它参数,我就不一一介绍了。

最重要的一步,就是编译,mvn clean一下:
你会发现在target目录的generated-sources下会生成一个GoodsInfoConverterImpl:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2018-12-20T15:44:31+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
)
public class GoodsInfoConverterImpl implements GoodsInfoConverter {

    @Override
    public GoodsInfoDto goods2GoodsInfoDto(Goods goods, User user) {
        if ( goods == null && user == null ) {
            return null;
        }

        GoodsInfoDto goodsInfoDto = new GoodsInfoDto();

        if ( goods != null ) {
            goodsInfoDto.setGoodsPrices( goods.getPrices() );
            goodsInfoDto.setGoodsName( goods.getName() );
            goodsInfoDto.setGoodsId( goods.getId() );
            goodsInfoDto.setDescs( goods.getDescs() );
            goodsInfoDto.setAbout( goods.getAbout() );
        }
        if ( user != null ) {
            goodsInfoDto.setUserDescs( user.getDescription() );
            goodsInfoDto.setUserId( user.getUserId() );
            goodsInfoDto.setUserName( user.getUserName() );
            goodsInfoDto.setAge( user.getAge() );
        }

        return goodsInfoDto;
    }
}

完全是自动生成的,你不需要管。当我们mvn打包的时候会自动将该文件打包进去。
客户端怎么使用呢?来看一下:

        Goods goods = new Goods();
        goods.setId(3);
        goods.setDescs("飞机");
        goods.setPrices(32000.0D);
        goods.setName("无人机");

        User user = new User();
        user.setUserId(13581);
        user.setUserName("jerrik");
        user.setAge(25);
        user.setDescription("帅小伙");

        GoodsInfoDto goodsInfoDto = GoodsInfoConverter.INSTANCE.goods2GoodsInfoDto(goods,user);
        System.out.println(goodsInfoDto);

//GoodsInfoDto(goodsId=3, descs=飞机, goodsPrices=32000.0, goodsName=无人机, about=null, userId=13581, userName=jerrik, age=25, userDescs=帅小伙)

是不是很简单,而且省去了很多事,比bean copy的性能也要好。完全是基于set来实现的。

三、注意事项

mapStruct集成lombok时,lombok的版本设置成新版本吧。反正笔者1.16.8的lombok会报错,提示Mappings找不到映射的属性,1.16.18是可以编译通过的。如果你觉得这些还不够用,可以去查看官网,谢谢~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • Java分层概念(转) 原文地址(也不属于原文吧,这也是别人转载的不知道原作者是谁,如有侵权,请联系,以删除):h...
    小小世界R阅读 1,614评论 0 0
  • VO(Value Object) 值对象 通常用于业务层之间的数据传递,和 PO 一样也是仅仅包含数据而已。但应是...
    Puny丶微芒阅读 2,804评论 0 1
  • 一个项目中说系统分为表现层、控制层、逻辑层、DAO层和最终数据库五层架构 表现层就是看到的东西,比如你现在看到的当...
    ZZS_简阅读 1,213评论 0 1
  • java的(PO,VO,TO,BO,DAO,POJO)解释 O/R Mapping 是 Object Relati...
    李序锴阅读 2,226评论 0 7
  • 浅析DO DTO VO的概念及用处 概念 VO(View Object):视图对象,用于展示层,它的作用是把某个指...
    cctN阅读 9,243评论 0 5