乐趣区

关于springboot:Mybatis入门篇之结果映射你射准了吗

继续原创输入,点击上方蓝字关注我吧

目录

  • 前言
  • 什么是后果映射?
  • 如何映射?

    • 别名映射
    • 驼峰映射

      • 配置文件开启驼峰映射
      • 配置类中开启驼峰映射
    • resultMap 映射
    • 总结
  • 高级后果映射

    • 关联(association)

      • 例子
      • 关联的嵌套 Select 查问
      • 关联的嵌套后果映射
      • 总结
    • 汇合 collection

      • 汇合的嵌套 Select 查问
      • 汇合的嵌套后果映射
  • 总结

前言

  • 上一篇文章介绍了 Mybatis 根底的 CRUD 操作、罕用的标签、属性等内容,如果对局部不相熟的敌人能够看 Mybatis 入门之基本操作。
  • 本篇文章持续解说 Mybatis 的后果映射的内容,想要在企业开发中灵便的应用 Mybatis,这部分的内容是必须要精通的。

什么是后果映射?

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

如何映射?

  • Myabtis 中的后果映射有很多种形式,上面会逐个介绍。

别名映射

  • 这个简略,放弃查问的 SQL 返回的字段和 Java 实体类一样即可,比方下面例子的 SQL 能够写成:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
   select user_id as userId,
   dept_id as deptId,
   doc_id as docId
   from patient_info; 
</select>
  • 这样就能和实体类中的属性映射胜利了。

驼峰映射

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

配置文件开启驼峰映射

  • 只须要在 application.properties 文件中增加如下一行代码即可:
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,而后设置属性即可,如下代码:
    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置 xml 文件的地位
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN));
        // 创立 Configuration
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 开启驼峰命名映射
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setDefaultFetchSize(100);
        configuration.setDefaultStatementTimeout(30);
        sqlSessionFactoryBean.setConfiguration(configuration);
        // 将 typehandler 注册到 mybatis
        sqlSessionFactoryBean.setTypeHandlers(typeHandlers());
        return sqlSessionFactoryBean.getObject();}
  • 留神 :如果对SqlSessionFactory 没有非凡定制,不介意重写,因为这会主动笼罩主动配置类中的配置。

resultMap 映射

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

<!-- 创立一个 resultMap 映射 -->
<resultMap id="patResultMap" type="com.xxx.domain.PatientInfo">
  <id property="userId" column="user_id" />
  <result property="docId" column="doc_id"/>
  <result property="deptId" column="dept_id"/>
</resultMap>

<!-- 应用 resultMap 映射后果到 com.xxx.domain.PatientInfo 这个 Bean 中 -->
<select id='selectPatientInfos' resultMap='patResultMap'>
    select user_id,dept_id,doc_id from patient_info;
  </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 查问语句的列名。这会使得 prop1 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    • select:用于加载简单类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给指标 select 语句。具体请参考上面的例子。留神:在应用复合主键的时候,你能够应用column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查问语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
    • fetchType:可选的。有效值为 lazyeager。指定属性后,将在映射中疏忽全局配置参数 lazyLoadingEnabled,应用属性的值。

例子

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

关联的嵌套 Select 查问【能够疏忽】

  • 查问员工和所在的部门在 Mybatis 如何写呢?代码如下:
<resultMap id="userResult" type="com.xxx.domain.User">
    <id column="id" property="id"/>
    <result column="password" property="password"/>
    <result column="age" property="age"/>
    <result column="username" property="username"/>
  <result column="dept_id" property="deptId"/>
  <!-- 关联查问,select 嵌套查问 -->
  <association property="department" column="dept_id" javaType="com.xxx.domain.Department" select="selectDept"/>
</resultMap>

<!-- 查问员工 -->
<select id="selectUser" resultMap="userResult">
  SELECT * FROM user WHERE id = #{id}
</select>

<!-- 查问部门 -->
<select id="selectDept" resultType="com.xxx.domain.Department">
  SELECT * FROM department WHERE ID = #{id}
</select>
  • 就是这么简略,两个 select 语句,一个用来加载员工,一个用来加载部门。
  • 这种形式尽管很简略,但在大型数据集或大型数据表上体现不佳。这个问题被称为N+1 查问问题。概括地讲,N+1 查问问题是这样子的:

    • 你执行了一个独自的 SQL 语句来获取后果的一个列表(就是+1)。
    • 对列表返回的每条记录,你执行一个 select 查问语句来为每条记录加载详细信息(就是N)。
  • 这个问题会导致成千盈百的 SQL 语句被执行。有时候,咱们不心愿产生这样的结果。

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

  • <association >标签中还能够间接嵌套后果映射,此时的 Mybatis 的查问如下:
<!-- 定义 resultMap -->
<resultMap id="UserDepartment" type="com.xxx.domain.User" >
    <id column="user_id" property="id"/>
    <result column="password" property="password"/>
    <result column="age" property="age"/>
    <result column="username" property="username"/>
  <result column="dept_id" property="deptId"/>
    
    <!--
        property: 指定 User 中对应的部门属性名称
        javaType: 指定类型,能够是全类名或者别名
     -->
    <association property="department" javaType="com.xx.domain.Department">
    <!-- 指定 Department 中的属性映射,这里也能够应用独自拎进去,而后应用 association 中的 resultMap 属性指定 -->
        <id column="id" property="id"/>
        <result column="dept_name" property="name"/>
    </association>
</resultMap>

<!-- 
    resultMap: 指定下面 resultMap 的 id 的值
 -->
 <select id="findUserAndDepartment" resultMap="UserDepartment">
     select 
   u.id as user_id,
   u.dept_id,
   u.name,
   u.password,
   u.age,
   d.id,
   d.name as dept_name
   from user u left join department d on u.department_id=d.id
 </select>

总结

  • 至此 有一个 类型的关联曾经实现了,学会一个 <association> 应用即能实现。
  • 留神 :关联的嵌套 Select 查问不倡议应用,N+1 是个重大问题,虽说 Mybatis 提供了提早加载的性能,然而依然不倡议应用,企业开发中也是不罕用的。

汇合 collection

  • 汇合,顾名思义,就是解决 有很多个 类型的关联。
  • 其中的属性和 association 中的属性相似,不再反复了。
  • 比方这样一个例子:查问一个部门中的全副员工,查问 SQL 如何写呢?如下:
select * from department d left join user u on u.department_id=d.id;
  • 此时的 User 实体类如下:
@Data
public class User {
 private Integer id;
 private String username;
 private String password;
 private Integer age;
 private Integer deptId; 
}
  • 此时的 Department 实体类如下:
@Data
public class Department {
 private Integer id;
 private String name;
 private List<User> users;
}
  • association 相似,同样有两种形式,咱们能够应用嵌套 Select 查问,或基于连贯的嵌套后果映射汇合。

汇合的嵌套 Select 查问【能够疏忽】

  • 不太重要,查问如下:
<resultMap id="deptResult" type="com.xxx.domain.Department">
  <!-- 指定 Department 中的属性映射,这里也能够应用独自拎进去,而后应用 association 中的 resultMap 属性指定 -->
        <id column="id" property="id"/>
        <result column="name" property="name"/>
  <!--
  ofType:指定理论的 JavaBean 的全类型或者别名
  select:指定嵌套的 select 查问
  javaType:汇合的类型,能够不写,Mybatis 能够揣测进去
-->
  <collection property="users" javaType="java.util.ArrayList" column="id" ofType="com.xxx.doamin.User" select="selectByDeptId"/>
</resultMap>

<select id="selectDept" resultMap="deptResult">
  SELECT * FROM department  WHERE ID = #{id}
</select>

<select id="selectByDeptId" resultType="com.xxx.domain.User">
  SELECT * FROM user WHERE dept_id = #{id}
</select>
  • 留神 :这里呈现了一个不同于association 的属性ofType,这个属性十分重要,它用来将 JavaBean(或字段)属性的类型和汇合存储的类型辨别开来。

汇合的嵌套后果映射【重点】

  • 当初你可能曾经猜到了汇合的嵌套后果映射是怎么工作的——除了新增的 ofType 属性,它和关联的完全相同。
  • 此时的 Mybatis 查问如下:

<!-- 部门的 resultMap-->
<resultMap id="deptResult" type="com.xxx.domain.Department">
  <!-- 指定 Department 中的属性映射,这里也能够应用独自拎进去,而后应用 association 中的 resultMap 属性指定 -->
        <id column="dept_id" property="id"/>
        <result column="dept_name" property="name"/>
  <!--
  ofType:指定理论的 JavaBean 的全类型或者别名
  resultMap:指定员工的 resultMap
-->
  <collection property="users" ofType="com.xxx.doamin.User" resultMap='userResult'/>
</resultMap>

<!-- 员工的 resultMap-->
<resultMap id="userResult" type="com.xxx.domain.User">
    <id column="user_id" property="id"/>
   <result column="password" property="password"/>
   <result column="age" property="age"/>
   <result column="username" property="username"/>
</resultMap>

<select id="selectDeptById" resultType="com.xxx.domain.Department">
  select 
  d.id as dept_id,
  d.name as dept_name,
  u.id as user_id,
  u.password,
  u.name
  from department d left join user u on u.department_id=d.id
  where d.id=#{id}
</select>

总结

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