优秀的持久层框架-Mybatis(下)


前言

mybatis是一个轻量级的ORM框架,之前我们已经学习了mybatis的基本概念,环境的搭建以及参数和结果处理,本章我们将继续学习mybatis剩余的内容,包括动态Sql,注解方式以及缓存。


一、懒加载

当我们在进行多表查询时,启动懒加载可以有效的缓解数据库的压力。首次查询我们只先查询主表中的信息,当用户需要用到关联表的信息时,才进行获取。Mybatis 一对一关联的 association 和一对多的 collection 可以实现懒加载。

注意:懒加载时要使用 resultMap,不能使用 resultType。

如何启动懒加载?

Mybatis 默认没有打开懒加载配置,需要在 SqlMapperConfig.xml 中通过 settings 配置 lazyLoadingEnabled 和lazyLoadTriggerMethods(一些方法会触发查询,导致懒加载无效,将value设置为空)来开启懒加载。

Employee里面的成员变量

public class Employee implements Serializable {
    private Integer id;
    private String name;
    private  String sex;
    private Dept dept;
    private  Admin admin;
}

懒加载配置

        <!--是否开启懒加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--指定哪些方法触发延迟查询  -->
        <setting name="lazyLoadTriggerMethods" value=""/>

mapper映射文件代码

    <select id="selectEmployeeLazy" resultMap="EmployeeResultLazyMap">
          SELECT id,NAME,deptid,adminid FROM employee where id=#{id}
    </select>

     <resultMap id="EmployeeResultLazyMap" type="Employee">
        <!--主键列-->
        <id column="id" property="id"/>
        <!--非主键列-->
        <result property="name" column="name"/>

        <association property="dept" javaType="Dept" fetchType="lazy" column="deptid" select="selectDeptLazy">
            <result property="name" column="name"/>
        </association>

        <association property="admin" javaType="Admin" fetchType="lazy" column="adminid" select="selectAdminLazy">
            <result property="name" column="name"/>
        </association>
    </resultMap>
    
    <select id="selectEmployeeLazy" resultMap="EmployeeResultLazyMap">
          SELECT id,NAME,deptid,adminid FROM employee where id=#{id}
    </select>

    <select id="selectDeptLazy" resultType="Dept">
           SELECT NAME FROM dept WHERE id=#{deptid}
    </select>

1.Select:指定关联查询懒加载对象的 Mapper Statement ID
2.column=“deptid”:关联查询时将 deptid 列的值传入 selectDeptLazy,并把查询结果映射到Employee的dept中去

测试代码

    @org.junit.Test
    public void select(){
        SqlSession sqlSession= sessionFactory.openSession();
        EmployeeMapper employeeMapper=sqlSession.getMapper(EmployeeMapper.class);
        Employee employee=employeeMapper.selectEmployeeLazy(2);
        System.out.println(employee.getName());
        System.out.println(employee.getDept().getName());
        System.out.println(employee.getAdmin().getName());
     }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由运行结果可以得出,当我们在进行查询的时候并没有一次性把所有的信息都查出来,当我们需要用到dept和admin时,才会进行新的查询。


二、注解方式

mybatis提供了注解的实现方法,可以不把sql代码写在mapper的映射文件中,使用注解方式直接写在接口的方法上面

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

接口中的方法

@Select("SELECT id,NAME,sex FROM employee")
    @Results(id = "empMap", value = {@Result(column = "id", property = "id", id = true), 
            @Result(column = "name", property = "name"), 
            @Result(column = "sex", property = "sex")})
    List<Employee> getEmployeesListByAnnotate();

测试代码

 public void select(){
        SqlSession sqlSession= sessionFactory.openSession();
        EmployeeMapper employeeMapper=sqlSession.getMapper(EmployeeMapper.class);
        List<Employee>list=employeeMapper.getEmployeesListByAnnotate();
        for(Employee temp:list){
            System.out.println(temp);
        }
}

运行结果
在这里插入图片描述


三、动态SQL

MyBatis 的一个强大的特性之一是它的动态 SQL 能力,有条件的拼接sql字符串常常使程序员感到痛苦,mybatis可以解决这个问题。

MyBatis中用于实现动态SQL的元素主要有

3.1 if元素

if 标签可以对传入的条件进行判断

3.2 where元素

对于查询条件个数不确定的情况

    <select id="getEmployeesList" parameterType="Employee" resultMap="EmployeeResultMap">
        SELECT
        e.id,
        e.name ename,
        e.sex,
        d.name dname,
        a.name aname
        FROM employee e LEFT JOIN dept d ON d.id=e.deptid
        LEFT JOIN admin a ON a.id=e.adminid
        <where>
            <if test="name != null &amp; name != ''">
                e.name like concat('%',#{name},'%')
            </if>
            <if test="sex != null &amp; sex != ''">
                AND e.sex= #{sex}
            </if>
        </where>
    </select>

1.where元素会进行判断,如果where包含的标签有返回值,就插入一个where。
2.如果返回值以and或者or开头,则它会剔除掉and或者or

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
该动态sql支持姓名的单条件模糊查询,支持性别的单条件查询,支持姓名和性别的多条件查询,利用动态sql可以极大的简化了我们的sql代码拼写。

3.3 trim元素

当 WHERE后紧随AND或OR的时候,就去除AND或者OR
prefix 前缀
prefixOverrides 覆盖首部指定内容

    <select id="getEmployeesList2" parameterType="Employee" resultMap="EmployeeResultMap">
        SELECT
        e.id,
        e.name ename,
        e.sex,
        d.name dname,
        a.name aname
        FROM employee e LEFT JOIN dept d ON d.id=e.deptid
        LEFT JOIN admin a ON a.id=e.adminid
        <trim prefix="where" prefixOverrides="and">
            <if test="name != null &amp; name != ''">
                and  e.name like concat('%',#{name},'%')
            </if>
            <if test="sex != null &amp; sex != ''">
                AND sex= #{sex}
            </if>
        </trim>
    </select>

3.4 choose元素

choose类似于switch,只执行其中一个条件。

    <select id="getEmployees" resultType="Employee" parameterType="Employee">
        SELECT
        e.id,
        e.name ename,
        e.sex,
        d.name dname,
        a.name aname
        FROM employee e LEFT JOIN dept d ON d.id=e.deptid
        LEFT JOIN admin a ON a.id=e.adminid
        <where>
            <choose>
                <when test="name != null &amp; name != ''">
                    e.name like concat('%',#{name},'%')
                </when>
                <otherwise>
                    e.name ='张三'
                </otherwise>
            </choose>
        </where>
    </select>

如果 name != null & name != ’ ’ 则进行姓名的模糊查询,否则查找张三的信息

3.5 set元素

一般将其用在修改的sql中 Set 元素可以把最后一个逗号去掉

    <update id="updateEmployee" parameterType="Employee">
        update employee
        <set>
            <if test="name!=null">
                name=#{name},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="admin.id !=null">
                adminid=#{admin.id},
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </update>

3.6 foreach元素

主要用在构建 in 条件中,它可以在 SQL 语句中进行迭代一个集合。

1.item:表示集合中每一个元素进行迭代时的别名
2.index:指定一个名字,用于表示在迭代过程中,每次迭代到的位置
3.open:表示该语句以什么开始
4.separator: 表示在每次进行迭代之间以什么符号作为分隔符
5.close: 表示以什么结束
6.collection:该属性是必须指定的,但是在不同情况下,该属性的值不一样
如果传入的是单参数且参数类型是一个 List 的时候,collection 属 性值为 list
如果传入的是单参数且参数类型是一个 array 数组的时候, collection 的属性值为 array

    <!--主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合-->
    <select id="selectEmployeeDeptIn" resultType="Employee" parameterType="Employee">
          SELECT id,NAME,deptid,adminid FROM employee where deptid in
          <foreach collection="list" item="deptid"  open="(" separator="," close=")">
              #{deptid}
          </foreach>
    </select>

接口中的方法

List<Employee> selectEmployeeDeptIn(List<Integer> list);

测试代码

        List<Integer>list=new ArrayList<Integer>();
        list.add(2);
        list.add(3);
        employeeMapper.selectEmployeeDeptIn(list);

该语义即为查找部门号为2或者3的员工信息


四、特殊符号处理

mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<> 等,正常书写 mybatis 会报错,需要对这些符号进行转义。
在这里插入图片描述


五、缓存

为什么使用缓存:减轻数据库的压力,从数据库中查询出来的对象先不销毁,存在内存中,当以后再使用时直接从内存中读取,缓存主要用于select语句,减小对数据库的查询次数,提高数据库的内存。

5.1一级缓存

一级缓存的作用域是sqlsession,当同一个sqlsession执行了两次相同的sql语句,第一次读取到的数据会存储在内存中
第二次查询将不再从数据库中读取,直接从缓存中读取,当sqlsession结束后,一级缓存也就不存在。mybatis默认开启一级缓存

在这里插入图片描述
测试代码

    @org.junit.Test
    public void select() {
        SqlSession sqlSession = sessionFactory.openSession();
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
        List<Integer> list = new ArrayList<Integer>();
        list.add(2);
        employeeMapper.selectEmployeeDeptIn(list);
        employeeMapper.selectEmployeeDeptIn(list);
    }

在这里插入图片描述
从控制台信息可以看到只执行了一次查询

5.2一级缓存的生命周期

MyBatis 在开启一个数据库会话时:
1.创建一个新的SqlSession 对象
2.SqlSession对象中会有一个新的 Executor 对象
3.Executor 对象中持有一个新的PerpetualCache对象
4.如果SqlSession调用了close()方法,会释放掉一级缓存 PerpetualCache对象,一级缓存将不可用

注意:
SqlSession调用clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用

    @org.junit.Test
    public void select() {
        SqlSession sqlSession = sessionFactory.openSession();
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
        List<Integer> list = new ArrayList<Integer>();
        list.add(2);
        employeeMapper.selectEmployeeDeptIn(list);
        /*清空缓存区域*/
        sqlSession.clearCache();
        employeeMapper.selectEmployeeDeptIn(list);
    }

在这里插入图片描述
在这里插入图片描述
从控制台信息可以看到执行了二次查询

注意:
SqlSession 中执行了任何一个update操作(update()、delete()、 insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用

5.3二级缓存

二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域 的,相同 namespace 的 mapper 查询的数据缓存在同一个区域
在这里插入图片描述

配置二级缓存
1.在mybatis中配置

<setting name="cacheEnabled" value="true"/>

2.在pojo类中实现序列化接口:Java.io. Serializable
在这里插入图片描述

3.在 Mapper 映射文件中添加,表示此 mapper 开启二级缓存
在这里插入图片描述
测试代码

    @org.junit.Test
    public void select1() {
        SqlSession sqlSession1 = sessionFactory.openSession();
        SqlSession sqlSession2 = sessionFactory.openSession();

        EmployeeMapper employeeMapper = sqlSession1.getMapper(EmployeeMapper.class);
        EmployeeMapper employeeMapper2 = sqlSession2.getMapper(EmployeeMapper.class);

        List<Integer> list = new ArrayList<Integer>();
        list.add(2);
        employeeMapper.selectEmployeeDeptIn(list);
        
        /*当 SqlSeesion 关闭时,会将数据存入到二级缓存*/
        sqlSession1.close();
        employeeMapper2.selectEmployeeDeptIn(list);
    }

当 SqlSeesion 关闭时,会将数据存入到二级缓存
在这里插入图片描述
从控制台信息可以看到只执行了一次查询


总结

本章主要讲解了mybatis的动态sql和缓存机制,动态sql作为本章的重点,一定要熟练掌握。关于mybatis的基本内容已经介绍完毕,在后续的学习中,我们将把mybatis和spring框架结合起来,利用spring的事务管理功能更好的实现mybatis框架。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JinziH Never Give Up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值