MyBatis3

一.MyBatis 实现多表查询

1.Mybatis 实现多表查询方式
  1)业务装配.对两个表编写单表查询语句,在业务(Service)把查询的两个结果进行关联.
  2)使用AutoMapping特性,在实现两表联合查询时通过别名完成映射.
  3)使用 MyBatis 的<resultMap>标签进行实现.
2.多表查询时,类中包含另一个类的对象的分类
  1)单个对象
  2)集合对象

二.resultMap 标签

1. <resultMap>标签写在mapper.xml中,由程序员控制SQL查询结果与实体类的映射关系.
  默认 MyBatis 使用 AutoMapping 特性.
2. 使用<resultMap>标签时,<select>标签不写 resultType 属性,而是使用 resultMap 属性引用<resultMap>标签.
3. 使用 resultMap 实现单表映射关系
  1)数据库设计
    表名:teacher   表字段:id , name
  2)实体类设计

public class Teacher{
    private int id1;
    private String name1;
}

  3)mapper.xml代码

<resultMap type="teacher" id="mymap">
    <!-- 主键使用 id 标签配置映射关系 --> 
    <id column="id" property="id1" />
    <!-- 其他列使用 result 标签配置映射关系 --> 
    <result column="name" property="name1"/> 
</resultMap>
<select id="selAll" resultMap="mymap">
    select * from teacher 
</select>

4. 使用 resultMap 实现关联单个对象(N+1 方式)
  1)N+1 查询方式,先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息.
  2)与业务装配的区别:
    在 service 里面写的代码,由 mybatis 完成装配
  3)实现步骤:
    在 Student 实现类中包含了一个 Teacher 对象

public class Student { 
    private int id; 
    private String name; 
    private int age; 
    private int tid; 
    private Teacher teacher;

    在 TeacherMapper 中提供一个查询

<select id="selById" resultType="teacher" parameterType="int"> 
    select * from teacher where id=#{0} 
</select>

    在 StudentMapper 中
      <association> 装配一个对象时使用
      property: 对象在类中的属性名
      select:通过哪个查询查询出这个对象的信息
      column: 把当前表的哪个列的值做为参数传递给另一个查询

<resultMap type="student" id="stuMap"> 
    <id property="id" column="id"/> 
    <result property="name" column="name"/> 
    <result property="age" column="age"/> 
    <result property="tid" column="tid"/>
    <!-- 如果关联一个对象 --> 
    <association property="teacher" select="com.su.mapper.TeacherMapper.selById" column="tid"></association> 
</resultMap> 
<select id="selAll" resultMap="stuMap"> 
    select * from student 
</select>

      大前提使用 N+1 方式.时如果列名和属性名相同可以不配置,使用 Automapping 特性.但是 mybatis 默认只会给列装配一次。把上面代码简化成: 

<resultMap type="student" id="stuMap"> 
    <result property="tid" column="tid"/>
    <!-- 如果关联一个对象 --> 
    <association property="teacher" select="com.su.mapper.TeacherMapper.selById" column="tid"></association> 
</resultMap> 
<select id="selAll" resultMap="stuMap"> 
    select * from student 
</select>

5. 使用 resultMap 实现关联单个对象(联合查询方式)
  1)只需要编写一个 SQL,在 StudentMapper 中添加下面效果
    <association/>只要专配一个对象就用这个标签
    此时把<association/>当做小的<resultMap>看待
    javaType 属性:<association/>装配完后返回一个什么类型的对象.取值是一个类(或类的别名)

<resultMap type="Student" id="stuMap1"> 
    <id column="sid" property="id"/> 
    <result column="sname" property="name"/> 
    <result column="age" property="age"/> 
    <result column="tid" property="tid"/> 
    <association property="teacher" javaType="Teacher" > 
        <id column="tid" property="id"/> 
        <result column="tname" property="name"/> 
    </association> 
</resultMap> 
<select id="selAll1" resultMap="stuMap1"> 
    select s.id sid,s.name sname,age age,t.id tid,t.name tname FROM student s left outer join teacher t on s.tid=t.id 
</select>

6. N+1 方式和联合查询方式对比
  1)N+1:需求不确定时.
  2)联合查询:需求中确定查询时两个表一定都查询.
7. N+1 名称由来
  1)举例:学生中有 3 条数据
    需求:查询所有学生信息级授课老师信息
    需要执行的 SQL 命令:
      查询全部学生信息:select * from 学生
      执行 3 遍 select * from 老师 where id=学生的外键
    使用多条 SQL命令查询两表数据时,如果希望把需要的数据都查询出来,需要执行 N+1 条 SQL才能把所有数据库查询出来.
  2)缺点:效率低
  3)优点:
    如果有的时候不需要查询学生时同时查询老师.只需要执行一个 select * from student;
  4)适用场景: 有的时候需要查询学生同时查询老师,有的时候只需要查询学生.
  5)如何解决 N+1 查询带来的效率低的问题:
    默认带的前提: 每次都是两个都查询.使用两表联合查询.

三.使用<resultMap>查询关联集合对象(N+1)   

1. 在 Teacher 中添加 List<Student>

public class Teacher { 
    private int id; 
    private String name; 
    private List<Student> list;

2. 在 StudentMapper.xml 中添加通过 tid 查询

<select id="selByTid" parameterType="int" resultType="student"> 
    select * from student where tid=#{0} 
</select>

3. 在 TeacherMapper.xml 中添加查询全部
  <collection/> 当属性是集合类型时使用的标签.

<resultMap type="teacher" id="mymap"> 
    <id column="id" property="id"/> 
    <result column="name" property="name"/> 
    <collection property="list" select="com.su.mapper.StudentMapper.selByTid" column="id"></collection> 
</resultMap> 
<select id="selAll" resultMap="mymap"> 
    select * from teacher 
</select>

四.使用<resultMap>实现加载集合数据(联合查询方式)

1.在 teacherMapper.xml 中添加
  1)mybatis 可以通过主键判断对象是否被加载过
  2)不需要担心创建重复 Teacher
  3)ofType 属性:表示集合的泛型是什么 (这里表示student)

 

<resultMap type="teacher" id="mymap1">
    <id column="tid" property="id"/> 
    <result column="tname" property="name"/>
    <collection property="list" ofType="student" > 
        <id column="sid" property="id"/> 
        <result column="sname" property="name"/> 
        <result column="age" property="age"/> 
        <result column="tid" property="tid"/> 
    </collection> 
</resultMap> 
<select id="selAll1" resultMap="mymap1"> 
    select t.id tid,t.name tname,s.id sid,s.name sname,age,tid from teacher t LEFT JOIN student s on t.id=s.tid; 
</select>

五.使用 AutoMapping 结合别名实现多表查询

1.只能使用多表联合查询方式.
2.要求:查询出的列名和属性名相同.
3.实现方式
  1)在 Student 实现类中包含了一个 Teacher 对象

public class Student { 
    private int id; 
    private String name; 
    private int age; 
    private int tid; 
    private Teacher teacher;

  2)在 SQL 是关键字符,两侧添加反单引号

<select id="selAll" resultType="student">
    select t.id `teacher.id`,t.name `teacher.name`,s.id id,s.name name,age,tid from student s LEFT JOIN teacher t on t.id=s.tid 
</select>

六.MyBatis 注解

1. 注解:为了简化配置文件.
2. Mybatis 的注解简化 mapper.xml 文件.
  如果涉及动态 SQL 依然使用 mapper.xml
3. mapper.xml 和注解可以共存.
4. 使用注解时 mybatis.xml 中<mappers>使用
  <package/>
  <mapper class="com.su.mapper.TeacherMapper"/>  :接口名
5. 实现查询
  在TeacherMapper接口:

@Select("select * from teacher") 
List<Teacher> selAll();

6. 实现新增
  在TeacherMapper接口:

@Insert("insert into teacher values(default,#{name})") 
int insTeacher(Teacher teacher);

7. 实现修改
  在TeacherMapper接口:

@Update("update teacher set name=#{name} where id=#{id}") 
int updTeacher(Teacher teacher);

8. 实现删除
  在TeacherMapper接口:

@Delete("delete from teacher where id=#{0}") 
int delById(int id);

9. 使用注解实现<resultMap>功能
  1)以 N+1 举例
  2)在 StudentMapper 接口添加查询

@Select("select * from student where tid=#{0}") 
List<Student> selByTid(int tid);

  3)在 TeacherMapper 接口添加
    @Results() 相当于<resultMap>
    @Result() 相当于<id/>或<result/>
    @Result(id=true) 相当于<id/>
    @Many() 相当于<collection/>
    @One() 相当于<association/>

@Results(value={ 
    @Result(id=true,property="id",column="id"),    
    @Result(property="name",column="name"),      
    @Result(property="list",column="id",many=@Many(select="com.su.mapper.StudentMapper.selByTid")) 
  }) @Select(
"select * from teacher") List<Teacher> selTeacher();

七. MyBatis运行原理

1. 运行过程中涉及到的类
  Resources:MyBatis中IO流的工具类
    加载配置文件
  SqlSessionFactoryBuilder():构建器
    作用:创建 SqlSessionFactory 接口的实现类
  XMLConfigBuilder:MyBatis 全局配置文件内容构建器类
    作用负责读取流内容并转换为 JAVA 代码.
  Configuration:封装了全局配置文件所有配置信息.
    全局配置文件内容存放在 Configuration 中
  DefaultSqlSessionFactory:是SqlSessionFactory接口的实现类
  Transaction:事务类
    每一个 SqlSession 会带有一个 Transaction 对象.
  TransactionFactory:事务工厂
    负责生产 Transaction
  Executor :MyBatis 执行器
    作用:负责执行 SQL 命令
    相当于 JDBC 中 statement 对象(或 PreparedStatement或 CallableStatement)
    默认的执行器 SimpleExcutor
    批量操作 BatchExcutor
    通过 openSession(参数控制)
  DefaultSqlSession:是 SqlSession 接口的实现类
  ExceptionFactory:MyBatis 中异常工厂 

2. 流程图

 

 3.文字解释
  在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.
  下面需要实例化 SqlSessionFactoryBuilder 构建器.帮助 SqlSessionFactory 接口实现类 DefaultSqlSessionFactory.
  在实例化 DefaultSqlSessionFactory 之前需要先创建 XmlConfigBuilder解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把Configuration传递给 DefaultSqlSessionFactory.到此 SqlSessionFactory 工厂创建成功.
  由 SqlSessionFactory 工厂创建 SqlSession.
  每次创建 SqlSession 时,都需要由 TransactionFactory 创建 Transaction对象,同时还需要创建 SqlSession 的执行器 Excutor,最后实例化DefaultSqlSession,传递给 SqlSession 接口.
  根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作.
  如果事务执行失败,需要进行 rollback 回滚事务.
  如果事务执行成功提交给数据库.关闭 SqlSession
到此就是 MyBatis 的运行原理.

posted @ 2020-04-05 22:41  溯鸣  阅读(276)  评论(0编辑  收藏  举报