目录

  • 前言
  • 什么是结果映射?
  • 如何映射?
    • 别名映射
    • 驼峰映射
      • 配置文件开启驼峰映射
      • 配置类中开启驼峰映射
    • resultMap映射
    • 总结
  • 高级结果映射
    • 关联(association)

      • 例子
      • 关联的嵌套 Select 查询
      • 关联的嵌套结果映射
      • 总结
    • 集合collection
      • 集合的嵌套 Select 查询
      • 集合的嵌套结果映射
  • 总结

前言

  • 上一篇文章介绍了Mybatis基础的CRUD操作、常用的标签、属性等内容,如果对部分不熟悉的朋友可以看Mybatis入门之基本操作
  • 本篇文章继续讲解Mybatis的结果映射的内容,想要在企业开发中灵活的使用Mybatis,这部分的内容是必须要精通的。

什么是结果映射?

  • 简单的来说就是一条SQL查询语句返回的字段如何与Java实体类中的属性相对应。
  • 如下一条SQL语句,查询患者的用户id,科室id,主治医生id:
  1. <select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
  2. select user_id,dept_id,doc_id from patient_info;
  3. </select>
  • Java实体类PatientInfo如下:
  1. @Data
  2. public class PatientInfo{
  3. private String userId;
  4. private String deptId;
  5. private String docId;
  6. }
  • 程序员写这条SQL的目的就是想查询出来的user_id,dept_id,doc_id分别赋值给实体类中的userId,deptId,docId。这就是简单的结果映射。

如何映射?

  • Myabtis中的结果映射有很多种方式,下面会逐一介绍。

别名映射

  • 这个简单,保持查询的SQL返回的字段和Java实体类一样即可,比如上面例子的SQL可以写成:
  1. <select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
  2. select user_id as userId,
  3. dept_id as deptId,
  4. doc_id as docId
  5. from patient_info;
  6. </select>
  • 这样就能和实体类中的属性映射成功了。

驼峰映射

  • Mybatis提供了驼峰命名映射的方式,比如数据库中的user_id这个字段,能够自动映射到userId属性。那么此时的查询的SQL变成如下即可:
  1. <select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
  2. select user_id,dept_id,doc_id from patient_info;
  3. </select>
  • 如何开启呢?与SpringBoot整合后开启其实很简单,有两种方式,一个是配置文件中开启,一个是配置类开启。

配置文件开启驼峰映射

  • 只需要在application.properties文件中添加如下一行代码即可:
  1. mybatis.configuration.map-underscore-to-camel-case=true

配置类中开启驼峰映射【简单了解,后续源码章节着重介绍】

  • 这种方式需要你对源码有一定的了解,上一篇入门教程中有提到,Mybatis与Springboot整合后适配了一个starter,那么肯定会有自动配置类,Mybatis的自动配置类是MybatisAutoConfiguration,其中有这么一段代码,如下:

  • @ConditionalOnMissingBean这个注解的意思就是当IOC容器中没有SqlSessionFactory这个Bean对象这个配置才会生效;applyConfiguration(factory)这行代码就是创建一个org.apache.ibatis.session.Configuration赋值给SqlSessionFactoryBean。源码分析到这,应该很清楚了,无非就是自己在容器中创建一个SqlSessionFactory,然后设置属性即可,如下代码:
  1. @Bean("sqlSessionFactory")
  2. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  3. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  4. //设置数据源
  5. sqlSessionFactoryBean.setDataSource(dataSource);
  6. //设置xml文件的位置
  7. sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN));
  8. //创建Configuration
  9. org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
  10. // 开启驼峰命名映射
  11. configuration.setMapUnderscoreToCamelCase(true);
  12. configuration.setDefaultFetchSize(100);
  13. configuration.setDefaultStatementTimeout(30);
  14. sqlSessionFactoryBean.setConfiguration(configuration);
  15. //将typehandler注册到mybatis
  16. sqlSessionFactoryBean.setTypeHandlers(typeHandlers());
  17. return sqlSessionFactoryBean.getObject();
  18. }
  • 注意:如果对SqlSessionFactory没有特殊定制,不介意重写,因为这会自动覆盖自动配置类中的配置。

resultMap映射

  • 什么是resultMap?简单的说就是一个类似Map的结构,将数据库中的字段和JavaBean中的属性字段对应起来,这样就能做到一一映射了。
  • 上述的例子使用resultMap又会怎么写呢?如下:

  1. <!--创建一个resultMap映射-->
  2. <resultMap id="patResultMap" type="com.xxx.domain.PatientInfo">
  3. <id property="userId" column="user_id" />
  4. <result property="docId" column="doc_id"/>
  5. <result property="deptId" column="dept_id"/>
  6. </resultMap>
  7. <!--使用resultMap映射结果到com.xxx.domain.PatientInfo这个Bean中-->
  8. <select id='selectPatientInfos' resultMap='patResultMap'>
  9. select user_id,dept_id,doc_id from patient_info;
  10. </select>
  • 其实很简单,就是创建一个<resultMap>,然后<select>标签指定这个resultMap即可。

  • <resultMap>的属性如下:

    • id:唯一标识这个resultMap,同一个Mapper.xml中不能重复
    • type:指定JavaBean的类型,可以是全类名,也可以是别名
  • 子标签<result>的属性如下:

    • column:SQL返回的字段名称
    • property:JavaBean中属性的名称
    • javaType:一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
    • jdbcType:JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
    • typeHandler: 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
    • resultMap:结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。

总结

  • 以上列举了三种映射的方式,分别是别名映射驼峰映射resultMap映射
  • 你以为这就结束了?要是世界这么简单多好,做梦吧,哈哈!!!

高级结果映射

  • MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
  • 我们知道在数据库的关系中一对一,多对一,一对多,多对多的关系,那么这种关系如何在Mybatis中体现并映射成功呢?

关联(association)

  • 关联(association)元素处理有一个类型的关系。 比如,在我们的示例中,一个员工属于一个部门。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。
  • 关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:
    • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
    • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
  • 首先,先让我们来看看这个元素的属性。你将会发现,和普通的结果映射相比,它只在 selectresultMap 属性上有所不同。
    • property: 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。
    • javaType:一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)

      jdbcType: JDBC 类型, 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型
    • typeHandler:使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。

      column: 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得prop1prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    • select:用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    • fetchType:可选的。有效值为 lazyeager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。

例子

  • 一对一的关系比如:一个员工属于一个部门,那么数据库表就会在员工表中加一个部门的id作为逻辑外键。
  • 创建员工JavaBean
  1. @Data
  2. public class User {
  3. private Integer id;
  4. private String username;
  5. private String password;
  6. private Integer age;
  7. private Integer deptId;
  8. //部门
  9. private Department department;
  10. }
  • 部门JavaBean
  1. @Data
  2. public class Department {
  3. private Integer id;
  4. private String name;
  5. }
  • 那么我们想要查询所有的用户信息和其所在的部门信息,此时的sql语句为:select * from user u left join department d on u.department_id=d.id;。但是我们在mybaits中如果使用这条语句查询,那么返回的结果类型是什么呢?如果是User类型的,那么查询结果返回的还有Department类型的数据,那么肯定会对应不上的。此时<resultMap>来了,它来了!!!

关联的嵌套 Select 查询【可以忽略】

  • 查询员工和所在的部门在Mybatis如何写呢?代码如下:
  1. <resultMap id="userResult" type="com.xxx.domain.User">
  2. <id column="id" property="id"/>
  3. <result column="password" property="password"/>
  4. <result column="age" property="age"/>
  5. <result column="username" property="username"/>
  6. <result column="dept_id" property="deptId"/>
  7. <!--关联查询,select嵌套查询-->
  8. <association property="department" column="dept_id" javaType="com.xxx.domain.Department" select="selectDept"/>
  9. </resultMap>
  10. <!--查询员工-->
  11. <select id="selectUser" resultMap="userResult">
  12. SELECT * FROM user WHERE id = #{id}
  13. </select>
  14. <!--查询部门-->
  15. <select id="selectDept" resultType="com.xxx.domain.Department ">
  16. SELECT * FROM department WHERE ID = #{id}
  17. </select>
  • 就是这么简单,两个select语句,一个用来加载员工,一个用来加载部门。
  • 这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为N+1 查询问题。 概括地讲,N+1 查询问题是这样子的:
    • 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是+1)。
    • 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是N)。
  • 这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。

关联的嵌套结果映射【重点】

  • <association >标签中还可以直接嵌套结果映射,此时的Mybatis的查询如下:
  1. <!-- 定义resultMap -->
  2. <resultMap id="UserDepartment" type="com.xxx.domain.User" >
  3. <id column="user_id" property="id"/>
  4. <result column="password" property="password"/>
  5. <result column="age" property="age"/>
  6. <result column="username" property="username"/>
  7. <result column="dept_id" property="deptId"/>
  8. <!--
  9. property: 指定User中对应的部门属性名称
  10. javaType: 指定类型,可以是全类名或者别名
  11. -->
  12. <association property="department" javaType="com.xx.domain.Department">
  13. <!--指定Department中的属性映射,这里也可以使用单独拎出来,然后使用association中的resultMap属性指定-->
  14. <id column="id" property="id"/>
  15. <result column="dept_name" property="name"/>
  16. </association>
  17. </resultMap>
  18. <!--
  19. resultMap: 指定上面resultMap的id的值
  20. -->
  21. <select id="findUserAndDepartment" resultMap="UserDepartment">
  22. select
  23. u.id as user_id,
  24. u.dept_id,
  25. u.name,
  26. u.password,
  27. u.age,
  28. d.id,
  29. d.name as dept_name
  30. from user u left join department d on u.department_id=d.id
  31. </select>

总结

  • 至此有一个类型的关联已经完成了,学会一个<association>使用即能完成。
  • 注意: 关联的嵌套 Select 查询不建议使用,N+1是个重大问题,虽说Mybatis提供了延迟加载的功能,但是仍然不建议使用,企业开发中也是不常用的。

集合collection

  • 集合,顾名思义,就是处理有很多个类型的关联。
  • 其中的属性和association中的属性类似,不再重复了。
  • 比如这样一个例子:查询一个部门中的全部员工,查询SQL如何写呢?如下:
  1. select * from department d left join user u on u.department_id=d.id;
  • 此时的User实体类如下:
  1. @Data
  2. public class User {
  3. private Integer id;
  4. private String username;
  5. private String password;
  6. private Integer age;
  7. private Integer deptId;
  8. }
  • 此时的Department实体类如下:
  1. @Data
  2. public class Department {
  3. private Integer id;
  4. private String name;
  5. private List<User> users;
  6. }
  • association类似,同样有两种方式,我们可以使用嵌套 Select 查询,或基于连接的嵌套结果映射集合。

集合的嵌套 Select 查询【可以忽略】

  • 不太重要,查询如下:
  1. <resultMap id="deptResult" type="com.xxx.domain.Department">
  2. <!--指定Department中的属性映射,这里也可以使用单独拎出来,然后使用association中的resultMap属性指定-->
  3. <id column="id" property="id"/>
  4. <result column="name" property="name"/>
  5. <!--
  6. ofType:指定实际的JavaBean的全类型或者别名
  7. select:指定嵌套的select查询
  8. javaType:集合的类型,可以不写,Mybatis可以推测出来
  9. -->
  10. <collection property="users" javaType="java.util.ArrayList" column="id" ofType="com.xxx.doamin.User" select="selectByDeptId"/>
  11. </resultMap>
  12. <select id="selectDept" resultMap="deptResult">
  13. SELECT * FROM department WHERE ID = #{id}
  14. </select>
  15. <select id="selectByDeptId" resultType="com.xxx.domain.User">
  16. SELECT * FROM user WHERE dept_id = #{id}
  17. </select>
  • 注意:这里出现了一个不同于association的属性ofType,这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。

集合的嵌套结果映射【重点】

  • 现在你可能已经猜到了集合的嵌套结果映射是怎样工作的——除了新增的 ofType 属性,它和关联的完全相同。
  • 此时的Mybatis查询如下:

  1. <!--部门的resultMap-->
  2. <resultMap id="deptResult" type="com.xxx.domain.Department">
  3. <!--指定Department中的属性映射,这里也可以使用单独拎出来,然后使用association中的resultMap属性指定-->
  4. <id column="dept_id" property="id"/>
  5. <result column="dept_name" property="name"/>
  6. <!--
  7. ofType:指定实际的JavaBean的全类型或者别名
  8. resultMap:指定员工的resultMap
  9. -->
  10. <collection property="users" ofType="com.xxx.doamin.User" resultMap='userResult'/>
  11. </resultMap>
  12. <!--员工的resultMap-->
  13. <resultMap id="userResult" type="com.xxx.domain.User">
  14. <id column="user_id" property="id"/>
  15. <result column="password" property="password"/>
  16. <result column="age" property="age"/>
  17. <result column="username" property="username"/>
  18. </resultMap>
  19. <select id="selectDeptById" resultType="com.xxx.domain.Department">
  20. select
  21. d.id as dept_id,
  22. d.name as dept_name,
  23. u.id as user_id,
  24. u.password,
  25. u.name
  26. from department d left join user u on u.department_id=d.id
  27. where d.id=#{id}
  28. </select>

总结

  • 至此Mybatis第二弹之结果映射已经写完了,如果觉得作者写的不错,给个在看关注一波,后续还有更多精彩内容推出。

Mybatis入门篇之结果映射,你射准了吗?的更多相关文章

  1. Mybatis入门篇之基础CRUD

    前言 作为一个资深后端码农天天都要和数据库打交道,最早使用的是 Hiberate,一个封装性极强的持久性框架.自从接触到 Mybatis 就被它的灵活性所折服了,可以自己写 SQL,虽然轻量级,但是麻 ...

  2. mybatis入门篇基——基本配置与参数说明

    Mybatis 好吧这是我第一次写这种文章~如果有不足和错误之处欢迎评论,指点.今天想谈谈关于mybatis的一些基础入门知识. 进入正题~~: a.关于mybatis: 我个人觉得mybatis深得 ...

  3. MyBatis入门篇

    一.什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改 ...

  4. mybatis入门基础(六)----高级映射(一对一,一对多,多对多)

    一:订单商品数据模型 1.数据库执行脚本 创建数据库表代码: CREATE TABLE items ( id INT NOT NULL AUTO_INCREMENT, itemsname ) NOT ...

  5. mybatis入门基础(四)----输入映射和输出映射

    一:输入映射 通过parameterType指定输入参数的类型,类型可以是简单类型.hashmap.pojo的包装类型. 1.1.传递pojo的包装对象 1.1.1.需求描述 完成用户信息的综合查询, ...

  6. mybatis入门篇:存储过程的操作

    1.无ResultType/ResultMap查询 先定义一个查询的存储过程: DELIMITER // CREATE PROCEDURE `select_user_by_id`( IN userId ...

  7. mybatis入门篇:代码生成器(MyBatis Generator)

    这篇文章只是按照自己的需要去配置代码生成器,未对所有配置进行讲解,需要了解具体详情的,请到官网查阅文档.传送门:http://www.mybatis.org/generator/ 1.首先引入相关的依 ...

  8. mybatis入门篇:通过SqlSession.selectList进行数据查询

    作为一个java菜鸟,早就从慕课网中学到一些基本的mybatis的用法,但是一直不成体系,懵懵懂懂,既然正式入了java这个坑,就打算好好学学,所以买了本<MyBatis从入门到精通>,在 ...

  9. mybatis入门篇:Mybatis高级查询

    1.ResultMap的association与collection association与collection功能类似,区别是一对一与一对多,这里以association为例. 首先说明一下需求: ...

随机推荐

  1. Java三大特性与实战

    三大特性: 封装,集成,多态 编程思想 类和对象: 方法的重载 this关键字 static关键字 静态代码块 package import Object 抽象类 接口 lambda表达式 字符串St ...

  2. Vulnhub靶场-Me and my girlfriend 学习笔记

    靶机下载地址:https://www.vulnhub.com/entry/me-and-my-girlfriend-1,409/ Description: This VM tells us that ...

  3. [POJ3783]Balls 题解

    题目大意 鹰蛋问题.$ n\(个蛋,\)m\(层楼. 存在一层楼\)E\(,使得\)E\(以及\)E\(以下的楼层鹰蛋都不会摔碎,问最坏情况下最少多少次能够知道\)E$. 非常经典的模型,初看题目根本 ...

  4. java 基本类型包装类

    一 基本类型包装类 1.包装类概述 Java中提供了相应的对象来解决实现字符串与基本数据之间转换问题,基本数据类 型对象包装类:java将基本数据类型值封装成了对象. 8种基本类型对应的包装类如下: ...

  5. 痞子衡嵌入式:一种i.MXRT下从App中进入ROM串行下载模式的方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT下在App中利用ROM API进ISP/SDP模式的方法. 我们知道i.MXRT系列分为两大阵营:CM33内核的i.MXRT ...

  6. effective java之使用构建器来创建对象

    第二章第2条:遇到多个构造器参数时要考虑使用构建器(builder) 就是建造者模式(不直接生成想要的对象,而是让客户端利用所有有必要的参数调用构造器或者静态工厂)直接上代码 package com. ...

  7. BiLSTM:序列标注任务的标杆

    Bidirectional LSTM-CRF Models for Sequence Tagging. Zhiheng Huang. 2015 在2015年,本文第一个提出使用BiLSTM-CRF来做 ...

  8. HTML基础-03

    盒子模型 盒子模型(框模型 box model) - 浏览器在渲染页面时,它会将页面中的每一个元素都想象成是一个矩形的盒子. - 想象成盒子以后,对于页面的布局就变成了如何摆放盒子 - 每一个盒子从内 ...

  9. python中操作csv文件

    python中操作csv文件 读取csv improt csv f = csv.reader(open("文件路径","r")) for i in f: pri ...

  10. asp.net core mvc和angular项目的一些问题

    最近公司布置任务,用asp.net core mvc和angular改写原来的一个用Silverlight做的项目.从来没搞过,找了两本书看了一天,又看了一天代码,大致心里有底了,就开始动手.没想到一 ...