MyBatis之关联查询和缓存

Posted 一宿君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis之关联查询和缓存相关的知识,希望对你有一定的参考价值。

关联查询分为

  • 一对一
  • 一对多
  • 多对一
  • 多对多

resultMap属性

  • id:resultMap的唯一标识
  • type:Java实体类类型

resultMap子元素

  • id:一般对应数据库中该行的主键id,设置此项可提高MyBatis性能
  • result:映射到JavaBean的某个“简单类型”属性
  • association:映射到JavaBean的某个“复杂类型”属性,比如JavaBean类
  • collection:映射到JavaBean的某个“复杂类型”属性,比如集合

association

  • 复杂的类型关联,一对一多对一
  • 内部嵌套:映射一个嵌套JavaBean属性
  • 属性

    property:映射数据库列的实体对象的属性
    javaType:完整Java类名或别名
    resultMap:引用外部resultMap

  • 子元素

    id
    result

    • property:映射数据库列的实体对象的属性
    • column:数据库列名或者别名

4.1 一对一(一个qq用户qqUser对应一个 个人信息baseInfo)

  • 实体类
    在这里插入图片描述
  • QQUserMapper.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">
    
    <!--namespace是命名空间,要与对应的接口的路径保持一致(如果没有创建该接口,MyBatis在内部编译时会自动创建)-->
    <mapper namespace="com.ebuy.dao.QQUserMapper">
    
        <resultMap id="qqUserMap" type="com.ebuy.pojo.QQUser">
            <result property="qqid" column="qqid"/>
            <result property="password" column="password"/>
            <result property="lastlogtime" column="lastlogtime"/>
            <result property="onlines" column="onlines"/>
            <result property="levels" column="levels"/>
            <!--关联表  一对一,一个qq号账户对应一个  个人用户信息-->
            <association property="baseInfo" javaType="com.ebuy.pojo.BaseInfo">
                <id property="qqid" column="qqid"/>
                <result property="nickname" column="nickname"/>
                <result property="sex" column="sex"/>
                <result property="age" column="age"/>
                <result property="province" column="province"/>
                <result property="city" column="city"/>
                <result property="address" column="address"/>
                <result property="phone" column="phone"/>
            </association>
        </resultMap>
    
    
        <!--关联查询所有用户信息——一对一-->
        <select id="selectQQUserAll" resultMap="qqUserMap">
            select * from qquser a,baseinfo b where a.qqid = b.qqid
        </select>
    
    </mapper>
    
    在这里插入图片描述
  • QQUserMapper接口
    /**
     * @author 一宿君(CSDN : qq_52596258)
     * @date 2021-07-07 10:08:36
     */
    public interface QQUserMapper {
    
        /**
         *
         * @return
         */
        List<QQUser> selectQQUserAll();
    }
    
  • QQUserMapperTest
    /**
         * 利用接口引用,调用mapper映射文件中的方法
         * @throws IOException
         */
        @Test
        public void selectQQUserAll() throws IOException {
            SqlSession sqlSession = MyBatisUtil.createSqlSession();
            QQUserMapper qqUserMapper = sqlSession.getMapper(QQUserMapper.class);
            List<QQUser> qqUserList = qqUserMapper.selectQQUserAll();
            for(QQUser qqUser:qqUserList){
                System.out.println(qqUser);
            }
            MyBatisUtil.closeSqlSession(sqlSession);
        }
    

4.2 多对一(多个用户SystemUser对应一个角色SystemRole),结构上与一对一的形式一致

  • SystemUser
    在这里插入图片描述
  • SystemUserMapper.xml
    <!--namespace是命名空间,要与对应的接口的路径保持一致(如果没有创建该接口,MyBatis在内部编译时会自动创建)-->
    	<mapper namespace="com.ebuy.dao.SystemUserMapper">
    	
    	    <resultMap id="userMap" type="com.ebuy.pojo.SystemUser">
    	        <result property="userinfouid" column="userinfo_uid"/>
    	        <result property="userinfologinid" column="userinfo_loginid"/>
    	        <result property="userinfoname" column="userinfo_name"/>
    	        <result property="userinfo_password" column="userinfo_password"/>
    	        <result property="userinfo_sex" column="userinfo_sex"/>
    	        <result property="userinfo_email" column="userinfo_email"/>
    	        <result property="userinfo_mobile" column="userinfo_mobile"/>
    	        <result property="userinfo_status" column="userinfo_status"/>
    	        <!--<result property="userinfo_roleid" column="userinfo_roleid"/>
    	        <result property="userinfo_rolename" column="role_name"/>-->
    	
    	        <!--关联查询,一对一-->
    	        <association property="systemRole" javaType="com.ebuy.pojo.SystemRole">
    	            <!--关联查询中的唯一标识-->
    	            <id property="role_id" column="userinfo_roleid"/>
    	            <result property="role_name" column="role_name"/>
    	            <result property="role_code" column="role_code"/>
    	            <result property="role_description" column="role_description"/>
    	        </association>
    	    </resultMap>
    	    
    	    <!--关联查询表system_userinfo和表system_role,查询结果是多对一,也就是多个用户对应一个角色-->
    	    <select id="selectUserAll" resultMap="userMap">
    	        /*select * from system_userinfo*/
    	        select a.*,b.role_name from system_userinfo a,system_role b where a.userinfo_roleid = b.role_id
    	    </select>
    	</mapper>
    
    在这里插入图片描述
  • SystemUSerMapper接口
    /**
         * 关联查询(多对一)
         * @return
         */
        List<SystemUser> selectUserAllByAssociation();
    
  • SysteUserMapperTest
    @Test
        public void selectUserAllByAssociation(){
            SqlSession sqlSession = MyBatisUtil.createSqlSession();
            SystemUserMapper systemUserMapper = sqlSession.getMapper(SystemUserMapper.class);
            List<SystemUser> systemUserList = systemUserMapper.selectUserAllByAssociation();
            for(SystemUser s:systemUserList){
                System.out.println(s);
            }
            MyBatisUtil.closeSqlSession(sqlSession);
        }
    

Collection

  • 复杂类型集合,一对多
  • 内部嵌套:映射一个嵌套结果集到一个列表
  • 属性

    property:映射数据库列的实体对象的属性
    ofType:完整Java类名或者别名(集合泛型中所包含的类型)
    resultMap:引用外部resultMap

  • 子元素

    id
    result

    • property:映射数据库列的实体对象的属性
    • column:数据库列名或者别名
  • resultMap自动映射(autoMappingBehavior)的三个匹配级别

    NONE:禁止自动匹配
    PARTIAL(默认):自动匹配所有属性,内部嵌套除外
    FULL:自动匹配所有(前提实体类属性和数据库列名保持一致)

4.3 一对多(一个角色SystemRole对应多个用户SystemUser)

  • SystemRole
    在这里插入图片描述
  • SystemRoleMapper.xml
    <!--namespace是命名空间,要与对应的接口的路径保持一致(如果没有创建该接口,MyBatis在内部编译时会自动创建)-->
    <mapper namespace="com.ebuy.dao.SystemRoleMapper">
    
        <resultMap id="roleMap" type="com.ebuy.pojo.SystemRole">
            <id property="role_id" column="role_id"/>
            <result property="role_name" column="role_name"/>
            <result property="role_code" column="role_code"/>
            <result property="role_description" column="role_description"/>
            <!--关联查询   多对一  -->
            <collection property="userInfoList" ofType="com.ebuy.pojo.SystemUser">
                <result property="userinfouid" column="userinfo_uid"/>
                <result property="userinfologinid" column="userinfo_loginid"/>
                <result property="userinfoname" column="userinfo_name"/>
                <result property="userinfo_password" column="userinfo_password"/>
                <result property="userinfo_sex" column="userinfo_sex"/>
                <result property="userinfo_email" column="userinfo_email"/>
                <result property="userinfo_mobile" column="userinfo_mobile"/>
                <result property="userinfo_status" column="userinfo_status"/>
            </collection>
        </resultMap>
    
        <!--关联查询查询system_role表和system_userinfo-->
        <select id="selectRoleAllByCollection" resultMap="roleMap">
            select * from system_role a,system_userinfo b where a.role_id = b.userinfo_roleid
        </select>
    </mapper>
    
  • SystemRoleMapper接口
    /**
         * 关联查询查询system_role表和system_userinfo
         * @return
         */
        List<SystemRole> selectRoleAllByCollection();
    
  • SystemRoleMapperTest
    /**
         * 利用接口引用,调用mapper映射文件中的方法
         * @throws IOException
         */
        @Test
        public void selectRoleAllByCollection(){
            SqlSession sqlSession = MyBatisUtil.createSqlSession();
            SystemRoleMapper systemRoleMapper = sqlSession.getMapper(SystemRoleMapper.class);
            List<SystemRole> systemRoleList = systemRoleMapper.selectRoleAllByCollection();
            for(SystemRole s:systemRoleList){
                System.out.println(s.getRole_name() + ":");
                for (SystemUser systemUser:s.getUserInfoList()){
                    System.out.println("\\t" + systemUser);
                }
            }
            MyBatisUtil.closeSqlSession(sqlSession);
        }
    

resultMap自动映射(autoMappingBehavior),可在mybatis-conf.xml配置文件中设置在这里插入图片描述

  • 示例1
    在这里插入图片描述
  • 示例2
    在这里插入图片描述

上述是在mybatis-conf.xml配置文件中配置的自动映射,还可以在mapper映射文件中配置自动映射(作用于局部mapper)

  • 示例3
    在这里插入图片描述

MyBatis缓存

  • 一级缓存
  • 二级缓存
  • 二级缓存的配置
    • MyBatis的全局cache配置
    • 在Mapper XML文件中设置缓存,默认情况下是没有开启缓存的
    • 在Mapper XML文件配置支持cache后,如果需要对个别查询进行调整,可以单独设置cache

一级缓存(第一次查询出的数据(默认)放入到了缓存中)

  • SystemUserMapperTest
    @Test
        public void count2() throws IOException {
            SqlSession sqlSession = MyBatisUtil.createSqlSession();
            SystemUserMapper systemUserMapper = sqlSession.getMapper(SystemUserMapper.class);
            //第一次查询
            System.out.println(systemUserMapper.count());
            /**
             * 第二次查询(查询到的是一级缓存中的数据,也就是第一次查询出的数据放入到了缓存中)
             * 第二次查询并不会再次创建sqlSession,也就是不会执行SQL语句操作数据库查询
             */
            System.out.println(systemUserMapper.count());
            MyBatisUtil.closeSqlSession(sqlSession);
        } 
    
    在这里插入图片描述

一级缓存(改进,执行两次sql后关闭sqlSession,再次开启sqlSession)

  • SystemUserMapper

    @Test
    public void count2() throws IOException {
        SqlSession sqlSession = MyBatisUtil.createSqlSession();
        SystemUserMapper systemUserMapper = sqlSession.getMapper(SystemUserMapper.class);
    
        System.out.println(systemUserMapper.count());//第一次查询
        /**
         * 第二次查询(查询到的是一级缓存中的数据,也就是第一次查询出的数据放入到了一级缓存中)
         * 第二次查询并不会再次创建sqlSession,也就是不会执行SQL语句操作数据库查询
         */
        System.out.println(systemUserMapper.count());//第二次查询
        MyBatisUtil.closeSqlSession(sqlSession);//关闭sqlSession
    
        sqlSession = MyBatisUtil.createSqlSession();
        systemUserMapper = sqlSession.getMapper(SystemUserMapper.class);
        System.out.println(systemUserMapper.count());//第三次查询
        /**
         * 同理
         * 第四次查询(查询到的是一级缓存中的数据,也就是第三次查询出的数据放入到了一级缓存中)
         * 第四次查询并不会再次创建sqlSession,也就是不会执行SQL语句操作数据库查询
         */
        System.out.println(systemUserMapper.count());//第四次查询
        MyBatisUtil.closeSqlSession(sqlSession);
    }
    

    在这里插入图片描述

    会发现上述执行了两次sql!上述每次开启sqlSession后的第一次不同的SQL查询都会将结果存入到缓存中,一级缓存只要sqlSession关闭就会一直存在,这样对于内存负荷量比较大,而且不利于频繁更新的数据要求。

二级缓存(可设置时效性,解决一级缓存会一直存在的弊端)

  • 首先开启二级缓存
    在这里插入图片描述

  • 在相关映射文件中设置时效性每次最大的缓存记录数

    <cache eviction="FIFO" flushInterval="5000" size="512" readOnly="true"/>
    

    在这里插入图片描述

  • SystemRoleMapperTest

    @Test
        public void selectRoleAllByCollection() throws InterruptedException {
            SqlSession sqlSession = MyBatisUtil.createSqlSession();//开启sqlSession
            SystemRoleMapper systemRoleMapper = sqlSession.getMapper(SystemRoleMapper.class);
            List<SystemRole> systemRoleList1 = systemRoleMapper.selectRoleAllByCollection();
            for(SystemRole s:systemRoleList1){
                System.out.println(s.getRole_name() + ":");
                for (SystemUser systemUser:s.getUserInfoList()){
                    System.out.println("\\t" + systemUser);
                }
            }
            MyBatisUtil.closeSqlSession(sqlSession);//关闭sqlSession
    
            /**
             * 循环8次,注意二级缓存设置的有效期为5秒
             */
            for (int i = 1; i <= 8; i++) {
                sqlSession = MyBatisUtil.createSqlSession();//开启sqlSession
                systemRoleMapper = sqlSession.getMapper(SystemRoleMapper.class);
                System.out.println("第" + i + "次查询---------------------------------");
                List<SystemRole> systemRoleList2 = systemRoleMapper.selectRoleAllByCollection();
                for(SystemRole s:systemRoleList2){
                    System.out.println(s.getRole_name() + ":");
                    for (SystemUser systemUser:s.getUserInfoList()){
                        System.out.println("\\t" + systemUser);
                    }
                }
                Thread.sleep(1000);//没循环一次线程等待1秒
                MyBatisUtil.closeSqlSession(sqlSession);//关闭sqlSession
            }
        }
    

    在这里插入图片描述

  • 控制台结果显示:

  • 初始查询
    在这里插入图片描述

  • 第1次循环查询
    在这里插入图片描述

  • 第2次循环查询
    在这里插入图片描述

  • 第3次循环查询
    在这里插入图片描述

  • 第4次循环查询
    在这里插入图片描述

  • 第5次循环查询
    在这里插入图片描述

  • 第6次循环查询
    在这里插入图片描述

  • 第7次循环查询
    在这里插入图片描述

  • 第8次循环查询
    在这里插入图片描述

以上是关于MyBatis之关联查询和缓存的主要内容,如果未能解决你的问题,请参考以下文章

mybatis基础系列——关联查询延迟加载一级缓存与二级缓存

Java学习三分钟之MyBatis缓存分析

Mybatis延迟加载和查询缓存

Mybatis延迟加载和缓存

SSM框架MyBatis笔记 --- 表之间的关联关系;MyBatis事务;MyBatis缓存机制;ORM概述

mybatis-----的延迟加载-----缓存(一级缓存和二级缓存)