MyBatis多表查询+SpringBoot底层(1.28)

一、MyBatis:

1.(1)员工表:

image-20260128203832705

(2)部门表:

image-20260128203945094

(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>
处理的关联关系 一对一、多对一(单个对象关联) 一对多、多对多(集合对象关联)
对应实体属性类型 单个对象类型(如 DeptEmpCard 集合类型(如 List<Emp>Set<Role>
指定关联类型的属性 javaType (指定单个对象的全类名) ofType (指定集合中元素的全类名)
赋值方式 调用实体的 setXxx(单个对象) 方法 调用实体的 setXxx(集合对象) 方法
经典场景 员工 → 部门(多对一)、员工 → 工牌(一对一) 部门 → 员工(一对多)、员工 → 角色(多对多)

property-->当前resultMap 对应实体类中关联对象的属性名

property -->当前 resultMap 对应实体类中,用来接收关联集合数据的属性名

补充:

1.<package name="cn.wolfcode.mapper"/>为什么不写resource?

  • 两个 cn.wolfcode.mapper源码目录与资源目录的同名包结构,是为了让 MyBatis 自动关联接口和 XML 文件。

  • 不需要写 resources 是因为资源文件最终会被放到类路径中,MyBatis 从类路径加载,和源码包的路径完全对齐。

image-20260128202000237

--它们的命名完全一致,是为了让 MyBatis 能自动把 Mapper 接口和对应的 XML 映射文件关联起来,这是 MyBatis 的约定规则。

二、SpringBoot:

1.SpringBoot:

SpringBoot 是整合工具,它把 Spring、SpringMVC、MyBatis 这些技术 “打包整合” 在一起,让项目开发更简单

image-20260128185739768

2.SpringMVC:

image-20260128185922166

  1. 请求入口

    用户(Actor)发送任意带/的请求,都会被DispatcherServlet统一接收(它是 SpringMVC 的请求入口)。

  2. URL 组成规则

    请求的 URL 由「Controller 类上的 URL(域)」+「方法上的 URL」拼接而成(比如类上是/user,方法上是/index,最终请求 URL 是/user/index)。

  3. 匹配目标资源

    DispatcherServlet通过HandlerMapping,从 Spring 容器中根据请求 URL 精准匹配对应的目标 Controller 方法(Handler),同时获取该方法对应的前置拦截器列表后置拦截器列表(拦截器可通过request.getParameter()获取请求参数)。

  4. 封装执行资源

    匹配到的「前置拦截器、目标 Controller 方法、后置拦截器」会被存入执行队列(按 “前置→目标方法→后置” 的顺序排列)。

  5. 执行流程

    DispatcherServlet循环所有HandlerMapping实例查找匹配的资源(找到后停止循环),随后按队列顺序执行

  • 先执行前置拦截器

  • 再执行目标 Controller 方法(方法执行完成后返回一个逻辑地址,比如return "index");

  • 最后执行后置拦截器

    6.地址解析与渲染DispatcherServlet获取逻辑地址后,按 “前缀(如/WEB-INF/jsp/user/)+ 逻辑地址(index)+ 后缀 (.jsp)” 的规则转换为物理地址,再根据物理地址渲染生成静态页面,最终返回给前端用户。

3.Spring:

  • IOC控制反转: Spring 的核心设计思想,指将对象的创建、初始化、依赖管理等控制权,从开发者手动编写代码转移到 Spring 容器,开发者不再直接通过new关键字创建对象,而是由容器统一管理。

  • DI依赖注入:是IOC 思想的具体实现方式,指 Spring 容器在创建对象时,自动将该对象依赖的其他对象注入到其属性中,完成依赖的装配。

image-20260128194228859

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

image-20260128200054868

补充:@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(便于异常统一处理)。
posted on 2026-01-28 22:48  冬冬咚  阅读(15)  评论(0)    收藏  举报