componentmodel(分析mybatis中@Mapper注解的componentModel属性)

发布时间:2025-12-11 00:40:31 浏览次数:10

一、准备工作

1.1、了解@Mapper 注解

从 mybatis3.4.0 开始加入的 @Mapper 注解,目的就是为了不再写mapper映射文件。

我们只需要在 dao 层定义的接口上使用注解就可以实现sql语句的编写,例如:

@select("select*fromuserwherename=#{name}")publicUserfind(Stringname);

如上就是一个简单的使用,虽然简单,但也确实体现出了这个注解的优越性,至少少写了一个xml文件。

所以我就只是想说下 @Mapper 注解的 componentModel 属性,componentModel 属性用于指定自动生成的接口实现类的组件类型,这个属性支持四个值:

  • default: 这是默认的情况,mapstruct 不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。

  • cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject

  • spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入

  • jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取

1.2、依赖包

首先需要把依赖包导入,主要由两个包组成:

  • org.mapstruct:mapstruct:包含了一些必要的注解,例如@Mapping。r若我们使用的JDK版本高于1.8,当我们在pom里面导入依赖时候,建议使用坐标是:org.mapstruct:mapstruct-jdk8,这可以帮助我们利用一些Java8的新特性。

  • org.mapstruct:mapstruct-processor:注解处理器,根据注解自动生成mapper的实现。

<dependency><groupId>org.mapstruct</groupId><!--jdk8以下就使用mapstruct--><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></dependency>

好了,准备工作做完了,接下来我们就看看巧媳妇儿巧在什么地方吧。

二、先简单玩一把

2.1、定义实体类以及被映射类

//实体类@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUser{privateIntegerid;privateStringname;privateStringcreateTime;privateLocalDateTimeupdateTime;}//被映射类VO1:和实体类一模一样@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUserVO1{privateIntegerid;privateStringname;privateStringcreateTime;privateLocalDateTimeupdateTime;}//被映射类VO1:比实体类少一个字段@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUserVO2{privateIntegerid;privateStringname;privateStringcreateTime;}

2.2、定义接口:

当实体类和被映射对象属性相同或者被映射对象属性值少几个时:

@Mapper(componentModel="spring")publicinterfaceUserCovertBasic{UserCovertBasicINSTANCE=Mappers.getMapper(UserCovertBasic.class);/***字段数量类型数量相同,利用工具BeanUtils也可以实现类似效果*@paramsource*@return*/UserVO1toConvertVO1(Usersource);UserfromConvertEntity1(UserVO1userVO1);/***字段数量类型相同,数量少:仅能让多的转换成少的,故没有fromConvertEntity2*@paramsource*@return*/UserVO2toConvertVO2(Usersource);}

从上面的代码可以看出:接口中声明了一个成员变量INSTANCE,母的是让客户端可以访问 Mapper 接口的实现。

2.3、使用

@RestControllerpublicclassTestController{@GetMapping("convert")publicObjectconvertEntity(){Useruser=User.builder().id(1).name("张三").createTime("2020-04-0111:05:07").updateTime(LocalDateTime.now()).build();List<Object>objectList=newArrayList<>();objectList.add(user);//使用mapstructUserVO1userVO1=UserCovertBasic.INSTANCE.toConvertVO1(user);objectList.add("userVO1:"+UserCovertBasic.INSTANCE.toConvertVO1(user));objectList.add("userVO1转换回实体类user:"+UserCovertBasic.INSTANCE.fromConvertEntity1(userVO1));//输出转换结果objectList.add("userVO2:"+"|"+UserCovertBasic.INSTANCE.toConvertVO2(user));//使用BeanUtilsUserVO2userVO22=newUserVO2();BeanUtils.copyProperties(user,userVO22);objectList.add("userVO22:"+"|"+userVO22);returnobjectList;}}

2.4、查看编译结果

通过IDE的反编译功能查看编译后自动生成 UserCovertBasic 的实现类 UserCovertBasicImpl ,内容如下:

@ComponentpublicclassUserCovertBasicImplimplementsUserCovertBasic{publicUserCovertBasicImpl(){}publicUserVO1toConvertVO1(Usersource){if(source==null){returnnull;}else{UserVO1userVO1=newUserVO1();userVO1.setId(source.getId());userVO1.setName(source.getName());userVO1.setCreateTime(source.getCreateTime());userVO1.setUpdateTime(source.getUpdateTime());returnuserVO1;}}publicUserfromConvertEntity1(UserVO1userVO1){if(userVO1==null){returnnull;}else{Useruser=newUser();user.setId(userVO1.getId());user.setName(userVO1.getName());user.setCreateTime(userVO1.getCreateTime());user.setUpdateTime(userVO1.getUpdateTime());returnuser;}}publicUserVO2toConvertVO2(Usersource){if(source==null){returnnull;}else{UserVO2userVO2=newUserVO2();userVO2.setId(source.getId());userVO2.setName(source.getName());userVO2.setCreateTime(source.getCreateTime());returnuserVO2;}}}

2.5、浏览器查看结果

好了,一个流程就走完了,是不是感觉贼简单呢?

而且呀,阿粉温馨提醒:如果是要转换一个集合的话,只需要把这里的实体类换成集合就行了,例如:

List<UserVO1>toConvertVOList(List<User>source);

三、不简单的情况

上面已经把整个流程都给过了一遍了,相信大家对 mapstruct 也有了一个基础的了解了,所以接下来的情况我们就不展示全部代码了,毕竟篇幅也有限,所以就直接上关键代码(因为不关键的和上面内容一样,哈哈)

3.1、类型不一致

实体类我们还是沿用 User;被映射对象 UserVO3 改为:

@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUserVO3{privateStringid;privateStringname;//实体类该属性是StringprivateLocalDateTimecreateTime;//实体类该属性是LocalDateTimeprivateStringupdateTime;}

那么我们定义的接口就要稍稍修改一下了:

@Mappings({@Mapping(target="createTime",expression="java(com.java.mmzsblog.util.DateTransform.strToDate(source.getCreateTime()))"),})UserVO3toConvertVO3(Usersource);UserfromConvertEntity3(UserVO3userVO3);

上面 expression 指定的表达式内容如下:

publicclassDateTransform{publicstaticLocalDateTimestrToDate(Stringstr){DateTimeFormatterdf=DateTimeFormatter.ofPattern("yyy-MM-ddHH:mm:ss");returnLocalDateTime.parse("2018-01-1217:07:05",df);}}

通过IDE的反编译功能查看编译后的实现类,结果是这样子的:

从图中我们可以看到,编译时使用了expression中定义的表达式对目标字段 createTime 进行了转换;然后你还会发现 updateTime 字段也被自动从 LocalDateTime 类型转换成了 String 类型。

阿粉小结:

当字段类型不一致时,以下的类型之间是 mapstruct 自动进行类型转换的:

1、基本类型及其他们对应的包装类型。此时 mapstruct 会自动进行拆装箱。不需要人为的处理

2、基本类型的包装类型和string类型之间

除此之外的类型转换我们可以通过定义表达式来进行指定转换。

3.2、字段名不一致

实体类我们还是沿用 User;被映射对象 UserVO4 改为:

@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUserVO4{//实体类该属性名是idprivateStringuserId;//实体类该属性名是nameprivateStringuserName;privateStringcreateTime;privateStringupdateTime;}

那么我们定义的接口就要稍稍修改一下了:

@Mappings({@Mapping(source="id",target="userId"),@Mapping(source="name",target="userName")})UserVO4toConvertVO(Usersource);UserfromConvertEntity(UserVO4userVO4);

通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:

小结:

当字段名不一致时,通过使用 @Mappings 注解指定对应关系,编译后即可实现对应字段的赋值。

很明显, mapstruct 通过读取我们配置的字段名对应关系,帮我们把它们赋值在了相对应的位置上,可以说是相当优秀了,但这也仅仅是优秀,而更秀的还请继续往下看:

3.3、属性是枚举类型

实体类我们还是改用 UserEnum:

@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUserEnum{privateIntegerid;privateStringname;privateUserTypeEnumuserTypeEnum;}

被映射对象 UserVO5 改为:

@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassUserVO5{privateIntegerid;privateStringname;privateStringtype;}

枚举对象是:

@Getter@AllArgsConstructorpublicenumUserTypeEnum{Java("000","Java开发工程师"),DB("001","数据库管理员"),LINUX("002","Linux运维员");privateStringvalue;privateStringtitle;}

那么我们定义的接口还是照常定义,不会受到它是枚举就有所变化:

@Mapping(source="userTypeEnum",target="type")UserVO5toConvertVO5(UserEnumsource);UserEnumfromConvertEntity5(UserVO5userVO5);

通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:

很明显, mapstruct 通过枚举类型的内容,帮我们把枚举类型转换成字符串,并给type赋值,可谓是小心使得万年船啊。

到此,相信大家对“分析mybatis中@Mapper注解的componentModel属性”有了更深的了解,不妨来实际操作一番吧!这里是本站网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

componentmodel
需要做网站?需要网络推广?欢迎咨询客户经理 13272073477