mybatis学习之级联

image.png

现在根据设计模型建立对应的pojo
首先看体检表,由于体检表分为男性和女性,因此可以先设计一个父类,然后通过继承的方式来完成pojo

package com.learn.ssm.chapter5.pojo;
//体检表父类
public  abstract class HealthForm {
@Override
    public String toString() {
        return "HealthForm [id=" + id + ", empId=" + empId + ", heart=" + heart
                + ", liver=" + liver + ", spleen=" + spleen + ", lung=" + lung
                + ", kidney=" + kidney + ", note=" + note + "]";
    }
private int id;
private int empId;
private String heart;
private String liver;
private String spleen;
private String lung;
private String kidney;
private String note;

女性体检表

//女性体检表
public class FemaleHealthForm  extends HealthForm{
private String uterus;

public String getUterus() {
    return uterus;
}



@Override
public String toString() {
    return super.toString()+    "FemaleHealthForm [uterus=" + uterus + "]";
}



public void setUterus(String uterus) {
    this.uterus = uterus;
}

男性体检表

//男性体检表

public class MaleHealthForm extends HealthForm {
    

    
    @Override
    public String toString() {
        return super.toString()+ "MaleHealthForm [prostate=" + prostate + "]";
    }

    private String prostate;

    public String getProstate() {
        return prostate;
    }

    public void setProstate(String prostate) {
        this.prostate = prostate;
    }
    

接下来设计员工表,工牌表,和任务表的pojo

//工牌表
public class WorkCard {
private int id;
private int empId;
private String realName;
private String department;
private String mobile;
private String position;
private String note;

任务表

public class Task {
//任务表
    private int id;//编号
    @Override
    public String toString() {
        return "Task [id=" + id + ", title=" + title + ", context=" + context
                + ", note=" + note + "]";
    }
    private String title;//任务标题
    private String context;//任务内容
    private String note;//备注

还剩雇员表和雇员任务表,它们有一定的关联。先从雇员任务表下手,雇员任务表是通过任务编号(task_id)和任务一一对应。

public class EmployeeTask {
//雇员任务表
/**
 * 雇员任务表示通过任务编号task_id来和任务表进行一一关联的,这里只考虑其自身和任务编号的关联
 * 
 */
    private int id;
    private int empId;
    private Task task=null;
    private String taskName;
    private String note;

属性task是一个Task类的对象,由它进行关联任务信息。设置雇员表是关键。雇员根据性别分为男雇员和女雇员。它们会有不同体检表记录,但是无论男,女都有一个雇员类,它有两个子类,男雇员类和女雇员类。在mybatis中,有一个鉴别器,通过雇员的字段sex来判断决定使用哪一个具体的子类(MaleEmployee和FemaleEmployee)初始化对象,它与工牌表示一一对应的关联关系,对于雇员任务表示一对多的关系。
雇员类POJO

/***
* 雇员父类
* 雇员根据性别分为男雇员和女雇员,他们会有不同的体检表,但是都有一个父类表。有两个子类(MaleEmployee男雇员)(FemaleEmploee女雇员)
* @author Administrator
*
*/
public class Employee {
private int id;
private String realName;
private SexEnum sex=null;
private Date birthday;
private String mobile;
private String email;
private String position;
private String note;
//工牌按一对一级联
private WorkCard workCard;
//雇员任务表,一对多的级联
private List<EmployeeTask> employeeTaskList=null;

男雇员类

//男性雇员表
public class MaleEmployee extends Employee {
private MaleHealthForm maleHealthForm=null;

public MaleHealthForm getMaleHealthForm() {
    return maleHealthForm;
}

public void setMaleHealthForm(MaleHealthForm maleHealthForm) {
    this.maleHealthForm = maleHealthForm;
}

女雇员表POJO

public class FemaleEmployee  extends Employee{
//女性雇员表包括体检表
    private FemaleHealthForm femaleHealthForm=null;

    public FemaleHealthForm getFemaleHealthForm() {
        return femaleHealthForm;
    }

    @Override
    public String toString() {
        return "FemaleEmployee [femaleHealthForm=" + femaleHealthForm + "]";
    }

    public void setFemaleHealthForm(FemaleHealthForm femaleHealthForm) {
        this.femaleHealthForm = femaleHealthForm;
    }

MaleEmployee和FemaleEmployee都继承了Employee类,有着不同体检表。Employee类是通过了employeeTaskList属性和多个雇员任务进行一对多级联。而工牌表则是通过workcard来进行一对一级联。
这样就完成了所有的POJO的设计

配置映射文件:
配置映射文件是级联的核心内容,而对于Mapper对的接口就不再书里给出了,因为根据映射文件编写接口十分简单,从最简单的内容入手,最简单的内容无非是那些关联最少的POJO,根据图5-2所示,4个POJO中task和workcard是星湖独立的。所以他们的映射文件相对简单
TaskMapper.xml&TaskMapper

TaskMapper
import com.learn.ssm.chapter5.pojo.Task;

public interface TaskMapper {
public Task getTask(int id);
}

TaskMapper.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="com.learn.ssm.chapter5.mapper.TaskMapper">
  <select  id="getTask" parameterType="int" resultType="task" >
  select id,title,context,note from t_task where id=#{id}

  </select>
  </mapper>

workcard.xml&workcardMapper

package com.learn.ssm.chapter5.mapper;

import com.learn.ssm.chapter5.pojo.WorkCard;

public interface WorkCardMapper {
public WorkCard getWorkCardByEmpId(int id );
}

<?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="com.learn.ssm.chapter5.mapper.WorkCardMapper">
  <select id="getWorkCardByEmpId" parameterType="int"  resultType="workCard" >
   SELECT  id, emp_id as empId, real_name as realName, department, mobile, position, note FROM t_work_card
        where emp_id = #{empId} 
  
  </select>
  </mapper>

这样就完成了两张表的映射文件。雇员任务表通过了任务编号(task_id)和任务表关联。这是一个一对一级联的关系。使用了association元素。雇员任务表一对一级联
雇员任务表一对一级联

<?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="com.learn.ssm.chapter5.mapper.EmployeeTaskMapper"  >
  <resultMap type="employeeTask" id="employeeTaskMap">
  <id column="id" property="id"/>
  <result column="emp_id" property="empId" />
  <result column="task_name" property="taskName" />
   <result column="note" property="note" />
   <!-- 级联一对一association 
   association元素代表着一对一元素级联的开始,property属性代表着 映射到POJO属性上,select配置是命名空间+SQL.ID的形式,这样皆可以指定
   对应Mapper的SQL,mybatis就会通过对应的SQL将数据查询回来。column代表SQL的列,作为参数传递给select属性指定的SQL,如果是多个属性则用逗号隔开
   
   -->
   <association property="task" column="task_id" select="com.learn.ssm.chapter5.mapper.TaskMapper.getTask"
   />
  </resultMap>
  <select id="getEmployeeTaskByEmpId" parameterType="int"  resultMap="employeeTaskMap">
  select id,emp_id,task_name,task_id,note from t_employee_task
  where emp_id=#{empId}
  </select>
  
  </mapper>

这里重点讲解一下association的几种不同用法
<association property="task" column="task_id" select="com.learn.ssm.chapter5.mapper.TaskMapper.getTask" />
第一种:
association的元素代表着一对一级联的开始,property属性代表映射到POJO属性上,select配置是命名空间+SQL id的形式,这样就可以指向对应的mapper的SQL。MyBatis就会通过对应的SQL将数据查询回来。column代表SQL的列,用作参数传递给select属性指定的SQL,如果是多个参数,则需要使用逗号隔开。
第二种:(不使用association标签的方式)

<select id="getEmployeeWithDeptById0" resultMap="employeeDept0">
        select e.id id, e.name name, e.gender gender, e.email email, e.d_id departmentId, d.department_name departmentName
        from tbl_employee e, tbl_department d
        where d.id=e.d_id and e.id=#{id}
    </select>
<!-- 第一种:利用resultMap进行级联查询,不使用association标签-->
    <resultMap id="employeeDept0" type="com.hly.entity.Employee">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
        <result column="gender" property="gender"></result>
        <result column="email" property="email"></result>
        <result column="departmentId" property="department.id"></result>
        <result column="departmentName" property="department.departmentName"></result>
    </resultMap>

第三种:使用association标签+javaType属性
这种方法个人感觉跟第一种没有本质上的区别,还是一条sql语句对两张表进行关联查询,只不过在结果集映射的时候有一些不同,引入了association标签。可读性比较好,对象的结构关系相较于第一种方式来说更为清晰和明朗。
sql部分,与第一种无异:

<select id="getEmployeeWithDeptById" resultMap="employeeDept">
        select e.id id, e.name name, e.gender gender, e.email email, e.d_id departmentId, d.department_name departmentName
        from tbl_employee e, tbl_department d
        where d.id=e.d_id and e.id=#{id}
    </select>
<!-- 第二种:利用resultMap进行级联查询,使用association标签 -->
    <resultMap id="employeeDept" type="com.hly.entity.Employee">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
        <result column="gender" property="gender"></result>
        <result column="email" property="email"></result>
        <association property="department" javaType="com.hly.entity.Department">
            <id column="departmentId" property="id"></id>
            <result column="departmentName" property="departmentName"></result>
        </association>
    </resultMap>

在级联元素中,association中是通过javaType的定义去声明实体映射,可以看到在这种写法中,通过association标签明确指定了department对象的类型,然后在这个association的子标签中对department对象进行结果映射

而前面使用association标签+select属性
这种方法就有意思了。与前面两种写法有比较大的不同,使用association的select标签,可以将原本两表联查的一条sql语句拆分为两条简单的sql语句。个人以为搞出这种方式的原因就是要支持级联查询的懒加载吧,这样可以很好的提升数据库的性能,毕竟只有在用到关联对象相关属性的时候,才会执行第二步的查询操作。这部分内容等到后面了解其原理,看过源码后再回来详细说明,在此留一个根。
sql部分,这里就分两部分了。第一是在t_mployee_task表中,根据id查出对应的记录。第二步就是根据前一步中查出的task_id的值,在task表中查询对应的记录。注意这两个sql是分散在两个mapper.xml中的哈。
association标签中有两个重要的属性,select是用来指定这个对象怎么去查,而column属性则是从第一步的查询结果中找出select所需的查询参数。

再研究一下体检表,它能拆分为男性雇员表和女性雇员表,所以就有两个简单的映射器。

<?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="com.learn.ssm.chapter5.mapper.MaleHealthFormMapper" >
  <select id="getMaleHealthForm" parameterType="int" resultType="maleHealthForm" >
select id, heart, liver, spleen, lung, kidney, prostate, note from
        t_male_health_form where emp_id= #{id}
  
  </select>
  
  </mapper>
<?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="com.learn.ssm.chapter5.mapper.FemaleHealthFormMapper" >
  <select id="getFemaleHealthForm" parameterType="int" resultType="femaleHealthForm" >
select id, heart, liver, spleen, lung, kidney, uterus, note from
        t_female_health_form where emp_id = #{id}
  </select>
  </mapper>
  <!--  这两个映射器都是通过雇员编号找到对应的体检表的记录,为雇员查询是提供了查询体检表的SQL-->
  

这两个映射器都是主要通过雇员编号找到对应的映射关系,为雇员查询是提供了查询的体检表SQL

现在创建雇员的映射关系

<?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="com.learn.ssm.chapter5.mapper.EmployeeMapper">
  <resultMap type="employee" id="employee">
  <id column="id" property="id" />
  <result column="real_name" property="realName"/>
  <!-- 第二种方式显式注册自定义typehandler -->
  <result column="sex" property="sex" typeHandler="com.learn.ssm.chapter4.typehandler.SexTypeHandler"/>
  <result column="birthday" property="birthday" />
   <result column="mobile" property="mobile" />
   <result column="email" property="email" />
   <result column="position" property="position" />
   <result column="note"  property="note" />
   <!-- 一对一工牌 这样就输出信息啦-->
   <association  property="workCard" column="id"     select="com.learn.ssm.chapter5.mapper.WorkCardMapper.getWorkCardByEmpId"   >
  </association>
  
  <!-- 一对多的任务表,通过雇员任务表的雇员编号,返回任务list -->
  <collection property="employeeTaskList"  column="id" fetchType="eager"
  select="com.learn.ssm.chapter5.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId" 
   >
  </collection>
  
  
  <!--鉴别器  -->
  <discriminator javaType="int" column="sex">
  <case value="1"  resultMap="maleHealthFormMapper" />
  <case value="0"  resultMap="femaleHealthFormMapper" />
  </discriminator>
    </resultMap>
    
  <resultMap type="femaleEmployee"  
  id="femaleHealthFormMapper" extends="employee" >
  <!--男女性的雇员表继承了resultmap的id为employee,从类上来说也确实是继承  -->
  <association  property="femaleHealthForm" column="id" select="com.learn.ssm.chapter5.mapper.FemaleHealthFormMapper.getFemaleHealthForm"  />
  </resultMap>
  
  <resultMap  type="maleEmployee" id="maleHealthFormMapper" extends="employee" >
  <association  property="maleHealthForm" column="id" select="com.learn.ssm.chapter5.mapper.MaleHealthFormMapper.getMaleHealthForm"  />
  </resultMap>
  <!--工牌  -->
  <resultMap type="workCard" id="inworkcard"  >

  </resultMap>
  
  
  
  
<select id="getEmployee" parameterType="int" resultMap="employee" >
select id,real_name as realName,sex,birthday,mobile,email,position,note from t_employee where id=#{id}

</select>

注意:

  • associaation元素:对工牌进行一对一级联,这个在雇员任务表中已经分析过
  • collection元素 一对多级联,其select元素指向SQL,将通过column制定的SQL字段作为参数进行传递,然后就将结果返回给雇员POJO的属性employeeTaskList
  • discriminator元素,鉴别器,它的属性column代表着使用哪个字段来进行鉴别,这里的sex,而它的子元素case,则用于区分。类似于switch...case语句,而resultMap属性表示采用哪个ResultMap去映射,比如sex=1,则使用maleHealthFormMapper进行映射。没有合适的case,使用employee进行映射

测试代码:

public static void testGetEmployee() {
    Logger logger=Logger.getLogger(chapter5Main.class);
    SqlSession sqlSession = null;
    try {
        sqlSession = SqlSessionFactoryUtils.openSqlSession();
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee employee = employeeMapper.getEmployee2(1);
//      EmployeeTaskMapper  employeeTaskMapper=sqlSession.getMapper(EmployeeTaskMapper.class);
//      List<EmployeeTask> employeeTask=employeeTaskMapper.getEmployeeTaskByEmpId(2);
//      TaskMapper taskmapper=sqlSession.getMapper(TaskMapper.class);
//      Task task=taskmapper.getTask(1);
//  logger.info(employee.getEmployeeTaskList());
//      System.out.println(employee.getEmployeeTaskList());
        System.out.println(employee);
    
    } catch(Exception ex) {
        ex.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

延迟加载:

配置项 作用 配置选项说明 默认值
lazyLoadingEnabled 延迟加载的全局开关,当开启时,所有关联对象都会延迟加载,在特定关联关系中,可通过设置fetchType属性来覆盖该项的开关状态 true\false false
aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,则每种属性按需加载 true\false 版本之前为true,之后为false

在mybatisconfig.xml中添加如下代码:
<settings >

<setting name="lazyLoadingEnabled" value="true" ></setting>

<setting name="aggressiveLazyLoading" value="false" ></setting>
</settings>
选项lazyLoadingEnabled决定是否开启延迟加载,而选项aggressiveLazyLoading则控制是否采用层级加载,采用层级加载的话,所有关联的信息同个层级的都会被加载出来。比如查询雇员信息,属性中的task关联雇员任务表,属性workcard关联工卡等等,这些是处于同一个层级的情况下。
我们要加载雇员信息只加载雇员任务信息,但是因为层级加载会把工牌信息也加载进来,为了处理这个问题我们可以使用fetchType的属性,它可以全局定义无法处理的问题。fetchType存在级联元素collection,association中。有两个值

  • eager,获得当前的POJO后,立即加载对应数据
  • lazy 获得当前POJO后延迟加载对应的数据

现在全面学习另一种级联,这个方式完全可以消除N+1的问题,但是也引发其他的问题,首先SQL比较复杂,其次所需要的配置比之前复杂的多。再次一次性将所有的数据提取出来会造成内存的浪费,一般用于比较简单的且关联不多的场景

<select id="getEmployee2"  parameterType="int"  resultMap="employee2" >
select
        emp.id, emp.real_name, emp.sex, emp.birthday,
        emp.mobile, emp.email,
        emp.position, emp.note,
        et.id as et_id, et.task_id as et_task_id,
        et.task_name as et_task_name,
        et.note as et_note,
        if (emp.sex = 1,
        mhf.id, fhf.id) as h_id,
        if (emp.sex = 1, mhf.heart, fhf.heart) as
        h_heart,
        if (emp.sex = 1, mhf.liver, fhf.liver) as h_liver,
        if (emp.sex
        = 1, mhf.spleen, fhf.spleen) as h_spleen,
        if (emp.sex = 1, mhf.lung,
        fhf.lung) as h_lung,
        if (emp.sex = 1, mhf.kidney, fhf.kidney) as
        h_kidney,
        if (emp.sex = 1, mhf.note, fhf.note) as h_note,
        mhf.prostate
        as h_prostate, fhf.uterus as h_uterus,
        wc.id wc_id, wc.real_name
        wc_real_name, wc.department wc_department,
        wc.mobile wc_mobile,
        wc.position wc_position, wc.note as wc_note,
        t.id as t_id, t.title as
        t_title, t.context as t_context, t.note as t_note
        from t_employee emp
        left join t_employee_task et on emp.id = et.emp_id
        left join
        t_female_health_form fhf on emp.id = fhf.emp_id
        left join
        t_male_health_form mhf on emp.id = mhf.emp_id
        left join t_work_card wc
        on emp.id = wc.emp_id
        left join t_task t on et.task_id = t.id
        where
        emp.id =#{id}


</select>

这里的SQL我们通过left join语句,将一个雇员模型信息所有的关联起来,这样便可以通过一条SQL将所有的信息都查询出来。对于列名做出了别名的处理。在mybatis中允许对这样的SQL进行配置,来完成级联。


<resultMap id="employee2" type="employee" >
<id column="id" property="id" />
  <result column="real_name" property="realName"/>
  <!-- 第二种方式显式注册自定义typehandler -->
  <result column="sex" property="sex" typeHandler="com.learn.ssm.chapter4.typehandler.SexTypeHandler"/>
  <result column="birthday" property="birthday" />
   <result column="mobile" property="mobile" />
   <result column="email" property="email" />
   <result column="position" property="position" />
   <result column="note"  property="note" />
   <!--在级联中assocaiation是通过javaType去声明实体映射,而collection则是通过ofType进行声明 -->
   <association property="workCard" column="id" javaType="workCard" >
   
   <id column="wc_id" property="id" ></id>
  <result column="wc_real_name" property="realName"  />
   <result column="id" property="empId"  />
    <result column="wc_department" property="department"  />
     <result column="wc_mbile" property="mobile"  />
      <result column="wc_position" property="position"  />
       <result column="wc_note" property="note"  />
  
   </association>

<collection property="employeeTaskList"  ofType="employeeTask"   column="id">
<id column="et_id" property="id"  />
<result column="id" property="empId"  />
<result column="et_task_name" property="taskName"  />
<result column="et_note" property="note"  />
<association property="task" javaType="task"
                column="et_task_id">
                <id column="t_id" property="id" />
                <result column="t_title" property="title" />
                <result column="t_context" property="context" />
                <result column="t_note" property="note" />
            </association>
</collection>
<discriminator javaType="int" column="sex"    >
<case value="1" resultMap="maleHealthFormMapper2"></case>
<case value="0" resultMap="femaleHealthFormMapper2"></case>

</discriminator>

</resultMap>
<resultMap type="maleEmployee" id="maleHealthFormMapper2" extends="employee2" >
<association property="maleHealthForm" column="id" javaType="maleHealthForm"    >
<id column="h_id"  property="id" />
<result column="h_heart"   property="heart"  />
<result column="h_liver"   property="liver"  />
<result column="h_spleen"   property="spleen"  />
<result column="h_lung"   property="lung"  />
<result column="h_kidney"   property="kidney"  />
<result column="h_prostate"   property="prostate"  />
<result column="h_note"   property="note"  />
</association>

</resultMap>


<resultMap type="femaleEmployee" id="femaleHealthFormMapper2" extends="employee2" >
<association property="femaleHealthForm" column="id" javaType="femaleHealthForm"    >
<id column="h_id"  property="id" />
<result column="h_heart"   property="heart"  />
<result column="h_liver"   property="liver"  />
<result column="h_spleen"   property="spleen"  />
<result column="h_lung"   property="lung"  />
<result column="h_kidney"   property="kidney"  />
<result column="h_uterus"   property="uterus"  />
<result column="h_note"   property="note"  />
</association>

</resultMap>
  • 每一个级联元素(association,discriminator,collection)中属性的id的配置和POJO实体配置的id一一对应,形成级联,比如上述的SQL列et_task_id和task实体的id是对应的,这是级联的关键所在。
    -在级联元素上,association是通过javaType的定义声明实体映射,而collection则是使用ofType进行声明
  • discriminator元素定义使用何种具体的resultMap进行级联,这里通过sex列进行判定

多对多级联

在现实生活中,有一种多对多的级联,而在程序中多对多的级联往往会被拆分成两个一对多级联处理
比如说:有许多用户,用户归属于一些角色,这样一个用户可以对应多个角色,而一个角色有可以由多个用户担当。
角色POJO

public class Role2 {
    private Long id;
    private String roleName;
    private String note;
    // 关联用户信息,一对多关联
    private List<User2> userList;

用户POJO

public class User2 {
    private Long id;
    private String userName;
    private String realName;
    private SexEnum sex;
    private String moble;
    private String email;
    private String note;
    // 对角色一对多关联
    private List<Role2> roleList;

两个List类型的属性是专门做一对多级联的时候使用的,使用collection的元素去完成,得到两个mapper
角色mapper.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="com.ssm.chapter5.mapper2.RoleMapper2">
    <resultMap type="com.ssm.chapter5.pojo2.Role2" id="roleMapper">
        <id column="id" property="id" />
        <result column="role_name" property="roleName" />
        <result column="note" property="note" />
        <collection property="userList" column="id" fetchType="lazy"
            select="com.ssm.chapter5.mapper2.UserMapper2.findUserByRoleId" />
    </resultMap>

    <select id="getRole" parameterType="long" resultMap="roleMapper">
        select id, role_name, note from t_role where id = #{id}
    </select>

    <select id="findRoleByUserId" parameterType="long" resultMap="roleMapper">
        select r.id, r.role_name, r.note from t_role r, t_user_role ur
        where r.id = ur.role_id and ur.user_id = #{userId}
    </select>
</mapper>

用户mapper.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="com.ssm.chapter5.mapper2.UserMapper2">
    <resultMap type="com.ssm.chapter5.pojo2.User2" id="userMapper">
        <id column="id" property="id" />
        <result column="user_name" property="userName" />
        <result column="real_name" property="realName" />
        <result column="sex" property="sex"
            typeHandler="com.ssm.chapter5.typeHandler.SexTypeHandler" />
        <result column="mobile" property="moble" />
        <result column="email" property="email" />
        <result column="position" property="position" />
        <result column="note" property="note" />
        <collection property="roleList" column="id" fetchType="lazy"
            select="com.ssm.chapter5.mapper2.RoleMapper2.findRoleByUserId" />
    </resultMap>
    <select id="getUser" parameterType="long" resultMap="userMapper">
        select id, user_name, real_name, sex, moble, email, note from t_user where
        id =#{id}
    </select>
    <select id="findUserByRoleId" parameterType="long" resultMap="userMapper">
        select u.id, u.user_name, u.real_name, u.sex, u.moble, u.email, u.note
        from
        t_user u , t_user_role ur where u.id = ur.user_id and ur.role_id =#{roleId}
    </select>
</mapper>

这里使用collection去关联,但是把fetchType设为laz,这样就能够进行延迟加载。
测试代码

public static void testUserRole() {
        SqlSession sqlSession = null;
        try {
            sqlSession = SqlSessionFactoryUtils.openSqlSession();
            RoleMapper2 roleMapper2 = sqlSession.getMapper(RoleMapper2.class);
            Role2 role2 = roleMapper2.getRole(1L);
            System.out.println(role2.getUserList().size());
            UserMapper2 userMapper2 = sqlSession.getMapper(UserMapper2.class);
            User2 user2 = userMapper2.getUser(1L);
            System.out.println(user2.getRoleList().size());
        } catch(Exception ex) {
            ex.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

一共有3条SQL被执行,因为在role.getUserList的方法中调用获取用户信息,所以延迟加载的语句被执行。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容