1. 拼接开头的字符第一章、动态SQL

1.1. 1.介绍

​ MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

场景:

【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户

​ 在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 开始精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的 OGNL 的表达式来淘汰其它大部分元素。

了解:
OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。

动态SQL中的业务逻辑判断需要使用到以下运算符: ognl表达式

1.   e1 or e2 满足一个即可
2.   e1 and e2 都得满足
3.    e1 == e2,e1 eq e2 判断是否相等
4.    e1 != e2,e1 neq e2 不相等
5.    e1 lt e2:小于   lt表示less than 
6.    e1 lte e2:小于等于,其他gt(大于),gte(大于等于) gt 表示greater than
7.    e1 in e2 
8.    e1 not in e2
9.    e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
10.   !e,not e:非,求反
11.   e.method(args)调用对象方法
12.   e.property对象属性值  user.userName
13.   e1[ e2 ]按索引取值,List,数组和Map
14.   @class@method(args)调用类的静态方法
15.   @class@field调用类的静态字段值

常见标签如下:

if:判断   if(1 gt 2){}
choose (when, otherwise):分支判断    switch:多选一
trim (where, set):去除
foreach:循环遍历标签

1.2. 2.if标签

语法格式:

 <if test="判断条件">
   满足条件执行的代码
 </if>
说明:
 1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容
 2)test属性:用来编写表达式,支持ognl;

【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用

要求:

​ 1.使用\判断

​ 2.test属性:使用OGNL表达式,完成具体的判断业务逻辑;

1)定义接口

    List<User> findBySexAndUserName(@Param("userName") String userName);

2)定义接口方法对应的映射文件信息

    <!--
     说明:使用if标签,如果满足条件,那么被if标签包裹的sql就会与拼接,否则忽略;
      <if test="条件">
         sql片段
      </if>
    -->
    <select id="findBySexAndUserName" resultMap="allUserMap">
         select * from user where sex='男'
         <if test="userName!=null and userName.trim()!=''">
             and user_name like concat('%',#{userName},'%')
         </if>
    </select>

3)测试方法

    @Test
    public void test30(){
        UserMapper userMapper = MybatisUtils.getMapper(UserMapper.class);
        //select * from user where sex='男'
        //List<User> users = userMapper.findBySexAndUserName(null);
        //select * from user where sex='男' and user_name like concat('%',?,'%');
        List<User> users = userMapper.findBySexAndUserName("柳");
        System.out.println(users);

    }

4)效果:

1604730390909

小结:

select * from t where id > 3 
<if test='条件'>
   sql片段
</if>
说明:如果满足条件就拼接,否则不拼接

1.3. 2、choose,when,otherwise

choose标签:分支选择(多选一,遇到成立的条件即停止)
    when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行。
           test属性:编写ognl表达式
    otherwise子标签:当所有条件都不满足时,才会执行该条件。

语句示例:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

需求:

编写一个查询方法,设置两个参数,一个是用户名,一个是住址。

根据用户名或者住址查询所有男性用户:
    如果输入了用户名则按照用户名模糊查找,
    否则就按照住址查找,两个条件只能成立一个,
    如果都不输入就查找用户名为“孙悟空”的用户。

【需求分析】

-- 输入了用户名
select * from user where sex='男' and user_name like '%参数%'
-- 没有输入姓名,但是输入了地址
select * from user where sex='男' and address = '参数'
--  什么也没输入
select * from user where sex='男' and user_name = '孙悟空'

1)定义接口方法

List<User> findByUserNameAndAddress(@Param("userName") String userName,@Param("address") String address);

2)定义接口方法对应的映射文件信息

    <!--
      使用choose+when+otherwise标签组合成多选一功能,
      从上到下执行,满足条件就退出,否则最后执行otherwise下的sql逻辑拼接
    -->
    <select id="findByUserNameAndAddress" resultMap="allUserMap">
        select * from user where sex='男'
        <choose>
            <when test="userName!=null and userName.trim()!=''">
                and user_name like concat('%',#{userName},'%')
            </when>
            <when test="address!=null">
                and address=#{address}
            </when>
            <otherwise>
                and user_name='孙悟空'
            </otherwise>
        </choose>
    </select>

3)测试

    @Test
    public void test31(){
        UserMapper userMapper = MybatisUtils.getMapper(UserMapper.class);
        //select * from user where sex='男' and user_name like concat('%',?,'%')
        //List<User> users = userMapper.findByUserNameAndAddress("柳", null);

        //select * from user where sex='男' and address=?
        //List<User> users = userMapper.findByUserNameAndAddress("", "上海2");

        //select * from user where sex='男' and user_name='孙悟空'
        List<User> users = userMapper.findByUserNameAndAddress("", null);

        System.out.println(users);
    }

小结:

​ 多条件选择其一?

1)多选一 从上到下执行,满足条件就退出,否则就拼接otherwise标签下的sql
select * from t where 字段 =值
<choose>
   <when test="条件">
     and sql
   </when>
   <when test="条件2">
     and sql2
   </when>
   ......
   <otherwise>
      and sqln
   </otherwise>
</choose>

1.4. 3、where

where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字

需求:按照如下条件查询所有用户

如果只输入了用户名按照用户名进行查询;
select * from user where user_name like concat('%',#{userName},'%');
如果只输入住址,按住址进行查询;
select * from user where address=#{address};
如果两者都输入,则按照两个条件查询;
select * from user where user_name like concat('%',#{userName},'%') and address=#{address};
如果两者都不合符条件,全表查询; 
select * from user

where多条件语法格式:

   <select/update/delete ...>
          sql语句
        <where>
            <if test="条件">
                字段 = #{值}
            </if>
            <if test="条件">
                AND 字段名 = #{值}
            </if>
        </where>
    </select/update/delete/insert>

1)定义接口

 List<User> findByUserNameAndAddress2(@Param("userName") String userName,@Param("address") String address);

2)定义xml

    <!--
      使用where关键字作用:
       1)将被where标签包裹的sql去除多余的and 或者or关键字
       2)为查询语句添加where关键字
    -->
    <select id="findByUserNameAndAddress2" resultMap="allUserMap">
        select * from user
        <where>
            <if test="userName!=null">
               and  user_name like concat('%',#{userName},'%')
            </if>
            <if test="address!=null">
                and address=#{address}
            </if>
        </where>
    </select>

3)测试

    @Test
    public void test32(){
        UserMapper userMapper = MybatisUtils.getMapper(UserMapper.class);
        //select * from user where  user_name like concat('%',?,'%')
        //List<User> users = userMapper.findByUserNameAndAddress2("柳", null);

        //select * from user where  address=?
        //List<User> users = userMapper.findByUserNameAndAddress2(null, "上海2");

        //select * from user
        List<User> users = userMapper.findByUserNameAndAddress2(null, null);

        System.out.println(users);
    }

小结:

通过使用\标签的功能有哪些作用?

作用:
1)去除被where标签包裹的多余的and或者or关键字;
2)按照需要添加where关键字
语法格式
select * from t 
<where>
   <if test="条件1">
     and 条件
   </if>
   <if test="条件2">
     and 条件
   </if>
   ......
</where>

1.5. 4、set

set标签:在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。

语法格式:

<update  .......>
   update 表名 
    <set>
        <if test='条件'>
              字段名1=值1,
        </if>
        <if test='条件2'>
              字段名2=值2,
        </if>
        ....
    </set>
    where 条件;
</update>

案例:修改用户信息,如果参数user中的某个属性为null,则不修改。

1)定义接口方法

    /**
     * 更新用户信息
     * @return
     */
    void updateUser2(User user);

2)定义映射文件

    <!--
      set标签作用:
         1)添加set关键字
         2)被set标签包裹的sql去除多余的逗号
    -->
    <update id="updateUser2">
        update user
        <set>
            <if test="userName!=null">
                user_name=#{userName},
            </if>
            <if test="birthday!=null">
                birthday=#{birthday},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="address!=null">
                address=#{address}
            </if>
        </set>
        where id=#{id}
    </update>

3)测试

    @Test
    public void test33(){
        UserMapper userMapper = MybatisUtils.getMapper(UserMapper.class);
        User user = new User();
        user.setId(8);
        user.setBirthday(Date.valueOf("1987-11-06"));
        user.setSex("女");
        user.setUsername("高圆圆");
        userMapper.updateUser2(user);
        MybatisUtils.commit();
        MybatisUtils.close();
    }

1604733699785

小结:

set标签的作用?

set标签作用:
1)给update语句添加set关键字;
2)去除被set标签包裹的多余的逗号;
语法格式:
update t 
<set>
   <if test="条件">
     字段名称1=#{xxx},
   </if>
   <if test="条件">
     字段名称1=#{xxx},
   </if>
   .....
</set>
where id=#{id}

1.6. 5、foreach

foreach标签:遍历集合或者数组
<foreach collection="集合名或者数组名" item="元素" separator="标签分隔符" open="以什么开始" close="以什么结束">
   #{元素}
</foreach>
    collection属性:接收的集合或者数组,集合名或者数组名
    item属性:集合或者数组参数中的每一个元素 
    separator属性:标签分隔符 
    open属性:以什么开始 
    close属性:以什么结束

[1,3,4]--->select * from user where id in(1,3,4);

举例:
  java接口方法:
      List<Post> selectPostIn(@Param("ids") List<Integer> ids);

  sql映射文件配置:
<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT *  FROM POST P
    WHERE ID in 
    <foreach  collection="ids" item="item" open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

需求:按照id值是1,2,3来查询(删除)用户数据;

1)定义接口

    List<User> findUserByIds(@Param("ids") List<Integer> ids);

2)定义xml映射

    <!--
      (1,2,4,6)
      说明:
      collection="ids":表示接口传入的集合变量名称
       item="id":表示集合遍历过程中的每一个元素
       open="(" :表示拼接的开始符号
       close=")":表示拼接的结束符号
       separator=",":拼接元素的间隔符
    -->
    <select id="findUserByIds" resultMap="allUserMap">
        select * from user where id in
        <foreach collection="ids"  item="id" open="("  close=")" separator=",">
            #{id}
        </foreach>
    </select>

3)测试

    @Test
    public void test34(){
        UserMapper userMapper = MybatisUtils.getMapper(UserMapper.class);
        List<User> users = userMapper.findUserByIds(Arrays.asList(1, 5, 4, 7, 9));
        System.out.println(users);//id=1,4,9
    }

4)效果

1604734714504

小结:

foreach标签属性作用?

foreach标签作用:遍历集合和数组
foreach标签有哪些属性?
  <foreach collection="接口出入的集合的名称" item="集合中每个元素字符" open="拼接开头的字符" close="拼接结尾的字符" separator="每个元素的间隔符号">
     #{集合中每个元素字符}
  </foreach>

1.7. 6.总结

核心的动态sql标签有哪些,各有什么作用?

2. 第二章、特殊字符处理

​ 我们在编写Mapper映射文件时,有时候需要使用到一些诸如:>,<之类的特殊字符。这些字符不能直接书写在xml文件中,需要我们对其处理。

​ 处理方式:使用转义字符代替特殊字符。

转义字符 sql符号 说明
\< < 小于
\> > 大于
\& & 和号
\' \' 单引号
\" \" 双引号

举例:批量将id小于3的用户性别改为男,在映射文件中直接写<号,xml约束会报错!

    <select id="xxx" resultType="user">
        select * from user where id &lt; #{num}
    </select>

3. 第三章 mybatis高级查询【掌握】

多表关联查询:

​ 2种方式:

​ 表关联查询: 内连接查询(显示内连接 inner join,隐式内连接) ; 外链接查询 :(左外--left join 右外 right join )

​ 子查询:一个查询的语句是另一个查询语句的组成部分;

表与表的关系:

一对一: ab两表的关系,由任意一张表维护(外键) 比如:b表维护ab的管理,那么在b表中创建一个字段aid,但是这个字段不添加外键约束;

一对多:ab两张表 比如:从a看是一个a对应b的多条数据,但是从b看是一个b只能对应一个a的数据;

多对多:ab两张表 比如:从a看是一个a对应b的多条数据,同时从b看是一个b对应a表的多条数据;

说明:通过用户,订单,商品,已经订单商品明细表练习多表关联查询操作;

3.1.1. 1、准备工作

【1】包结构

创建java项目,导入jar包和log4j日志配置文件以及连接数据库的配置文件;

【2】导入SQL脚本

​ 运行资料中的sql脚本:mybatis.sql

image-20201024145357706

【3】创建实体来包,导入资料中的pojo

image-20201024151932213

【5】配置UserMapper.xml映射文件和接口

​ 注:可以根据id查询用户信息为例搭建起工程;

接口:

public interface UserDao {

    User findById(@Param("id") Long 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:命名空间,全局唯一-->
<mapper namespace="com.heima.dao.UserDao">
    <resultMap id="userMap" type="user" autoMapping="true">
        <id column="id" property="id"/>
        <result column="user_name" property="userName"/>
    </resultMap>

    <select id="findById" resultMap="userMap">
        select * from tb_user where id=#{id}
    </select>

</mapper>

【6】单元测试

​ 目的:测试框架是否搭建成功;

3.1.2. 2、表介绍和表关系说明

导入资料中mybatis.sql脚本。新建以下4张表

tb_user:用户表
tb_order:订单表
tb_item:商品表
tb_orderdetail:订单详情表

3.1.3. 3、一对一查询

需求:通过订单编号20140921003查询出订单信息,并查询出下单人信息

​ 说明:一个订单编号对应一个订单,一个订单只能属于一个人。所以上述需求实现是一对一的实现;

3.1 需求分析

两种实现方式:

-- 分步查询
-- 1.查询订单信息
select * from tb_order where order_number='20140921003';-- user_id=1
-- 2.根据用户ID查询用户信息
select * from tb_user where id=1;
-- 一步查询
select
  tor.id as order_id,
  tor.order_number,
  tu.*
from tb_order as tor,
  tb_user as tu
where tor.user_id = tu.id
    and tor.order_number = '20140921003';

3.2 订单实体添加属性映射

3.3 添加order接口及方法

3.4 创建order映射文件,编写SQL

​ 说明:配置关联对象一对一映射关系,语法格式:

<resultMap id="映射ID" type="主表实体名称" autoMapping="true" >
    <!-- 添加主表语主表实体映射 -->
        ......
   <!--association:配置关联对象(User)的映射关系,一般与resultMap标签联合使用 --> 
    <association property="主表实体中对应从表的属性名称" javaType="从表实体类型" autoMapping="true">
               <!-- 添加从表中字段与实体属性映射关系 -->
     </association>
</resultMap>

属性说明: property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象 成员变量名 javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型

1)定义接口

public interface OrderDao {

    Order findOrderByOrderNumber(@Param("orderNumber") String orderNumber);
}

2)定义映射文件

<?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">
<!--映射文件的跟标签 namespace:命名空间,表示对当前映射文件的唯一标识-->
<mapper namespace="com.heima.dao.OrderDao">

    <resultMap id="orderUserMap" type="order" autoMapping="true">
        <id column="order_id" property="id"/>
        <result column="order_number" property="orderNumber"/>
        <!-- association一对一查询关联标签-->
        <association property="user" javaType="User" autoMapping="true">
            <id column="id" property="id"/>
            <result column="user_name" property="userName"/>
        </association>
    </resultMap>

    <select id="findOrderByOrderNumber" resultMap="orderUserMap">
        select
          tor.id as order_id,
          tor.order_number,
          tu.*
        from tb_order as tor,
          tb_user as tu
        where tor.user_id = tu.id
            and tor.order_number = #{orderNumber};
    </select>
</mapper>

3)测试

    @Test
    public void test2(){
        OrderDao orderDao = MybatisUtils.getMapper(OrderDao.class);
        Order order = orderDao.findOrderByOrderNumber("20140921003");
        System.out.println(order);
    }

4)效果

1604739067731

1604738855882

小结:

一对一关联查询实现步骤?

重要的前提:sql查询的字段名称要唯一,不能重复;
1)维护好pojo之间的关联关系,如果是一个对应一个,那么在主类中生命一个全局的从类的变量;
2)配置一对一的关联映射:
 <resultMap id="xx" type="主表对应实体pojo类型" autoMapping="true">
    <id columon="表中主键字段名称" property="pojo类中属性名称"/>
    <result  columon="其他字段名称" property="pojo类中其他属性名称"/>
    <association property="主表pojo中从类变量名称" javaType="从类的类型"  autoMapping="true">
        <id columon="表中主键字段名称" property="pojo类中属性名称"/>
        <result  columon="其他字段名称" property="pojo类中其他属性名称"/>
    </association>
 </resultMap>

3.1.4. 4、一对多查询

核心映射标签预览:

    <!--配置一对多关系映射-->
    <resultMap id="xx" type="xx" autoMapping="true">
        <!--user表主键映射-->
        <id column="xx" property="xx"/>
        <!--映射实体类中List<Order>集合使用功能Collection标签-->
        <collection property="xxx" javaType="list" ofType="xxx" autoMapping="true">
            <!--主键映射-->
            <id column="xx" property="xx"/>
            <result column="xx" property="xx"/>
        </collection>
    </resultMap>

需求:查询id为1的用户及其订单信息

【分析】

​ 一个用户可以有多个订单。

​ 一个订单只能属于一个用户。

​ 用户(1)-----订单(n)

-- **需求:查询id为1的用户及其订单信息**
-- 1.分步查询
-- 1.1 查询用户信息
select * from tb_user where id=1;
-- 1.2 根据用户id查询订单集合
select * from tb_order where user_id=1;
-- 2.一次性获取
select
  tor.id as order_id,
  tor.order_number,
  tu.*
from tb_user as tu,
  tb_order tor
where tu.id = tor.user_id
    and tu.id = 1;

【步骤】

第一步:查询SQL分析;
第二步:添加关联关系;
第三步:编写接口方法;
第四步:编写映射文件;
第五步:测试

【实现】

1.User实体添加映射关系

public class User implements Serializable{

    private List<Order> orders;

    private Long id;

    // 用户名
    private String userName;

    // 密码
    private String password;

    // 姓名
    private String name;
.....

分析两种实现查询的方式:

2.编写接口

    /**
     * 根据用户id查询用户信息,包含当前用户下订单信息
     * @param id
     * @return
     */
    User findUserAndOrdersByUserId(@Param("uId") Long id);

3.编写sql映射文件关联订单集合

    <!--
       collection标签表示一对多映射标签
       property="orders":表示主类中对应从类的变量名称
       javaType="list"
       ofType="order":表示集合的泛型,或者collection标签内部需要去映射的类型
    -->
    <resultMap id="userOrderMap" type="user" autoMapping="true">
        <id column="id" property="id"/>
        <result column="user_name" property="userName"/>
        <!--collection 表示一对多的关系-->
        <collection property="orders" javaType="list" ofType="order" autoMapping="true">
            <id column="order_id" property="id"/>
            <result column="order_number" property="orderNumber"/>
        </collection>
    </resultMap>

    <select id="findUserAndOrdersByUserId" resultMap="userOrderMap" >
        select
          tor.id as order_id,
          tor.order_number,
          tu.*
        from tb_user as tu,
          tb_order tor
        where tu.id = tor.user_id
            and tu.id = #{uId}
    </select>

4.测试:

    @Test
    public void test3(){
        UserDao userDao = MybatisUtils.getMapper(UserDao.class);
        User user = userDao.findUserAndOrdersByUserId(1L);
        System.out.println(user);
    }

5.效果

1604742057222

映射示意图:

1604741864068

小结:

1.在一对多的场景中,一般主表中通过创建一个集合属性来包含从表的数据(如:user类包含List\ orders);

2.使用\标签建立对集合的关联映射;

一对多场景关联查询:
1)维护pojo之间的一对多的关系,一般在主表对应的pojo中定义一个从表集合的属性;
2)使用collection标签进行一对多的映射
    <resultMap id="唯一" type="主表的pojo类型" autoMapping="true">
        <id column="主表主键ID名称" property="主表pojo属性"/>
        <result column="xx" property="xx"/>
        <!--collection 表示一对多的关系-->
        <collection property="主表pojo中从表pojo的集合属性" javaType="list" ofType="从表pojo类型" autoMapping="true">
           <!--内部的映射都是针对从表pojo类型的属性映射 -->
            <id column="从表主键名称" property="从表pojo属性名称"/>
            <result column="xx" property="xx"/>
        </collection>
    </resultMap>

3.1.5. 5、多对多(扩展1)

多对多查询本质是一对多和一对一查询的组合,核心标签用法如下:

    <!--多对多映射-->
    <resultMap id="xx" type="xx" autoMapping="true">
        <!--主表主键映射-->
        <id column="xx" property="xx"/>
        <collection property="xx" javaType="list" ofType="xx" autoMapping="true" >
            <!--主表主键映射-->
            <id column="xx" property="xx"/>
            <!-- 一对一映射 -->
            <association property="xx" javaType="xx" autoMapping="true">
                <!--主表主键映射-->
                <id column="xx" property="xx"/>
            </association>
        </collection>
    </resultMap>

【需求】:查询订单号为20140921001的订单的详情信息即查询订单信息+订单中的商品信息;

【步骤】

第一步:需求分析;
第二步:添加关联关系;
第三步:编写SQL;
第四步:配置关联关系;
第五步:运行;
【需求分析】
1、查询订单详情信息即:查询订单信息+订单中的商品信息;
2、订单信息在tb_order中,订单中的商品信息在tb_item中,这两个表是通过中间表 tb_orderdetail进行关联的。
3、关联查询思路:先查询订单表,通过订单表中的id关联中间表order_id,然后查询中间表,根据中间表的item_id关联商品表的id,最后查询商品表;
第一步:分析2种sql查询的实现思路
select
  tor.id           as order_id,
  tor.order_number,
  tord.id          as detail_id,
  tord.status,
  tord.total_price,
  ti.id            as item_id,
  ti.item_detail,
  ti.item_name,
  ti.item_price
from tb_order as tor,
  tb_orderdetail as tord,
  tb_item ti
where tor.id = tord.order_id
    and ti.id = tord.item_id
    and tor.order_number = '20140921001';
第二步:添加类属性关联关系

​ 一个订单表中关联了多个订单详情信息,所以在订单表中添加List<Orderdetail>属性;

​ 一条订单详情记录中都包含了一条商品信息,所以需要在Orderdetail中添加一个Item属性;

第三步:编写接口方法

​ 在OrderMapper接口中新增,根据orderNumber查询订单及订单详情的方法;

    Order findByOrderNumber2(@Param("orderNumber") String orderNumber);
第四步:编写SQL映射文件

​ 说明:一定要记住这里给order表的id起别名是order_id,订单详情表的id起别名是detail_id,商品表item的id起别名是item_Id。在resultMap标签的id子标签中的column属性值书写对应的order_id、detail_id和item_Id.

    <resultMap id="orderMap2" type="order" autoMapping="true">
        <id column="order_id" property="id"/>
        <result column="order_number" property="orderNumber"/>
        <collection property="orderdetails" javaType="list" ofType="Orderdetail" autoMapping="true">
            <id column="detail_id" property="id"/>
            <result column="total_price" property="totalPrice"/>
            <association property="item" javaType="item" autoMapping="true">
                <id column="item_id" property="id"/>
                <result column="item_detail" property="itemDetail"/>
                <result column="item_name" property="itemName"/>
                <result column="item_price" property="itemPrice"/>
            </association>
        </collection>
    </resultMap>
    <select id="findByOrderNumber2" resultMap="orderMap2">
        select
          tor.id  as order_id,
          tor.order_number,
          tord.id  as detail_id,
          tord.status,
          tord.total_price,
          ti.id  as item_id,
          ti.item_detail,
          ti.item_name,
          ti.item_price
        from tb_order as tor,
          tb_orderdetail as tord,
          tb_item ti
        where tor.id = tord.order_id
            and ti.id = tord.item_id
            and tor.order_number = #{orderNumber}
    </select>
第五步:测试
    @Test
    public void test4(){
        Order order = orderDao.findByOrderNumber2("20140921001");
        System.out.println(order);
    }

【扩展2】

【需求】根据订单号(20140921001)

查询订单信息

查询订单所属用户信息

查询订单中的详细商品信息

1)接口方法

 Order findByOrderNumber3(@Param("orderNumber") String orderNumber);

2)映射文件

    <resultMap id="orderMap3" type="order" autoMapping="true">
        <id column="order_id" property="id"/>
        <result column="order_number" property="orderNumber"/>
        <association property="tuser" javaType="user" autoMapping="true">
            <id column="id" property="id"/>
            <result column="user_name" property="userName"/>
        </association>
        <collection property="orderdetails" javaType="list" ofType="Orderdetail" autoMapping="true">
            <id column="detail_id" property="id"/>
            <result column="total_price" property="totalPrice"/>
            <association property="item" javaType="item" autoMapping="true">
                <id column="item_id" property="id"/>
                <result column="item_detail" property="itemDetail"/>
                <result column="item_name" property="itemName"/>
                <result column="item_price" property="itemPrice"/>
            </association>
        </collection>
    </resultMap>
    <select id="findByOrderNumber3" resultMap="orderMap3">
        select
          tus.*,
          tor.id as order_id,
          tor.order_number,
          tord.id as detail_id,
          tord.status,
          tord.total_price,
          ti.id as item_id,
          ti.item_detail,
          ti.item_name,
          ti.item_price
        from tb_order as tor,
          tb_orderdetail as tord,
          tb_item ti,
          tb_user as tus
        where tus.id = tor.user_id
            and tor.id = tord.order_id
            and ti.id = tord.item_id
            and tor.order_number = #{orderNumber}
    </select>

3)测试

    @Test
    public void test5(){
        Order order = orderDao.findByOrderNumber3("20140921001");
        System.out.println(order);
    }

3.1.6. 6、ResultMap继承extend

​ 如果两个ResultMap结果集有重叠的部分,可以通过extend属性继承简化;

继承后简化映射文件配置:

    <resultMap id="orderMap3" type="order" autoMapping="true" extends="orderMap2">
        <association property="tuser" javaType="user" autoMapping="true">
            <id column="id" property="id"/>
            <result column="user_name" property="userName"/>
        </association>
    </resultMap>

3.1.7. 7、高级查询小结

前提:查询的字段必须唯一!

一对一
<resultMap id=xxx type="主表" automapping=true
     <id column="xx" property="xx"/>
    <assocaition property="从表中pojo属性名称" javaType="pojo类型" autoxxx>
       <id column="xx" property="xx"/>
       <result colun='xxx' property="xx"/>
        .....
    </asssociation>
</result>
一对多
<resultMap id=xxx type="主表" automapping=true
     <id column="xx" property="xx"/>
    <collection property="从表中pojo属性名称" javaType="list" ofType="集合的泛型" autoxxx>
       <id column="xx" property="xx"/>
       <result colun='xxx' property="xx"/>
        .....
    </collection>
</result>
多对多
<resultMap id=xxx type="主表" automapping=true
     <id column="xx" property="xx"/>
    <collection property="从表中pojo属性名称" javaType="list" ofType="集合的泛型" autoxxx>
       <id column="xx" property="xx"/>
       <result colun='xxx' property="xx"/>
        .....
       <assocaition property="从表中pojo属性名称" javaType="pojo类型" autoxxx>
           <id column="xx" property="xx"/>
           <result colun='xxx' property="xx"/>
            .....
        </asssociation>

    </collection>
</result>

重点:

1)一对一映射配置

2)一对多映射配置

3)动态sql标签

4)#{}和${}区别(面试)

4. 第四章 mybatis延迟加载【了解】

4.1.1. 1、延迟加载概述

  1. 应用场景

    ​ 如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。

  2. 延迟加载的好处

    先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

  3. 延迟加载的条件:

    1)resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

    2)延迟加载是针对分步查询而言的

4.1.2. 2、开启延迟加载

Mybatis的延迟加载功能默认是关闭的

需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能

需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能开启延迟加载的属性:

lazyLoadingEnabled:全局性设置懒加载。默认为false,true表示开启延迟加载
aggressiveLazyLoading:false表示关闭积极加载
说明:这两个属性必须一起设置

【示例】

<settings>
     <!--开启延迟加载-->
     <setting name="lazyLoadingEnabled" value="true"/>
     <!--关闭积极加载-->
     <setting name="aggressiveLazyLoading" value="false"/>
 </settings>

4.1.3. 3、延迟加载测试

延迟加载需求:通过订单编号20140921003查询order并延迟加载user。就是演示上述演示过的一对一的表关系案例。

分析:

如果改成延迟加载,也就意味着,先查询order,等需要的时候再去查询user,那就相当于将上面的一条语句变成了两条语句:

1、通过订单编号查询order

2、通过查询出来的order中的user_id查询user

SQL语句:

分步查询:
    #第一步:根据order_number查询订单信息;
    SELECT * FROM tb_order WHERE order_number = '20140921003';
    #第二步:根据订单信息中的user_id查询出下单人的信息;
    SELECT * FROM tb_user WHERE id = 1;

第一步:编写接口方法及映射文件

1.在OrderMapper接口中新建:queryOrderUserLazy方法;

    Order queryOrderUserLazy(String orderNumber);

2.编写映射文件,分部查询,通过association标签下的column和select属性获取参数并传递到子查询中;

    <!--引入在查询order信息的同事需要将用户的信息一并封装到order实体下,需要进行高级映射-->
    <resultMap id="orderUserLazyMap" type="order" autoMapping="true">
        <!--主键映射-->
        <id column="order_id" property="id"/>
         <!--普通字段映射 order_number:orderNumber, 因为settting中设置了驼峰规则,所以可省略-->
        <result column="order_number" property="orderNumber"/>
        <!--用于映射user对象,关系:一对一,可使用association实现映射-->
        <association property="user" javaType="user"  column="user_id" select="com.heima.dao.UserMapper.queryById">
        </association>
    </resultMap>

    <select id="queryOrderUserLazy" resultMap="orderUserLazyMap">
           select * from tb_order where order_number=#{orderNumber}
    </select>

第二步:开启懒加载

在mybatis-config.xml全局配置文件中,开启懒加载;

<settings>
     <!--开启延迟加载-->
     <setting name="lazyLoadingEnabled" value="true"/>
     <!--关闭积极加载-->
     <setting name="aggressiveLazyLoading" value="false"/>
 </settings>

第三步:测试

    @Test
    public void queryOrderUserLazy(){
        Order order = orderMapper.queryOrderUserLazy("20140921001");
        System.out.println("此时没有进行用户信息查询");
        System.out.println(order.getOrderNumber());
        //当我们使用user对象时,才从数据库中加载
        User user = order.getUser();
        System.out.println(user.toString());
    }

小结:

延迟加载的利与弊 好处:先从单表进行查询数据,需要时再从关联表去关联查询,将会提高数据库性能,因为查询单表要比关联查询多张表速度快。

坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

results matching ""

    No results matching ""