📢📢📢📣📣📣
哈喽!大家好,我是【一心同学】,一位上进心十足的【Java领域博主】!😜😜😜
✨【一心同学】的写作风格:喜欢用【通俗易懂】的文笔去讲解每一个知识点,而不喜欢用【高大上】的官方陈述。
✨【一心同学】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。
✨如果有对【后端技术】感兴趣的【小可爱】,欢迎关注【一心同学】💞💞💞
❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️
一心同学将在本章为大家讲解一个MyBatis最强大的元素---ResultMap,让我们一起来欣赏它迷人的地方。
目录
一、ResultMap介绍
resultMap 元素是 MyBatis 中最重要最强大的元素,同时也是MyBatis最复杂的元素之一,它是一种数据库映射模式。描述如何从结果集中加载对象,主要作用是定义映射规则、级联的更新、定制类型转化器。
功能:解决数据库到对象的映射问题。
在此我们先来了解一下ResultMap的组成元素:
ResultMap的组成元素:
- constructor - 实例化类时,注入结果到构造方法中
idArg - ID 参数; 将结果集标记为ID,以方便全局调用
arg -注入构造器的结果集- id – 结果集ID,将结果集标记为ID,以方便全局调用
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个关联;一对一的关系,即将结果包装成这种类型
嵌套结果映射 – associations能引用自身,或者从其它地方引用- collection – 一个复杂类型的集合,一对多的关系
嵌套结果映射 – collections能引用自身,或者从其它地方引用- discriminator – 使用结果值来决定使用哪个 resultMap
- case – 基于某些值的结果映射
嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
接下来就开始一心同学的讲解。
二、id & result
实体类:
public class Blog {
private int id;
private String name;
private String password;//这里的password与数据库的pwd不一致
//set,get,无参,有参
}
接口:
public Blog selectBlogById(int id);
映射文件:
<resultMap id="blogResult" type="Blog">
<id column="id" property="id"/><!--主键映射-->
<result column="pwd" property="password"/><!--数据库表字段到实体类属性的映射-->
<result column="name" property="name"/>
</resultMap>
<select id="selectBlogById" parameterType="int" resultMap="blogResult">
select * from blog where id=#{id}
</select>
一心同学先用通俗易懂的话来讲一下各个属性。
id和result的属性:id:一般作为主键映射,方便全局调用。
result:数据库表字段到实体类属性的映射。
column:数据库字段名
property:实体属性名
javaType:指Java类该属性的类型,可以是完全限定名,或一个类型别名(例如:int的类型别名就是_int,Integer的类型别名就是int)如果是映射到一个 JavaBean,MyBatis 通常可以自动推断类型,即不需要我们写。
而对于各个属性详细解释如下:
属性 | 描述 |
---|---|
id | 主键,当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
type | 类的完全限定名, 或者一个类型别名 |
autoMapping | 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 |
而id和result的属性细节注意如下:
属性 | 描述 |
---|---|
property | 需要映射到JavaBean 的属性名称。 |
column | 数据库中的列名,或者是列的别名。 |
javaType | 一个 Java 类的全限定名,或一个类型别名。 如果你映射到一个 JavaBean,MyBatis 通常可以自动推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | 数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。 |
typeHandler | 这个属性可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。 |
三、CONSTRUCTOR 构造方法
介绍:构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。
构造方法:
public Blog(@Param("id") int id, @Param("name") String name, @Param("password") String password) {
this.id = id;
this.name = name;
this.password = password;
}
映射文件:
<resultMap id="constructResult" type="Blog">
<constructor>
<idArg column="id" javaType="_int" name="id"/>
<arg column="name" javaType="String" name="name"/>
<arg column="pwd" javaType="String" name="password"/>
</constructor>
</resultMap>
<select id="selectBlogById" parameterType="int" resultMap="constructResult">
select * from blog where id=#{id}
</select>
讲解:
idArg:一般是作为主键元素,方便全局调用。 javaType:指Java类该属性的类型,可以是完全限定名,或一个类型别名(例如:int的类型别名就是_int,Integer的类型别名就是int) name:指构造方法的参数名,需要对构造方法用@Param("指定名字")指定相应属性的名字,不然MyBatis不知道。
四、关联association
介绍:一对一的关系,将结果包装成这种类型,例如:
一篇博客只有一个作者,但一个作者可以有多篇博客,那么就应该在博客这边关联作者,对博客而言,他只需认定一个作者即1对1。
Blog实体类:
public class Blog {
private int id;
private String name;
private String pwd;
private Author author;
//set,get,无参,有参
}
Author实体类:
public class Author {
private int author_id;
private String author_name;
private String author_pwd;
//set,get,无参,有参
}
注意:现在有两个实体类了,意味着为了规范化,需要在resource目录下创建实体类相同的目录,将两个映射文件AuthorMapper.xml和BlogMapper.xml移到该目录下并记得去配置文件重新匹配路径,这是为了规范化,当然,你不这么做也可以,只是当项目大了之后就会非常难以维护。
配置文件配置路径:
<mappers>
<mapper resource="com/yixin/dao/BlogMapper.xml"/>
<mapper resource="com/yixin/dao/AuthorMapper.xml"/>
</mappers>
这是我的目录截图:
MyBatis 有两种不同的方式加载关联:
- 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
- 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
4.1关联的嵌套 Select 查询
属性:
select:用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。
AuthorMapper接口:
public interface AuthorMapper {
public Author getAuthor(int author_id);
}
BlogMapper接口:
public interface BlogMapper {
public Blog selectBlogById(int id);
}
映射文件:
AuthorMapper.xml
<mapper namespace="com.yixin.dao.AuthorMapper">
<select id="getAuthor" resultType="Author">
select * from author where author_id=#{id}<!--只有一个属性,id可以取任意值-->
</select>
</mapper>
BlogMapper.xml
<mapper namespace="com.yixin.dao.BlogMapper">
<resultMap id="blogResultSelect" type="Blog">
<id column="id" property="id"/>
<result column="pwd" property="pwd"/>
<result column="name" property="name"/>
<!---以上三个属性由于跟数据库字段相同,故也可以不写,这里只是为了方便大家理解-->
<association property="author" column="author_id" javaType="Author" select="com.yixin.dao.AuthorMapper.getAuthor"/>
</resultMap>
<select id="selectBlogById" parameterType="int" resultMap="blogResultSelect">
select * from blog where id=#{id}
</select>
</mapper>
讲解:
property:实体的属性名
column:数据库的字段名
javaType:表示传给实体对象的类型是Author
select:将column拿到的author_id的值传进去,getAuthor根据传进来的id值拿到Author这个对象,并返回去。
注意:如果希望通过两个或多个参数确定Author的对象,则column的取值可以用","分割。
AuthorMapper.xml
<select id="getAuthorByIdAndName" resultType="Author">
select * from author where author_id=#{id} and author_name=#{name}
</select>
BlogMapper.xml
<select id="selectBlogById" parameterType="int" resultMap="blogResultSelectByIdName">
select * from blog where id=#{id}
</select>
<resultMap id="blogResultSelectByIdName" type="Blog">
<association property="author" column="{id=id,name=name}" javaType="Author" select="com.yixin.dao.AuthorMapper.getAuthorByIdAndName"/>
</resultMap>
讲解:
通过column定义的id,name,然后用select将id和name传入getAuthorByIdAndName,进入返回Author.
4.2关联的嵌套结果映射
属性:
resultMap :结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。
嵌套结果映射又分为两种:
1.结果映射作为子元素嵌套在内
2.外部结果映射元素来映射关联
4.2.1结果映射作为子元素嵌套在内
接口方法:
public Blog selectBlog();
映射文件:
<select id="selectBlog" resultMap="blogResultMap">
select a.*,b.* from blog b,author a
</select>
<resultMap id="blogResult" type="Blog">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
<association property="author" javaType="Author" >
<id column="author_id" property="author_id"/>
<result column="author_name" property="author_name"/>
<result column="author_pwd" property="author_pwd"/>
</association>
</resultMap>
讲解:
查询出两个表的所有属性,然后利用这些属性在Blog里面进行分配,association则是用这些属性,生成一个Author对象。
注意:
id 元素在嵌套结果映射中扮演着非常重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性。 虽然,即使不指定这个属性,MyBatis 仍然可以工作,但是会产生严重的性能问题。
4.2.2.外部结果映射元素来映射关联
这种方式的特点是子结果映射可重用。
映射文件:
<resultMap id="blogResultMap" type="Blog">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
<association property="author" column="author_id" javaType="Author" resultMap="getAuthor"/>
</resultMap>
<resultMap id="getAuthor" type="Author">
<id column="author_id" property="author_id"/>
<result column="author_name" property="author_name"/>
<result column="author_pwd" property="author_pwd"/>
</resultMap>
讲解:
查询出所有元素后,利用resultMap嵌套了另一个resultMap---getAuthor,getAuthor同样能拿到所有的属性字段,并生成一个Author对象返回。
五、collection 一对多级联
讲解:一个作者可以有多个博客,那么为了获取作者的全部博客,我们可以对实体类Author进行如下定义
public class Author {
private int author_id;
private String author_name;
private String author_pwd;
private List<Blog> blogs;
//get,set,有参构造,无参构造
}
对于这样的映射文件的实现方式,又分为两种:
1.集合的嵌套 Select 查询
2.集合的嵌套结果映射
5.1集合的嵌套 Select 查询
接口:
public Author getAuthor(int author_id);
映射文件:
AuthorMapper.xml
<select id="getAuthor" resultMap="authorResult2">
select a.*,b.* from blog b,author a where b.author_id=#{id}
</select>
<resultMap id="authorResult2" type="Author">
<id column="author_id" property="author_id"/>
<result column="author_name" property="author_name"/>
<result column="author_pwd" property="author_pwd"/>
<collection property="blogs" javaType="ArrayList" column="id" ofType="Blog"
select="com.yixin.dao.BlogMapper.selectBlogById"/>
</resultMap>
BlogMapper.xml
<select id="selectBlogById" parameterType="int" resultMap="blogResultSelectByIdName">
select * from blog where id=#{id}
</select>
<resultMap id="blogResultSelectByIdName" type="Blog">
<association property="author" column="{id=id,name=name}" javaType="Author" select="com.yixin.dao.AuthorMapper.getAuthorByIdAndName"/>
</resultMap>
讲解:
column拿到字段id,传递给select,然后通过返回一个Blog组成集合blogs。
Collection:
- JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型。
ofType指定的是映射到list集合属性中pojo的类型。
5.2集合的嵌套结果映射
映射文件:
<select id="getAuthor" resultMap="authorResult2">
select a.*,b.* from blog b,author a where b.author_id=#{id}
</select>
<resultMap id="authorResult" type="Author">
<id column="author_id" property="author_id"/>
<result column="author_name" property="author_name"/>
<result column="author_pwd" property="author_pwd"/>
<collection property="blogs" ofType="Blog">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</collection>
</resultMap>
讲解:
ofType:指定的是映射到list集合属性中pojo的类型。
property:该集合的属性名。
六、鉴别器
介绍:
有时候,一个数据库查询可能会返回多个不同的结果集,鉴别器元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。
理解:一个作者的博客可以有多种类型,可以是技术类型的,娱乐类型的,工作类型等等,而这些类型的博客都是继承了Blog这个类,当我们要去找出某种类型的博客时(type_blog),并将其加载出来,这时候就需要用到鉴别器了。
class TechnologyBlog extends Blog{
private String study;
}
class PlayBlog extends Blog{
private String game;
}
通过type_blog这个属性判定要加载哪个博客。
对于Author这个表的属性如下:
int id,
String name,
String pwd,
int type_blog,
study,
game.
映射文件:
<resultMap id="BlogResult" type="Blog">
<id property="id" column="id" />
<result property="name" column="name"/>
<result property="pwd" column="pwd"/>
<discriminator javaType="int" column="type_blog">
<case value="1" resultMap="technologyResult"/>
<case value="2" resultMap="playResult"/>
</discriminator>
</resultMap>
<resultMap id="technologyResult" type="TechnologyBlog" extends="BlogResult">
<result property="study" column="study" />
</resultMap>
<resultMap id="playResult" type="PlayBlog" extends="BlogResult">
<result property="game" column="game" />
</resultMap>
讲解:
MyBatis 会从结果集中得到每条记录,然后比较它的 best_blog 值。 如果它匹配任意一个鉴别器的 case,就会使用这个 case 指定的结果映射。就像继承关系一样,resultMap也可以继承,在父类的基础上加入自己的属性。
注意:这个过程是互斥的,也就是说,剩余的结果映射将被忽略。
如果匹配到的是technologyResult,那么就只有TechnologyBlog这个类会被加载,返回结果也是返回TechnologyBlog这个对象。
看完上面的代码你如果觉得太复杂,更倾向于简洁的映射风格,那么也可以换成以下这种写法:
<resultMap id="BlogResult" type="Blog">
<id property="id" column="id" />
<result property="name" column="name"/>
<result property="pwd" column="pwd"/>
<discriminator javaType="int" column="type_blog">
<case value="1" resultType="TechnologyBlog">
<result property="study" column="study" />
</case>
<case value="2" resultType="PlayBlog"/>
<result property="game" column="game" />
</case>
</discriminator>
</resultMap>
总结
1、关联-association
2、集合-collection
3、association是用于一对一和多对一,而collection是用于一对多的关系
4、JavaType和ofType都是用来指定对象类型的
-
JavaType是用来指定pojo中属性的类型
-
ofType指定的是映射到list集合属性中pojo的类型。
结语
不知不觉,已经写到凌晨了,回头一看,原来,十一月已经结束了。
以上就是一心同学对MyBatis最强大的元素ResultMap的讲解,通过文档的整理+上机实操,说实话确实耗费了我好大精力,因为这个知识点的量确实很多,但同时在这个过程也让我认识到了ResultMap的强大之处,明天一心同学将满血复活,也就是下一篇博客,将为大家讲解映射文件中的自动映射。
十二月,你好。