Mapper.xml--映射文件详解

1 概述

  • MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。因为这个配置文件,几乎省略掉了90%的JDBC代码。
  • 我们重点学习以下部分:
    • CRUD操作:insert\update\delete\select
    • sql片段:sql
    • 结果集映射:resultMap

2 select

  • 进行查询的statement,
    在这里插入图片描述
  • 常用的几个属性:
    • select元素:代表查询,类似的还有update、insert、delete
    • id:这个statement的唯一标示
    • parameterType: 输入参数类型,可选参数,mybatis可以自动推断数据类型
    • resultType:查询结果类型,如果结果是集合,请写集合内元素类型!
    • resultMap:结果集映射,这个和resultType 只能存在1个,应对复杂的结果集。后面详细讲!
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
resultType从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。

3 insert update delete

  • insert update delete基本类似
    在这里插入图片描述
  • 比较常用的几个属性:
    • id:这个statement的唯一标识
    • parameterType:输入参数类型,可选参数,mybatis可以自动推断数据类型
 <!--添加用户-->
    <insert id="insertUser" parameterType="User">
        insert into tb_user
        (
            id,
            user_name,
            password,
            name,
            age,
            sex,
            birthday,
            created,
            updated
         )
        values (
            null,
            #{userName},
            #{password},
            #{name},
            #{age},
            #{sex},
            #{birthday},
            now(),
            now()
        )
    </insert>

    <!--修改用户-->
    <update id="updateUser" parameterType="User">
        update tb_user
            set
                user_name = #{userName},
                password = #{password},
                name = #{name},
                age = #{age},
                sex = #{sex},
                birthday = #{birthday},
                updated = now()
        where id = #{id}
    </update>

    <!--删除用户-->
    <delete id="deleteById" parameterType="java.lang.Long">
        delete from tb_user
        where id = #{id}
    </delete>

3.1 insert语句实现ID回填

需求: 记录这个用户 刚刚下单 买了哪些商品, 订单 和 商品 是多对多的关系

  • 1 在订单表插入一条语句, 同时 还需要 获取新增的订单编号
  • 2 在订单和商品的中间表插入记录: 需要 新增的订单编号 和 商品编号

3.1.1 方式一

<!--
  useGeneratedKeys : 是否开启自增主键的回显功能
  keyProperty: 对应javabean的属性名
  keyColumn : 对应数据库的列名
 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into tb_user (id,user_name,password,name,age,sex,birthday,created, updated )
values(null,#{userName},#{password},#{name},#{age},#{sex},#{birthday},now(),now() )
</insert>
    @Test
    public void inserUser() throws Exception {
        User user = new User();
        user.setuserName("zhuobotong2");
        user.setPassword("123456");
        user.setName("周伯通2");
        user.setAge(38);
        user.setSex(1);
        Date birthday = new SimpleDateFormat("yyyy-MM-dd").parse("2000-9-9");
        user.setBirthday(birthday);

        userMapper.insertUser(user);

        System.out.println(user.getId());
    }
2019-10-09 13:08:41,685 [main] [cn.hanjiaxiaozhi.mapper.UserMapper.insertUser]-[DEBUG] ==>  Preparing: insert into tb_user ( id, user_name, password, name, age, sex, birthday, created, updated ) values ( null, ?, ?, ?, ?, ?, ?, now(), now() ) 
2019-10-09 13:08:41,701 [main] [cn.hanjiaxiaozhi.mapper.UserMapper.insertUser]-[DEBUG] ==> Parameters: zhuobotong2(String), 123456(String), 周伯通2(String), 38(Integer), 1(Integer), 2000-09-09 00:00:00.0(Timestamp)
2019-10-09 13:08:41,701 [main] [cn.hanjiaxiaozhi.mapper.UserMapper.insertUser]-[DEBUG] <==    Updates: 1
16

3.1.2 方式二

<insert id="insertUser">
insert into tb_user (id,user_name,password,name,age,sex,birthday,created, updated )
values(null,#{userName},#{password},#{name},#{age},#{sex},#{birthday},now(),now() )

    <selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="Long">
        select last_insert_id()
    </selectKey>
</insert>

4 #{}的用法

  • 我们发现,在Mapper.xml映射文件中,经常使用#{属性名} 来作为SQL语句的占位符,来映射Sql需要的实际参数
    在这里插入图片描述
  • 也就是说:#{}就是一个预编译的占位符作用

4.1 如果方法中只有一个参数

  • 只有一个参数时,入参可以是以下情况:
  • 1)Java的基本类型、基本类型包装类、String类型等
    • 只有一个参数情况下,#{}中写什么都无所谓,MyBatis会把实际参数直接传入这个位置,不管参数名称和类型
  • 2)Java中的POJO,类似User这样的对象
    • 这种情况下,MyBatis会把User对象中的属性名和属性值以键值对形式保存到一个类似Map的结构中。
    • 因此,当我们用#{属性名} 来取值时,其实就相当于根据键查找值。可以直接获取到User的属性值!

在这里插入图片描述

  • 3)HashMap类型(很少用)
    • 如果参数是一个HashMap,mybatis无序做处理,已经是map结构了。我们可以直接用 #{键的名称} 来获取到对应的值。这种方式与上面的方式2类似,因此很少使用

总结

  • 简单数据类型:#{}不管写什么都可以获取值,建议用参数名称
  • 对象类型:#{}可以根据参数对象中的属性名获取属性值
  • Map类型:#{}可以根据Map的键来取到对应的值

4.2 如果接口中方法有多个参数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 控制台
    在这里插入图片描述
  • 分析:
  • 当有多个参数的情况下,MyBatis就会尝试把这多个参数放入一个Map中,这样才能方便我们通过#{}来取出这些值。
  • 那么问题来了:MyBatis封装这些参数为Map时,键应该是什么??
  • 有同学说:为什么直接用参数的名称做键呢?
  • 大家一起思考一下,MyBatis底层肯定是用反射来进行的这些操作,那么反射可以获取到方法,但是是否能获取的方法的参数名称呢?
  • 答案是不行,方法的参数名称是形参,是可变的。反射类Method中,并没有提供获取方法名称的功能。所以MyBatis并不知道我们传递的参数名称,只知道这些参数的值!
  • 再次回到上面的问题,只有值,那么MyBatis封装Map的时候,应该以什么做键呢?
  • 答案是这样的:默认情况下MyBatis会设置两种键:
    • A:以从0开始的递增数字作为键,第一个参数是0,第2个参数就是1,以此类推
    • B:以”param” + i 作为键,i是从1递增的数字,第一个参数键就是param1,第2个就是param2,以此类推
  • 所以我们可以通过#{arg0} 或 #{param1} 来取到第一个参数,以此类推
    在这里插入图片描述
    在这里插入图片描述

4.2.1 解决方案1

在这里插入图片描述

4.2.2 解决方案2

在这里插入图片描述

4.2.3 解决方案3

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  • 结论:如果有多个参数,请使用@Param注解来指定参数名称,代码可读性好!

5 ${}的用法

5.1 只有一个参数时

在这里插入图片描述
在这里插入图片描述

  • 如果是一个POJO或Map,处理方式与#{}一样。

5.2 如果是多个参数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 最后的结论:无论是1个参数,还是多个参数,都使用@Param注解,然后通过注解的中指定的名称取值

5.3 注意1

  • ${}在取值时,并不会进行预编译,而是直接拼接SQL语句:
    在这里插入图片描述
  • 这样无法防止SQL注入问题

5.4 注意2

  • ${}取值的时候,需要自己判断参数数据类型,如果是字符串,还得自己加引号:‘${}’
    在这里插入图片描述
  • 因此,一般在SQL语句中,如果要获取参数,一般我们都会使用#{},而不是${}"

6 面试题 #{} 和 ${}的区别

  • 区别:
    • #是占位符, 会对SQL进行预编译,相当于?; $是做sql拼接, 有SQL注入的隐患
      • #不需要关注数据类型, MyBatis自动实现数据类型转换; ${} 必须自己判断数据类型
  • 联系:
    • 两者都支持@param注解, 指定参数名称, 获取参数值. 推荐这种方式
      在这里插入图片描述
      在这里插入图片描述
  • 一般做参数传递,都会使用#{}
  • 如果不是做预编译,而是做拼接sql, 会使用${}, 例如表名称的变化

7 ResultMap的高级映射(重点重点重点)

7.1 概述

  • ResultMap用来定义SQL查询的结果与Java对象的映射关系。非常重要!
    在这里插入图片描述

7.2 解决属性名和列名不一致的问题

  • mybatis-config.xml: 关闭 驼峰标识
<settings>
    <!--是否开启驼峰标识-->
    <setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
  • UserMapper.xml
<resultMap id="userResultMap" type="User">
    <!--
        property: javabean对应的属性
        column : 数据库对应的列名
    -->
    <id property="id" column="id"></id>
    <result property="userName" column="user_name"/>
</resultMap>
<!--
    select元素:代表查询,类似的还有update、insert、delete
    id:这个statement的唯一标示
    parameterType:输入参数类型,可选参数,mybatis可以自动推断数据类型
    resultType:查询结果类型,如果结果是集合,请写集合内元素类型!
    resultMap:结果集映射,这个和resultType 只能存在1个,应对复杂的结果集。后面详细讲!
-->
<select id="queryUserById" resultMap="userResultMap">
    select * from tb_user where id=${id}
</select>
  • 这就是解决列名与字段名不一致问题的解决方案3,如果名称不符合驼峰规则,就必须使用这种方案。

7.2 autoMapping自动映射

  • 我们刚才的配置中,只配置了id和userName字段,其它字段没有配置,映射也没有问题,为什么呢?
  • 这是因为,在resultMap中,有一个属性叫做:autoMapping,如果值为true,并且列名称和字段名一致,是可以完成自动映射的。
  • 默认情况下,这个autoMapping的值就是为true的。
  • 那么,如果名称不一致,但是符合驼峰规则,能否自动映射呢?
  • 也是可以的,但是要开启驼峰匹配:
  <settings>
        <!--是否开启驼峰标识-->
        <setting name="mapUnderscoreToCamelCase" value="false"/>
    </settings>
  • 因此,一般我们配置resultMap,只要字段符合规则,我们只需要把ID配置出来就OK了。其它字段可以自动映射!
    在这里插入图片描述

8 sql片段

在这里插入图片描述

  • 我们经常会把SQL中比较通用的部分,提前出来,变成一个SQL片段,然后在各个SQL中都可以调用,简化书写:

  • 例如,查询语句中,一般不会使用:Select * ,而是把列名一一列出,但是表的列名往往比较多,这时就可以提取出来:

 <!--
        定义sql片段
            id: sql片段的唯一标识
    -->
    <sql id="userColumns">
        id, user_name, password, name, age, sex, birthday, created, updated
    </sql>

    <!--
        select元素:代表查询,类似的还有update、insert、delete
        id:这个statement的唯一标示
        parameterType:输入参数类型,可选参数,mybatis可以自动推断数据类型
        resultType:查询结果类型,如果结果是集合,请写集合内元素类型!
        resultMap:结果集映射,这个和resultType 只能存在1个,应对复杂的结果集。后面详细讲!
    -->
    <select id="queryUserById" resultMap="userResultMap">
        select <include refid="userColumns"/>
        from tb_user where id=${id}
    </select>

    <select id="queryUserList" resultType="User">
        select <include refid="userColumns"/>
        from tb_user
    </select>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值