MyBatis多表查询+SpringBoot底层(1.28)
一、MyBatis:
1.(1)员工表:

(2)部门表:

(3)员工与部门关系:
-
员工 → 部门:多对一
-
部门 → 员工:一对多
1. 多对一(Emp → Dept)
Domain 设计:在
Emp类中添加Dept类型的属性dept。MyBatis 映射(EmpMapper.xml):用
<association>标签关联部门信息。2. 一对多(Dept → Emp)
Domain 设计:在
Dept类中添加List<Emp>类型的属性empList。MyBatis 映射(DeptMapper.xml):用
<collection>标签关联员工列表。多对一 一对一 设计相同 ,一对多 多对多 设计相同
2.多对一(emp表):
①Emp类中加入
@Data @NoArgsConstructor @AllArgsConstructor public class Emp { private Long empno; private String ename; private String job; private Long mgr; private Date hiredate; private Double sal; private Double comm; private Long deptno; // 多对一 一对一 private Dept dept; }②MyBatisTest测试类中:
private EmpMapper empMapper=new EmptMapperImpl(); @Test public void test(){ // 查询全部员工信息(左外) List<Emp> empList=empMapper.findAll(); empList.stream().forEach(System.out::println); }③EmpMapper中:
public interface EmpMapper { List<Emp> findAll(); }④EmptMapperImpl中:
@Override public List<Emp> findAll() { SqlSession sqlSession = MyBatisTool.getSqlSession(); // 之前通过 xml 找 class List<Emp> empList = sqlSession.selectList("cn.wolfcode.mapper.EmpMapper.findAll"); // 现在通过 class 找 xml List<Emp> empList = sqlSession.getMapper(EmpMapper.class).findAll(); MyBatisTool.close(sqlSession); return empList; }⑤EmpMapper.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"> <mapper namespace="cn.wolfcode.mapper.EmpMapper"> <resultMap id="empMap" type="Emp"> <id column="empno" property="empno" /> <result column="ename" property="ename"/> <result column="job" property="job"/> <result column="mgr" property="mgr"/> <result column="hiredate" property="hiredate"/> <result column="sal" property="sal"/> <result column="comm" property="comm"/> <result column="edeptno" property="deptno"/> <!-- 1.dept的字段需打点调用 2.对于相同的deptno字段,通过起别名(columns属性)设置 3.同时也不要忘记sql片段中要写 表名.字段名 as 别名 --> <!-- 方法一 --> <result column="ddeptno" property="dept.deptNo"/> <result column="dname" property="dept.dname"/> <result column="loc" property="dept.loc"/> <!--<association property="dept" javaType="cn.wolfcode.domain.Dept" select="cn.wolfcode.mapper.DeptMapper.selectById" column="deptno" > </association>--> </resultMap> <sql id="empColumns"> empno,ename,job,mgr,hiredate,sal,comm,emp.deptno as edeptno,dept.deptno as ddeptno, dname,loc </sql> <select id="findAll" resultMap="empMap"> select <include refid="empColumns"/> from emp left join dept on emp.deptno=dept.deptno </select> </mapper>简化部分:
方法二:将
<result column="ddeptno" property="dept.deptNo"/>
<result column="dname" property="dept.dname"/>
<result column="loc" property="dept.loc"/>
变为
<association property="dept" javaType="cn.wolfcode.domain.Dept">
<id column="dno" property="deptNo" /> <!--注意要与sql片段别名一致 -->
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>
</association>
最终简化版:
<association property="dept" javaType="cn.wolfcode.domain.Dept"
resultMap="cn.wolfcode.mapper.DeptMapper.deptMap"> <!--注意要与sql片段别名一致 -->
</association>
方法三:直接引用DeptMapper.xml中写的方法
<association property="dept" javaType="cn.wolfcode.domain.Dept"
select="cn.wolfcode.mapper.DeptMapper.selectById" column="deptno" >
</association>
column="deptno" :column="deptno" 就是「把查员工时得到的 deptno 值,传给 DeptMapper.selectById 方法当参数」。
常规调用sqlSession.getMapper(DeptMapper.class).selectById()必须有接口方法,但 MyBatis 内部通过select="命名空间.id"调用 SQL 时,只要 XML 有对应配置,无需接口方法也能执行。也就是说不用在DeptMapper接口中定义selectById();
DeptMapper.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">
<mapper namespace="cn.wolfcode.mapper.DeptMapper">
<resultMap id="deptMap" type="Dept">
<id column="dno" property="deptNo" />
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>
</resultMap>
<select id="selectById" resultMap="deptMap">
select deptno,dname,loc from dept where deptno = #{id}
</select>
</mapper>
3.一对多(dept表)
①Dept类中:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
private Long deptNo;
private String dname;
private String loc;
private List<Emp> empList;
}
②MybatisTest 测试类中:
private DeptMapper deptMapper=new DeptMapperImpl();
@Test
public void test2(){
List<Dept> deptList= deptMapper.findAll();
deptList.stream().forEach(System.out::println);
}
③DeptMapper接口中:
public interface DeptMapper {
List<Dept> findAll();
}
④DeptMapperImpl实现类中:
public class DeptMapperImpl implements DeptMapper {
@Override
public List<Dept> findAll() {
SqlSession sqlSession = MyBatisTool.getSqlSession();
List<Dept> deptList = sqlSession.getMapper(DeptMapper.class).findAll();
sqlSession.close();
return deptList;
}
}
⑤DeptMapper.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">
<mapper namespace="cn.wolfcode.mapper.DeptMapper">
<resultMap id="deptMap" type="Dept">
<id column="dno" property="deptNo" />
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>
<collection property="empList" ofType="cn.wolfcode.domain.Emp"
resultMap="cn.wolfcode.mapper.EmpMapper.empMap1"><!-- 注意 -->
</collection>
</resultMap>
<sql id="deptColumns">
dept.deptno as dno , dname,loc, empno,ename,job,mgr,hiredate,sal,comm,emp.deptno
</sql>
<select id="findAll" resultMap="deptMap">
select <include refid="deptColumns"/> from dept left join emp on dept.deptno=emp.deptno
</select>
</mapper>
注意:在emp表中引用DeptMapper.xml的deptMap,而在DeptMapper.xml需引用EmpMapper.xml的字段,但由于原requestMap已经引用了DeptMapper.xml中的字段,所以需在EmpMapper.xml中新建:
<resultMap id="empMap1" type="Emp">
<id column="empno" property="empno" />
<result column="ename" property="ename"/>
<result column="job" property="job"/>
<result column="mgr" property="mgr"/>
<result column="hiredate" property="hiredate"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="deptno" property="deptno"/>
</resultMap>
混淆点:与区别:
| 对比维度 | <association> |
<collection> |
|---|---|---|
| 处理的关联关系 | 一对一、多对一(单个对象关联) | 一对多、多对多(集合对象关联) |
| 对应实体属性类型 | 单个对象类型(如 Dept、EmpCard) |
集合类型(如 List<Emp>、Set<Role>) |
| 指定关联类型的属性 | javaType (指定单个对象的全类名) |
ofType (指定集合中元素的全类名) |
| 赋值方式 | 调用实体的 setXxx(单个对象) 方法 |
调用实体的 setXxx(集合对象) 方法 |
| 经典场景 | 员工 → 部门(多对一)、员工 → 工牌(一对一) | 部门 → 员工(一对多)、员工 → 角色(多对多) |
补充:
1.<package name="cn.wolfcode.mapper"/>为什么不写resource?
-
两个
cn.wolfcode.mapper是源码目录与资源目录的同名包结构,是为了让 MyBatis 自动关联接口和 XML 文件。 -
不需要写
resources是因为资源文件最终会被放到类路径中,MyBatis 从类路径加载,和源码包的路径完全对齐。

--它们的命名完全一致,是为了让 MyBatis 能自动把 Mapper 接口和对应的 XML 映射文件关联起来,这是 MyBatis 的约定规则。
二、SpringBoot:
1.SpringBoot:
SpringBoot 是整合工具,它把 Spring、SpringMVC、MyBatis 这些技术 “打包整合” 在一起,让项目开发更简单

2.SpringMVC:

-
请求入口:
用户(Actor)发送任意带
/的请求,都会被DispatcherServlet统一接收(它是 SpringMVC 的请求入口)。 -
URL 组成规则:
请求的 URL 由「Controller 类上的 URL(域)」+「方法上的 URL」拼接而成(比如类上是
/user,方法上是/index,最终请求 URL 是/user/index)。 -
匹配目标资源:
DispatcherServlet通过HandlerMapping,从 Spring 容器中根据请求 URL 精准匹配对应的目标 Controller 方法(Handler),同时获取该方法对应的前置拦截器列表和后置拦截器列表(拦截器可通过request.getParameter()获取请求参数)。 -
封装执行资源:
匹配到的「前置拦截器、目标 Controller 方法、后置拦截器」会被存入执行队列(按 “前置→目标方法→后置” 的顺序排列)。
-
执行流程:
DispatcherServlet循环所有HandlerMapping实例查找匹配的资源(找到后停止循环),随后按队列顺序执行:
-
先执行前置拦截器;
-
再执行目标 Controller 方法(方法执行完成后返回一个逻辑地址,比如
return "index"); -
最后执行后置拦截器。
6.地址解析与渲染:
DispatcherServlet获取逻辑地址后,按 “前缀(如/WEB-INF/jsp/user/)+ 逻辑地址(index)+ 后缀 (.jsp)” 的规则转换为物理地址,再根据物理地址渲染生成静态页面,最终返回给前端用户。
3.Spring:
-
IOC控制反转: Spring 的核心设计思想,指将对象的创建、初始化、依赖管理等控制权,从开发者手动编写代码转移到 Spring 容器,开发者不再直接通过new关键字创建对象,而是由容器统一管理。 -
DI依赖注入:是IOC 思想的具体实现方式,指 Spring 容器在创建对象时,自动将该对象依赖的其他对象注入到其属性中,完成依赖的装配。

AOP面向切面的编程:是一种编程思想,指在不修改原有业务代码的前提下,通过「动态代理」技术,将日志记录、权限校验、事务管理等横切逻辑(跨越多个业务组件的通用逻辑)抽取出来,统一织入到目标方法的执行流程中(如方法执行前、执行后、异常时)。

补充:@Mapper与@Repository的区别?
1. 核心定位与依赖框架
| 注解 | 定位 | 依赖框架 |
|---|---|---|
@Mapper |
专门用于标记MyBatis 的 Mapper 接口,让 MyBatis 自动生成接口的代理实现类 | 依赖 MyBatis 框架 |
@Repository |
Spring 的通用注解,标记数据访问层(DAO)的实现类,语义上表示 “数据仓库” | 仅依赖 Spring 框架 |
2. 核心功能区别
(1)@Mapper的核心功能
- 作用于MyBatis 的 Mapper 接口(不是实现类);
- 让 MyBatis 自动为该接口生成代理实现类(无需手动写 Impl);
- 同时,Spring 会将这个代理类注册为 IOC 容器的 Bean,后续可通过
@Autowired注入使用。
(2)@Repository的核心功能
- 作用于数据访问层的实现类(不是接口);
- 标记该类是 “数据访问组件”,让 Spring 扫描并注册为 IOC 容器的 Bean;
- 额外功能:Spring 会捕获该类抛出的数据访问异常,并转换为 Spring 统一的
DataAccessException(便于异常统一处理)。
浙公网安备 33010602011771号