精品专题:
01.《C语言从不挂科到高绩点》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12753294.html?spm=1001.2014.3001.5482
02. 《SpringBoot详细教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12789841.html?spm=1001.2014.3001.5482
03.《SpringBoot电脑商城项目》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12752883.html?spm=1001.2014.3001.5482
04.《VUE3.0 核心教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12769996.html?spm=1001.2014.3001.5482
================================
|| 持续分享系列教程,关注一下不迷路 ||
|| 视频教程:小破站:墨轩大楼 ||
================================
一、resultMap映射
1.1 问题描述
在mybatis映射文件配置中,多数时候查询的数据库表中的列名称和实体类中的属性名应该是保持一致的,因此,在执行查询操作时,通过配置(select标签)上配置resultType可以实现数据库表与实体的映射关系;
但是以上配置是一种理想状态(约定优于配置),实际情况是有可能数据库表中的列名称与实体类的属性不一致,例如:
》》数据表如下:
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books` (`id` int NOT NULL AUTO_INCREMENT,`no` varchar(255) DEFAULT NULL,`name` varchar(255) DEFAULT NULL,`author` varchar(255) DEFAULT NULL,`publish` varchar(255) DEFAULT NULL,`price` decimal(10,0) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3;-- ----------------------------
-- Records of books
-- ----------------------------
INSERT INTO `books` VALUES ('1', '1001', '金瓶梅', '兰陵笑笑生', '墨轩出版社', '69');
INSERT INTO `books` VALUES ('2', '1002', '剑来', '烽火戏诸侯', '墨轩出版社', '59');
INSERT INTO `books` VALUES ('3', '1003', '雪中悍刀行', '烽火戏诸侯', '墨轩出版社', '50');
INSERT INTO `books` VALUES ('4', '1004', '诛仙', '萧鼎', '墨轩出版社', '69');
》》 实体类如下:
package com.moxuan.mybatis_mapping.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {private Integer bId;private Integer bNo;private String bName;private String bAuthor;private Double bPrice;private String bPublish;
}
》》数据映射接口,BookDao代码如下:
package com.moxuan.mybatis_mapping.dao;import com.moxuan.mybatis_mapping.entity.Book;import java.util.List;public interface BookDao {List<Book> findBooks();
}
》》 映射文件,BookMapper.xml 代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 此处的namespace需要对应上dao包中的数据操作接口-->
<mapper namespace="com.moxuan.mybatis_mapping.dao.BookDao"><select id="findBooks" resultType="book">select * from books;</select>
</mapper>
》》 在Mybatis主配置文件中,引入BookMapper.xml文件
<mappers><mapper resource="mapper\BookMapper.xml"></mapper>
</mappers>
》》 测试方法,代码如下:
@Test
public void testFindBooks(){SqlSession session = MyBatisUtil.getSession();BookDao dao = session.getMapper(BookDao.class);List<Book> books = dao.findBooks();for (Book book:books){System.out.println(book);}
}
》》 测试结果:
在上面的测试结果中可以看出,当实体类的属性名和数据表的列名不对应是,在查询的时候直接设置resultType="book"时,所查询到的结果无法映射到实体类中。
解决方案有以下两种:
- 在映射文件中为查询语句的查询列指定别名,别名与属性名保持一致。
- 通过配置resultMap标签实现结果集映射。
1.2 指定列别名解决方案
此种方式是在映射文件中,查询的时候为列查询结果取上和实体类属性相同的名字,具体操作如下:
<select id="findBooks" resultType="book">select id as bId ,no as bNo,name as bName ,author as bAuthor,price as bPrice ,publish as bPublishfrom books;
</select>
》》 测试方法不变,测试结果如下:
指定列名的方式,也可以使用sql标签将指定列名的语句封装起来,方便后续重用,具体做法如下:
<sql id="book_mapping">id as bId ,no as bNo,name as bName ,author as bAuthor,price as bPrice ,publish as bPublish</sql><select id="findBooks" resultType="book">select <include refid="book_mapping"></include>from books;</select>
1.3 配置resultMap解决方案
此种方式是在映射文件中,添加resultMap标签,指定类属性和数据列名的对应关系,然后在查询的时候使用resultMap属性引用对应关系,具体操作如下:
》》 在映射文件中,添加实体类属性与列名的映射对应关系
<resultMap id="book_info" type="book"><result property="bId" column="id"></result><result property="bNo" column="no"></result><result property="bName" column="name"></result><result property="bAuthor" column="author"></result><result property="bPrice" column="price"></result><result property="bPublish" column="publish"></result>
</resultMap>
id 属性,指定该resultMap的名称,后续可以根据这个id属性来引用此实体类属性和列名的映射关系。
》》 在映射文件的查询语句中使用resultMap属性引入实体类属性和列名的映射关系,具体如下:
<select id="findBooks" resultMap="book_info">select * from books;
</select>
resultMap属性中填写的是前面一步resultMap标签中的id属性值。
》》 测试方法不变,运行效果如下:
二、关联映射(association)
2.1 问题描述
对于单表的查询虽然简单,但是实际开发中,多数的业务涉及到的查询一定是多表的关联查询,因此需要在查询一个表的同时将其关联的其他表数据一并查询,此时就需要使用到Mybatis的关联映射:
关联映射,即在N对1关系中,在查询N的一方时需要将1的一方查询出来,例如:
- 查询员工时需要同时查询其部门信息
- 查询学生时需要同时查询其所在需学院信息
- 查询商品的同时需要查询其分类信息
2.2 案例讲解
王者荣耀英雄技能与英雄信息关联映射:查询技能时查出技能对应的英雄信息。
》》 数据表如下:
-- ----------------------------
-- Table structure for `hero`
-- ----------------------------
DROP TABLE IF EXISTS `hero`;
CREATE TABLE `hero` (`id` int NOT NULL AUTO_INCREMENT,`hname` varchar(255) DEFAULT NULL,`job` varchar(255) DEFAULT NULL,`level` int DEFAULT NULL,`sex` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb3;-- ----------------------------
-- Records of hero
-- ----------------------------
INSERT INTO `hero` VALUES ('1', '夏侯惇', '战士', '2', '男');
INSERT INTO `hero` VALUES ('3', '甄姬', '法师', '2', '女');
INSERT INTO `hero` VALUES ('4', '安琪拉', '法师', '3', '女');
INSERT INTO `hero` VALUES ('5', '廉颇', '辅助', '1', '男');
INSERT INTO `hero` VALUES ('6', '张飞', '辅助', '1', '男');
INSERT INTO `hero` VALUES ('7', '后羿', '射手', '3', '男');
INSERT INTO `hero` VALUES ('8', '虞姬', '射手', '3', '女');
INSERT INTO `hero` VALUES ('9', '阿珂', '刺客', '3', '女');
INSERT INTO `hero` VALUES ('10', '孙悟空', '刺客', '3', '男');
INSERT INTO `hero` VALUES ('12', '王昭君', '法师', '3', '女');-- ----------------------------
-- Table structure for `skill`
-- ----------------------------
DROP TABLE IF EXISTS `skill`;
CREATE TABLE `skill` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`description` varchar(255) DEFAULT NULL,`level` int DEFAULT NULL,`hurt` double DEFAULT NULL,`hero_id` int DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3;-- ----------------------------
-- Records of skill
-- ----------------------------
INSERT INTO `skill` VALUES ('1', '噬目', '被动', '1', '0', '1');
INSERT INTO `skill` VALUES ('2', '豪气斩', '物理、控制', '1', '240', '1');
INSERT INTO `skill` VALUES ('3', '龙卷闪', '真实', '1', '200', '1');
INSERT INTO `skill` VALUES ('4', '不羁之刃', '物理、控制', '1', '300', '1');
INSERT INTO `skill` VALUES ('5', '凝泪成冰', '被动', '1', '0', '2');
INSERT INTO `skill` VALUES ('6', '泪如泉涌', '法术', '1', '500', '2');
INSERT INTO `skill` VALUES ('7', '叹息水流', '法术、AOE', '1', '300', '2');
INSERT INTO `skill` VALUES ('8', '洛神降临', '法术', '1', '200', '2');
》》 添加对应的实体类
++ Hero类
package com.moxuan.mybatis_mapping.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@NoArgsConstructor
@AllArgsConstructor
@Data
public class Hero {private Integer id;private String hname;private String job;private Integer level;private String sex;
}
++ Skill技能类
package com.moxuan.mybatis_mapping.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@NoArgsConstructor
@AllArgsConstructor
@Data
public class Skill {private Integer id;private String name;private String description;private Integer level;private double hurt;private Integer hero_id;// 存储关联查询出来的hero数据private Hero hero;}
关联查询时,需要在类中添加一个被关联的类型属性,如上面,在查询技能时需要将对应的英雄数据查询出来,那么就可以在技能类Skill中定义英雄类Hero的属性。这样在查询时可以将关联查询出来的英雄数据存入其中。
》》 添加数据操作映射接口SkillDao
package com.moxuan.mybatis_mapping.dao;import com.moxuan.mybatis_mapping.entity.Skill;public interface SkillDao {Skill findSkillById(int id);
}
》》 添加映射文件SkillMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 此处的namespace需要对应上dao包中的数据操作接口-->
<mapper namespace="com.moxuan.mybatis_mapping.dao.SkillDao"><resultMap id="skillMap" type="skill"><id property="id" column="id"></id><result property="name" column="name"></result><result property="description" column="description"></result><result property="level" column="level"></result><result property="hero_id" column="hero_id"></result><association property="hero" javaType="hero"><id property="id" column="id"></id><result property="hname" column="hname"></result><result property="job" column="job"></result><result property="level" column="level"></result><result property="sex" column="sex"></result></association></resultMap><select id="findSkillById" resultMap="skillMap">select s.*,h.* from skill s join hero hon s.hero_id=h.id where s.id=#{id}</select></mapper>
在上面映射文件中,我们使用了association 关联了hero,使用association需要注意的问题:
- property: 实体类中关联对象的引用名称
- javaType:实体类中,关联对象所属的数据类型名称(或别名)
- column:关联列的名称
- 关联映射一般用于N对1时,在N的一方配置。
》》 在mybatis主配置文件中引入SkillMapper.xml
<mapper resource="mapper\SkillMapper.xml"></mapper>
》》 编写测试方法
@Test
public void testFindSkillById(){SqlSession session = MyBatisUtil.getSession();SkillDao dao = session.getMapper(SkillDao.class);Skill skill = dao.findSkillById(1);System.out.println(skill);System.out.println(skill.getHero().getHname());
}
2.3 拓展-嵌套查询
上面的案例中,我们可以将SkillMapper.xml中的代码修改如下,测试方法 不变:
<resultMap id="skillMap2" type="skill">
<!-- 嵌套关联查询,使用selectHeroById 查询英雄信息 --><association property="hero" column="hero_id" javaType="hero" select="selectHeroById"></association>
</resultMap><!--根据id查询英雄信息 -->
<select id="selectHeroById" resultType="hero">select * from hero where id=#{id}
</select><!--根据id查询技能信息 -->
<select id="findSkillById" resultMap="skillMap2">select * from skill where id=#{id}
</select>
总结:
- 使用sql语句级别的关联查询代码编写相对复杂,需要非常熟悉sql关联查询,但是执行效率较高,查询次只有一次
- 使用嵌套查询代码编码写简单,但是运行效率相对sql语句关联低,执行多次查询操作
三、集合映射
3.1 问题描述
除了通过关联映射实现将关联表的数据查询出来之外,在1对N的关系中,也可能会出现查询1的一方数据时需要将N的一方数据同步查询出来,此时,需要使用集合映射,例如:
- 查询商品时需要将商品的所有评论信息查询(图片信息)
- 查询博客时将博客的所有评论信息查询出来
- 查询英雄时将所有的技能信息查出来
3.2 使用嵌套查询实现集合映射
》 》 修改Hero类,添加存储关联查询skill技能的属性
package com.moxuan.mybatis_mapping.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@NoArgsConstructor@AllArgsConstructor@Datapublic class Hero {private Integer id;private String hname;private String job;private Integer level;private String sex;// 一对多,添加集合属性,用来存储关联查询出来的多个技能数据List<Skill> skills;}
》 》 添加查询根据id查询英雄数据的映射器接口方法
package com.moxuan.mybatis_mapping.dao;import com.moxuan.mybatis_mapping.entity.Hero;public interface HeroDao {Hero findHeroById(int id);
}
》 》 添加映射文件HeroMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 此处的namespace需要对应上dao包中的数据操作接口-->
<mapper namespace="com.moxuan.mybatis_mapping.dao.HeroDao"><select id="findSkillByHeroId" resultType="skill">select * from skill where hero_id=#{id}</select><resultMap id="heroMap" type="hero"><collection property="skills"column="id"ofType="skill"select="findSkillByHeroId"></collection></resultMap><select id="findHeroById" resultMap="heroMap">select * from hero where id=#{id}</select></mapper>
》》注意:
- collection: 将关联查询到的多条数据映射到集合中。
- property:将数据映射到指定的属性中。
- ofType: 指定被关联数据的类型。
》 》在Mybatis主映射文件中引入HeroMapper.xml
<mapper resource="mapper\HeroMapper.xml"></mapper>
》 》 编写测试方法:
@Test
public void testFindHeroById(){SqlSession session = MyBatisUtil.getSession();HeroDao dao = session.getMapper(HeroDao.class);Hero hero = dao.findHeroById(1);List<Skill> skills = hero.getSkills();System.out.println(hero);System.out.println(skills);
}
3.3 使用SQL语句级别的关联查询
在上一个案例中使用嵌套查询虽然能够解决查询集合问题,但是由于是分步查询,效率并不高,因此还可以使用sql语句的关联查询。
》》 映射文件如下:
<resultMap id="heroInfo" type="hero"><id property="id" column="id"></id><result property="hname" column="hname"></result><result property="job" column="job"></result><result property="level" column="level"></result><result property="sex" column="sex"></result><collection property="skills" ofType="skill" column="id"><id property="id" column="id"></id><result property="name" column="name"></result><result property="description" column="description"></result><result property="level" column="level"></result><result property="hurt" column="hurt"></result><result property="hero_id" column="hero_id"></result></collection>
</resultMap>
》》查询语句如下:
<select id="findHeroById" resultMap="heroInfo">select h.*,s.* from hero h join skill s on h.id=s.hero_id
</select>