02-2-高级查询

第一章 动态SQLTODO

1.动态标签介绍

  1. MyBatis 的强大特性之一便是**它的动态 SQL**。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。<br />场景:<br />【需求】:查询**男性**用户,**如果输入了用户名,按用户名模糊查询**,如果**没有输入用户名,就查询所有男性用户**。<br />![](imgs/2-%E5%8A%A8%E6%80%81SQL%E5%9C%BA%E6%99%AF.png#)<br /> 在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 开始精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的 OGNL 的表达式来淘汰其它大部分元素。
  1. 了解:
  2. OGNL Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。

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

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

常见标签如下:

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

2.if标签

语法格式:

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

【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用
要求:
1.使用判断
2.test属性:使用OGNL表达式,完成具体的判断业务逻辑;
1)定义接口

  1. /**
  2. * 【需求】:查询男性用户,如果输入了用户名,
  3. * 按用户名模糊查询,如果没有输入用户名,就查询所有男性用
  4. */
  5. List<User> findUsersByUserName(@Param("userName") String userName);

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

  1. <select id="findUsersByUserName" resultMap="userMap">
  2. select * from user where sex='男'
  3. <if test="userName!=null">
  4. and user_name like concat('%',#{userName},'%')
  5. </if>
  6. </select>

3)测试方法

  1. @Test
  2. public void test22(){
  3. UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
  4. //select * from user where sex='男'
  5. //List<User> users = mapper.findUsersByUserName(null);
  6. //select * from user where sex='男' and concat('%',?,'%');
  7. List<User> users = mapper.findUsersByUserName("唐僧");
  8. System.out.println(users);
  9. MybatisUtil.close();
  10. }

4)效果:
02-2-高级查询 - 图1
小结:
if标签使用方式?

  1. <!--如果条件满足,则if标签包裹的sql会参与拼接,否则忽略-->
  2. <if test="条件表达式">
  3. sql代码
  4. </if>

3.choose,when,otherwise

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

语句示例:

  1. <select id="findActiveBlogLike"
  2. resultType="Blog">
  3. SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  4. <choose>
  5. <when test="title != null">
  6. AND title like #{title}
  7. </when>
  8. <when test="author != null and author.name != null">
  9. AND author_name like #{author.name}
  10. </when>
  11. <otherwise>
  12. AND featured = 1
  13. </otherwise>
  14. </choose>
  15. </select>

需求:

  1. 编写一个查询方法,设置两个参数,一个是用户名,一个是住址。
  2. 根据用户名或者住址查询所有男性用户:
  3. 如果输入了用户名则按照用户名模糊查找,
  4. 否则就按照住址查找,两个条件只能成立一个,
  5. 如果都不输入就查找用户名为“孙悟空”的用户。username address

【需求分析】

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

1)定义接口方法

  1. List<User> findUsersByNameAndAddress(@Param("name") String userName,@Param("address") String address);

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

  1. <select id="findUsersByNameAndAddress" resultMap="userMap">
  2. select * from user where sex='男'
  3. <choose>
  4. <when test="name!=null">
  5. and user_name like concat('%',#{name},'%')
  6. </when>
  7. <when test="address!=null">
  8. and address=#{address}
  9. </when>
  10. <otherwise>
  11. and user_name='孙悟空'
  12. </otherwise>
  13. </choose>
  14. </select>

3)测试

  1. @Test
  2. public void test23(){
  3. UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
  4. //select * from user where sex='男' and user_name like concat('%',?,'%')
  5. //List<User> users = mapper.findUsersByNameAndAddress("唐僧", "花果山水帘洞");
  6. //select * from user where sex='男' and address=?
  7. //List<User> users = mapper.findUsersByNameAndAddress(null, "花果山水帘洞");
  8. //select * from user where sex='男' and user_name='孙悟空'
  9. List<User> users = mapper.findUsersByNameAndAddress(null, null);
  10. System.out.println(users);
  11. MybatisUtil.close();
  12. }

小结:
多选一使用方式?

  1. 规则:多选一,自上而下执行,遇到满足条件就退出,否则otherwise下的语句参与拼接
  2. <choose>
  3. <when test="条件">
  4. sql代码
  5. </when>
  6. <when test="条件">
  7. sql代码
  8. </when>
  9. ....
  10. <otherwise>
  11. sql代码
  12. </otherwise>
  13. </choose>

4.where

where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字
需求:按照如下条件查询所有用户

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

where多条件语法格式:

  1. <select/update/delete ...>
  2. sql语句
  3. <where>
  4. <if test="条件">
  5. 字段 = #{值}
  6. </if>
  7. <if test="条件">
  8. AND 字段名 = #{值}
  9. </if>
  10. </where>
  11. </select/update/delete/insert>

1)定义接口

  1. List<User> findByNameAndAddress(@Param("name") String name,@Param("address") String address);

2)定义xml

  1. <!--
  2. where标签作用:
  3. 1)适当添加where关键字
  4. 2)被where标签包裹的sql会自动去除多余的and或者or关键字
  5. -->
  6. <select id="findByNameAndAddress" resultMap="userMap">
  7. select * from user
  8. <where>
  9. <if test="name!=null">
  10. user_name like concat('%',#{name},'%')
  11. </if>
  12. <if test="address!=null">
  13. and address=#{address}
  14. </if>
  15. </where>
  16. </select>

3)测试

  1. @Test
  2. public void test24(){
  3. UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
  4. //select * from user where user_name like concat('%',?,'%')
  5. //List<User> users = mapper.findByNameAndAddress("唐僧", null);
  6. //select * from user where address=?
  7. //List<User> users = mapper.findByNameAndAddress(null, "长安");
  8. //select * from user
  9. //List<User> users = mapper.findByNameAndAddress(null, null);
  10. //select * from user user_name like concat('%',?,'%') and address=?
  11. List<User> users = mapper.findByNameAndAddress("唐僧", "花果山水帘洞");
  12. System.out.println(users);
  13. MybatisUtil.close();
  14. }

小结:
通过使用标签的功能有哪些作用?

  1. 作用:
  2. 1)适当添加where关键字
  3. 2)去除被where标签包裹的多余的and或者or关键字
  4. 语法格式:
  5. <where>
  6. <if test="条件1">
  7. and sql1
  8. </if>
  9. <if test="条件2">
  10. and sql2
  11. </if>
  12. </where>

5.set

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

  1. <update .......>
  2. update 表名
  3. <set>
  4. <if test='条件'>
  5. 字段名1=值1,
  6. </if>
  7. <if test='条件2'>
  8. 字段名2=值2,
  9. </if>
  10. ....
  11. </set>
  12. where 条件;
  13. </update>

案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
1)定义接口方法

  1. /**
  2. * 案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
  3. * @param user
  4. */
  5. void updateSelectiveUser(User user);

2)定义映射文件

  1. <update id="updateSelectiveUser">
  2. update user
  3. <set>
  4. <if test="username!=null">
  5. user_name=#{username},
  6. </if>
  7. <if test="birthday!=null">
  8. birthday=#{birthday},
  9. </if>
  10. <if test="sex!=null">
  11. sex=#{sex},
  12. </if>
  13. <if test="address!=null">
  14. address=#{address}
  15. </if>
  16. </set>
  17. where id=#{id}
  18. </update>

3)测试

  1. @Test
  2. public void test25(){
  3. UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
  4. User user = new User();
  5. user.setUsername("沙悟净");
  6. user.setId(2);
  7. user.setSex("男");
  8. //update user set user_name=?,sex=? where id=?
  9. mapper.updateSelectiveUser(user);
  10. MybatisUtil.commit();
  11. MybatisUtil.close();
  12. }

02-2-高级查询 - 图2
小结:
set标签的作用?

  1. 1)去除被set标签包裹的多余的逗号','
  2. 2)添加set关键字;

6.foreach

  1. foreach标签:遍历集合或者数组
  2. <foreach collection="集合名或者数组名" item="元素" separator="标签分隔符" open="以什么开始" close="以什么结束">
  3. #{元素}
  4. </foreach>
  5. collection属性:接收的集合或者数组,集合名或者数组名
  6. item属性:集合或者数组参数中的每一个元素
  7. separator属性:标签分隔符
  8. open属性:以什么开始
  9. close属性:以什么结束
  10. 举例:
  11. java接口方法:
  12. List<Post> selectPostIn(@Param("ids") List<Integer> ids);
  13. sql映射文件配置:
  14. <select id="selectPostIn" resultType="domain.blog.Post">
  15. SELECT * FROM POST P
  16. WHERE ID in
  17. <foreach collection="ids" item="item" open="(" separator="," close=")">
  18. #{item}
  19. </foreach>
  20. </select>

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

  1. List<User> findByIds(@Param("ids") List<Integer> ids);

2)定义xml映射

  1. <!--
  2. collection="ids":表示接口中传入的集合名称
  3. item="item":表示循环集合过程中的每一个元素
  4. separator=",":表示元素与元素拼接时的分隔符
  5. open="(" :表示拼接字符串时以指定字符开头
  6. close=")":表示拼接字符串时以指定字符结尾
  7. 拼接格式:
  8. (1,3,6,8)
  9. -->
  10. <select id="findByIds" resultMap="userMap">
  11. select * from user where id in
  12. <foreach collection="ids" item="item" separator="," open="(" close=")">
  13. #{item}
  14. </foreach>
  15. </select>

3)测试

  1. @Test
  2. public void test21(){
  3. UserMapper mapper = MybatisUtils.getMapper(UserMapper.class);
  4. //List<Integer> integers = Arrays.asList(1, 3, 5, 7, 9);
  5. //select * from user where id in(?,?,?,?,?)
  6. List<User> users = mapper.findByIds(Arrays.asList(1, 3, 5, 7));
  7. System.out.println(users);
  8. MybatisUtils.close();
  9. }

4)效果
02-2-高级查询 - 图3
小结:
foreach标签属性作用?

  1. 作用:
  2. 遍历从接口中出入的集合的;
  3. 语法格式:
  4. <foreach collection="接口中集合名称" item="集合中每一个元素" open="拼接以指定字符开头" close="拼接以指定字符结尾" separator="指定间隔符">
  5. #{集合中每一个元素}
  6. </forech>

6.动态sql总结

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

  1. 1)if标签
  2. <if test="条件">
  3. sql语句(如果满足条件,当前sql片段参与拼接)
  4. </if>
  5. 2)choose when otherwise多选一 自上而下执行,遇到满足条件则退出,否则otherwise小的sql参与拼接
  6. <choose>
  7. <when test="条件1">
  8. sql语句(如果满足条件,就参与sql拼接)
  9. </when>
  10. <when test="条件1">
  11. sql语句(如果满足条件,就参与sql拼接)
  12. </when>
  13. <otherwise>
  14. sql语句
  15. </otherwise>
  16. </choose>
  17. 3)where
  18. 作用:1)适当添加where关键字 2)去除多余and或者or关键字
  19. 4)set
  20. 作用:1)添加set关键字 2)去除多余的逗号
  21. 5)foreach标签:
  22. 作用:
  23. 遍历从接口中出入的集合的;
  24. 语法格式:
  25. <foreach collection="接口中集合名称" item="集合中每一个元素" open="拼接以指定字符开头" close="拼接以指定字符结尾" separator="指定间隔符">
  26. #{集合中每一个元素}
  27. </forech>

第二章 特殊字符处理

  1. 我们在编写Mapper映射文件时,有时候需要使用到一些诸如:`>`,`<`之类的特殊字符。这些字符不能直接书写在xml文件中,需要我们对其处理。<br /> 处理方式:使用转义字符代替特殊字符。
转义字符 sql符号 说明
< < 小于
> > 大于
& & 和号
' 单引号
" 双引号

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

  1. <select id="findUsersLt" resultMap="userMap">
  2. select * from user where id &lt; #{id}
  3. </select>

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

3.1 mybatis高级查询环境准备

表与表的关系:
一对一: ab两表的关系,由任意一张表维护(外键) 比如:b表维护ab的管理,那么在b表中创建一个字段aid,但是这个字段不添加外键约束;
一对多:ab两张表 比如:从a看是一个a对应b的多条数据,但是从b看是一个b只能对应一个a的数据;
多对多:ab两张表 比如:从a看是一个a对应b的多条数据,同时从b看是一个b对应a表的多条数据;
说明:通过用户,订单,商品,已经订单商品明细表练习多表关联查询操作;

【1】包结构

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

【2】导入SQL脚本

  1. 运行资料中的sql脚本:**mybatis.sql**<br />![](https://cdn.nlark.com/yuque/0/2021/png/280648/1640162302857-ab3b084c-fad7-4a2f-9c65-4a528739c123.png#)

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

02-2-高级查询 - 图4

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

  1. 注:可以根据id查询用户信息为例搭建起工程;<br />1.定义接口
  1. public interface UserMapper {
  2. /**
  3. * 根据id查询用户信息
  4. * @param id
  5. * @return
  6. */
  7. User findByUserId(@Param("id") Long id);
  8. }

2.配置映射文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.heima.mapper.UserMapper">
  6. <select id="findByUserId" resultType="user">
  7. select * from tb_user where id=#{id}
  8. </select>
  9. </mapper>

3.单元测试

  1. public class TestAll {
  2. @Test
  3. public void test1(){
  4. UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
  5. User user = mapper.findByUserId(1l);
  6. System.out.println(user);
  7. MybatisUtil.close();
  8. }
  9. }

工程结构:
02-2-高级查询 - 图5


3.2 一对一查询

一对一映射语法格式:

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

需求:通过订单编号20140921003查询出订单信息,并查询出下单人信息
说明:一个订单只能对应一个用户信息;
3.1 需求分析
两种实现方式:

  1. -- 方式1:分步查询
  2. -- 1.1根据订单编号查询订单信息
  3. select * from tb_order where order_number='20140921003';-- user_id:1
  4. -- 1.2根据user_id=1查询下单人信息
  5. select * from tb_user where id=1;
  6. -- 方式2:关联查询
  7. select tb_order.id as order_id,tb_order.order_number,tb_user.*
  8. from tb_order,tb_user
  9. where
  10. tb_user.id=tb_order.user_id
  11. and tb_order.order_number='20140921003';

3.2 订单实体添加属性映射

  1. public class Order {
  2. private Integer id;
  3. private String orderNumber;
  4. private User orderUser;
  5. //getter setter toString
  6. }

3.3 添加order接口及方法

  1. public interface OrderMapper {
  2. /**
  3. * 根据订单编号查询订单信息,包含下单人信息
  4. * @param orderNumber
  5. * @return
  6. */
  7. Order findOrderByOrderNumber(@Param("orderNumber") String orderNumber);
  8. }

3.4 创建order映射文件,编写SQL
说明:配置关联对象一对一映射关系,语法格式:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.heima.mapper.OrderMapper">
  6. <!--自定义映射规则-->
  7. <resultMap id="orderMap" type="Order" autoMapping="true">
  8. <id column="order_id" property="id"/>
  9. <result column="order_number" property="orderNumber"/>
  10. <!--
  11. association:表示一对一映射
  12. property="orderUser":表示指定映射的属性名称
  13. javaType="User":指定映射属性的类型
  14. autoMapping="true":开启自动映射(查询字段名称与pojo类中属性名称一致,可以不写)
  15. -->
  16. <association property="orderUser" javaType="User" autoMapping="true">
  17. <id column="id" property="id"/>
  18. <result column="user_name" property="userName"/>
  19. </association>
  20. </resultMap>
  21. <select id="findOrderByOrderNumber" resultMap="orderMap">
  22. select
  23. tb_order.id as order_id,
  24. tb_order.order_number,
  25. tb_user.*
  26. from tb_order,
  27. tb_user
  28. where tb_user.id = tb_order.user_id
  29. and tb_order.order_number = #{orderNumber}
  30. </select>
  31. </mapper>

3.5 测试

  1. @Test
  2. public void test2(){
  3. OrderMapper mapper = MybatisUtil.getMapper(OrderMapper.class);
  4. Order order = mapper.findOrderByOrderNumber("20140921003");
  5. System.out.println(order);
  6. MybatisUtil.close();
  7. }

3.6 效果
02-2-高级查询 - 图6


一对一查询总结:
1.映射规则配置?
PPT演示:
02-2-高级查询 - 图7
2.一对一关联查询实现步骤?

  1. 1)维护pojo对象之间一对一的关系:
  2. eg:Order 包含属性:User类型的变量
  3. 2)配置一对一的映射关系
  4. <resultMap id="唯一标识" type="映射的类" autoMapping="true">
  5. <id column="主键字段" property="映射类中属性名称"/>
  6. <result column="非主键字段" property="映射类中属性名称"/>
  7. <!--一对一映射-->
  8. <association property="映射的类中属性名称" javaType="java类型" autoMapping="true">
  9. <id column="主键字段" property="java类型中属性名称"/>
  10. <result column="非主键字段" property="java类型中属性名称"/>
  11. </association>
  12. </resultMap>

3.3.一对多查询TODO

核心映射标签预览:

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

需求:查询id为1的用户及其订单信息
【分析】
一个用户可以有多个订单。
一个订单只能属于一个用户。
用户(1)——-订单(n)

  1. -- 方式1:分步查询
  2. -- 1.1 根据id查询用户信息
  3. select * from tb_user where id=1; -- id=1
  4. -- 1.2 根据用户id查询订单集合
  5. select * from tb_order where user_id=1;
  6. -- 方式1:一步查询
  7. select
  8. tb_user.*,
  9. tb_order.id as order_id,
  10. tb_order.order_number
  11. from tb_user,
  12. tb_order
  13. where tb_user.id = tb_order.user_id
  14. and tb_user.id = 1;

【步骤】

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

【实现】
1.User实体添加映射关系

  1. public class User implements Serializable{
  2. private List<Order> orders;
  3. private Long id;
  4. // 用户名
  5. private String userName;
  6. // 密码
  7. private String password;
  8. // 姓名
  9. private String name;
  10. // 年龄
  11. private Integer age;
  12. //0-女 1-男
  13. private Integer sex;
  14. // getter and setter and toString
  15. }

2.编写接口

  1. /**
  2. * 根据用户id查询用户信息,包含订单集合信息
  3. * @param id
  4. * @return
  5. */
  6. User findUserAndOrdersByUserId(@Param("id") Long id);

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

  1. <!--定义一对多的映射规则-->
  2. <resultMap id="userMap" type="user" autoMapping="true">
  3. <!--映射主表-->
  4. <id column="id" property="id"/>
  5. <result column="user_name" property="userName"/>
  6. <!--配置一对多映射-->
  7. <collection property="orders" javaType="list" ofType="order" autoMapping="true">
  8. <id column="order_id" property="id"/>
  9. <result column="order_number" property="orderNumber"/>
  10. </collection>
  11. </resultMap>
  12. <select id="findUserAndOrdersByUserId" resultMap="userMap">
  13. select
  14. tb_user.*,
  15. tb_order.id as order_id,
  16. tb_order.order_number
  17. from tb_user,
  18. tb_order
  19. where tb_user.id = tb_order.user_id
  20. and tb_user.id = #{id}
  21. </select>

4.测试:

  1. @Test
  2. public void test3(){
  3. UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
  4. User user = mapper.findUserAndOrdersByUserId(1l);
  5. System.out.println(user);
  6. MybatisUtil.close();
  7. }

5.效果
02-2-高级查询 - 图8


一对多总结:
1.映射规则配置?
PPT演示:
映射示意图:
02-2-高级查询 - 图9
小结:
1.在一对多的场景中,一般主表中通过创建一个集合属性来包含从表的数据

  1. 如:user类包含List\<Order> orders);

2.如何使用collection标签建立对集合的关联映射?

  1. <!--一对多映射-->
  2. <resultMap id="唯一标识" type="映射的类" autoMapping="true">
  3. <id column="主键字段" property="映射类中属性名称"/>
  4. <result column="非主键字段" property="映射类中属性名称"/>
  5. <!--一对一映射-->
  6. <collection property="映射的类中属性名称" javaType="list" ofType="集合泛型" autoMapping="true">
  7. <id column="主键字段" property="java类型中属性名称"/>
  8. <result column="非主键字段" property="java类型中属性名称"/>
  9. </collection>
  10. </resultMap>

3.4 多对多查询

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

  1. <!--多对多映射-->
  2. <resultMap id="xx" type="xx" autoMapping="true">
  3. <!--主表主键映射-->
  4. <id column="xx" property="xx"/>
  5. <collection property="xx" javaType="list" ofType="xx" autoMapping="true" >
  6. <!--主表主键映射-->
  7. <id column="xx" property="xx"/>
  8. <!-- 一对一映射 -->
  9. <association property="xx" javaType="xx" autoMapping="true">
  10. <!--主表主键映射-->
  11. <id column="xx" property="xx"/>
  12. </association>
  13. </collection>
  14. </resultMap>

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

  1. 第一步:需求分析;
  2. 第二步:添加关联关系;
  3. 第三步:编写SQL
  4. 第四步:配置关联关系;
  5. 第五步:运行;

【需求分析】
  1. 1、查询订单详情信息即:查询订单信息+订单中的商品信息;
  2. 2、订单信息在tb_order中,订单中的商品信息在tb_item中,这两个表是通过中间表 tb_orderdetail进行关联的。
  3. 3、关联查询思路:先查询订单表,通过订单表中的id关联中间表order_id,然后查询中间表,根据中间表的item_id关联商品表的id,最后查询商品表;

第一步:分析sql查询的实现思路

第二步:添加类属性关联关系

1)一个订单表中关联了多个订单详情信息,所以在订单表中添加List<Orderdetail>属性;
2)一条订单详情记录中都包含了一条商品信息,所以需要在Orderdetail中添加一个Item属性;

第三步:编写接口方法
  1. OrderMapper接口中新增,根据orderNumber查询订单及订单详情的方法;

第四步:编写SQL映射文件

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

第五步:测试

3.5 多表查询扩展

【需求】根据订单号(20140921001)
查询订单信息
查询订单所属用户信息
查询订单中的详细商品信息
1)接口方法
2)映射文件
3)测试


3.6 ResultMap继承extend

  1. 如果两个ResultMap结果集有重叠的部分,可以通过extend属性继承简化;<br />继承后简化映射文件配置:
  1. <resultMap id="orderMap4" type="order" extends="orderMap2" autoMapping="true">
  2. <association property="user" javaType="user" autoMapping="true">
  3. <id column="user_id" property="id"/>
  4. <result column="user_name" property="userName"/>
  5. </association>
  6. </resultMap>

3.7 高级查询小结

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

  1. 一对一:
  2. 一对多:
  3. 多对多:

重点:
1)一对一映射配置
2)一对多映射配置
3)动态sql标签
4)#{}和${}区别(面试)


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

1、延迟加载概述

  1. 应用场景
    如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
  2. 延迟加载的好处
    先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。
  3. 延迟加载的条件:
    1)resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
    2)延迟加载是针对分步查询而言的

    2、开启延迟加载

    Mybatis的延迟加载功能默认是关闭的
    1. 需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能
    需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能开启延迟加载的属性:
    1. lazyLoadingEnabled:全局性设置懒加载。默认为falsetrue表示开启延迟加载
    2. aggressiveLazyLoadingfalse表示关闭积极加载
    3. 说明:这两个属性必须一起设置
    【示例】
    1. <settings>
    2. <!--开启延迟加载-->
    3. <setting name="lazyLoadingEnabled" value="true"/>
    4. <!--关闭积极加载-->
    5. <setting name="aggressiveLazyLoading" value="false"/>
    6. </settings>

    3、延迟加载测试

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

    分析:

    如果改成延迟加载,也就意味着,先查询order,等需要的时候再去查询user,那就相当于将上面的一条语句变成了两条语句:
    1、通过订单编号查询order
    2、通过查询出来的order中的user_id查询user
    SQL语句:
    1. 分步查询:
    2. #第一步:根据order_number查询订单信息;
    3. SELECT * FROM tb_order WHERE order_number = '20140921003';
    4. #第二步:根据订单信息中的user_id查询出下单人的信息;
    5. SELECT * FROM tb_user WHERE id = 1;

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

    1.在OrderMapper接口中新建:queryOrderUserLazy方法;
    1. Order queryOrderUserLazy(String orderNumber);
    2.编写映射文件,分部查询,通过association标签下的column和select属性获取参数并传递到子查询中;
    1. <!--引入在查询order信息的同事需要将用户的信息一并封装到order实体下,需要进行高级映射-->
    2. <resultMap id="orderUserLazyMap" type="order" autoMapping="true">
    3. <!--主键映射-->
    4. <id column="order_id" property="id"/>
    5. <!--普通字段映射 order_number:orderNumber, 因为settting中设置了驼峰规则,所以可省略-->
    6. <result column="order_number" property="orderNumber"/>
    7. <!--用于映射user对象,关系:一对一,可使用association实现映射-->
    8. <association property="user" javaType="user" column="user_id" select="com.heima.dao.UserMapper.queryById">
    9. </association>
    10. </resultMap>
    11. <select id="queryOrderUserLazy" resultMap="orderUserLazyMap">
    12. select * from tb_order where order_number=#{orderNumber}
    13. </select>

    第二步:开启懒加载

    在mybatis-config.xml全局配置文件中,开启懒加载;
    1. <settings>
    2. <!--开启延迟加载-->
    3. <setting name="lazyLoadingEnabled" value="true"/>
    4. <!--关闭积极加载-->
    5. <setting name="aggressiveLazyLoading" value="false"/>
    6. </settings>

    第三步:测试

    1. @Test
    2. public void queryOrderUserLazy(){
    3. Order order = orderMapper.queryOrderUserLazy("20140921001");
    4. System.out.println("此时没有进行用户信息查询");
    5. System.out.println(order.getOrderNumber());
    6. //当我们使用user对象时,才从数据库中加载
    7. User user = order.getUser();
    8. System.out.println(user.toString());
    9. }
    小结:
    延迟加载的利与弊
    好处:先从单表进行查询数据,需要时再从关联表去关联查询,将会提高数据库性能,因为查询单表要比关联查询多张表速度快。
    坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。