Mybatis_Mapper

引入 Mapper

前面我们所写的增删改查是存在问题的。主要问题就是冗余代码过多,模板化代码过多。发现它有很多可以优化的地方。每个方法中都要获取 SqlSession,涉及到增删改的方法,还需要 commit,SqlSession 用完之后,还需要关闭,sqlSession 执行时需要的参数就是方法的参数,sqlSession 要执行的 SQL ,和 XML 中的定义是一一对应的。这是一个模板化程度很高的代码。

既然模板化程度很高,我们就要去解决它,原理很简单,就是前面 Spring 中所说的动态代理。我们可以将当前方法简化成 一个接口:

在这里插入图片描述

public interface UserMapper {

    List<User> getAllUser();

    User findUserById(Long id);

    Integer deleteUserById(Long id);

    Integer addUser(User user);

    Integer updateUserById(User user);
}

这个接口对应的 Mapper 文件如下(注意,UserMapper.xml 和 UserMapper 需要放在同一个包下面)

<?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.java.lang.mapper.UserMapper">

    <select id="getAllUser" resultType="com.java.lang.pojo.User"> /*resultType 为返回值类型*/
    select *
    from user;
    </select>

    <insert id="addUser" parameterType="com.java.lang.pojo.User">
        insert into user (username, address)
        values (#{userName}, #{address});
    </insert>

    <delete id="deleteUserById" parameterType="Long">
        delete
        from user
        where id = #{id};
    </delete>

    <update id="updateUserById" parameterType="com.java.lang.pojo.User">
        update user
        set username = #{userName}
        where id = #{id};
    </update>

    <select id="findUserById" parameterType="Long" resultType="com.java.lang.pojo.User">
        select *
        from user
        where id = #{id};
    </select>


    <!--当主键为UUID 时-->
    <!--    <insert id="addUser" parameterType="com.java.lang.pojo.User">
            <selectKey resultType="String" keyProperty="id" order="BEFORE">
                select uuid();
            </selectKey>
            insert into user
            (id,username,address)
            values (#{id},#{username},#{address})
        </insert>-->
</mapper>

    @Test
    public void selectUserById2() {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        // System.out.println(userMapper); 这个userMapper为Mybatis提供的代理对象
        User user = userMapper.findUserById(3L);
        System.out.println("user = " + user);
    }

Mapper 映射文件

1.parameterType
这个表示传入的参数类型。

  1. $#
    这是一个非常非常高频的面试题,虽然很简单。在面试中,如果涉及到 MyBatis,一般情况下,都是这个问题。

在 MyBatis 中,我们在 mapper 引用变量时,默认使用的是 #,像下面这样:

<select id="getUserById" resultType="org.javaboy.mybatis.model.User">
    select * from user where id=#{id};
</select>

除了使用 # 之外,我们也可以使用 $ 来引用一个变量:

<select id="getUserById" resultType="org.javaboy.mybatis.model.User">
    select * from user where id=${id};
</select>

在旧的 MyBatis 版本中,如果使用 KaTeX parse error: Expected 'EOF', got '#' at position 40: …MyBatis 中,无论是 `#̲` 还是 ``,如果只有一个参数,可以不用取别名,如下:

public interface UserMapper {
    User getUserById(Integer id);
}

既然 #$ 符号都可以使用,那么他们有什么区别呢?
在这里插入图片描述

上面这个日志,是 $ 符号执行的日志,可以看到,SQL 直接就拼接好了,没有参数。

下面这个,是 # 执行的日志,可以看到,这个日志中,使用了预编译的方式:
在这里插入图片描述
在 JDBC 调用中,SQL 的执行,我们可以通过字符串拼接的方式来解决参数的传递问题,也可以通过占位符的方式来解决参数的传递问题。当然,这种方式也传递到 MyBatis 中,在 MyBatis 中,$ 相当于是参数拼接的方式,而 # 则相当于是占位符的方式。

一般来说,由于参数拼接的方式存在 SQL 注入的风险,因此我们使用较少,但是在一些特殊的场景下,又不得不使用这种方式。其实就是jdbc中Statement和PreparedStatement 的区别。

Mapp 多个参数

当我们传入多个参数时,mapper里面该如何接收?

    // 根据ID 修改 名字
    int updateUserNameById(String userName, Long id);
    <update id="updateUserNameById">
        update user
        set username = #{username}
        where id = #{id};
    </update>

然后直接报错:
在这里插入图片描述
这里是说,找不到我们定义的 username 和 id 这两个参数。同时,这个错误提示中指明,可用的参数名是 [arg1, arg0, param1, param2],相当于我们自己给变量取的名字失效了,要使用系统提供的默认名字,默认名字实际上是两套体系:

第一套就是 arg0、arg1、、、、
第二套就是 param1、param2、、、

注意,这两个的下标是不一样的。

因此,按照错误提示,我们将参数改为下面这样:

    <update id="updateUserNameById">
        update user
        set username = #{arg0}
        where id = #{arg1};
    </update>

或者这样:

    <update id="updateUserNameById">
        update user
        set username = #{param1}
        where id = #{param2};
    </update>

这两种方式,都可以使该方法顺利执行。

但是,默认的名字不好记,容易出错,我们如果想要使用自己写的变量的名字,可以通过给参数添加 @Param 来指定参数名(一般在又多个参数的时候,需要加),一旦用 @Param 指定了参数类型之后,可以省略掉参数类型,就是在 xml 文件中,不用定义 parameterType 了:

    // 根据ID 修改 名字
    int updateUserNameById(@Param("username") String userName, @Param("id") Long id);
    <update id="updateUserNameById">
        update user
        set username = #{username}
        where id = #{id};
    </update>

这样定义之后,我们在 mapper.xml 文件中,就可以直接使用 username 和 id 来引用变量了。

3 对象参数

例如添加一个用户:

    int addUser(User user);
    <insert id="addUser" parameterType="com.java.lang.pojo.User">
        insert into user(username, address)
        values (#{username},#{address});
    </insert>

也可以加@Param :

   int addUser(@Param("user") User user);
    <insert id="addUser" parameterType="com.java.lang.pojo.User">
        insert into user(username, address)
        values (#{user.username}, #{user.address});
    </insert>

map参数

一般不推荐在项目中使用 Map 参数。如果想要使用 Map 传递参数,技术上来说,肯定是没有问题的。

Integer updateUsernameById(HashMap<String,Object> map);

XML 文件写法如下:

<update id="updateUsernameById">
    update user set username = #{username} where id=#{id};
</update>

引用的变量名,就是 map 中的 key。基本上和实体类是一样的,如果给 map 取了别名,那么在引用的时候,也要将别名作为前缀加上,这一点和实体类也是一样的。

两种主键回填的方式

1.(常用的)

    <insert id="addUser" parameterType="com.java.lang.pojo.User" useGeneratedKeys="true" keyProperty="id">
        /*  useGeneratedKeys参数只针对 insert 语句生效,默认为 false;
        useGeneratedKeys : 表示如果插入的表id以自增列为主键,则允许 JDBC 支持自动生成主键,并可将自动生成的主键id返回。
        keyProperty : 映射到哪里
          */
        insert into user(username, address)
        values (#{user.username}, #{user.address});
    </insert>

2.(不常用的)

<insert id="addUser" parameterType="com.java.lang.pojo.User">
    <selectKey keyProperty="id" resultType="Long" order="AFTER">/*after 先执行插入语句,然后再执行这个自带函数*/
        select last_insert_id(); /*mysql 自带函数,作用是可以查询到刚刚插入的id*/
    </selectKey>
    insert into user(username, address)
    values (#{username}, #{address});
</insert>

resultType 是返回类型,在实际开发中,如果返回的数据类型比较复杂,一般我们使用 resultMap,但是,对于一些简单的返回,使用 resultType 就够用了。


resultMap
在实际开发中,resultMap 是使用较多的返回数据类型配置。因为实际项目中,一般的返回数据类型比较丰富,要么字段和属性对不上,要么是一对一、一对多的查询,等等,这些需求,单纯的使用 resultType 是无法满足的,因此我们还需要使用 resultMap,也就是自己定义映射的结果集。

    <resultMap id="bookMap" type="com.java.lang.pojo.Book"> <!--type 映射到哪一个对象-->
        <id property="id" column="id"/> <!--id 标签只有一个  ,property 对象里的字段, column 数据库里的字段,这里关联映射-->
        <result property="name" column="b_name"/> <!--其他的字段映射-->
        <result property="author" column="author"/>
    </resultMap>

    <select id="listBook" resultMap="bookMap">
        select *
        from t_book;
    </select>

动态sql

if

是一个判断节点,如果满足某个条件,节点中的 SQL 就会生效。例如分页查询,要传递两个参数,页码和查询的记录数,如果这两个参数都为 null,那我就查询所有。

List<Book> listBookByPage(@Param("start") int start , @Param("size") int size);
    <select id="listBookByPage" resultMap="bookMap">
        select *
        from t_book
        <if test="start !=null and size != null">
            limit #{start}, #{size};
        </if>;
    </select>

<choose>

    // 根据书名或者作者查询 同时存在时,以书名为准
    List<Book> listBookByNameOrAuthor(Book book);
    <select id="listBookByNameOrAuthor" resultMap="bookMap" parameterType="com.java.lang.pojo.Book">
        select *
        from t_book
        where 1 = 1
        <choose>
            <when test="name != null and name != ''">/*如果有多个when 只会满足其中一个,比如第一个满足了,下面的就不走了,类似于switch*/
                and b_name = #{name}
            </when>
            <when test="author != null and author != ''">
                and author = #{author}
            </when>
            <otherwise>and 2 = 2</otherwise><!--<otherwise>如果上述条件都不满足</otherwise> -->
        </choose>
    </select>

<where>

用 where 节点将所有的查询条件包起来,如果有满足的条件,where 节点会自动加上,如果没有,where 节点也将不存在,在有满足条件的情况下,where 还会自动处理 and 关键字。

    // 根据id/name 查询Book 如果都为null ,则返回所有
    List<Book> listBookByIdOrName(Book book);
    <select id="listBookByIdOrName" resultMap="bookMap" parameterType="com.java.lang.pojo.Book">
        select *
        from t_book
        <where>
            <if test="name != null">
                and b_name = #{name}
            </if>
            <if test="id != null">
                and id = #{id}
            </if>
        </where>
    </select>

<Set>

    /*根据传入的属性更新Book */
    int updateBook(Book book);
    <update id="updateBook" parameterType="com.java.lang.pojo.Book">
        update t_book
        <set>
            <if test="name != null ">
                b_name = #{name},
            </if>
            <if test="author != null">/* 包含在set种 如果此条件也满足,后面的',' 会被自动去掉*/
                author = #{author},
            </if>
        </set>
        where id = #{id}
    </update>

<trim>

可以代替where / set

    // 根据id/name 查询Book 如果都为null ,则返回所有
    List<Book> listBookByIdOrName2(Book book);

代替where 实例

    <select id="listBookByIdOrName2" resultMap="bookMap" parameterType="com.java.lang.pojo.Book">
        <!--        /*<trim>会自动判断是否需要where以及 是否prefixOverrides前缀需要and*/-->
        select *
        from t_book
        <trim prefix="where" prefixOverrides="and ">
            <if test="name != null">
                and b_name = #{name}
            </if>
            <if test="id != null">
                and id = #{id}
            </if>
        </trim>
    </select>

代替set 实例:

    <update id="updateBook2" parameterType="com.java.lang.pojo.Book">
        update t_book
        <trim prefix="set" suffixOverrides=", ">
            <if test="name != null ">
                b_name = #{name},
            </if>
            <if test="author != null">/* 包含在set种 如果此条件也满足,后面的',' 会被自动去掉*/
                author = #{author},
            </if>
        </trim>
        where id = #{id}
    </update>

<foreach>

遍历 数组/list

    /*根据id集合查询*/
    List<Book> listUserByIds(@Param("ids")Long ...ids);

    <select id="listUserByIds" resultMap="bookMap">
        select *
        from t_book where id in 
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>

遍历对象

    /*批量插入*/
    int bathAddBooks(@Param("books") List<Book> list);
    <insert id="bathAddBooks">
        insert into t_book (b_name,author) values /*(xxx,xxx),(xxx,xxx)*/
        <foreach collection="books" separator="," item="book">
            (#{book.name},#{book.author})
        </foreach>
    </insert>

遍历map

    /*批量更新*/
    int bathAddBooks2(@Param("map")Map<String,Object> map,@Param("id") Long id);
    <update id="bathAddBooks2">
        update t_book
        <set>
            <foreach collection="map" index="key" item="val" separator=",">
                ${key} = #{val}
            </foreach>
        </set>
        where id = #{id}
    </update>

<bind>

    /*根据作者名字查询*/
    List<Book> getBooksByAuthorFirstName(@Param("author") String author);
    <select id="getBooksByAuthorFirstName" resultMap="bookMap">
        <bind name="authorLike" value="author + '%'"/>
        /*相当于定义变量*/
        select * from t_book
        where author like #{authorLike};
    </select>

sql 片段

大家知道,在 SQL 查询中,一般不建议写 *,因为 select * 会降低查询效率。但是,每次查询都要把字段名列出来,太麻烦。这种使用,我们可以利用 SQL 片段来解决这个问题。

例如,我们先在 mapper 中定义一个 SQL 片段:

<sql id="Base_Column">
    id,usename,address
</sql>

然后,在其他 SQL 中,就可以引用这个变量:

<select id="getUserByIds" resultType="org.javaboy.mybatis.model.User">
    select <include refid="Base_Column" /> from user where id in
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
        #{id}
    </foreach>
</select>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值