首先来了解一下dto,dto简单的理解就是做数据传输对象的,类似于vo,但是vo用于传输到前端。
1.mapstruct是用来做什么的? 现在有这么个场景,从数据库查询出来了一个user对象(包含id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象role(包含id,角色名,角色描述这些字段),现在在controller需要用到user对象的id,用户名,和角色对象的角色名三个属性。
一种方式是直接把两个对象传递到controller层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(dto),通过传输这个类的实例来完成数据传输。
user.java
@allargsconstructor @data public class user { private long id; private string username; private string password; private string phonenum; private string email; private role role; } role.java
@allargsconstructor @data public class role { private long id; private string rolename; private string description; } userroledto.java,这个类就是封装的类
@data public class userroledto { /** * 用户id */ private long userid; /** * 用户名 */ private string name; /** * 角色名 */ private string rolename; } 测试类,模拟将user对象转换成userroledto对象
public class maintest { user user = null; /** * 模拟从数据库中查出user对象 */ @before public void before() { role role = new role(2l, administrator, 超级管理员); user = new user(1l, zhangsan, 12345, 17677778888, 123@qq.com, role); } /** * 模拟把user对象转换成userroledto对象 */ @test public void test1() { userroledto userroledto = new userroledto(); userroledto.setuserid(user.getid()); userroledto.setname(user.getusername()); userroledto.setrolename(user.getrole().getrolename()); system.out.println(userroledto); } } 从上面代码可以看出,通过getter、setter的方式把一个对象属性值复制到另一个对象中去还是很麻烦的,尤其是当属性过多的时候。而mapstruct就是用于解决这种问题的。
2.使用mapstruct解决上述问题 这里我们沿用user.java、role.java、userroledto.java。
新建一个userrolemapper.java,这个来用来定义user.java、role.java和userroledto.java之间属性对应规则:
userrolemapper.java
import org.mapstruct.mapper; import org.mapstruct.mapping; import org.mapstruct.mappings; import org.mapstruct.factory.mappers; /** * @mapper 定义这是一个mapstruct对象属性转换接口,在这个类里面规定转换规则 * 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制 */ @mapper public interface userrolemapper { /** * 获取该类自动生成的实现类的实例 * 接口中的属性都是 public static final 的 方法都是public abstract的 */ userrolemapper instances = mappers.getmapper(userrolemapper.class); /** * 这个方法就是用于实现对象属性复制的方法 * * @mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性 * * @param user 这个参数就是源对象,也就是需要被复制的对象 * @return 返回的是目标对象,就是最终的结果对象 */ @mappings({ @mapping(source = id, target = userid), @mapping(source = username, target = name), @mapping(source = role.rolename, target = rolename) }) userroledto touserroledto(user user); } 在测试类中测试:
通过上面的例子可以看出,使用mapstruct方便许多。
3.添加默认方法 添加默认方法是为了这个类(接口)不只是为了做数据转换用的,也可以做一些其他的事。
import org.mapstruct.mapper; import org.mapstruct.mapping; import org.mapstruct.mappings; import org.mapstruct.factory.mappers; /** * @mapper 定义这是一个mapstruct对象属性转换接口,在这个类里面规定转换规则 * 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制 */ @mapper public interface userrolemapper { /** * 获取该类自动生成的实现类的实例 * 接口中的属性都是 public static final 的 方法都是public abstract的 */ userrolemapper instances = mappers.getmapper(userrolemapper.class); /** * 这个方法就是用于实现对象属性复制的方法 * * @mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性 * * @param user 这个参数就是源对象,也就是需要被复制的对象 * @return 返回的是目标对象,就是最终的结果对象 */ @mappings({ @mapping(source = id, target = userid), @mapping(source = username, target = name), @mapping(source = role.rolename, target = rolename) }) userroledto touserroledto(user user); /** * 提供默认方法,方法自己定义,这个方法是我随便写的,不是要按照这个格式来的 * @return */ default userroledto defaultconvert() { userroledto userroledto = new userroledto(); userroledto.setuserid(0l); userroledto.setname(none); userroledto.setrolename(none); return userroledto; } } 测试代码:
@test public void test3() { userrolemapper userrolemapperinstances = userrolemapper.instances; userroledto userroledto = userrolemapperinstances.defaultconvert(); system.out.println(userroledto); } 4. 可以使用abstract class来代替接口 mapper可以用接口来实现,也可以完全由抽象来完全代替
import org.mapstruct.mapper; import org.mapstruct.mapping; import org.mapstruct.mappings; import org.mapstruct.factory.mappers; /** * @mapper 定义这是一个mapstruct对象属性转换接口,在这个类里面规定转换规则 * 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制 */ @mapper public abstract class userrolemapper { /** * 获取该类自动生成的实现类的实例 * 接口中的属性都是 public static final 的 方法都是public abstract的 */ public static final userrolemapper instances = mappers.getmapper(userrolemapper.class); /** * 这个方法就是用于实现对象属性复制的方法 * * @mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性 * * @param user 这个参数就是源对象,也就是需要被复制的对象 * @return 返回的是目标对象,就是最终的结果对象 */ @mappings({ @mapping(source = id, target = userid), @mapping(source = username, target = name), @mapping(source = role.rolename, target = rolename) }) public abstract userroledto touserroledto(user user); /** * 提供默认方法,方法自己定义,这个方法是我随便写的,不是要按照这个格式来的 * @return */ userroledto defaultconvert() { userroledto userroledto = new userroledto(); userroledto.setuserid(0l); userroledto.setname(none); userroledto.setrolename(none); return userroledto; } } 5.可以使用多个参数 可以绑定多个对象的属性值到目标对象中:
package com.mapstruct.demo; import org.mapstruct.mapper; import org.mapstruct.mapping; import org.mapstruct.mappings; import org.mapstruct.factory.mappers; /** * @mapper 定义这是一个mapstruct对象属性转换接口,在这个类里面规定转换规则 * 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制 */ @mapper public interface userrolemapper { /** * 获取该类自动生成的实现类的实例 * 接口中的属性都是 public static final 的 方法都是public abstract的 */ userrolemapper instances = mappers.getmapper(userrolemapper.class); /** * 这个方法就是用于实现对象属性复制的方法 * * @mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性 * * @param user 这个参数就是源对象,也就是需要被复制的对象 * @return 返回的是目标对象,就是最终的结果对象 */ @mappings({ @mapping(source = id, target = userid), @mapping(source = username, target = name), @mapping(source = role.rolename, target = rolename) }) userroledto touserroledto(user user); /** * 多个参数中的值绑定 * @param user 源1 * @param role 源2 * @return 从源1、2中提取出的结果 */ @mappings({ @mapping(source = user.id, target = userid), // 把user中的id绑定到目标对象的userid属性中 @mapping(source = user.username, target = name), // 把user中的username绑定到目标对象的name属性中 @mapping(source = role.rolename, target = rolename) // 把role对象的rolename属性值绑定到目标对象的rolename中 }) userroledto touserroledto(user user, role role); 对比两个方法~
5.直接使用参数作为属性值 package com.mapstruct.demo; import org.mapstruct.mapper; import org.mapstruct.mapping; import org.mapstruct.mappings; import org.mapstruct.factory.mappers; /** * @mapper 定义这是一个mapstruct对象属性转换接口,在这个类里面规定转换规则 * 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制 */ @mapper public interface userrolemapper { /** * 获取该类自动生成的实现类的实例 * 接口中的属性都是 public static final 的 方法都是public abstract的 */ userrolemapper instances = mappers.getmapper(userrolemapper.class); /** * 直接使用参数作为值 * @param user * @param myrolename * @return */ @mappings({ @mapping(source = user.id, target = userid), // 把user中的id绑定到目标对象的userid属性中 @mapping(source = user.username, target = name), // 把user中的username绑定到目标对象的name属性中 @mapping(source = myrolename, target = rolename) // 把role对象的rolename属性值绑定到目标对象的rolename中 }) userroledto useparameter(user user, string myrolename); } 测试类:
public class test1 { role role = null; user user = null; @before public void before() { role = new role(2l, administrator, 超级管理员); user = new user(1l, zhangsan, 12345, 17677778888, 123@qq.com, role); } @test public void test1() { userrolemapper instances = userrolemapper.instances; userroledto userroledto = instances.useparameter(user, myuserrole); system.out.println(userroledto); } } 6.更新对象属性 在之前的例子中userroledto useparameter(user user, string myrolename);都是通过类似上面的方法来生成一个对象。而mapstruct提供了另外一种方式来更新一个对象中的属性。@mappingtarget
public interface userrolemapper1 { userrolemapper1 instances = mappers.getmapper(userrolemapper1.class); @mappings({ @mapping(source = userid, target = id), @mapping(source = name, target = username), @mapping(source = rolename, target = role.rolename) }) void updatedto(userroledto userroledto, @mappingtarget user user); @mappings({ @mapping(source = id, target = userid), @mapping(source = username, target = name), @mapping(source = role.rolename, target = rolename) }) void update(user user, @mappingtarget userroledto userroledto); } 通过@mappingtarget来指定目标类是谁(谁的属性需要被更新)。@mapping还是用来定义属性对应规则。
以此为例说明:
@mappings({ @mapping(source = id, target = userid), @mapping(source = username, target = name), @mapping(source = role.rolename, target = rolename) }) void update(user user, @mappingtarget userroledto userroledto); @mappingtarget标注的类userroledto 为目标类,user类为源类,调用此方法,会把源类中的属性更新到目标类中。更新规则还是由@mapping指定。
7.没有getter/setter也能赋值 对于没有getter/setter的属性也能实现赋值操作
public class customer { private long id; private string name; //getters and setter omitted for brevity } public class customerdto { public long id; public string customername; } @mapper public interface customermapper { customermapper instance = mappers.getmapper( customermapper.class ); @mapping(source = customername, target = name) customer tocustomer(customerdto customerdto); @inheritinverseconfiguration customerdto fromcustomer(customer customer); } @mapping(source = “customername”, target = “name”)不是用来指定属性映射的,如果两个对象的属性名相同是可以省略@mapping的。
mapstruct生成的实现类:
@generated( value = org.mapstruct.ap.mappingprocessor, date = 2019-02-14t1521+0800, comments = version: 1.3.0.final, compiler: javac, environment: java 1.8.0_181 (oracle corporation) ) public class customermapperimpl implements customermapper { @override public customer tocustomer(customerdto customerdto) { if ( customerdto == null ) { return null; } customer customer = new customer(); customer.setname( customerdto.customername ); customer.setid( customerdto.id ); return customer; } @override public customerdto tocustomerdto(customer customer) { if ( customer == null ) { return null; } customerdto customerdto = new customerdto(); customerdto.customername = customer.getname(); customerdto.id = customer.getid(); return customerdto; } } @inheritinverseconfiguration在这里的作用就是实现customerdto.customername = customer.getname();功能的。如果没有这个注解,tocustomerdto这个方法则不会有customername 和name两个属性的对应关系的。
8.使用spring依赖注入 @data @noargsconstructor @allargsconstructor public class customer { private long id; private string name; } @data public class customerdto { private long id; private string customername; } // 这里主要是这个componentmodel 属性,它的值就是当前要使用的依赖注入的环境 @mapper(componentmodel = spring) public interface customermapper { @mapping(source = name, target = customername) customerdto tocustomerdto(customer customer); } @mapper(componentmodel = “spring”),表示把当前mapper类纳入spring容器。可以在其它类中直接注入了:
@springbootapplication @restcontroller public class demomapstructapplication { // 注入mapper @autowired private customermapper mapper; public static void main(string[] args) { springapplication.run(demomapstructapplication.class, args); } @getmapping(/test) public string test() { customer customer = new customer(1l, zhangsan); customerdto customerdto = mapper.tocustomerdto(customer); return customerdto.tostring(); } } 看一下由mapstruct自动生成的类文件,会发现标记了@component注解。
@generated( value = org.mapstruct.ap.mappingprocessor, date = 2019-02-14t1517+0800, comments = version: 1.3.0.final, compiler: javac, environment: java 1.8.0_181 (oracle corporation) ) @component public class customermapperimpl implements customermapper { @override public customerdto tocustomerdto(customer customer) { if ( customer == null ) { return null; } customerdto customerdto = new customerdto(); customerdto.setcustomername( customer.getname() ); customerdto.setid( customer.getid() ); return customerdto; } } 9.自定义类型转换 有时候,在对象转换的时候可能会出现这样一个问题,就是源对象中的类型是boolean类型,而目标对象类型是string类型,这种情况可以通过@mapper的uses属性来实现:
@data @noargsconstructor @allargsconstructor public class customer { private long id; private string name; private boolean isdisable; } @data public class customerdto { private long id; private string customername; private string disable; } 定义转换规则的类:
public class booleanstrformat { public string tostr(boolean isdisable) { if (isdisable) { return y; } else { return n; } } public boolean toboolean(string str) { if (str.equals(y)) { return true; } else { return false; } } } 定义mapper,@mapper( uses = { booleanstrformat.class}),注意,这里的users属性用于引用之前定义的转换规则的类:
@mapper( uses = { booleanstrformat.class}) public interface customermapper { customermapper instances = mappers.getmapper(customermapper.class); @mappings({ @mapping(source = name, target = customername), @mapping(source = isdisable, target = disable) }) customerdto tocustomerdto(customer customer); } 这样子,customer类中的isdisable属性的true就会转变成customerdto中的disable属性的yes。
mapstruct自动生成的类中的代码:
@generated( value = org.mapstruct.ap.mappingprocessor, date = 2019-02-14t1618+0800, comments = version: 1.3.0.final, compiler: javac, environment: java 1.8.0_181 (oracle corporation) ) public class customermapperimpl implements customermapper { // 引用 uses 中指定的类 private final booleanstrformat booleanstrformat = new booleanstrformat(); @override public customerdto tocustomerdto(customer customer) { if ( customer == null ) { return null; } customerdto customerdto = new customerdto(); // 转换方式的使用 customerdto.setdisable( booleanstrformat.tostr( customer.getisdisable() ) ); customerdto.setcustomername( customer.getname() ); customerdto.setid( customer.getid() ); return customerdto; } } 要注意的是,如果使用了例如像spring这样的环境,mapper引入uses类实例的方式将是自动注入,那么这个类也应该纳入spring容器:
customermapper.java指定使用spring
@mapper(componentmodel = spring, uses = { booleanstrformat.class}) public interface customermapper { customermapper instances = mappers.getmapper(customermapper.class); @mappings({ @mapping(source = name, target = customername), @mapping(source = isdisable, target = disable) }) customerdto tocustomerdto(customer customer); } 转换类要加入spring容器:
@component public class booleanstrformat { public string tostr(boolean isdisable) { if (isdisable) { return y; } else { return n; } } public boolean toboolean(string str) { if (str.equals(y)) { return true; } else { return false; } } } mapstruct自动生成的类:
@generated( value = org.mapstruct.ap.mappingprocessor, date = 2019-02-14t1635+0800, comments = version: 1.3.0.final, compiler: javac, environment: java 1.8.0_181 (oracle corporation) ) @component public class customermapperimpl implements customermapper { // 使用自动注入的方式引入 @autowired private booleanstrformat booleanstrformat; @override public customerdto tocustomerdto(customer customer) { if ( customer == null ) { return null; } customerdto customerdto = new customerdto(); customerdto.setdisable( booleanstrformat.tostr( customer.getisdisable() ) ); customerdto.setcustomername( customer.getname() ); customerdto.setid( customer.getid() ); return customerdto; } } 原文标题:实体映射最强工具类:mapstruct 真香!
文章出处:【微信公众号:android编程精选】欢迎添加关注!文章转载请注明出处。
视频硬核解读安森美图像传感器各项关键成像技术
AI及物联网企业佳华科技发布2022第一季度报告
PCB设计后期检查的六个重点
国产CPU大腾飞前夜:从一场参加人员严重爆仓的大会说起
教你怎么选择智能汽车域控制器芯片?
MapStruct是用来做什么的
全球最快记忆卡32秒就能塞满一个iPhone7 Plus
高速数字电路中电子隔离应用
浅谈碳化硅流程的核心技术
M5256-000002-350BG压力传感器的坚固设计
如何使用4017 IC和RGB LED制作LED追光器
海康威视人工智能(AI)平台正式对外开放
TMS320C5402芯片在复用器中有哪些应用
OrCAD FPGA系统规划
python环境变量的配置pip
英睿达新款P2固态硬盘,250GB读取速度可达2100M/s
联通将推自有品牌手机“沃Phone”
三星/SK海力士/美光纷纷减产 存储器行业或将面临相当大的挑战
浅谈KUKA机器人CWRITE编程用法
采用FPGA技术芯片模块实现运动估计的设计方案并进行仿真研究