Mybatis(二)映射文件配置与动态SQL

Mybatis(二)映射文件配置

1.Mybatis映射文件配置

1.入参

1.1.parameterType(了解)

CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。入参数据有以下几种类型:HashMap,基本数据类型(包装类),实体类

设置传入这条语句的参数类的完全限定名或别名。
这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数类型。

说明:

在mybatis中入参的数据类型分为2大类:

  1. 基本数据类型:int,string,long,Date等;

  2. 复杂数据类型:类(pojo)和Map;

​ 说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。

【示例】

public interface UserMapper {
    //根据id查询
    User findById(Integer id);
}
【基本类型数据】
<!--根据id查询-->
<!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名.MyBatis 可以通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。-->
<select id="findById" resultType="user" parameterType="int">
    select * from user where id = #{id}
</select>

【pojo类型】
<insert id="savetUser" parameterType="User"> 
  INSERT INTO user(...) values(#{userName},...);
</insert>

底层原理:
User类:user_name属性,自动生成setter或者getter方法时,getUserName
当我们向xml传入#{user_Name}---->getUser_Name--->利用反射根据方法名称获取方法对象--->报反射异常
综上:
1)传入的时pojo的话,传入xml的变量名称与pojo类下属性名称要一致;
2)pojo中属性名称定义要尽量遵循驼峰命名,或者在自动生成getter或者setter方法时,自己手动纠正;

说明:对于parameterType属性可以不书写,那么MyBatis 就会通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。


1.2.自增主键回填(了解)

需求:新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值。

方式1:使用insert标签的**子标签selectKey+last_insert_id()**函数实现实现

属性说明
keyColumn主键在表中对应的列名
keyProperty主键在实体类中对应的属性名
resultType主键的数据类型
orderBEFORE:会首先选择主键,设置 keyProperty 然后执行插入语句 AFTER: 在添加语句后执行查询主键的语句

测试代码:

1)接口

    /**
     * 添加用户
     * 返回值时影响的行数
     * @param user
     * @return
     */
    Integer addUserAndGetFkId(User user);

2)映射文件:

    <!--
       selectKey:表示查询主键字段的标签 keyColumn:表示表中字段名称,一般指主键名称
       keyProperty="id":表示pojo类中对应主键的属性名称
       order="AFTER":表示在操作之前或者之后获取主键值
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
    -->
    <insert id="addUserAndGetFkId">
        insert into user values(null,#{username},#{birthday},#{sex},#{address})
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
    </insert>

3)测试

    @Test
    public void test13(){
        UserMapper userMapper = MybatisUtil.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("唐僧2");
        user.setSex("男");
        Date date = Date.valueOf("2020-12-15");
        user.setBirthday(date);
        user.setAddress("长安");
        Integer count = userMapper.addUserAndGetFkId(user);
        System.out.println(user.getId());
        //mybatis默认事务手动提交
        MybatisUtil.commit();
        MybatisUtil.close();
    }

4)效果

在这里插入图片描述

方式2:使用insert标签的属性useGeneratedKeys,keyProperty,keyColumn实现

参数说明:

属性说明
useGeneratedKeystrue 获取自动生成的主键,相当于select last_insert_id()
keyColumn表中主键的列名
keyProperty实体类中主键的属性名

说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库,比如MySQL或SQL Server;

测试代码:

接口:

    /**
     * 添加用户
     * 返回值时影响的行数
     * @param user
     * @return
     */
    Integer addUserAndGetFkId2(User user);

映射文件:

    <!--
    useGeneratedKeys="true"表示开启获取主键id的功能
     keyColumn="id":表示指定表中主键字段名称
      keyProperty="id":表示指定pojo类中主键对应的属性名称
    -->
    <insert id="addUserAndGetFkId2" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into user values(null,#{username},#{birthday},#{sex},#{address})
    </insert>

测试:

    @Test
    public void test14(){
        UserMapper userMapper = MybatisUtil.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("唐僧3");
        user.setSex("男");
        Date date = Date.valueOf("2020-12-15");
        user.setBirthday(date);
        user.setAddress("长安");
        Integer count = userMapper.addUserAndGetFkId2(user);
        System.out.println(user.getId());
        //mybatis默认事务手动提交
        MybatisUtil.commit();
        MybatisUtil.close();
    }

效果:

在这里插入图片描述

说明:使用方式2的话,数据库必须支持主键自增;


1.3.Mybatis入参是单参和多参(掌握)

1)单个参数
单个参数:接口方法传入一个参数
【接口传参】
 User queryById(Integer id);
【接收参数】

1、通过#{参数名}接收

    <!--根据id查询-->
    <select id="queryById" resultType="User" parameterType="int">
        select *,user_name AS  userName from user where id = #{id}
    </select>

2、通过#{任意变量名}接收

    <!--根据id查询-->
    <select id="queryById" resultType="User" parameterType="int">
        select *,user_name AS  userName from user where id = #{abc}
    </select>
【结论】
如果接口传入的时单个参数,可以在xml中使用任意变量取接收,但是不建议乱写,最好见名知意;
2) 多个参数

需求:根据用户名和性别查询用户

2.1 接口传参

    /**
     * 需求:根据用户名和性别查询用户
     * @param name
     * @param sex
     * @return
     */
    List<User> findUsersByUserNameAndSex(String name,String sex);

2.2 UserMapper.xml

    <select id="findUsersByUserNameAndSex" resultType="user">
        select * from user where user_name=#{name} and sex=#{sex}
    </select>

2.3 测试类

    @Test
    public void test15(){
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        List<User> users = mapper.findUsersByUserNameAndSex("孙悟空", "男");
        System.out.println(users);
        MybatisUtil.close();
    }

2.3 结果

在这里插入图片描述

此时会报参数绑定异常

解决方案:

方式1、使用参数索引获取:arg0,arg1(了解,不推荐)

    <select id="findUsersByUserNameAndSex" resultType="user">
        select * from user where user_name=#{arg0} and sex=#{arg1}
    </select>

说明: 接口参数顺序要与arg0与arg1顺序一致;

方式2:使用参数位置获取:param1,param2(了解,不推荐)

    <select id="findUsersByUserNameAndSex" resultType="user">
        select * from user where user_name=#{param1} and sex=#{param2}
    </select>

方式3:使用命名参数获取,明确指定传入参数的名称:(掌握)

步骤一:在接口中传入参数时通过@Param指定参数名称

    /**
     * 需求:根据用户名和性别查询用户
     * @param name
     * @param sex
     * @return
     */
    List<User> findUsersByUserNameAndSex(@Param("name") String name,@Param("sex") String sex);

步骤二:在接收参数时,通过指定的名称获取参数值;

    <select id="findUsersByUserNameAndSex" resultType="user">
        select * from user where user_name=#{name} and sex=#{sex}
    </select>

测试:
在这里插入图片描述

小结:

1)入参是单个参数(单个参数是单值类型)
  在xml中可以任意变量取接收,但是建议名称见名知意;
2)多个参数
  建议使用方式3通过注解方式给参数取别名,然后再xml中使用这个别名
eg:
 List<User> findUsersByUserNameAndSex(@Param("name") String name,@Param("sex") String sex);
 那么在xml中:
     <select id="findUsersByUserNameAndSex" resultType="user">
        select * from user where user_name=#{name} and sex=#{sex}
    </select>

1.4 入参类型是Pojo和Map

1)pojo参数【掌握】

说明:接口方法传入pojo类型的数据时,mybatis底层直接使用pojo封装数据。 sql语句中 #{username}取值==》到pojo中调用 getUsername(){}

测试代码:

接口:

    /**
     * 插入功能
     * @param user
     */
    void saveAndGetkey2(User user);

映射文件:

    <insert id="saveAndGetkey2" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into user values(null,#{username},#{age},#{birthday},#{sex},#{address})
    </insert>

测试:

    @Test
    public void testAdd3(){
        User user = new User();
        user.setAddress("北京");
        user.setUsername("于彪");
        user.setBirthday(new Date());
        mapper.saveAndGetkey2(user);
        //直接获取返回的主键id值
        System.out.println(user.getId());
    }

小结:

使用pojo的话,xml中参数一定名称要与pojo中的属性名称一致,否则报错(binding exception);

2)HashMap参数

需求:模拟用户登录,登录方法参数是Map集合,泛型都是String类型分别表示用户名和性别。

​ 注意事项:参数map中的key值是与SQL语句中 #{} 的取值名称一致。

代码实现:

2.1 接口:

    /**
     * 需求:模拟用户登录,登录方法参数是Map集合,泛型都是String类型分别表示用户名和性别。
     * @return
     */
    User loginByNameAndSex(Map map);

2.2 映射文件:

    <select id="loginByNameAndSex" resultType="user" parameterType="map">
         select * from user where user_name=#{name} and sex=#{sex}
    </select>

2.3 测试:

    @Test
    public void test16(){
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        HashMap<String, String> map = new HashMap<>();
        //map中的key要与xml映射文件下的参数名称要一致
        map.put("name","孙悟空");
        map.put("sex","男");
        User user = mapper.loginByNameAndSex(map);
        System.out.println(user);
        MybatisUtil.close();
    }

小结:

1)入参类型pojo注意事项?
	xml映射文件下的参数名称要与pojo类中属性名称一致;
2)入参是map注意事项?
    xml映射文件下的参数名称要与map中key的名称一致;

2.参数值的获取

2.1.#{}和${}两种获取参数方式TODO

​ 参数值的获取指的是statement获取接口方法中传入的参数。

​ 获取参数,有两种方式:#{}${}

​ 以根据id查询为例测试#{}和${}区别:

使用#{}接收参数:
   select * from user where id=? //预编译处理,防止sql注入
使用${}接收参数:
   select * from user where id=1 //参数值直接拼接到sql中,会有sql注入的风险
1).#{}取值

使用#{}的sql是进行预编译的,可以防止sql注入;

2).${} 取值

注意:${id} 获取id值时,必须使用命名参数取值@param:

​ 补充:如果是取单个值,也可使用${value}获取

1)映射文件

    <select id="findById2" resultType="user" parameterType="int">
        select * from user where id=${id}
    </select>

2)接口

    /**
     * 根据id查询用户信息 测试${}
     * @param id
     * @return
     */
    User findById2(@Param("id") Integer id);

3)测试

    @Test
    public void test12(){
        UserMapper mapper = MybatisUtils.getMapper(UserMapper.class);
        User user = mapper.findById2(1);
        System.out.println(user);
        MybatisUtils.close();
    }

小结:

面试:#{}和${}取值有什么区别?

#{}
sql进行预编译处理,防止sql注入;
${}
参数与sql直接拼接,有sql注入的风险;

2.2.${}应用场景和注意事项(了解)

1)${}取值的应用场景

​ 在一些特殊的应用场景中,需要对SQL语句部分(不是参数)进行拼接,这个时候就必须使用${}来进行拼接,不能使用#{}.例如:

1、企业开发中随着数据量的增大,往往会将数据表按照年份进行分表,如:2017_user,2018_user....,对这些表进行查询就需要动态把年份传入进来,而年份是表名的一部分,并不是参数,JDBC无法对其预编译,所以只能使用${}进行拼接:  
	SELECT * FROM ${year}_user;
	
2、根据表名查询数据总记录数:
		SELECT COUNT(*) FROM user
		SELECT COUNT(*) FROM order
		SELECT COUNT(*) FROM  ${tableName}
简言之:如果需要设置到SQL中的不是查询的条件,只能使用${}拼接;

示例:

需求:根据输入的表名统计指定表下的总记录数;

1)接口

    /**
     * 需求:根据输入的表名统计指定表下的总记录数;
     */
    Integer countByTableName(@Param("tableName") String tableName);

2)映射文件

    <select id="countByTableName" resultType="integer">
        select count(*) from ${tableName}
    </select>

3)测试类

    @Test
    public void test18(){
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        Integer count = mapper.countByTableName("user");
        System.out.println(count);
        MybatisUtil.close();
    }

4)效果:
在这里插入图片描述

小结:

${}使用方式及场景?

使用方式:
 1.接口中@param给参数取别名 2.在xml中使用注解的别名${注解别名}
场景:
 一切非条件查询的参数拼接可以使用${}
2)${}取值注意事项 (了解)

【 ${}获取单个值】

${} 获取单个值时,最好是通过命名参数的形式获取。如果不指定参数的,也可以使用${value}来获取传入的单个值;

传入参数:没有指定参数名称

User selectUserById(Integer id);

获取参数通过${value}获取

<select id="selectUserById" resultType="user">
       select * from user where id = ${value}
 </select>

【${}获取配置文件中的值】

有时候,我们如果非要使用$来接收参数,将login修改如下:

    <!--根据用户名和性别查询-->
    <select id="queryByUserNameAndSex" resultType="User">
        SELECT  * FROM  user WHERE  user_name = '${jdbc.user}' AND  sex = #{sex}
    </select>

说明:上述sql语句中:SELECT * FROM user WHERE user_name = ‘${username}’ AND sex = #{sex}

对于 ' u s e r n a m e ′ 加单引号是因为 {username}' 加单引号是因为 username加单引号是因为{}获取数据的方式直接将获取的数据拼接到字符串上,并不会加引号,如果获取的值是数值型,没有问题,但是如果是字符类型就会有问题,所以需要加上引号进行拼接。

使用${}注意事项:

1.使用${变量}方式获取变量值时,不要与全局的properties下定义的参数名称冲突,否则数据注入错误;
2.使用${变量}传入字符串类型时,需要自己维护字符串的上引号;

3.1 简单结果集映射TODO

​ 在使用原生的JDBC操作时,对于结果集ResultSet,需要手动处理。

​ mybatis框架提供了resultType和resultMap来对结果集进行封装。

注意:只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个

1.resultType
	从sql语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
1.1 返回值是基本类型

例如 int ,string ===>resultType=“书写对应的基本类型别名或者全名即可”

​ 测试:findNameById:

1)基本类型 int short double ... 别名: _基本类型名称
2)包装类 类 String ArrayList .... 别名:类名首字母小写
3)自定义类 扫包取别名 类首字母小写(大写也可)
1.2 返回值为一个pojo(User)对象时

​ 测试:findById:

1.3 返回值为一个List<User>时

​ 当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型:

​ 测试:findAllUsers:

3.2 Map类型结果映射

【1】返回一条数据,封装到map中

需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中

1)定义接口

    /**
     * 需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
     * @param id
     * @return
     */
    Map<String,Object> findMapById(@Param("id") Integer id);

2)定义映射文件配置

    <select id="findMapById" resultType="map">
        select id,user_name as userName,address from user where id=#{id}
    </select>

3)测试:

    @Test
    public void test19(){
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        Map<String, Object> map = mapper.findMapById(1);
        System.out.println(map);
        MybatisUtil.close();
    }

4)效果

在这里插入图片描述

【2】返回多条数据,封装到map中

​ 需求:查询数据表所有的数据封装到Map<Integer,User>集合中

​ 要求: Key值为一条记录的主键,Value值为pojo的对象.

说明:需要在接口的方法上使用注解@MapKey指定数据表中哪一列作为Map集合的key,否则mybatis不知道具体哪个列作为Map集合的key.

1)映射文件:

    <select id="findAllToMap" resultType="map">
        select id,user_name as name,birthday,sex,address from user
    </select>

2)接口:

/**
     * 获取所有用户,其中key为id值,value为user对象
     * @return
     */
@MapKey("id")
Map<Integer,User> findAllToMap();

3)测试:

    @Test
    public void test16(){
        UserMapper mapper = MybatisUtils.getMapper(UserMapper.class);
        Map map = mapper.findAllToMap();
        System.out.println(map);
        MybatisUtils.close();
    }

在这里插入图片描述

小结:

1)map接收单条记录
  map中的key就是查询的表的字段名称,如果使用as区别名,那么可以就是对应的别名的名称;
2)map接收多条记录
  1)需要指定作为key的字段,一般是主键字段 @MapKey("指定字段名称")
  2)指定每一条记录封装的对象类型;

5.resultMap映射(掌握)

1.正常开发中,数据库字段名称与Pojo类属性名称不一致时,一般通过驼峰映射或者As关键字取别名可以搞定,但是很多场景下,对于复杂的orm映射,上述的2种方式就不能适用了;

2.ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决复杂映射问题:

1. POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
2. 完成高级查询,比如说,一对一、一对多、多对多。
【需求】
使用resultMap完成结果集的封装(resultSet===》JavaBean)
【实现步骤】
手动配置实体类属性和表字段映射关系的步骤如下:
1、	配置自定义结果集<resultMap>
2、	配置id映射
3、	配置其他普通属性的映射

**步骤一:**将驼峰匹配注释掉
​ 一旦注释掉驼峰匹配,那么再通过findAll查询的结果中,用户名就无法封装了,此时我们可以尝试使用ResultMap来解决这个问题。

    <settings>
        <!--作用:表:user_name 类:userName/username 自动映射-->
        <setting name="mapUnderscoreToCamelCase" value="false"/>
    </settings>

**步骤二:**配置resultMap

resultMap标签的作用:自定义结果集,自行设置结果集的封装方式

id属性:resultMap标签的唯一标识,不能重复,一般是用来被引用的
type属性:结果集的封装类型
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射。

在映射文件中自定义结果集类型:

<!--type="user" 表示结果集的封装类型是user-->
<resultMap id="userResultMap" type="user" autoMapping="true">
   <!--配置主键映射关系-->
   <id column="id" property="id"></id>
   <!--配置用户名的映射关系  column 表示数据表列  property表示pojo的属性-->
   <result column="user_name" property="name"></result>   
</resultMap>

步骤三:修改查询语句的statement
在查询语句的select标签中通过resultMap属性可以引用自定义结果集作为数据的封装方式

    <!--自定义映射规则-->
    <!--
    autoMapping="true":表示如果字段名称与pojo属性名称一致,可以省略不写
    -->
    <resultMap id="userMap" type="user" autoMapping="true">
        <!--主键字段映射-->
        <id column="id" property="id"/>
        <!--非主键字段-->
        <result column="user_name" property="username"/>
    </resultMap>

    <select id="findByIdUseResutMap" resultMap="userMap">
        select * from user where id=#{id}
    </select>

定义接口:

    /**
     * 使用resultMap标签自定义映射规则
     * @param id
     * @return
     */
    User findByIdUseResutMap(@Param("id") Integer id);

测试代码:

    @Test
    public void test21(){
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        User user = mapper.findByIdUseResutMap(1);
        System.out.println(user);
        MybatisUtil.close();
    }

**注意:**测试完记得将驼峰命名的配置重新开启,因为其他的测试方法还要用。

在这里插入图片描述


resultMap小结

1.resultMap标签的作用?

1)提高了代码的复用性;
2)结果集映射的;(将查询的结果映射到pojo类下)

2.resultMap有哪些属性和子标签?

<resultMap id="唯一标识" type="映射的pojo类" autoMapping="true">
	<!--主键字段映射-->
    <id column="表中主键字段" property="pojo类中主键对应的属性名称"/>
    <!--普通字段映射-->
    <result column="表中非主键字段" property="pojo类中非主键对应的属性名称"/>
</resultMap>

6.SQL片段(了解)

6.1.引用当前文件中的SQL片段

​ sql标签可以定义一个sql片段,在需要使用该sql片段的地方,通过<include refid=“sql片段id”/>标签来使用。

【1】定义SQL片段
    <sql id="userCommonSql">
        id,user_name,birthday,sex,address
    </sql>

注意:SQL片段必须设置id属性;

【2】使用SQL片段

​ 在SQL语句中通过<include>标签引入SQL片段;

    <select id="findByIdUseResutMap" resultMap="userMap">
        select <include refid="userCommonSql"/> from user where id=#{id}
    </select>

6.2 引入独立文件中的SQL片段

​ 很多时候同一个sql片段,可能在多个映射文件中都有使用,如果每一个映射文件都编写一个相同的sql就比较麻烦,因此可以将通用的sql片段都定义在一个专门存放sql片段的映射文件中,然后由其他映射文件引用它即可。
如下,在src目录下新增CommonSQL.xml文件:

【1】新建SQL片段文件

​ 复制一份映射文件,将SQL片段写入即可

【CommonSQL.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="CommonSql">
    <sql id="userSql">
        id,user_name,birthday,sex,address
    </sql>

    <sql id="empSql">
        eid,ename,age
    </sql>

</mapper>
【2】在mybatis核心配置文件mybatis-config.xml引入SQL片段文件

​ 定义好sql片段的映射文件之后,接下来就该使用它了,首先应该把该映射文件引入到mybatis的全局配置文件中(mybatis-config.xml):

    <mappers>
        <mapper resource="CommonSql.xml"/>
        <package name="com.heima.mapper"/>
    </mappers>
【3】引用SQL片段

​ 最后在需要使用该sql片段的地方通过include标签的refId属性引用该sql片段:<include refId=”名称空间.sql片段的id” />
在UserMapper.xml的映射文件中,进一步改造根据用户名查询用户信息

    <select id="findUserById2" resultMap="userMap" parameterType="int">
        select <include refid="CommonSql.userSql"/> from user where id = ${id}
    </select>

# Mybatis(三)动态SQL # 第一章 动态SQLTODO

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:多选一
where标签
set标签
foreach:循环遍历标签

2.if标签

语法格式:

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

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

要求:

​ 1.使用<if>判断

​ 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();
    }

小结:

通过使用<where>标签的功能有哪些作用?

作用:
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>


7.批量删除操作

mapper.xml中的动态SQL:

<!--根据id批量删除用户-->
    <delete id="deleteByIds" >
        delete from user  where id in
        <foreach collection="ids" item="uid" separator="," open="(" close=")">
            #{uid}
        </foreach>
    </delete>

接口方法:

 /**
     * 根据id批量删除
     * @param ids
     * @return
     */
    Integer deleteByIds(@Param("ids") List<Integer>  ids);

单元测试方法:

 /**
     * 测试 批量删除
     */
    @Test
    public void deleteByIds(){
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        Integer counts = mapper.deleteByIds(Arrays.asList(7, 8));
        System.out.println(counts);
        session.close();
    }

8.批量添加操作

mapper.xml中的动态SQL:

<!--
   批量插入: insert into user values(null,#{username},#{birthday},...),(),();
   分析:
   传入多个用户对象,就有多少个(null,#{username},#{birthday},...)
   因此拼接过程中open开头字符和结尾字符没有,所以无需配置
   循环过程中通过u这个对象调用u.属性名称,或获取属性值
   -->
 <insert id="insertUsers">
  insert into user values
  <foreach collection="users" item="user" separator=",">
      (null,#{user.username},#{user.birthday},#{user.sex},#{user.address})
  </foreach>
 </insert>

接口方法:

  /**
     * 批量插入用户
     * @param users
     * @return
     */
    Integer insertUsers(@Param("users") List<User> users);

单元测试方法:

 /**
     * 批量插入用户
     */
   @Test
    public void insertUsers(){
       SqlSession session = MybatisUtils.getSession();
       UserMapper mapper = session.getMapper(UserMapper.class);
       User user = new User();
       user.setBirthday(Date.valueOf("2023-08-20"));
       user.setAddress("上海");
       user.setSex("男");
       user.setUsername("高圆圆");
       User user2 = new User();
       user2.setBirthday(Date.valueOf("2023-08-20"));
       user2.setAddress("上海");
       user2.setSex("男");
       user2.setUsername("刘亦菲");
       ArrayList<User> list = new ArrayList<>();
       list.add(user);
        list.add(user2);
       Integer count = mapper.insertUsers(list);
       System.out.println(count);
   }

9.动态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文件中,需要我们对其处理。

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

转义字符sql符号说明
&lt;<小于
&gt;>大于
&amp;&和号
&apos;单引号
&quot;"双引号

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

    <select id="findUsersLt" resultMap="userMap">
        select * from user where id &lt; #{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

在这里插入图片描述

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

在这里插入图片描述

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

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

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属性继承简化;

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

    <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. 应用场景

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

  2. 延迟加载的好处

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

  3. 延迟加载的条件:

    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方法;

    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());
    }

小结:

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值