0
点赞
收藏
分享

微信扫一扫

WEB应用程序开发--Mybatis框架实现懒加载,嵌套查询,注解,trim,choose等元素,特殊符号处理,缓存讲解

惠特曼 2022-02-07 阅读 51

Mybatis框架实现懒加载,嵌套查询,注解,trim,choose等元素,特殊符号处理,缓存讲解

懒加载

需要查询关联信息时,使用 Mybatis 懒加载特性可有效的减少数据库压力,首次查询只查询主表信息,关联表的信息在用户获取时再加载。
Mybatis 一对一关联的 association 和一对多的 collection 可以实现懒加载。懒加载时要使用 resultMap,不能使用 resultType。

启动懒加载

Mybatis 默认没有打开懒加载配置,需要在 SqlMapperConfig.xml 中通过settings 配置 lazyLoadingEnabled 来开启懒加载。

<settings>
<setting name="lazyLoadingEnabled" value="true"/></settings>
 <resultMap id="empmap1" type="Employee">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <!--嵌套查询管理员信息-->
        <association property="admin" javaType="Admin" fetchType="lazy"
                     select="findAdminById" column="adminId">
            <result property="account" column="account"></result>
        </association>
        <!--嵌套查询部门信息
         fetchType="lazy"  开启延迟加载(需要时采取触发查询) 前提:是要写成嵌套查询
        -->
        <association property="dept" javaType="Dept" fetchType="eager"
                     select="findDeptById" column="deptId">
            <result property="name" column="name"></result>
        </association>
    </resultMap>

    <select id="getEmployeeById1" resultMap="empmap1">
         select  id,name,age,adminId,deptId from employee where id = #{id}
    </select>
    <select id="findAdminById" resultType="Admin">
         select account from admin where id = #{adminId}
    </select>
    <select id="findDeptById" resultType="Dept">
        select name from dept where id = #{deptId}
    </select>

(1). Select:指定关联查询懒加载对象的 Mapper Statement ID 为findDeptByID

(2). column=“dept_id”:关联查询时将 dept_id 列的值传入 findDeptByID,并将 findDeptByID 查询的结果映射到 Emp 的 dept 属性中

(3).collection 和 association 都需要配置 select 和 column 属性,两者配置方法相同

代码测试

 @Test
    public void find1(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);

        Employee employee = mapper.getEmployeeById1(1);

        System.out.println(employee.getName());
        System.out.println(employee.getDept().getName());
        System.out.println(employee.getAdmin().getAccount());

        sqlSession.commit();
        sqlSession.close();
    }

在这里插入图片描述

在首次查询时优先查询employee的主信息,其次再开始查询与其相关联的部门与操作人信息

注解方式

@Insert : 插入 sql , 和 xml insert sql 语法完全一样
@Select : 查询 sql, 和 xml select sql 语法完全一样
@Update : 更新 sql, 和 xml update sql 语法完全一样
@Delete : 删除 sql, 和 xml delete sql 语法完全一样
@Param : 入参
@Results : 设置结果集合
@Result : 结果

这些注解sql的作用在于简化代码,不必在对应的Mapper中在此创建映射填写所对应的sql代码,我们可以将那些简单sql代码直接写入注解,直接运用

查询所有信息

@Select("select * from t_emp")
List <Employee>getAll();

查询单个信息

@Select("select * from t_emp where emp_id=#{empId}")
@ResultMap(value="empMap")
Employee getById(@Param("empId") Integer empId)

插入信息

@Insert("insert into t_emp (emp_id, emp_name, emp_tel, " +
" emp_education, emp_birthday, fk_dept_id" +
" )" values (#{empId}, #{empName}, #{empTel}, " +
" #{empEducation}, #{empBirthday}, #{fkDeptId}" +
" )")
int insert(Employee record);

删除信息

@Delete("delete from t_emp where emp_id=#{empId}")
int deleteByPrimaryKey(@Param("empId") Integer empId)

Mybatis 动态 SQL

MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。 如果你有使用JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。

MyBatis 中用于实现动态 SQL 的元素主要有:
If where trim set choose (when, otherwise) foreach

If 元素可以对传入的条件进行判断

对于查询条件个数不确定的情况,可使用元素。如下:

 <select id="getEmployeeList" parameterType="Employee" resultMap="empmap">
            SELECT
            emp.id,
            emp.name ename,
            emp.age,
            d.name dname,
            a.account
            FROM employee emp LEFT JOIN dept d ON emp.deptId = d.id
            LEFT JOIN admin a ON emp.adminId = a.id
            <where>
                <if test="name!=null &amp; name !=''">
                    emp.name  =#{name}
                </if>
                <if test="age!=null &amp; age !='' ">
                    and emp.age  =#{age}
                </if>
            </where>
    </select>

元素会进行判断,如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或 OR 开头,它会剔除掉AND 或 OR

代码测试

查询年龄为20的人

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

查询姓名为李四的人

在这里插入图片描述

trim 元素

where 标签,其实用 trim 也可以表示,当 WHERE 后紧随 AND 或则 OR 的时候,就去除 AND 或者 OR。prefix 前缀,prefixOverrides 覆盖首部指定内容

<select id="getEmployeeList" parameterType="Employee" resultMap="empmap">
        SELECT emp.id,emp.name ename,emp.age,d.name
        dname,a.accountFROM employee emp LEFT JOIN dept d ON emp.deptId = d.idLEFT JOIN admin a ON emp.adminId = a.id
        <trim prefix="where" prefixOverrides="and|or">
            <if test="name!=null &amp; name !=''">emp.name =#{name}</if>
            <if test="age!=null &amp; age !='' ">and emp.age =#{age}</if>
        </trim>
    </select>

Choose 元素

当if有成立的条件时,添加一个指定的前缀如果以and or 等关键字开头,可以覆盖掉关键字 choose when test=“条件” 执行 otherwise 不成立执行

    <select id="getEmployeeList" parameterType="Employee" resultMap="empmap">
        SELECTemp.id,emp.name ename,emp.age,d.name dname,a.accountFROM employee emp LEFT JOIN dept d ON emp.deptId =
        d.idLEFT JOIN admin a ON emp.adminId = a.id
        <trim prefix="where" prefixOverrides="and|or">
            <choose>
                <when test="name!=null & name!=''">emp.name = #{name}</when>
                <otherwise>emp.name = '张三'</otherwise>
            </choose>
        </trim>
    </select>

Set 元素

set标签可以动态添加set关键字,可以去掉set语句中最后一个逗号

   <update id="updateEmployee" parameterType="Employee">
        update employee
        <trim suffixOverrides="," prefix="set">
        <if test="name!=null">name=#{name}, </if>
        <if test="age!=null">age=#{age}, </if>
        <if test="dept.id!=null">deptId=#{dept.id}, </if>
    </trim>
        where id=#{id}
    </update>

也可以使用 trim 实现

   <update id="updateEmployee" parameterType="Employee">
        update employee
        <trim suffixOverrides="," prefix="set" suffix="where">
        <if test="name!=null">name=#{name}, </if>
        <if test="age!=null">age=#{age}, </if>
        <if test="dept.id!=null">deptId=#{dept.id}, </if>
    </trim>
        where id=#{id}
    </update>

Foreach 元素

foreach 元素非常强大,允许您指定一个集合,申明能够用在元素体内的项和索引变量。也允许您指定开始和结束的字符,也可以加入一个分隔符到迭代器之间。这个元素的聪明之处在于它不会意外地追加额外的分隔符

   <select id="getEmployeeListByAge" resultType="Employee">
        select * from employee where age in
        <foreach close=")" separator="," open="(" item="age" collection="list">#{age} </foreach>
    </select>

测试代码

   @Test
    public void find3(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Integer []a = new Integer[]{20,22,24};
        List<Employee> employees = mapper.getEmployeeListByAge1(a);

        sqlSession.close();
    }
List<Employee> getEmployeeListByAge1(Integer[] array);

在这里插入图片描述

特殊符号处理

在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:
特殊字符 转义字符

< &lt; > &gt;  " &quot;&apos; & &amp;
除了可以使用上述转义字符外,还可以使用<![CDATA[]]>来包裹特殊字符。如下所示:
<if test="id != null">
AND <![CDATA[ id <> #{id} ]]>
</if>
<![CDATA[ ]]>是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略。
但是有个问题那就是 <if> </if> <where> </where>
<choose> </choose> <trim> </trim> 等这些标签都不会被解析,所以
我们只把有特殊字符的语句放在 <![CDATA[ ]]> 尽量缩小<![CDATA[ ]]>的范围。

缓存

为什么使用缓存

由于从硬盘上读数据相对来说比较慢, 大批量查询时,数据库压力较大, 可以将第一次查询到的数据放在内存中不销毁,下次查询可以直接从内存中取这样就可以减轻数据库压力

缓存(cache)的作用是为了减去数据库的压力,提高数据库的性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库执行 select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。

Mybatis 有一级缓存和二级缓存。一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。
二 级 缓 存 是 多 个 SqlSession 共 享 的 , 其 作 用 域 是 mapper 的 同 一 个namespace,不同的 sqlSession 两次执行相同 namespace 下的 sql 语句且向sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis 默认没有开启二级缓存需要在 setting 全局参数中配置开启二级缓存

一级缓存

Mybatis 对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个 SqlSession 而言。所以在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用一个 Mapper 方法,往往只执行一次 SQL,因为使用 SelSession 第一次查询后,MyBatis 会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
在这里插入图片描述

一级缓存的生命周期

a、MyBatis 在开启一个数据库会话时,会 创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新的 PerpetualCache 对象,如果 SqlSession 调用了 close()方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存将不可用。
b、如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象仍可使用。
c、SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空 PerpetualCache 对象的数据,但是该对象可以继续使用

二级缓存

二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分。
每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。
sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读。二级缓存参考下图所示:

MyBatis 的缓存机制整体设计以及二级缓存的工作模式

在这里插入图片描述

配置二级缓存配置

第一步:启用二级缓存
在 SqlMapperConfig.xml 中启用二级缓存,如下代码所示,当cacheEnabled 设置为 true 时启用二级缓存,设置为 false 时禁用二级缓存。

第二步:POJO 序列化
将所有的 POJO 类实现序列化接口 Java.io. Serializable。

第三步:配置映射文件
在 Mapper 映射文件中添加,表示此 mapper 开启二级缓存。
当 SqlSeesion 关闭时,会将数据存入到二级缓存

在这里不详细讲解了,可以通过文档了解
链接:https://pan.baidu.com/s/1ikQ9TcT0pyEXBB8i68i_tA
提取码:xryy

举报

相关推荐

0 条评论