美文网首页
MyBatis 使用笔记

MyBatis 使用笔记

作者: 霍霍霍霍霍霍霍霍霍霍霍 | 来源:发表于2019-08-27 17:13 被阅读0次

    resultMap 自定义结果集映射

    查询结果集与映射pojo属性不一致时,可以使用resultMap指定结果集列名与pojo的属性名进行手动映射
    ​手动指定 主键列名 与 普通列名 跟 javabean 属性进行一一映射
    (就算结果集列名跟属性名一致,推荐也是进行映射)

    <!--自定义结果集
        1. resultMap
            id:唯一标识,用于被 select 标签中的 resultMap 属性所引用
            type:指当前结果集中的列名跟哪个 JavaBean 的属性去对应,最终映射到哪个 JavaBean 中
        2. 子标签:
            id: 代表映射主键列名
                property: JavaBean中的属性名
                column:结果集列名
            result:映射普通列
                property: JavaBean中的属性名
                column:结果集列名
    -->
    <!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
    <resultMap id="userMap" type="com.itheima.domain.User">
        <!-- 主键字段的对应 -->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="userSex" column="sex"></result>
        <result property="userBirthday" column="birthday"></result>
    </resultMap>
    

    一对一(多对一):association

    ==javaType:指定具体关联的属性类型 #f44336==

    <!--
        自定义结果集封装
            映射 一对一 或者 多对一
    -->
    <resultMap id="accountAndUserResultMap" type="com.itheima.domain.Account">
        <!--
             帐户信息 封装成功
        -->
        <id property="id" column="id"></id>
        <result property="money" column="money"></result>
        <result property="uid" column="uid"></result>
        <!--
            association : 映射 一对一 的关联关系
                property : 关联信息的 属性
                column : 关联属性对应的列名
                javaType : 关联属性的具体类型
        -->
        <association property="user" column="uid" javaType="com.itheima.domain.User">
            <id property="id" column="uid"></id>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </association>
    </resultMap>
    
    <!--
        List<Account> findAccountAndUser();
    -->
    <select id="findAccountAndUser" resultMap="accountAndUserResultMap">
        SELECT
          a.id,
          a.money,
          u.id uid,
          u.username,
          u.birthday,
          u.sex,
          u.address
        FROM
          account a
          LEFT JOIN USER u
            ON a.uid = u.id
    </select>
    

    一对多:collection

    ==ofType:指定具体关联的集合中元素的类型 #f44336==

    <!--
         resultMap 标签可以被继承 extends="baseResultMap"
                baseResultMap 是指 mapper.xml 中某一个 resultMap 的 id
    -->
    <resultMap id="userAndAccountsResultMap" type="com.itheima.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <!--
            collection : 映射一对多关联关系
            property : 关联信息的 属性
            column : 关联属性对应的列名
            javaType : 关联属性的类型
            ofType : 集合中元素的具体类型
        -->
        <collection property="accounts" column="id" ofType="com.itheima.domain.Account">
            <id property="id" column="aid"></id>
            <result property="money" column="money"></result>
            <result property="uid" column="uid"></result>
        </collection>
    </resultMap>
    
    <!--
        List<User> findUserAndAccounts();
    -->
    <select id="findUserAndAccounts" resultMap="userAndAccountsResultMap">
        SELECT
          u.id,
          u.username,
          u.birthday,
          u.sex,
          u.address,
          a.id aid,
          a.money,
          a.uid
        FROM
          USER u
          LEFT JOIN
          account a
            ON a.uid = u.id
    </select>
    
    

    ​ 多对多映射:collection,在 mybatis 中其实也是看成 一对多

    ​ 需要借助 中间表

    ​ userid roleid 联合主键

    ​ 1 1

    ​ 2 1

    ​ 1 2

    
    <!--定义role表的ResultMap-->
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="user">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </collection>
    </resultMap>
    ###     输入映射 (参数封装)parameterType
    
    ​       简单参数(8大基本数据类型或其包装类型,还有 String 类型)   
    
    ​           #{key}    key随意,一般key与形参名称一致
    
    ​       pojo       #{key}    key名必须与 pojo 的属性名一致,具体参看 增删改配置
    
    ​       包装 pojo(QueryVo)    参数名必须与 包装 pojo 的属性一致(可以使用 . 的形式访问其属性)
    
    ​           #{user.username}     该 user 是 queryVo 的一个属性, username 是 user 的一个属性
    
    ​           上述ONGL表达式获取到的是 QueryVo中的user中的 username 的值
    
    ​   例如:
    
    ```java
    class QueryVo{
        private User user;
        //省略 getter/setter 方法
    }
    

    一对一

    查询 账户时,关联查询用户信息(用户信息实现延迟加载)

    <resultMap id="baseResultMap" type="com.itheima.domain.Account">
        <id property="id" column="id"></id>
        <result property="money" column="money"></result>
        <result property="uid" column="uid"></result>
        <!--
            select : 指定要执行的查询 SQL 语句所在的 statementId
            column : 指的是 select 属性的值所需要的参数
            fetchType : 抓取策略   
                lazy : 延迟加载   eager : 立即加载
        -->
        <association property="user" javaType="com.itheima.domain.User" column="uid"
                     select="com.itheima.dao.UserPlusMapper.findById" fetchType="lazy"></association>
    </resultMap>
    
    <!--
        List<Account> findAll();
    -->
    <select id="findAll" resultMap="baseResultMap">
        SELECT * FROM account
    </select>
    <!--
        List<Account> findByUid(Integer uid);
    -->
    <select id="findByUid" resultMap="baseResultMap">
        SELECT * FROM account where uid = #{uid}
    </select>
    

    ​ 需要在全局配置文件中配置延迟加载

    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
    

    一对多

    <resultMap id="baseResultMap" type="com.itheima.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
        <collection property="accounts" column="id" javaType="list" ofType="com.itheima.domain.Account"
            select="com.itheima.dao.AccountPlusMapper.findByUid" fetchType="eager"></collection>
    </resultMap>
    
    <!--
        User findById(Integer id);
    -->
    <select id="findById" resultMap="baseResultMap">
        SELECT * FROM USER WHERE id = #{id}
    </select>
    
    <update id="update">
        UPDATE USER SET username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id = #{id}
    </update>
    

    @param注解

    多个参数前,使用@param("name") 修饰形参,在 xml 中使用 #{name} 映射绑定形参

    ​ 例如:

    public UserInfo login(@Param("username") String username, @Param("password")String password);
    

    @Param("key") 该注解为入参参数指定 key #{key}

    <select id="login" resultType="com.itheima.domain.UserInfo">
        SELECT * FROM user_info where username = #{username} and password = #{password}
    </select>
    

    映射结果封装

    resultType:底层是 resultMap

    ​ 简单类型:int、double、String 等,直接返回 类型全类名 或者 别名

    ​ 譬如:int、java.lang.String 等

    ​ pojo:可以是全类名,也可以是别名。推荐使用全类名:包名.类名

    ​ 譬如:com.itheima.domain.User

    ​ List 集合:是集合中元素的具体类型,格式同 pojo

    ​ map集合:java.util.map 或者 map

    常用标签、属性

    • 标签:mapper、select、insert、update、delete、resultMap、sql、include、cache、selectKey

    select 标签
    1、查询所有

    <?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="dao接口全限定类名">
        <!--List<User> findAll();-->
        <!--配置查询所有-->
        <select id="dao接口的方法名" resultType="JavaBean的全限定类名">
            select * from user <!--原生SQL语句-->
        </select>
    </mapper>
    

    2、查询单个

    <select id="dao接口的方法名" resultType="JavaBean的全限定类名">
         select * from user where id = #{id}
    </select>
    

    3、模糊查询

    <select id="dao接口的方法名" resultType="JavaBean的全限定类名">
         select * from user where username like #{username}
    </select>
    <!-- 当调用接口方法时,需要为传入参数前后加上 %,如 username="%王%" -->
    

    4、查询返回单行单列,返回一个值

    <select id="dao接口的方法名" resultType="long">
         select count(1) from user
    </select>
    

    insert 标签

    • 子标签:selectKey
    <!--
        insert: 代表新增
        parameterType:可以省略不写,推荐写上
    -->
    <insert id="接口方法名" parameterType="接口方法中形参的全类名">
        <!-- 
            keyProperty : 返回的值 赋值给 JavaBean 的哪个属性
            keyColumn : 返回的哪个列的值
            resultType : select LAST_INSERT_ID() 返回值类型
            order 表示 : select LAST_INSERT_ID() 的执行时机 
                AFTER 代表在插入语句执行成功之后执行   适用场景:自增主键
                BEFORE 代表在插入语句执行之前执行    适用场景:非自增主键,主键是UUID()获取的字符串
        -->
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select LAST_INSERT_ID()
        </selectKey>
         <!-- user 是表名 -->
        insert into user (username,address) values (#{username},#{address})
    </insert>
    
    <!-- 
        返回自增主键值的另一种写法
        useGeneratedKeys : 是否支持主键自增    true : 支持    false : 不支持
        keyProperty : 返回的值 赋值给 JavaBean 的哪个属性
        keyColumn : 返回的哪个列的值
        * 该方式支持 批量插入返回主键
    -->
    <insert id="saveReturnId" parameterType="com.itheima.domain.User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        INSERT INTO USER (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
    </insert>
    
    <!-- 
        返回非自增主键的值
        以 UUID() 为例
    -->
    <insert id="接口方法名" parameterType="com.itheima.domain.User">
        <!-- BEFORE 代表在插入语句执行之前  执行    用于主键是 UUID() 的情况 -->
        <selectKey keyProperty="id" keyColumn="id" resultType="string" order="BEFORE">
            select UUID()
        </selectKey>
         <!-- user_info 是表名 -->
        insert into user_info (id,username) values (#{id},#{username})
    </insert>
    

    修改update和删除delete

    <!-- 更新用户 -->
    <update id="updateUser" parameterType="com.itheima.domain.User">
        update user set username=#{username},address=#{address},sex=#{sex},
            birthday=#{birthday} where id=#{id}
    </update>
    
    <!-- 删除用户-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        <!--
            当入参参数为 8大基本数据类型或其包装类型,还有 String 类型时,入参参数在 sql映射文件中,
        使用ONGL表达式取值,可以指定随意名称,只表示占位符,但是推荐使用方法形参名,提高可读性
        -->
        delete from user where id = #{id}
    </delete>
    

    动态sql

    ​ if、where、foreach、sql片段

    ​ if 、 where

    <!--
        List<User> findByCondition(User user);
    -->
    <select id="findByCondition" parameterType="com.itheima.domain.User" resultType="com.itheima.domain.User">
        select * from user
        <where>
            <if test="username != null and username != ''">
                and username = #{username}
            </if>
        <!--
            3.4.5 不支持以下写法
                 <if test="sex == '男' or sex == '女'">
                     and sex = #{sex}
                 </if>
        -->
            <!-- 单引号中包含 双引号的形式 -->
            <!--
                <if test='sex == "男" or sex == "女"'>
                    and sex = #{sex}
                </if>
            -->
    
            <!-- 把 传入的值 使用 toString() 转成 字符串 -->
            <if test="sex == '男'.toString() or sex == '女'.toString()">
                and sex = #{sex}
            </if>
        </where>
    </select>
    <!--
        where 会自动过滤其中条件成立的多余最前面的 and 或者 or
        if
           test 表示条件判断
           写的是 ognl 表达式,其中 判断的参数为 Javabean 的属性名
           注意 :不能写特殊符号   譬如  &&  >  <
           等值判断写法,需要是 单引号中包含 双引号的形式
        -->
    

    ​ foreach

    <!--
        foreach  循环
            collection :  循环的集合
            item : 当前正在遍历的对象
            open : 开始的部分
            close : 结束的部分 
            separator : 当前正在遍历的每个对象之间的分隔符
    -->
    <!--
        List<User> findByIds(@Param("ids") List<Integer> ids);
    -->
    <select id="findByIds" parameterType="list" resultType="com.itheima.domain.User">
        select * from user
        <where>
            <if test="ids != null and ids.size() > 0">
                <foreach collection="ids" item="userId" open="and id in (" close=")" separator=",">
                    #{userId}
                </foreach>
            </if>
        </where>
    </select>
    
    <!--
        int saveManyUser(@Param("users") List<User> users);
        判断 集合是否为空,可以在 service 层进行业务处理
    -->
    <insert id="saveManyUser" parameterType="list">
        INSERT INTO USER (username,birthday,sex,address) VALUES
        <foreach collection="users" item="user" open="" close="" separator=",">
            (#{user.username},#{user.birthday},#{user.sex},#{user.address})
        </foreach>
    </insert>
    

    ​ trim

    <!--
        trim
            prefix : 条件拼接成立后要加的 前缀
            prefixOverrides : 条件拼接成立后要去掉的前面多余的 字符串
            suffix : 条件拼接成立后要加的 后缀
            suffixOverrides : 条件拼接成立后要去掉的后面多余的 字符串
    -->
    <select id="findByCondition" parameterType="com.itheima.domain.User" resultType="com.itheima.domain.User">
        select * from user
        <trim prefix="where" prefixOverrides="" suffix="" suffixOverrides="and">
            <if test="username != null and username != ''">
                username = #{username} and
            </if>
            <if test="sex != null and sex != ''">
                sex = #{sex} and
            </if>
        </trim>
    </select>
    

    set

    <!--
        set : 只能用于 update 标签中
            会自动过滤条件成立 多余的部分(,)
    -->
    <update id="updateUser" parameterType="com.itheima.domain.User">
        UPDATE USER
        <set>
            <if test="username != null">
                username = #{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>
            <if test="id != null">
                id = #{id}
            </if>
        </where>
    </update>
    

    sql 片段

    <!-- 定义 sql片段,该片段可以是 mapper.xml 中的任何内容 -->
    <sql id="base_column">
      username,birthday,sex,address
    </sql>
    
    <!--
        int saveManyUser(@Param("users") List<User> users);
        判断 集合中是否为空,可以在 service 层进行业务处理
    -->
    <insert id="saveManyUser" parameterType="list">
        INSERT INTO USER (
        <!-- include 标签 的 refid 属性用于引用 当前 mapper.xml 中已经定义的 sql 片段 -->
          <include refid="base_column"></include>
        ) VALUES
        <foreach collection="users" item="user" open="" close="" separator=",">
            (#{user.username},#{user.birthday},#{user.sex},#{user.address})
        </foreach>
    </insert>
    

    sql 的子节点

    • if、where、foreach、trim、set、choose、when、otherwise
      • 属性:namespace、id、resultType、resultMap、parameterType

    <select id="findAll" resultMap="roleMap">
    select u.*,r.id as rid,r.role_name,r.role_desc from role r
    left outer join user_role ur on r.id = ur.rid
    left outer join user u on u.id = ur.uid
    </select>

    
    据 id 查询用户
         * @param id
         * @return
         */
        User findById(Integer id);
    
        /**
         * 更新
         * 没有返回值
         * @param user
         */
        void update(User user);
    
    }
    

    注解配置

    • @Insert:实现新增
    • @Update:实现更新
    • @Delete:实现删除
    • @Select:实现查询
    • @Result:实现结果集封装
    • @Results:可以与@Result 一起使用,封装多个结果集 (等同于 xml 中的 ResultMap)
    • @ResultMap:实现引用@Results 定义的封装
    • @One:实现一对一结果集封装
    • @Many:实现一对多结果集封装
    • @SelectProvider: 实现动态 SQL 映射
    • @CacheNamespace:实现注解二级缓存的使用

    一对一

    /**
     * 查询所有账户,并且获取每个账户所属的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id="accountMap",value = {
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",
                    one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();
    

    一对多

    /**
     * @Results  等同于 Sql映射文件(mapper.xml) 中 resultMap 节点
     *      id : 唯一标识,以便其他@Select注解标记的方法引用结果集
     *          等同于 Sql映射文件(mapper.xml) 中 resultMap 节点中的 id属性
     *      value : 指定如何映射结果集
     *          @Result
     *              id : 代表映射是否是主键    true:是     false:不是 (默认值)
     *
     * @ResultMap 表示引用 其他 @Results的 id属性的值
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts",column = "id",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                                fetchType = FetchType.LAZY))
    })
    List<User> findAll();
    

    8、延迟加载(懒加载)

    ​ 在关联查询的基础上才可能出现延迟加载(发送两条或两条以上的SQL)

    ​ 多对一(一对一)或者一对多 都可以使用懒加载

    ​ AccountPlusMapper.java

    /**
     * @author 黑马程序员
     * @Company http://www.ithiema.com
     */
    public interface AccountPlusMapper {
    
        /**
         * 查询帐户关联用户信息
         * @return
         */
        List<Account> findAll();
        
        /**
         * 根据用户id查询账户列表
         * @param id
         * @return
         */
        List<Account> findByUid(Integer uid);
    }
    

    ​ UserPlusMapper.java

    /**
     * 也是操作 User类的持久化接口
     * @author ghy
     */
    public interface UserPlusMapper {
    
        /**
         * 根
    
    ## 缓存
    
    ```java
    /**
     * 测试一级缓存
     *      注意:只有两次相同查询操作才会有缓存.
     *  1.二级缓存是 SqlSession 级别,也就是只有同一个 SqlSession 才具备相同的缓存。一级缓存存放的数据是 对象的副本
     *  2.一级缓存默认开启,程序员无法关闭。一级缓存的介质通常是内存
     *
     *  3.当 SqlSession 对象执行以上方法时,缓存失效
     *      close
     *      clearCache
     *      commit
     *      增删改方法,不管是否提交事务
     */
    @Test
    public void testFirstLevelCache(){
        User u1 = userPlusMapper.findById(88);
        System.out.println(u1);
    
        //session.commit();
        User user = new User();
        user.setId(87);
        user.setUsername("87改一下");
        userPlusMapper.update(user);
    
    
        User u2 = userPlusMapper.findById(88);
        System.out.println(u2);
        System.out.println(u1 == u2);
    }
    
    
    /**
     * 测试二级缓存
     *      注意:在 SqlSession 关闭之后,数据才会保存到二级缓存中
     *      1.二级缓存是 SqlSessionFactory 级别,由同一个 SqlSessionFactory 生产的 SqlSession 共享一个二级缓存。
     *          二级缓存存放的数据是 散装数据
     *      2.二级缓存默认开启(3.4.5版本,之前的版本默认是有关闭的),程序员可以手动开启。二级缓存的介质可以是内存或者硬盘
     *      3.二级缓存使用步骤
     *          3.1 要缓存的类必须实现 Serializable 接口
     *          3.2 全局配置文件中开启
     *          3.3 在要使用二级缓存的 Mapper.xml文件中 使用 <cache/>
     *
     *  当用户发送查询需求时,查询操作是怎么执行的?
     */
    @Test
    public void testSecondLevelCache(){
        SqlSession session1 = sqlSessionFactory.openSession();
        UserPlusMapper userPlusMapper1 = session1.getMapper(UserPlusMapper.class);
        User user1 = userPlusMapper1.findById(79);
        session1.close();
    
        SqlSession session2 = sqlSessionFactory.openSession();
        UserPlusMapper userPlusMapper2 = session2.getMapper(UserPlusMapper.class);
        User user2 = userPlusMapper2.findById(79);
        session2.close();
    
        System.out.println(user1 == user2); // false
    }
    
    

    逆向工程(Mybatis官方提供的)

    • 就是由 数据库表 生成 pojo,mapper接口,mapper.xml (只能生成单表CRUD)

    1、改数据库的连接信息

    2、改包名

    3、指定要逆向生成的表名与类名

    tb_user (表名) ---- TbUser (默认的类名) ---- User (自定义类名)

    双击运行插件 ---- idea

    相关文章

      网友评论

          本文标题:MyBatis 使用笔记

          本文链接:https://www.haomeiwen.com/subject/pmoswqtx.html