MapStruct
是一種類型安全的bean映射
類生成java註釋處理器。
咱們要作的就是定義一個映射器接口,聲明任何須需的映射方法。在編譯的過程當中,MapStruct
會生成此接口的實現。該實現使用純java方法調用的源和目標對象之間的映射,MapStruct
節省了時間,經過生成代碼完成繁瑣和容易出錯的代碼邏輯。下面咱們來揭開它的神祕面紗html
基於SpringBoot
平臺完成MapStruct
映射框架的集成。java
咱們使用idea開發工具建立一個SpringBoot
項目,添加相應的依賴,pom.xml配置文件以下所示:mysql
...省略部分代碼 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <org.mapstruct.version>1.2.0.CR1</org.mapstruct.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <!--<scope>provided</scope>--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--mapStruct依賴--> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>${org.mapstruct.version}</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> ....省略部分代碼
集成MapStruct官方提供了兩種方式,上面配置文件內咱們採用的是直接添加Maven依賴,而官方文檔還提供了另一種方式,採用Maven插件形式配置,配置以下所示:git
...引用官方文檔 ... <properties> <org.mapstruct.version>1.2.0.CR1</org.mapstruct.version> </properties> ... <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> ...
我我的比較喜歡採用第一種方式,不須要配置過多的插件,依賴方式比較方便。
接下來咱們開始配置下數據庫鏈接信息以及簡單的兩張表的SpringDataJPA相關接口。web
在resource下新建立一個application.yml文件,並添加以下數據庫鏈接配置:spring
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8 username: root password: 123456 #最大活躍數 maxActive: 20 #初始化數量 initialSize: 1 #最大鏈接等待超時時間 maxWait: 60000 #打開PSCache,而且指定每一個鏈接PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 #經過connectionProperties屬性來打開mergeSql功能;慢SQL記錄 #connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 minIdle: 1 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: select 1 from dual testWhileIdle: true testOnBorrow: false testOnReturn: false #配置監控統計攔截的filters,去掉後監控界面sql將沒法統計,'wall'用於防火牆 filters: stat, wall, log4j jpa: properties: hibernate: show_sql: true format_sql: true
有關SpringDataJPA相關的學習請訪問第三章:SpringBoot使用SpringDataJPA完成CRUD,咱們在數據庫內建立兩張表信息分別是商品基本信息表、商品類型表。
兩張表有相應的關聯,咱們在不採用鏈接查詢的方式模擬使用MapStruct
,表信息以下所示:sql
--商品類型信息表 CREATE TABLE `good_types` ( `tgt_id` int(11) NOT NULL AUTO_INCREMENT, `tgt_name` varchar(30) DEFAULT NULL, `tgt_is_show` int(1) DEFAULT NULL, `tgt_order` int(255) DEFAULT NULL, PRIMARY KEY (`tgt_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; --商品基本信息表 CREATE TABLE `good_infos` ( `tg_id` int(11) NOT NULL AUTO_INCREMENT, `tg_type_id` int(11) DEFAULT NULL, `tg_title` varchar(30) DEFAULT NULL, `tg_price` decimal(8,2) DEFAULT NULL, `tg_order` int(2) DEFAULT NULL, PRIMARY KEY (`tg_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `good_types` VALUES ('1', '青菜', '1', '1'); INSERT INTO `good_infos` VALUES ('1', '1', '芹菜', '12.40', '1');
下面咱們根據這兩張表建立對應的實體類。數據庫
package com.yuqiyu.chapter30.bean; import lombok.Data; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; /** * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:11:17 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ @Entity @Table(name = "good_types") @Data public class GoodTypeBean { @Id @Column(name = "tgt_id") private Long id; @Column(name = "tgt_name") private String name; @Column(name = "tgt_is_show") private int show; @Column(name = "tgt_order") private int order; }
package com.yuqiyu.chapter30.bean; import lombok.Data; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; /** * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:11:16 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ @Entity @Table(name = "good_infos") @Data public class GoodInfoBean { @Id @Column(name = "tg_id") private Long id; @Column(name = "tg_title") private String title; @Column(name = "tg_price") private double price; @Column(name = "tg_order") private int order; @Column(name = "tg_type_id") private Long typeId; }
接下來咱們繼續建立相關的JPA。apache
package com.yuqiyu.chapter30.jpa; import com.yuqiyu.chapter30.bean.GoodTypeBean; import org.springframework.data.jpa.repository.JpaRepository; /** * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:11:24 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ public interface GoodTypeJPA extends JpaRepository<GoodTypeBean,Long> { }
package com.yuqiyu.chapter30.jpa; import com.yuqiyu.chapter30.bean.GoodInfoBean; import org.springframework.data.jpa.repository.JpaRepository; /** * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:11:23 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ public interface GoodInfoJPA extends JpaRepository<GoodInfoBean,Long> { }
到目前爲止咱們的準備工做差很少完成了,下面咱們開始配置使用MapStruct
。咱們的最終目的是爲了返回一個自定義的DTO實體,那麼咱們就先來建立這個DTO,DTO的代碼以下所示:tomcat
package com.yuqiyu.chapter30.dto; import lombok.Data; /** * 轉換Dto * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:11:25 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ @Data public class GoodInfoDTO { //商品編號 private String goodId; //商品名稱 private String goodName; //商品價格 private double goodPrice; //類型名稱 private String typeName; }
能夠看到GoodInfoDTO實體內集成了商品信息、商品類型兩張表內的數據,對應查詢出信息後,咱們須要使用MapStruct
自動映射到GoodInfoDTO。
Mapper
這個定義通常是被普遍應用到MyBatis
半自動化ORM框架上,而這裏的Mapper跟Mybatis
沒有關係。下面咱們先來看下代碼,以下所示:
package com.yuqiyu.chapter30.mapper; import com.yuqiyu.chapter30.bean.GoodInfoBean; import com.yuqiyu.chapter30.bean.GoodTypeBean; import com.yuqiyu.chapter30.dto.GoodInfoDTO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; /** * 配置映射 * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:11:26 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ @Mapper(componentModel = "spring") //@Mapper public interface GoodInfoMapper { //public static GoodInfoMapper MAPPER = Mappers.getMapper(GoodInfoMapper.class); @Mappings({ @Mapping(source = "type.name",target = "typeName"), @Mapping(source = "good.id",target = "goodId"), @Mapping(source = "good.title",target = "goodName"), @Mapping(source = "good.price",target = "goodPrice") }) public GoodInfoDTO from(GoodInfoBean good, GoodTypeBean type); }
能夠看到GoodInfoMapper是一個接口的形式存在的,固然也能夠是一個抽象類,若是你須要在轉換的時候才用個性化的定製的時候能夠採用抽象類的方式,相應的代碼配置官方文檔已經聲明。@Mapper
註解是用於標註接口、抽象類是被MapStruct
自動映射的標識,只有存在該註解纔會將內部的接口方法自動實現。MapStruct
爲咱們提供了多種的獲取Mapper的方式,比較經常使用的兩種分別是
默認配置,咱們不須要作過多的配置內容,獲取Mapper的方式就是採用Mappers
經過動態工廠內部反射機制完成Mapper實現類的獲取。
默認方式獲取Mapper以下所示:
//Mapper接口內部定義 public static GoodInfoMapper MAPPER = Mappers.getMapper(GoodInfoMapper.class); //外部調用 GoodInfoMapper.MAPPER.from(goodBean,goodTypeBean);
Spring
方式咱們須要在@Mapper
註解內添加componentModel
屬性值,配置後在外部能夠採用@Autowired
方式注入Mapper實現類完成映射方法調用。
Spring方式獲取Mapper以下所示:
//註解配置 @Mapper(componentModel = "spring") //注入Mapper實現類 @Autowired private GoodInfoMapper goodInfoMapper; //調用 goodInfoMapper.from(goodBean,goodTypeBean);
在Mapper
接口定義方法上面聲明瞭一系列的註解映射@Mapping
以及@Mappings
,那麼這兩個註解是用來幹什麼工做的呢?@Mapping
註解咱們用到了兩個屬性,分別是source
、target
source
表明的是映射接口方法內的參數名稱,若是是基本類型的參數,參數名能夠直接做爲source
的內容,若是是實體類型,則能夠採用實體參數名.字段名的方式做爲source
的內容,配置如上面GoodInfoMapper
內容所示。
target
表明的是映射到方法方法值內的字段名稱,配置如上面GoodInfoMapper
所示。
下面咱們執行maven compile命令,到target/generated-sources/annotations目錄下查看對應Mapper
實現類,實現類代碼以下所示:
package com.yuqiyu.chapter30.mapper; import com.yuqiyu.chapter30.bean.GoodInfoBean; import com.yuqiyu.chapter30.bean.GoodTypeBean; import com.yuqiyu.chapter30.dto.GoodInfoDTO; import javax.annotation.Generated; import org.springframework.stereotype.Component; @Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2017-08-20T12:52:52+0800", comments = "version: 1.2.0.CR1, compiler: javac, environment: Java 1.8.0_111 (Oracle Corporation)" ) @Component public class GoodInfoMapperImpl implements GoodInfoMapper { @Override public GoodInfoDTO from(GoodInfoBean good, GoodTypeBean type) { if ( good == null && type == null ) { return null; } GoodInfoDTO goodInfoDTO = new GoodInfoDTO(); if ( good != null ) { if ( good.getId() != null ) { goodInfoDTO.setGoodId( String.valueOf( good.getId() ) ); } goodInfoDTO.setGoodName( good.getTitle() ); goodInfoDTO.setGoodPrice( good.getPrice() ); } if ( type != null ) { goodInfoDTO.setTypeName( type.getName() ); } return goodInfoDTO; } }
MapStruct
根據咱們配置的@Mapping
註解自動將source
實體內的字段進行了調用target
實體內字段的setXxx方法賦值,而且作出了一切參數驗證。
咱們採用了Spring方式
獲取Mapper
,在自動生成的實現類上MapStruct
爲咱們自動添加了@Component
Spring聲明式注入註解配置。
下面咱們來建立一個測試的Controller,用於訪問具體請求地址時查詢出商品的基本信息以及商品的類型後調用GoodInfoMapper.from(xxx,xxx)
方法完成返回GoodInfoDTO
實例。Controller代碼實現以下所示:
package com.yuqiyu.chapter30.controller; import com.yuqiyu.chapter30.bean.GoodInfoBean; import com.yuqiyu.chapter30.bean.GoodTypeBean; import com.yuqiyu.chapter30.dto.GoodInfoDTO; import com.yuqiyu.chapter30.jpa.GoodInfoJPA; import com.yuqiyu.chapter30.jpa.GoodTypeJPA; import com.yuqiyu.chapter30.mapper.GoodInfoMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 測試控制器 * ======================== * Created with IntelliJ IDEA. * User:恆宇少年 * Date:2017/8/20 * Time:12:24 * 碼雲:http://git.oschina.net/jnyqy * ======================== */ @RestController public class GoodInfoController { /** * 注入商品基本信息jpa */ @Autowired private GoodInfoJPA goodInfoJPA; /** * 注入商品類型jpa */ @Autowired private GoodTypeJPA goodTypeJPA; /** * 注入mapStruct轉換Mapper */ @Autowired private GoodInfoMapper goodInfoMapper; /** * 查詢商品詳情 * @param id * @return */ @RequestMapping(value = "/detail/{id}") public GoodInfoDTO detail(@PathVariable("id") Long id) { //查詢商品基本信息 GoodInfoBean goodInfoBean = goodInfoJPA.findOne(id); //查詢商品類型基本信息 GoodTypeBean typeBean = goodTypeJPA.findOne(goodInfoBean.getTypeId()); //返回轉換dto return goodInfoMapper.from(goodInfoBean,typeBean); } }
在Controller內咱們注入了GoodInfoJPA
、GoodTypeJPA
以及GoodInfoMapper
,在查詢商品詳情方法時作出了映射處理。接下來咱們啓動項目訪問地址http://127.0.0.1:8080/detail/1查看界面輸出效果,以下所示:
{ goodId: "1", goodName: "芹菜", goodPrice: 12.4, typeName: "青菜" }
能夠看到界面輸出了GoodInfoDTO
內的全部字段內容,而且經過from方法將對應配置的target
字段賦值。
本章主要講述了基於SpringBoot
開發框架上集成MapStruct
自動映射框架,完成模擬多表獲取數據後將某一些字段經過@Mapping
配置自動映射到DTO實體實例指定的字段內。MapStruct
官方文檔地址:http://mapstruct.org/documentation/dev/reference/html/
本章代碼已經上傳到碼雲:
網頁地址:http://git.oschina.net/jnyqy/lessons
Git地址:https://git.oschina.net/jnyqy/lessons.git
SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄
SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄
感謝閱讀!
歡迎加入QQ技術交流羣,共同進步。
QQ技術交流羣