02-2-高级查询
第一章 动态SQLTODO
1.动态标签介绍
MyBatis 的强大特性之一便是**它的动态 SQL**。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。<br />场景:<br />【需求】:查询**男性**用户,**如果输入了用户名,按用户名模糊查询**,如果**没有输入用户名,就查询所有男性用户**。<br /><br /> 在 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:多选一
where标签
set标签
foreach:循环遍历标签
2.if标签
语法格式:
<if test="判断条件">
满足条件sql加入拼接
</if>
说明:
1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容
2)test属性:用来编写表达式,支持ognl;
【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用
要求:
1.使用
2.test属性:使用OGNL表达式,完成具体的判断业务逻辑;
1)定义接口
/**
* 【需求】:查询男性用户,如果输入了用户名,
* 按用户名模糊查询,如果没有输入用户名,就查询所有男性用
*/
List<User> findUsersByUserName(@Param("userName") String userName);
2)定义接口方法对应的映射文件信息
<select id="findUsersByUserName" resultMap="userMap">
select * from user where sex='男'
<if test="userName!=null">
and user_name like concat('%',#{userName},'%')
</if>
</select>
3)测试方法
@Test
public void test22(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
//select * from user where sex='男'
//List<User> users = mapper.findUsersByUserName(null);
//select * from user where sex='男' and concat('%',?,'%');
List<User> users = mapper.findUsersByUserName("唐僧");
System.out.println(users);
MybatisUtil.close();
}
4)效果:
小结:
if标签使用方式?
<!--如果条件满足,则if标签包裹的sql会参与拼接,否则忽略-->
<if test="条件表达式">
sql代码
</if>
3.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>
需求:
编写一个查询方法,设置两个参数,一个是用户名,一个是住址。
根据用户名或者住址查询所有男性用户:
如果输入了用户名则按照用户名模糊查找,
否则就按照住址查找,两个条件只能成立一个,
如果都不输入就查找用户名为“孙悟空”的用户。username address
【需求分析】
-- 输入了用户名
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> findUsersByNameAndAddress(@Param("name") String userName,@Param("address") String address);
2)定义接口方法对应的映射文件信息
<select id="findUsersByNameAndAddress" resultMap="userMap">
select * from user where sex='男'
<choose>
<when test="name!=null">
and user_name like concat('%',#{name},'%')
</when>
<when test="address!=null">
and address=#{address}
</when>
<otherwise>
and user_name='孙悟空'
</otherwise>
</choose>
</select>
3)测试
@Test
public void test23(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
//select * from user where sex='男' and user_name like concat('%',?,'%')
//List<User> users = mapper.findUsersByNameAndAddress("唐僧", "花果山水帘洞");
//select * from user where sex='男' and address=?
//List<User> users = mapper.findUsersByNameAndAddress(null, "花果山水帘洞");
//select * from user where sex='男' and user_name='孙悟空'
List<User> users = mapper.findUsersByNameAndAddress(null, null);
System.out.println(users);
MybatisUtil.close();
}
小结:
多选一使用方式?
规则:多选一,自上而下执行,遇到满足条件就退出,否则otherwise下的语句参与拼接
<choose>
<when test="条件">
sql代码
</when>
<when test="条件">
sql代码
</when>
....
<otherwise>
sql代码
</otherwise>
</choose>
4.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> findByNameAndAddress(@Param("name") String name,@Param("address") String address);
2)定义xml
<!--
where标签作用:
1)适当添加where关键字
2)被where标签包裹的sql会自动去除多余的and或者or关键字
-->
<select id="findByNameAndAddress" resultMap="userMap">
select * from user
<where>
<if test="name!=null">
user_name like concat('%',#{name},'%')
</if>
<if test="address!=null">
and address=#{address}
</if>
</where>
</select>
3)测试
@Test
public void test24(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
//select * from user where user_name like concat('%',?,'%')
//List<User> users = mapper.findByNameAndAddress("唐僧", null);
//select * from user where address=?
//List<User> users = mapper.findByNameAndAddress(null, "长安");
//select * from user
//List<User> users = mapper.findByNameAndAddress(null, null);
//select * from user user_name like concat('%',?,'%') and address=?
List<User> users = mapper.findByNameAndAddress("唐僧", "花果山水帘洞");
System.out.println(users);
MybatisUtil.close();
}
小结:
通过使用
作用:
1)适当添加where关键字
2)去除被where标签包裹的多余的and或者or关键字
语法格式:
<where>
<if test="条件1">
and sql1
</if>
<if test="条件2">
and sql2
</if>
</where>
5.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)定义接口方法
/**
* 案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
* @param user
*/
void updateSelectiveUser(User user);
2)定义映射文件
<update id="updateSelectiveUser">
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 test25(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
User user = new User();
user.setUsername("沙悟净");
user.setId(2);
user.setSex("男");
//update user set user_name=?,sex=? where id=?
mapper.updateSelectiveUser(user);
MybatisUtil.commit();
MybatisUtil.close();
}
小结:
set标签的作用?
1)去除被set标签包裹的多余的逗号','
2)添加set关键字;
6.foreach
foreach标签:遍历集合或者数组
<foreach collection="集合名或者数组名" item="元素" separator="标签分隔符" open="以什么开始" close="以什么结束">
#{元素}
</foreach>
collection属性:接收的集合或者数组,集合名或者数组名
item属性:集合或者数组参数中的每一个元素
separator属性:标签分隔符
open属性:以什么开始
close属性:以什么结束
举例:
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> findByIds(@Param("ids") List<Integer> ids);
2)定义xml映射
<!--
collection="ids":表示接口中传入的集合名称
item="item":表示循环集合过程中的每一个元素
separator=",":表示元素与元素拼接时的分隔符
open="(" :表示拼接字符串时以指定字符开头
close=")":表示拼接字符串时以指定字符结尾
拼接格式:
(1,3,6,8)
-->
<select id="findByIds" resultMap="userMap">
select * from user where id in
<foreach collection="ids" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</select>
3)测试
@Test
public void test21(){
UserMapper mapper = MybatisUtils.getMapper(UserMapper.class);
//List<Integer> integers = Arrays.asList(1, 3, 5, 7, 9);
//select * from user where id in(?,?,?,?,?)
List<User> users = mapper.findByIds(Arrays.asList(1, 3, 5, 7));
System.out.println(users);
MybatisUtils.close();
}
4)效果
小结:
foreach标签属性作用?
作用:
遍历从接口中出入的集合的;
语法格式:
<foreach collection="接口中集合名称" item="集合中每一个元素" open="拼接以指定字符开头" close="拼接以指定字符结尾" separator="指定间隔符">
#{集合中每一个元素}
</forech>
6.动态sql总结
核心的动态sql标签有哪些,各有什么作用?
1)if标签
<if test="条件">
sql语句(如果满足条件,当前sql片段参与拼接)
</if>
2)choose when otherwise多选一 自上而下执行,遇到满足条件则退出,否则otherwise小的sql参与拼接
<choose>
<when test="条件1">
sql语句(如果满足条件,就参与sql拼接)
</when>
<when test="条件1">
sql语句(如果满足条件,就参与sql拼接)
</when>
<otherwise>
sql语句
</otherwise>
</choose>
3)where
作用:1)适当添加where关键字 2)去除多余and或者or关键字
4)set
作用:1)添加set关键字 2)去除多余的逗号
5)foreach标签:
作用:
遍历从接口中出入的集合的;
语法格式:
<foreach collection="接口中集合名称" item="集合中每一个元素" open="拼接以指定字符开头" close="拼接以指定字符结尾" separator="指定间隔符">
#{集合中每一个元素}
</forech>
第二章 特殊字符处理
我们在编写Mapper映射文件时,有时候需要使用到一些诸如:`>`,`<`之类的特殊字符。这些字符不能直接书写在xml文件中,需要我们对其处理。<br /> 处理方式:使用转义字符代替特殊字符。
转义字符 | sql符号 | 说明 |
---|---|---|
< | < | 小于 |
> | > | 大于 |
& | & | 和号 |
' | ‘ | 单引号 |
" | “ | 双引号 |
举例:批量将id小于3的用户性别改为男,在映射文件中直接写<号,xml约束会报错!
<select id="findUsersLt" resultMap="userMap">
select * from user where id < #{id}
</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脚本
运行资料中的sql脚本:**mybatis.sql**<br />
【3】创建实体来包,导入资料中的pojo
【5】配置UserMapper.xml映射文件和接口
注:可以根据id查询用户信息为例搭建起工程;<br />1.定义接口
public interface UserMapper {
/**
* 根据id查询用户信息
* @param id
* @return
*/
User findByUserId(@Param("id") Long id);
}
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">
<mapper namespace="com.heima.mapper.UserMapper">
<select id="findByUserId" resultType="user">
select * from tb_user where id=#{id}
</select>
</mapper>
3.单元测试
public class TestAll {
@Test
public void test1(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
User user = mapper.findByUserId(1l);
System.out.println(user);
MybatisUtil.close();
}
}
工程结构:
3.2 一对一查询
一对一映射语法格式:
<resultMap id="映射ID" type="主表实体名称" autoMapping="true" >
<!-- 添加主表语主表实体映射 -->
......
<!--association:配置关联对象(User)的映射关系,一般与resultMap标签联合使用 -->
<association property="主表实体中对应从表的属性名称" javaType="从表实体类型" autoMapping="true">
<!-- 添加从表中字段与实体属性映射关系 -->
</association>
</resultMap>
需求:通过订单编号20140921003查询出订单信息,并查询出下单人信息。
说明:一个订单只能对应一个用户信息;
3.1 需求分析
两种实现方式:
-- 方式1:分步查询
-- 1.1根据订单编号查询订单信息
select * from tb_order where order_number='20140921003';-- user_id:1
-- 1.2根据user_id=1查询下单人信息
select * from tb_user where id=1;
-- 方式2:关联查询
select tb_order.id as order_id,tb_order.order_number,tb_user.*
from tb_order,tb_user
where
tb_user.id=tb_order.user_id
and tb_order.order_number='20140921003';
3.2 订单实体添加属性映射
public class Order {
private Integer id;
private String orderNumber;
private User orderUser;
//getter setter toString
}
3.3 添加order接口及方法
public interface OrderMapper {
/**
* 根据订单编号查询订单信息,包含下单人信息
* @param orderNumber
* @return
*/
Order findOrderByOrderNumber(@Param("orderNumber") String orderNumber);
}
3.4 创建order映射文件,编写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.heima.mapper.OrderMapper">
<!--自定义映射规则-->
<resultMap id="orderMap" type="Order" autoMapping="true">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
<!--
association:表示一对一映射
property="orderUser":表示指定映射的属性名称
javaType="User":指定映射属性的类型
autoMapping="true":开启自动映射(查询字段名称与pojo类中属性名称一致,可以不写)
-->
<association property="orderUser" javaType="User" autoMapping="true">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
</association>
</resultMap>
<select id="findOrderByOrderNumber" resultMap="orderMap">
select
tb_order.id as order_id,
tb_order.order_number,
tb_user.*
from tb_order,
tb_user
where tb_user.id = tb_order.user_id
and tb_order.order_number = #{orderNumber}
</select>
</mapper>
3.5 测试
@Test
public void test2(){
OrderMapper mapper = MybatisUtil.getMapper(OrderMapper.class);
Order order = mapper.findOrderByOrderNumber("20140921003");
System.out.println(order);
MybatisUtil.close();
}
3.6 效果
一对一查询总结:
1.映射规则配置?
PPT演示:
2.一对一关联查询实现步骤?
1)维护pojo对象之间一对一的关系:
eg:Order 包含属性:User类型的变量
2)配置一对一的映射关系
<resultMap id="唯一标识" type="映射的类" autoMapping="true">
<id column="主键字段" property="映射类中属性名称"/>
<result column="非主键字段" property="映射类中属性名称"/>
<!--一对一映射-->
<association property="映射的类中属性名称" javaType="java类型" autoMapping="true">
<id column="主键字段" property="java类型中属性名称"/>
<result column="非主键字段" property="java类型中属性名称"/>
</association>
</resultMap>
3.3.一对多查询TODO
核心映射标签预览:
<!--配置一对多关系映射-->
<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)
-- 方式1:分步查询
-- 1.1 根据id查询用户信息
select * from tb_user where id=1; -- id=1
-- 1.2 根据用户id查询订单集合
select * from tb_order where user_id=1;
-- 方式1:一步查询
select
tb_user.*,
tb_order.id as order_id,
tb_order.order_number
from tb_user,
tb_order
where tb_user.id = tb_order.user_id
and tb_user.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;
// 年龄
private Integer age;
//0-女 1-男
private Integer sex;
// getter and setter and toString
}
2.编写接口
/**
* 根据用户id查询用户信息,包含订单集合信息
* @param id
* @return
*/
User findUserAndOrdersByUserId(@Param("id") Long id);
3.编写sql映射文件关联订单集合
<!--定义一对多的映射规则-->
<resultMap id="userMap" type="user" autoMapping="true">
<!--映射主表-->
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<!--配置一对多映射-->
<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="userMap">
select
tb_user.*,
tb_order.id as order_id,
tb_order.order_number
from tb_user,
tb_order
where tb_user.id = tb_order.user_id
and tb_user.id = #{id}
</select>
4.测试:
@Test
public void test3(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
User user = mapper.findUserAndOrdersByUserId(1l);
System.out.println(user);
MybatisUtil.close();
}
5.效果
一对多总结:
1.映射规则配置?
PPT演示:
映射示意图:
小结:
1.在一对多的场景中,一般主表中通过创建一个集合属性来包含从表的数据
如:user类包含List\<Order> orders);
2.如何使用collection标签建立对集合的关联映射?
<!--一对多映射-->
<resultMap id="唯一标识" type="映射的类" autoMapping="true">
<id column="主键字段" property="映射类中属性名称"/>
<result column="非主键字段" property="映射类中属性名称"/>
<!--一对一映射-->
<collection property="映射的类中属性名称" javaType="list" ofType="集合泛型" autoMapping="true">
<id column="主键字段" property="java类型中属性名称"/>
<result column="非主键字段" property="java类型中属性名称"/>
</collection>
</resultMap>
3.4 多对多查询
多对多查询本质是一对多和一对一查询的组合,核心标签用法如下:
<!--多对多映射-->
<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,最后查询商品表;
第一步:分析sql查询的实现思路
第二步:添加类属性关联关系
1)一个订单表中关联了多个订单详情信息,所以在订单表中添加List<Orderdetail>
属性;
2)一条订单详情记录中都包含了一条商品信息,所以需要在Orderdetail中添加一个Item属性;
第三步:编写接口方法
在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
如果两个ResultMap结果集有重叠的部分,可以通过extend属性继承简化;<br />继承后简化映射文件配置:
<resultMap id="orderMap4" type="order" extends="orderMap2" autoMapping="true">
<association property="user" javaType="user" autoMapping="true">
<id column="user_id" property="id"/>
<result column="user_name" property="userName"/>
</association>
</resultMap>
3.7 高级查询小结
前提:查询的字段必须唯一!
一对一:
一对多:
多对多:
重点:
1)一对一映射配置
2)一对多映射配置
3)动态sql标签
4)#{}和${}区别(面试)
第四章 mybatis延迟加载【了解】
1、延迟加载概述
- 应用场景
如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。 - 延迟加载的好处
先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。 - 延迟加载的条件:
1)resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
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>
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方法;
2.编写映射文件,分部查询,通过association标签下的column和select属性获取参数并传递到子查询中;Order queryOrderUserLazy(String orderNumber);
<!--引入在查询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());
}
延迟加载的利与弊
好处:先从单表进行查询数据,需要时再从关联表去关联查询,将会提高数据库性能,因为查询单表要比关联查询多张表速度快。
坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。