Mybatis sql映射文件浅析 Mybatis简介(三)

简介
除了配置相关之外,另一个核心就是 SQL 映射,MyBatis 的真正强大也在于它的映射语句。
Mybatis 创建了一套规则以 XML 为载体映射 SQL
之前提到过,各项配置信息将 Mybatis 应用的整体框架搭建起来,而映射部分则是准备了一次 SQL 操作所需的信息
一次 SQL 执行的主要事件是什么?
输入参数解析,绝大多数 SQL 都是需要参数的
SQL,通过 SQL 与数据库交互,所以最根本的是 SQL,如果连 SQL 都没有,还扯个蛋蛋?
结果映射,Mybatis 可以帮我们完成字段与 Java 类型的映射
image_5c524d1f_4a9
所以说 SQL 映射的核心内容为:
  • SQL 内容指定
  • 参数信息设置
  • 输出结果设置
当然,每个 SQL 都需要指定一个 ID 作为用于执行时的唯一标识符
比如下面示例
<select id="selectPerson"parameterType="int"resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>
SELECT * FROM PERSON WHERE ID = #{id}  为 SQL 内容部分
parameterType="int" 以及 SQL 中的 #{id} 为参数信息设置部分
resultType="hashmap" 为输出结果设置部分
原文地址: Mybatis sql 映射文件浅析 Mybatis 简介(三)

概况

如上所述,核心内容为:
  • ID
  • SQL 内容
  • 入参设置
  • 结果配置
ID用于执行时唯一定位一个映射
对于SQL 内容,也没有什么特别的,就是平常所说的数据库可以执行的 SQL 语句
对于SQL 内容中的参数,MyBatis 会通过 JDBC 创建一个预处理语句参数
这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,类似这样:
// Similar JDBC code, NOT MyBatis…String selectPerson ="SELECT * FROM PERSON WHERE ID=?";PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
输入的类型使用 parameterType 进行指定(parameterMap – 已废弃!)
输出信息使用 resultMap 或者 resultType 进行指定
 
从包含的信息的角度分析 Mybatis 映射文件的核心内容
如下图所示:
image_5c524d1f_63d
而对于数据库的 CRUD 操作,Mybatis 的 XML 配置中分别使用了 insert、select、update、delete 四个标签进行分别处理
所以一个映射(映射文件中的一个)常见的形式如下,parameterType 以及 resultType | resultMap 会根据 SQL 的类型需要或者不需要
<select | insert | update | delete  id="......"       parameterType="......"    resultType | resultMap="......">
SQL 内容......
</select | insert | update | delete>
 
核心信息为通过 Mybatis 执行一次 SQL 的必备信息,Mybatis 还可以提供更多的功能设置
所以对于不同类型的 SQL,还会有更多的一些配置条目
比如之前提到过的数据库厂商标识符 databaseId,所有类型的 SQL 映射都可以设置这一属性
而对于其他的附加辅助属性配置,有些是所有类型共同的,而有些是特有的
databaseId 就是共有的,比如用于返回自动生成的键的配置 useGeneratedKeys 只有 insert 与 update 才拥有

文档结构解析

所以从文档结构的形式角度看 SQL 映射,有四种类型的映射 select、insert、update、delete 
每种类型又都有各自的属性设置,有一些是共同的,有一些是特有的
下图如果不清楚,请到评论区中,右键,新标签查看图片,可以查看到大图
image_5c524d20_637b

属性角度解析

如果从属性的角度去看待各自的归属,每种属性都有各自的作用功能
他们自身的功能也决定了那些类型才能拥有他
比如键值的返回相关的 useGeneratedKeys,就只可能发生在 insert 或者 update 中,只有他们才可能自动生成键
image_5c524d20_c2
以上为 SQL 映射文件的核心关键信息以及属性的解读
有些细节还需要注意,关于 flushCache 以及 userCache,前者是是否清空清空本地缓存和二级缓存,后者是本条语句的结果是否进行二级缓存,含义完全不一样
四种类型都有 flushCache 属性,对于 select 默认 false,对于 insert、update、delete 默认是 true
而 userCache 只有 select 有,默认是 true
 
因为缓存机制,比如update 的时候如果 设置 flushCache="false",则当你更新后,查询的数据数据还是老的数据。

额外的馈赠 - 语法糖

在编程实践中,经常有一些公共的方法或者处理逻辑,我们通常将他们提取单独封装,以便提高代码复用程序
那么,对于 SQL 的编写呢?
Mybatis 也提供了封装提取的手段 ---SQL 元素标签
<sql id="xxx">

........

</sql>

然后可以使用 include,将他包含到指定的位置
<include refid="xxx"></include>

 

这是一种静态的织入,通过 SQL 元素,你可以方便的完成公共 SQL 片段的提取封装
如果有两个表,都有 name、age 等字段,我想将他们封装,但是表名却又不一样怎么办?
SQL 元素还提供了别名的设置,可以很容易的解决这个问题,请参考官方文档
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

 

这个 SQL 片段可以被包含在其他语句中,例如:
<select id="selectUsers"resultType="map">
  select
    <include refid="userColumns"><propertyname="alias"value="t1"/></include>,
    <include refid="userColumns"><propertyname="alias"value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>

 

上面示例中包含了两次 SQL 片段,第一次中 alias 被替换为 t1 ,第二次中的 alias 被替换为 t2,最终的结果形式为:
select
t1.id,
t1.username,
t1.password,
t2.id,
t2.username,
t2.password
from
some_table t1
cross join some_table t2

 

深入映射

参数(Parameters)细节配置

<selectid="selectPerson"parameterType="int"resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>
示例中入参类型通过 parameterType 指定为 int,参数占位符为 #{id},这是最简单的一种形式了,入参只是一个 Java 基本类型(非自定义的对象类型)
对于对象类型 Mybatis 也可以很好的完成工作,不管是入参时的解析,还是输出结果的映射解析
能够根据属性的名称进行自动的配对
<select id="selectUsers"resultType="User">
  select id, username, password
  from users
  where id = #{id}
</select>
<insert id="insertUser"parameterType="User">
  insert into users (id, username, password)
  values (#{id}, #{username}, #{password})
</insert>

 

不仅仅支持对象,而且还支持 map,当 parameterType="map" 时,map 的 key 会被用来和占位符中的名称进行匹配
也就是说对于: SELECT * FROM PERSON WHERE ID = #{id}    ,当 parameterType="map" 时,你的参数 map 需要存在  key=id  的元素
parameterType也支持 list,当 parameterType="list" 时,可以借助于动态 SQL 的 foreach 进行循环
如果是基本数据类型的 List,比如 List<Integer> 那么直接循环即可;如果是 List<User>,可以通过遍历每个元素,然后通过 #{item.username}、#{item.password} 的形式进行读取
<insert id="..." parameterType="List">
INSERT INTO xxx_table(
username,
password,
createTime
)
values
<foreach collection="list" item="item" index="index" separator=",">
(#{item.username},
#{item.password},
#{item.createTime}
)
</foreach>
</insert>

 

 

可以看得出来,类型的形式很丰富,Mybatis 很多时候都可以自动处理,但是你可以对他进行显式的明确指明,比如
 
#{property,javaType=int,jdbcType=NUMERIC}
property 表示字段名称,javaType 为 int,jdbcType 为 NUMERIC
(jdbcType 是 JDBC 对于数据库类型的抽象定义,详见 java.sql.JDBCType 或者 java.sql.Types,可以简单认为数据库字段类型
javaType 通常可以由参数对象确定,除非该对象是一个 HashMap,是 map 的时候通常也可以很好的工作,但是建议在入参类型是 Map 对他进行明确的指定
需要注意的是:如果一个列允许 null 值,并且会传递值 null 的参数,就必须要指定 JDBC Type
 
当你在插入时,如果需要使用自定义的 typeHandler , 也应该在此处进行指定
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
对于数值类型,还可以设置保留小数的位数
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
对于参数的细化配置也很容易理解,他要么是用于使用时确定入参或者数据库字段的具体类型,如 javaType 或者 jdbcType
要么就是在字段处理过程中增加的一些处理所需要的信息,比如是不是需要按照自定义处理器处理后在执行到数据库?是不是将数值的小数位数处理后在去执行数据库?
另外对于存储过程的调用 Mybatis 也是有支持的,mode 属性允许你指定 IN,OUT 或 INOUT 参数。
通常我们使用 #{} 的格式进行字符串处理,这样可以安全,是通常的首选,但是如果你就是想直接插入一个字符串到 SQL 中,可以使用的使用你要非常慎重

ResultMap- 别名映射

Mybatis 好用的一大神器就是 ResultMap,可以让你高效灵活的从结果集映射到你想要的类型中,能够进行很多高级的映射
 一般的映射可以借助于 resultType 就可以解决了,resultType 后面的值同 parameterType 类似
parameterType  resultType 的值都用于明确类型,可以使用完全限定名
不过你是否还记得入门简介中关于 typeAlias 中的介绍?
Mybatis 内置了 Java 基础类型的别名,你都可以直接使用
借助于 resultType 可以完成一些基本的诉求,比如从单表到对应实体类对象的映射,能够自动的根据字段名称和属性名称进行匹配
image_5c524d21_3917
 
但是如果名称不对应又该怎么办?
如果你的实体中的属性名称为 userName,数据库字段名为 name,Mybatis 真的敢擅自的将这两者对应起来么?
如下图所示,将之前的第一个示例稍作修改,增加一个 StudentAnother,name 更改为了 userName,并将测试代码稍作修改
从结果可以看得到,实体中的 userName 是 null ,Mybatis 肯定不敢擅自映射
image_5c524d21_120a
一种可行的方式是使用别名,通过数据库字段 AS 设置别名,就可以成功的完成映射
image_5c524d21_2a0a
通过别名,将数据库列名通过别名与属性字段建立映射关系,然后 Mybatis 就可以进行自动匹配了
但是这种形式如果有多条 SQL,每个 SQL 中都需要有别名,而且,如果后续有原因修改对象的字段名字,怎么办?
 
另外的方式就是使用 ResultMap,ResultMap 的基础用法就是相当于设置别名
但是借助于 ResultMap,将别名的映射关系,维护在 ResultMap 中,所有使用到此映射类型的 SQL 都只需要关联这个 ResultMap 即可,如果有变更,仅仅需要变更 ResultMap 中的属性字段对应关系
所有的 SQL 中的内容并不需要变动
 
如下图所示,SQL 中字段与实体类中不匹配,查询的结果为 null
右侧通过 ResultMap 将 userName 与列名 name 进行了映射,就可以成功读取数据
image_5c524d21_3f71
 
ResultMap 最基础的形式如下
<resultMap id="............" type=".................">
<id property="............" column="............"/>
<result property="............" column="............"/>
</resultMap>

 

ResultMap 需要 id 和 type,id 用于唯一标识符,type 用于指明类型,比如 Blog
ResultMap 最基础的两个信息是 id 和 result 元素
他们的内容均为 property="......." column="...........",property(对象的属性字段)和 clumn(数据库的列名)
对于基础性的映射借助于 id 和 result 就可以完全搞定, id 表示的结果将是对象的标识属性,可以认为对象的唯一标识符用 id 指定,这对于性能的提高很有作用

小结

对于 ResultMap 就是做字段到属性的映射,id 和 result 都是这个作用,但是如果是唯一标识符请使用 id 来指定
另外对于每一个字段,还可以明确的声明 javaType 和 jdbcType,以及 typeHandler 用于更加细致的解析映射
所以说基本元素为:
image_5c524d21_553b

ResultMap- 高级映射

ResultMap 当然不仅仅是像上面那样只是别名的转换,还可以进行更加复杂的映射
 
对于结果集返回有哪些场景?
“将一行记录映射为一个对象”与“将多行记录映射为对象列表”这两者本质是一样的,因为所需要做的映射是一样的
比如上面数据库列名 name 到字段 userName 的映射,不管是一行记录还是多行记录,他们都是一样的
所以下面就以一个对象为例
 
单纯的映射
比如上面的例子,数据库列名与实体类中的字段一一对应(尽管名称不完全匹配,但是仍旧是一一对应的)
组合的映射
对于关系型数据库存在着关联关系的说法,一对一,一对多等
这些关联关系最终也是要映射到对象中的, 所以对象中经常也会存在多种对应关系
比如下面官方文档中的示例 ---- 查询博客详情 
一个博客 Blog 对应着一个作者 Author ,一个作者可能有多个博文 Post,每篇博文有零或多条的评论 Post_Tag 和标签 Tag
image_5c524d21_751
<!-- Very Complex Statement -->
<selectid="selectBlogDetails"resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>

 

对于实体类,一种可能的形式如下
Blog 中有一个 Author,有一个 List<Post> ,每一个 Post 中又有 List<Comment> 和  List<Tag>
image_5c524d21_7662
 
可以看得出来对于组合映射又有一对一以及一对多两种形式
(尽管 Blog 存在 List<Post> postList; 但是在 Mybatis 中使用时,对于关系是从紧邻的上一层确定的,比如对于 Comment 看 Post,对于 Post 看 Blog,而不是从 Blog 看 Comment  )
Mybatis 的 ResultMap 可以完成类似上述 SQL 与实体类的映射
在 Mybatis 中只有两种情况,一对一和一对多

一对一 Association

对于一对一被称作关联,在 ResultMap 中使用 association 元素表示这种关系 
含义为:
association 中的所有的字段 映射为 association 元素上 property 指定的一个属性
比如下面示例,将 id 和 username 映射为 author,谁的 author?他的直接外层是谁就是谁!
<association property="author"column="blog_author_id"javaType="Author">
<id property="id"column="author_id"/>
<result property="username"column="author_username"/>

</association>

 

对于 association 的基本格式如下,相当于在基础的 ResultMap 中插入了一个“一对一”的对应
<resultMap id="............" type=".................">
<id property="............" column="............"/>
<result property="............" column="............"/>
<association property="............" column="............" javaType="............">
<id property="............" column="............"/>
<result property="............" column="............"/>

</association>

</resultMap>

 

association 中对于字段和属性的映射也是使用 id 和 result,对于唯一标志使用 id 来表示

关联的嵌套查询

对于一个 association 还可以对他进行嵌套查询,也就是在查询中进行查询
比如官方示例中
<resultMap id="blogResult"type="Blog">
<association property="author"column="author_id"javaType="Author"select="selectAuthor"/>
</resultMap>

<select id="selectBlog"resultMap="blogResult">
SELECT * FROM BLOG WHERE ID = #{id}
</select>

<selectid="selectAuthor"resultType="Author">
SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

当执行 selectBlog 时,会执行 SELECT * FROM BLOG WHERE ID = #{id}  ,查询得到的结果映射到 blogResult,在这个 ResultMap 中使用了 association 元素
这个 association 元素使用 select 标签进行了嵌套查询,也就是使用另外的一个映射 selectAuthor 进行处理
处理流程:
  1. 先查询 selectBlog 查询所有的结果
  2. 对于每一条结果,然后又再一次的 select,这就是嵌套查询
这会出现“N+1 查询问题”,查询一次 SQL 查询出一个列表(这是 1)然后对于这个列表的每一个结果都再次的查询(这是 N)性能有些时候很不好
 
嵌套查询使用 select,还有一个重要的就是 association 上的 column,这个 column 用于指定嵌套查询的参数
比如上面的例子,将会使用 author_id 传递给 SELECT * FROM AUTHOR WHERE ID = #{id} 中的 id,然后进行查询
此处仅仅只是一个参数,如果是多个参数仍旧可以,使用 column= ” {prop1=col1,prop2=col2} ”的形式
比如:
image_5c524d21_1931
 
上面就是通过 column 指定将要传递给嵌套内查询的参数
 
鉴于 ResultMap 可以提供很好地映射,所以上面的示例完全可以修改为普通的 association 形式,通过 join 将关联查询的结果映射到指定的对象中,而不是借助于 select 元素进行嵌套查询

一对多 collection

对于一对多关系,Mybatis 使用 collection
collection 的逻辑本质上与 association 是一样的,都是对象字段映射
只不过用于区分,也用于在除了数据时,具体的指定类型
一个 collection 形式为:
<collection property="posts"ofType="domain.blog.Post">
<id property="id"column="post_id"/>
<result property="subject"column="post_subject"/>
<result property="body"column="post_body"/>
</collection>

 

内部依然是使用 id 和 result 完成字段和属性的映射
但是 collection 上使用 ofType 来指定这个属性的类型,而不是之前的 javaType
这也很好理解,对于一对一或者检查的查询,他就是一个对象类型,所以使用 JavaType
对于集合的映射,我们很清楚的知道他是一个集合,所以集合类型是他的 javaType,比如 javaType="ArrayList",Mybatis 在很多情况下会为你算出来,所以可以省略 javaType
但是,什么类型的集合?还需要说明,所以使用 ofType 进行指定,看起来更加清晰
 
使用 collection 的基础形式为:
<resultMap id="............" type=".................">
<id property="............" column="............"/>
<result property="............" column="............"/>

<collection property="............" column="............" ofType="............">
<id property="............" column="............"/>
<result property="............" column="............"/>
</collection>

</resultMap>

集合的嵌套查询

对于 collection 也可以采用类似 association 中的 select 元素进行嵌套查询
原理也是类似,当检索出来结果后,借助于 select 指定的查询语句,循环查询
<resultMap id="blogResult"type="Blog">
<collection property="posts"javaType="ArrayList"column="id"ofType="Post"select="selectPostsForBlog"/>
</resultMap>
<select id="selectBlog"resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<selectid="selectPostsForBlog"resultType="Post">
SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>

ResultMap 的嵌套

在前面的叙述中,所有的内部的关联或者集合的属性映射都是直接嵌套在外部 ResultMap 中的
image_5c524d21_2587
 
借助于嵌套查询的形式 select 属性,可以进行嵌套查询,通过嵌套查询的方式,相当于经过这个 select,内部的字段映射部分被路由到另一个 ResultMap(ResultType)中了
而不需要在这个 ResultMap 中逐个重新的进行字段的映射指定
但是 select 会有 1+N 的问题,但是使用 select 时这种使用外部 ResultMap(resultType)的形式却是很有实用意义
因为如果可以进行分离,被剥离的那一部分既可以单独使用,又可以嵌套在其他的 ResultMap 中,组合成更加强大的形式
Mybatis 是支持 ResultMap 嵌套的
image_5c524d21_4ab0
不仅仅 association 支持 ResultMap 的嵌套,collection 也是支持的
image_5c524d21_68a6
 
可以看得出来,不管是借助于 select 的嵌套查询,还是 ResultMap 的嵌套,都只是在 association 上或者 collection 上附加 select 或者 resultMap 属性即可
然后就可以省略掉标签内部的字段映射处理了(id 和 result)
除非开发前对 ResultMap 的层级结构进行过统一设计布局,否则,内嵌其他人开发的 ResultMap,也并不一定总是好事,当内嵌的 ResultMap 发生变动时,某些情况可能会导致问题
 
嵌套的 ResultMap 一定需要是本文件中的吗?当然不是必须的,比如下面示例中借助于:接口的全限定名称进行索引
<association property="courseEntity" column="course_id"
javaType="com.xxx.xxx.domain.CourseEntity" resultMap="com.xxx.xxx.dao.CourseMapper.courseResultMap">
</association>

ResultMap 的重用

ResultMap 的嵌套也是一种复用,此处说的重用非解耦后的复用
在 ResultMap 中,我们通过 id 或者 result 将数据库字段和实体类中的属性名进行对应
列名和属性名的对应,以及列名和属性名全部都是固定的了,如下图所示,username 就是和 author_username 对应
image_5c524d21_62a1
 
在之前的例子中,一个 blog 有一个作者,但是如果一个博客还有一个联合作者怎么办?就像很多书可能不仅仅只有一个作者
在这种场景下:有两个作者,他们的 java 类型必然都是 Author
而且他们的字段也是相同的,但是你不得不将他们进行区分,如下面 SQL 中所示,关联了两次 Author 表,通过前缀进行了区分
一种解决方法就是将映射部分也重写两次,就像关联两次那样,仅仅是列名 column 前缀不同(可以将这两个 ResultMap 嵌入到 blogResult 中或者内容移入到外层 ResultMap 中,总之是写两遍映射)
image_5c524d22_448c
 
还有一种方法就是借助于 columnPrefix,如下图所示,Blog 中有两个 Author 的实例,一个是 author 另一个是 coAuthor,关联关系,使用 association
他们都是 Author 类的实例,所以使用同样的 ResultMap,通过 columnPrefix 对其中一个映射添加列前缀
通过这个列前缀,就相当于有了另外的一个 ResultMap,这个 ResultMap 就是指定的 ResultMap 中的 column 中每一个值都加上一个前缀
image_5c524d22_ce9

构造方法字段值注入

使用 Mybatis 的核心就是为了执行 SQL 以及完成结果映射,结果的映射必然要创建最终需要映射的结果的对象
通过 ResultMap 中的 id 和 result 指定的字段值都是通过 setter 设置器方法进行值的设置的
既然最终就是要创建一个指定类型并且具有指定属性的对象结果,那么为什么一定非得是通过 setter, 难道不能在创建对象的时候通过构造方法初始化对象吗?
Mybatis 的 ResultMap 是支持构造方法设置的
对于构造方法的属性值设置,通过 constructor 进行
将之前的例子稍作修改,增加一个构造方法,复制一个 ResultMap,添加 constructor,就可以完成映射
image_5c524d22_106a
 
借助于 constructor 与使用 id 和 result 映射在业务逻辑上没有什么本质的区别,都是将列名与字段进行映射,变的是形式
因为是借助于构造函数,所以 constructor 中与 ResultMap 中的其他字段映射是有区别的,不是使用 id 和 result 使用的是 arg 参数
简言之,使用构造方法需要根据方法签名进行匹配,方法签名就是类型和个数的匹配,所以需要 javaType
对于有些场景你可能不希望暴露某些属性的共有 setter 设置器,就可以使用构造方法的形式
上面的示例中没有通过 constructor 对 id 进行映射,如果对 id 进行映射需要使用   <idArg column="id" javaType="int"/>(没写错 就是idArg
 
对于使用 constructor 对值进行解析映射,根本就是匹配正确的构造方法,除了使用 javaType 还有 name,通过 name 指定构造方法参数的名称
从版本 3.4.3 开始,如果指定了名称 name,就不需要严格死板的按照顺序对应了,可以打乱顺序。
没有人会刻意的打乱顺序,但是永远的保证映射的顺序不变动是很难得
image_5c524d22_3b4c

鉴别器

重新建一个表作为示例,配置信息还是如原来一样,SQL 映射文件也是在第一个示例中的 XML 中编写的
主要的信息如下,表以及数据以及实体类以及映射文件等
image_5c524d22_78e7
 
定义了三个类,一个 Person 类作为抽象模型(尽管我这个不是抽象类)
一个成人类 Adult 和一个儿童类 Child
Adult 增加了 company 属性,Child 增加了 school 属性
image_5c524d22_2a00
每个类都有 setter 和 getter 方法,并且还重写了 toString 方法
 
映射文件
image_5c524d22_2ae3
 
测试类
package third;

import first.StudentAnother;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Test {

public static void main(String[] args) throws Exception {

/*

  • 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。

  • SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。

  • 而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

  • /
    String resource
    = "config/mybatis-config.xml";
    InputStream inputStream
    = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory
    = new SqlSessionFactoryBuilder().build(inputStream,"development");
    /

  • 从 SqlSessionFactory 中获取 SqlSession

  • */
    SqlSession session
    = sqlSessionFactory.openSession();
    try {
    List
    <Person> personList = session.selectList("mapper.myMapper.selectPerson");
    personList.stream().forEach(i
    ->{
    System.out.print(i);

      System.out.println(i.getClass().getName());
    });
    

} finally {
session.close();
}
}

}

 

测试结果
image_5c524d22_35e4
 
 
Mybatis 很神奇的将结果映射为了不同的子类对象
 
所以说如果一条记录可能会对应多种不同类型的对象,就可以借助于 discriminator,通过某个字段的数据鉴别,映射为不同的类
ResultMap 中的 type 对应了父类型,discriminator 上的 column 对应了需要鉴别的列名
每一个 case 对应着一种类型或者一个 ResultMap,通过 discriminator 就可以根据鉴别的值的不同进行动态的选择
discriminator 可以很轻松的处理者中类层次关系中数据的映射
 
使用 discriminator 的结果处理步骤
  • MyBatis 将会从结果集中取出每条记录,然后比较它的指定鉴别字段的值。
  • 如果匹配任何 discriminator 中的 case,它将使用由 case 指定的 resultMap(resultType)
  • 如果没有匹配到任何 case,MyBatis 只是简单的使用定义在 discriminator 块外面的 resultMap
如果将映射关系中 case 后面的值设置为 3 和 4(数据库中只有 1 和 2)
结果如下,仅仅匹配了 discriminator 外面的部分
image_5c524d22_4d54
原文地址:Mybatis sql 映射文件浅析 Mybatis 简介(三)