• 学习MyBatis必知必会(8)~myBatis对象关系映射(多对一关系、一对多关系)、延迟/懒加载



    一、myBatis对象关系映射(多对一关系、一对多关系)

    1、多对一关系:

    ---例子:多个员工同属于一个部门。


    (1)myBatis发送 额外SQL:

    ■ 案例:员工表通过 dept_id 关联 部门表,需求:查询指定员工id、name、所属的部门名称的信息。

    image

    //部门对象的接口、映射文件省略,跟员工逻辑差不多
    
    /* 员工对象的接口 */
    public interface EmployeeMapper {
    	Employee get(Long id);
    }
    	
    <!--员工对象的映射文件-->
    <!-- 解决列名和属性名不匹配问题 -->
    <resultMap  id="BaseResultMap" type="Employee">
    	<id column="id" property="id"/>
    	<result column="name" property="name"/>
    	<result column="dept_id" property="dept.id"/> 		
    </resultMap>
    
    <!-- 查询操作 -->
    <select id="get" resultMap="BaseResultMap">
        select id, name, dept_id from employee2 where id = #{id}
    </select>
    
    
    /* 测试:查询指定员工id、name、所属的部门名称的信息 */
    	@Test
    	public void testGet() throws Exception {	
    		SqlSession session = MyBatisUtil.getSession();	
    		EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
    		Employee e = employeeMapper.get(1L);
    		System.out.println(e);
    		//需要通过查询得到的dept_id查询获取得到部门对象
    		/**
    		 * 额外的查询语句,可以通过配置resultMap的association属性,让myBatis帮我们执行
    		 */
    		//手动添加额外查询语句
    //		Long dept_id = e.getDept().getId();
    //		DepartmentMapper departmentMapper = session.getMapper(DepartmentMapper.class);
    //		Department d = departmentMapper.get(dept_id);
    //		e.setDept(d);
    		
    
    		System.out.println(e.getDept());	
    		session.commit();
    		session.close();
    	}
    
    • 上面是通过手动添加的额外SQL查询,通过配置resultMap的association属性,让myBatis帮我们执行如下:

    	<!-- 解决列名和属性名不匹配问题 -->
    	<resultMap  id="BaseResultMap" type="Employee">
    		<id column="id" property="id"/>
    		<result column="name" property="name"/>
    		<!--<result column="dept_id" property="dept.id"/> -->
    		<!-- 额外的SQL配置方式
    			association元素:配置单一元素的关联关系
    			property 属性:对象的属性
    			select 属性:发送额外的sql
    			column 属性: 将指定的列的值传递给额外sql
    		 -->
    		<association property="dept"
    			 select="com.shan.hello.mappe r.DepartmentMapper.get"
                  column="dept_id" 
    			 />
    			
    	</resultMap>
    

    (2)使用额外的 SQL 做映射配置会导致的问题:N+1问题

    例如:每个员工的部门编号dept_id 是不同的,当查询所有员工的id、name、所属的部门名称的信息时就会发送额外N条SQL语句。

    • N:发送额外N条SQL语句去查询员工所属的部门名称: select name from department where id = dept_id;

    • 1:查询所有员工的id、name、所属的部门的编号dept_id:select * from employee;

    ----解决:使用内联映射(多表查询),此时一条 SQL 语句搞定。

    ----处理多表查询的结果集的方法:内联映射


    (3)对象关联的查询:

    方式一:额外的SQL

    方式二:内联映射


    (4)处理多表查询的结果集的方法【内联映射】:

    • 方式一:属性名-列名 通过resultMap的子元素result进行映射配置[内联映射-使用result]:

    image


    • 方式二:属性名-列名 通过resultMap的子元素association进行映射配置[内联映射-使用association]

    image


    (5)多对一关系配置总结:

    使用association元素,配置单一对象属性。

    • 方式一:额外的SQL[分步查询],一般需要进入另外一个页面展示更加详细的信息。
    • 方式二:内联映射[多表查询],常用。需要在列表中显示关联对象的数据,使用内联映射,否则会出现N+1问题。



    2、一对多关系配置:

    ---例子:一个部门有多个员工。


    (1)一对多关系-额外的SQL:

    <!-- 针对单一对象的属性,使用association元素 -->
    <!-- 针对集合类型的属性,使用collection元素 -->  
    <!-- 额外的SQL配置方式
    	collection元素:配置集合类型元素的关联关系
    	property 属性:对象的属性
    	select 属性:发送额外的sql
    	column 属性: 将指定的列的值传递给额外sql
    -->
    
    <resultMap id="BaseResultMap" type="Department">
    	<id column="id" property="id"/>
    	<result column="name" property="name"/>
    	<!--<result column="" property="emps"/> -->
    	<collection property="emps"
    		select="com.shan.hello.mapper.EmployeeMapper.get"
    	 	column="id" >
    	 </collection>
    </resultMap>
    
    
    <select id="get" resultMap="BaseResultMap">
    	 select id, name from department where id = #{id}  
    </select>
    

    (2)一对多关系-内联查询:

    <!-- 针对单一对象的属性,使用association元素 -->
    <!-- 针对集合类型的属性,使用collection元素 --> 
    <resultMap id="BaseResultMap" type="Department">
    	<id column="id" property="id"/>
    	<result column="name" property="name"/>
    	<!--<result column="" property="emps"/> -->
    	<!-- 
    		内联查询:ofType是集合中泛型的类型
    	 --> 
    	<collection property="emps" ofType="Employee">
    		<result column="e_id" property="id"/>
    		<result column="e_name" property="name"/>
    		<result column="e_dept_id" property="deptId"/>
    	</collection>
    </resultMap>
    
    
    <select id="get" resultMap="BaseResultMap">
    	 <!-- select id, name from department where id = #{id}  --> 
         select d.id, d.name, e.id e_id, e.name e_name, e.dept_id e_dept_id from department d
      	 join employee2 e on d.id = e.dept_id where d.id = #{id}	
    </select>
    



    二、设置延迟/懒加载

      	<!-- 全局配置文件 -->
      	<settings>
      		<!-- 懒加载/延迟加载,开启延迟加载功能 -->
      		<setting name="lazyLoadingEnabled" value="true"/>
      		<!-- 设置不要积极地去查询关联对象 -->
      		<setting name="aggressiveLazyLoading" value="false"/>
             <!-- 延迟加载的触发方法 -->
      		<setting name="lazyLoadTriggerMethods" value="close"/>
      	</settings>
    
    • 动态代理-增强功能--延迟加载



    总结:多对一、一对多关系的单属性对象/集合属性对象,使用association或collection元素?使用额外SQL或内联查询?

    • 针对【单属性对象】:使用assoication元素,通常需要使用多表查询操作,即内联查询

    • 针对【集合属性对象】:使用collection元素,通常还要使用延迟加载,即额外SQL处理

  • 相关阅读:
    洛谷 P1048 采药
    一本通 1267:【例9.11】01背包问题
    一本通 1265:【例9.9】最长公共子序列
    一本通 1282:最大子矩阵
    一本通 1285:最大上升子序列和
    一本通 1284:摘花生
    一本通 1283:登山
    一本通 1264:【例9.8】合唱队形
    洛谷 P1126 机器人搬重物
    洛谷P1522 牛的旅行 Cow Tours
  • 原文地址:https://www.cnblogs.com/shan333/p/15866677.html
Copyright © 2020-2023  润新知