Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
MyBatis教程
-> 自动映射,使用需谨慎
1、MyBatis未出世之前我们那些痛苦的经历
2、MyBatis入门,你确定mybatis你玩的很溜?
3、MyBatis使用详解(-)
4、MyBatis使用详解(2)
5、详解传参的几种方式、原理、源码解析
6、增删改知识点汇总及主键获取3种方式详解
7、种查询详解
8、自动映射,使用需谨慎
9、延迟加载、鉴别器、继承
10、动态SQL,这么多种你都会么?
11、类型处理器
12、掌握缓存为查询提速!
上一篇:种查询详解
下一篇:延迟加载、鉴别器、继承
<div style="display:none"></div> ### 案例代码 ```java 链接:https://pan.baidu.com/s/1vt-MAX3oJOu9gyxZAhKkbg 提取码:i8op ``` 本文案例代码入口,配合源码看案例,效果更好。 ```java mybatis-series\chat05\src\test\java\com\javacode2018\chat05\demo7\Demo7Test.java ``` ### 什么是自动映射? 介绍自动映射之前先看一下手动映射,如下: ```XML <resultMap id="orderModelMap1" type="com.javacode2018.chat05.demo7.model.OrderModel"> <id column="id" property="id"/> <result column="userId" property="userId" /> <result column="createTime" property="createTime" /> <result column="upTime" property="upTime" /> </resultMap> <select id="getById1" resultMap="orderModelMap1"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = #{value} ]]> </select> ``` 注意上面的resultMap元素中有4行配置,如下: ```xml <id column="id" property="id"/> <result column="userId" property="userId" /> <result column="createTime" property="createTime" /> <result column="upTime" property="upTime" /> ``` 这4行代码用于配置sql结果的列和OrderModel对象中字段的映射关系。 大家有没有注意到,映射规则中column和property元素的值都是一样,mybatis中支持自动映射配置,当开启自动映射之后,当sql的列名和Model中的字段名称是一样的时候(不区分大小写),mybatis内部会进行自动映射,不需要我们手动去写上面的4行映射规则。 下面我们将上面的示例改成自动映射的方式,如下: ```xml <resultMap id="orderModelMap2" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true"> </resultMap> <select id="getById2" resultMap="orderModelMap2"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = #{value} ]]> </select> ``` **注意上面的resultMap中的autoMapping属性,是否开启自动映射,我们设置为true,这样mybatis会自动按照列名和Model中同名的字段进行映射赋值。** 上面两个配置最后查询结果是一样的,都会将查询结果对应的4个字段的值自动赋值给OrderModel中同名的属性。 ### 自动映射开关 mybatis中自动映射主要有2种配置,一种是全局的配置,对应用中所有的resultMap起效,这个是在mybatis配置文件中进行设置的;另外一种是通过`resultMap`的`autoMapping`属性进行配置。 mybatis判断某个resultMap是否开启自动映射配置的时候,会先查找自身的`autoMapping`属性,如果这个属性设置值了,就直接用这个属性的值,如果resultMap元素的`autoMapping`属性没有配置,则走全局配置的自动映射规则。 下面我们来详解介绍一下这款的内容。 ### mybatis自动映射全局配置 在mybatis全局配置文件中加入下面配置: ```xml <settings> <setting name="autoMappingBehavior" value="自动映射规则"/> </settings> ``` autoMappingBehavior值来源于枚举:`org.apache.ibatis.session.AutoMappingBehavior`,源码: ```java public enum AutoMappingBehavior { /** * Disables auto-mapping. */ NONE, /** * Will only auto-map results with no nested result mappings defined inside. */ PARTIAL, /** * Will auto-map result mappings of any complexity (containing nested or otherwise). */ FULL } ``` - NONE:关闭全局映射开关 - PARTIAL:对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射,这个也是默认值。 - FULL:自动映射所有属性。 > 小提示:settings元素中有很多配置,这些配置最后都会被解析成`org.apache.ibatis.session.Configuration`的属性,源码位于`org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement`方法中。 下面我们来演示一下`autoMappingBehavior`每种配置的效果。 #### NONE ##### mybatis-config.xml加入配置 ```xml <settings> <!-- 关闭自动映射开关 --> <setting name="autoMappingBehavior" value="NONE"/> </settings> ``` ##### OrderMapper.xml ```xml <resultMap id="orderModelMap4" type="com.javacode2018.chat05.demo7.model.OrderModel"> </resultMap> <select id="getById4" resultMap="orderModelMap4"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = #{value} ]]> </select> ``` ##### OrderMapper.java加入 ```java OrderModel getById4(int id); ``` ##### 测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById4 @Test public void getById4() throws IOException { this.before("demo7/mybatis-config1.xml"); try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); OrderModel orderModel = mapper.getById4(2); log.info("{}", orderModel); } } ``` ##### 运行结果 ```java 21:58.821 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = ? 21:58.850 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==> Parameters: 2(Integer) 21:58.868 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - <== Total: 1 21:58.868 [main] INFO c.j.chat05.demo7.Demo7Test - null ``` 从输出中可以看到最后一样输出结果为null,sql实际上返回的是有结果的,但是结果映射的时候返回的是空。 ##### 结果解释 由于mybatis全局配置中将`autoMappingBehavior`的值置为了`NONE`,表示全局自动映射被关闭了,而resultMapper中的orderModelMap4没有配置`autoMapping`属性,所以最终这个查询结果不会自动映射,所以最后查询结果为null。 #### PARTIAL 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射,这个也是autoMappingBehavior的默认值。 ##### mybatis-config.xml加入配置 ```xml <settings> <!-- 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射,这个也是autoMappingBehavior的默认值。 --> <setting name="autoMappingBehavior" value="PARTIAL"/> </settings> ``` ##### OrderMapper.xml ```xml <resultMap id="orderModelMap5" type="com.javacode2018.chat05.demo7.model.OrderModel"> </resultMap> <select id="getById5" resultMap="orderModelMap5"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = #{value} ]]> </select> ``` ##### OrderMapper.java加入 ```java OrderModel getById5(int id); ``` ##### 测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById5 @Test public void getById5() throws IOException { this.before("demo7/mybatis-config2.xml"); try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); OrderModel orderModel = mapper.getById5(2); log.info("{}", orderModel); } } ``` ##### 运行结果 ```java 28:32.612 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById5 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = ? 28:32.648 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById5 - ==> Parameters: 2(Integer) 28:32.664 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById5 - <== Total: 1 28:32.665 [main] INFO c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=null) ``` OrderModel中的4个属性被自动映射成功了。 ##### 结果解释 orderModelMap5中没有指定`autoMapping`属性,所以自动映射会走全局配置的规则,即`PARTIAL`,会进行自动映射。 我们再来看看`PARTIAL`的解释:对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射。这句话是什么意思? 有些复杂的查询映射会在resultMap中嵌套一些映射(如:association,collection),当使用`PARTIAL`的时候,如果有嵌套映射,则这个嵌套映射不会进行自动映射了。 通过订单id查询出订单以及订单用户的信息,sqlmap如下: ```xml <resultMap id="orderModelMap6" type="com.javacode2018.chat05.demo7.model.OrderModel"> <association property="userModel"> </association> </resultMap> <select id="getById6" resultMap="orderModelMap6"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = #{value} ]]> </select> ``` ##### OrderModel.java ```java package com.javacode2018.chat05.demo7.model; import lombok.*; import java.util.List; @Getter @Setter @Builder @ToString @NoArgsConstructor @AllArgsConstructor public class OrderModel { private Integer id; private Integer userId; private Long createTime; private Long upTime; private UserModel userModel; } ``` 内部有个`userModel`属性引用用户对象。 ##### UserModel.java ```java package com.javacode2018.chat05.demo7.model; import lombok.*; @Getter @Setter @Builder @ToString @NoArgsConstructor @AllArgsConstructor public class UserModel { private Integer id; private String name; } ``` ##### OrderMapper.java中加入 ```java OrderModel getById6(int id); ``` 测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById6 @Test public void getById6() throws IOException { this.before("demo7/mybatis-config2.xml"); try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); OrderModel orderModel = mapper.getById6(2); log.info("{}", orderModel); } } ``` ##### 运行输出 ```java 52:49.037 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 52:49.066 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Parameters: 2(Integer) 52:49.087 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - <== Total: 1 52:49.088 [main] INFO c.j.chat05.demo7.Demo7Test - null ``` sql查询实际上是有一条记录的,但是最后返回的是null,说明没有进行自动映射。 #### FULL 自动映射所有属性。 这次Mapper我们不动,还是下面这样,没有手动指定映射规则。 ```xml <resultMap id="orderModelMap6" type="com.javacode2018.chat05.demo7.model.OrderModel"> <association property="userModel"> </association> </resultMap> <select id="getById6" resultMap="orderModelMap6"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = #{value} ]]> </select> ``` 修改一下autoMappingBehavior的值为`FULL`,看看效果。 ##### mybatis配置 ```xml <settings> <!-- 自动映射所有属性 --> <setting name="autoMappingBehavior" value="FULL"/> </settings> ``` ##### 测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById6_0 @Test public void getById6_0() throws IOException { this.before("demo7/mybatis-config3.xml"); try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); OrderModel orderModel = mapper.getById6(2); log.info("{}", orderModel); } } ``` ##### 运行输出 ``` 56:05.127 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 56:05.155 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Parameters: 2(Integer) 56:05.186 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - <== Total: 1 56:05.186 [main] INFO c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=2, name=张学友)) ``` 输出中可以看到OrderModel所有属性都是有值的,userModel的2个属性也有值,userModel.id是2,我们运行一下sql看看,用户id是多少,如下: ```java mysql> SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = 2; +----+--------+------------+------------+---------+-----------+ | id | userId | createTime | upTime | user_id | name | +----+--------+------------+------------+---------+-----------+ | 2 | 1 | 1577947790 | 1577947790 | 1 | 张学友 | +----+--------+------------+------------+---------+-----------+ 1 row in set (0.00 sec) ``` user_id实际上是1,mybatis中按照sql字段和model结果字段同名进行自动映射,所以将订单的id赋值给userModel的id属性了。 此时需要我们orderModelMap6的配置,手动指定一下user_id和userModel.id的映射规则,如下: ```xml <resultMap id="orderModelMap6" type="com.javacode2018.chat05.demo7.model.OrderModel"> <association property="userModel"> <id column="user_id" property="id"/> </association> </resultMap> ``` ##### 再次运行测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById6_0 ``` ##### 输出 ```java 15:02.751 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 15:02.783 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - ==> Parameters: 2(Integer) 15:02.801 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById6 - <== Total: 1 15:02.801 [main] INFO c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=1, name=张学友)) ``` 这次userModel中的id正确了。 ### autoMapping使用 上面我们有说过,当在resultMap中指定了autoMapping属性之后,这个resultMap的自动映射就受autoMapping属性的控制,和mybatis中全局映射配置(autoMappingBehavior)行为无关了。 #### 案例1 ##### 这个核心配置主要在sqlmap中,如下: ```xml <resultMap id="orderModelMap7" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true"> <association property="userModel" autoMapping="true"> <id column="user_id" property="id"/> </association> </resultMap> <select id="getById7" resultMap="orderModelMap7"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = #{value} ]]> </select> ``` ##### 对应测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById7 @Test public void getById7() throws IOException { this.before("demo7/mybatis-config1.xml"); try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); OrderModel orderModel = mapper.getById7(2); log.info("{}", orderModel); } } ``` ##### 运行输出 ```java 24:37.544 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById7 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime, b.id as user_id, b.name FROM t_order a,t_user b WHERE a.user_id = b.id AND a.id = ? 24:37.589 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById7 - ==> Parameters: 2(Integer) 24:37.610 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById7 - <== Total: 1 24:37.610 [main] INFO c.j.chat05.demo7.Demo7Test - OrderModel(id=2, userId=1, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=1, name=张学友)) ``` OrderModel中所有属性都自动映射成功。 自动装配并不是那么好玩,玩不转可能带来一些隐患,我们看一个案例,见下面的示例2。 #### 示例2 根据订单编号,查询出订单信息,顺便查询出订单明细列表。这个我们使用mybatis中的一对多查询。 ##### OrderDetaiMapper.xml加入 ```xml <select id="getListByOrderId1" resultType="com.javacode2018.chat05.demo7.model.OrderDetailModel"> <![CDATA[ SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = #{value} ]]> </select> ``` 这个可以根据订单的id,查询出订单关联的明细列表。 ##### OrderMapper.xml加入 ```xml <resultMap id="orderModelMap8" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true"> <collection property="orderDetailModelList" select="com.javacode2018.chat05.demo7.mapper.OrderDetailMapper.getListByOrderId1" column="id"/> </resultMap> <select id="getById8" resultMap="orderModelMap8"> <![CDATA[ SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = #{value} ]]> </select> ``` ##### 测试用例 ```java com.javacode2018.chat05.demo7.Demo7Test#getById8 @Test public void getById8() throws IOException { this.before("demo7/mybatis-config.xml"); try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) { OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); OrderModel orderModel = mapper.getById8(1); log.info("{}", orderModel); } } ``` ##### 运行输出 ```java 11:06.193 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById8 - ==> Preparing: SELECT a.id, a.user_id userId, a.create_time createTime, a.up_time upTime FROM t_order a WHERE a.id = ? 11:06.229 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById8 - ==> Parameters: 1(Integer) 11:06.250 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====> Preparing: SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM t_order_detail a WHERE a.order_id = ? 11:06.251 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====> Parameters: 1(Integer) 11:06.255 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - <==== Total: 2 11:06.256 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById8 - <== Total: 1 11:06.256 [main] INFO c.j.chat05.demo7.Demo7Test - OrderModel(id=null, userId=2, createTime=1577947790, upTime=1577947790, userModel=null, orderDetailModelList=[OrderDetailModel(id=1, orderId=1, goodsId=1, num=2, totalPrice=17.76), OrderDetailModel(id=2, orderId=1, goodsId=1, num=1, totalPrice=16.66)]) ``` 注意输出中OrderModel的id属性,怎么是null值?主要是下面这行配置导致的 ```xml <collection property="orderDetailModelList" select="com.javacode2018.chat05.demo7.mapper.OrderDetailMapper.getListByOrderId1" column="id"/> ``` 上面这个配置中有个column属性,指定的是id,此时mybatis认为你对id字段手动指定了映射关系,就跳过了对id字段到OrderModel.id属性的自动映射,所以导致OrderModel对象的id属性没有赋值,此时需要我们在`orderModelMap8`手动指定id的映射规则,如下: ```xml <resultMap id="orderModelMap8" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true"> <id column="id" property="id" /> <collection property="orderDetailModelList" select="com.javacode2018.chat05.demo7.mapper.OrderDetailMapper.getListByOrderId1" column="id"/> </resultMap> ``` 再去运行测试用例就正常了。 ### 总结一下 对于咱们开发来说,自动映射确实可以帮助我们节省一些代码,不过也存在一些隐患,我们希望自己开发的系统是健壮的,建议大家写mapper xml的时候,还是花点时间将映射的配置都给写上去,这样能够杜绝一些隐患,使我们的系统更稳定。 <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container