MyBatis 使用笔记

2019-08-27  本文已影响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

常用标签、属性

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 标签

<!--
    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 的子节点

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

}

注解配置

一对一

/**
 * 查询所有账户,并且获取每个账户所属的用户信息
 * @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官方提供的)

1、改数据库的连接信息

2、改包名

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

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

双击运行插件 ---- idea

上一篇下一篇

猜你喜欢

热点阅读