mybatis入门篇3 ---- 动态sql,缓存,以及分页jar包的使用

首先我们来看一下动态sql,动态sql就是传递的参数不确定的时候,使用if,where,select,choose,set等标签,先来看一下

lib,rescources,以及utils里面文件不变,直接来看使用

直接看if跟where,if使用比较简单,就是if会有一个条件判断,如果条件满足,就会把if里面的sql语句块加入slq语句,where就是帮助我们加载一个where条件判断,并且会把拼接语句中的第一个and删除掉,接下来看一下例子

看一下UserMapper

public interface UserMapper {
    List getUsers(@Param("username") String username, @Param("password") String password);
}

 看一下UserMapper.xml

<mapper namespace="com.yang.mapper.UserMapper">








    <select id="getUsers" resultType="com.yang.domain.User">
        select * from `user`
        <where>
            <if test="username != null and username != ''">
                and username=#{username}
            if>
            <if test="password != null and password != ''">
                and password=#{password}
            if>
        where>
    select>
mapper>

看一下测试

    @Test
    public void testWhere(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 调用这个封装的where标签,password传入为空,可以看出,执行的sql语句就会不带后面的password
        List userList = userMapper.getUsers("yang", null);  // ==>  Preparing: select * from `user` WHERE username=?
        // 如果全部为空,那么就会忽略所有的if字段
        List userList2 = userMapper.getUsers(null, null);  // ==>  ==>  Preparing: select * from `user`
        // 第一个if条件我们的是带有if的,而sql语句中不存在,因此可以确认,where确实是把第一个语句中的开头的and删除了。
        List users = userMapper.getUsers("shi", "5678");  // ==>  Preparing: select * from `user` WHERE username=? and password=?
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }

where可以删除第一个语句的前置and,但是无法删除结尾的and,接下来看一下trim标签,该标签可以删除前置或后置的指定的字符串

UserMapper

// trim
    List getUserList(@Param("username") String username, @Param("password") String password);

UserMapper.xml

    
    <select id="getUserList" resultType="com.yang.domain.User">
        select * from `user`
        <trim prefix="where" prefixOverrides="and" suffixOverrides="and">
            <if test="username != null and username != ''">
                and username=#{username}
            if>
            <if test="password != null and password != ''">
                and password=#{password} and
            if>
        trim>
    select>

测试类

    @Test
    public void testTrim(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 调用trim标签的映射,在if判断中第一个前置有and,最后一个语句后置and,利用trim的删除,删除掉了
        List users = userMapper.getUserList("shi", "5678");  // ==>  Preparing: select * from `user` where username=? and password=?
        for (User user : users) {
            System.out.println(user);  // User{id=2, username='shi', password='5678'}
        }
        // 两个传入都是空值,可以看出不符合if,不执行
        List users2 = userMapper.getUserList("", "");  // ==>  Preparing: select * from `user`
        sqlSession.close();
    }

接下来看一下choose标签,这个标签的作用就是只要满足一个条件不执行其他条件了,相当于select标签

看一下mapper文件

    // choose
    List getUserChoose(@Param("username") String username, @Param("password") String password);

看一下UserMapper.xml文件

    
    <select id="getUserChoose" resultType="com.yang.domain.User">
        select * from `user`
        <where>
            <choose>
                <when test="username != null and username != ''">
                    username=#{username}
                when>
                <when test="password != null and password != ''">
                    password=#{password}
                when>
                <otherwise>
                    1 = 1
                otherwise>
            choose>
        where>
    select>

测试一下

@Test
    public void testChoose(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 我们可以看出这个两个参数均满足之前所说的条件,但是最终只是执行了最上面的满足条件的语句
        List users = userMapper.getUserChoose("shi", "5678");  // ==>  Preparing: select * from `user` WHERE username=? 
        for (User user : users) {
            System.out.println(user);
        }
        // 两个都不满足,就会执行otherwise
        List users2 = userMapper.getUserList("", "");  // ==>  Preparing: select * from `user` 
    }

在实际应用时,我们会遇到传入一个主键数组列表,返回对应对象,sql语句中使用的是in(?),不能直接传入数组,因此这时候可以使用forEach标签

看一下接口文件

 // forEach, 这个可以传入数组,列表,pojo对象也可以,只要是列表类的就行
    List getUserByIds(@Param("idList") Integer[] ids);

映射文件

    
    <select id="getUserByIds" resultType="com.yang.domain.User">
        select * from `user` where id in
        <foreach collection="idList" open="(" close=")" separator="," item="ids">
            #{ids}
        foreach>
    select>

看一下测试

    @Test
    public void testForEach() {
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 使用数组或者列表都可以,主要看java中的映射是如何定义的,可以看出,foreach会帮助我们循环传入的数据并格式化为sql需求的样子
        List users = userMapper.getUserByIds(new Integer[]{11, 12, 13, 14, 15});  // ==>  Preparing: select * from `user` where id in ( ? , ? , ? , ? , ? )  ==> Parameters: 11(Integer), 12(Integer), 13(Integer), 14(Integer), 15(Integer)
        for (User user : users) {
            System.out.println(user);
        }
        // User{id=11, username='mark', password='1111'}
        // User{id=12, username='mark', password='1111'}
        // User{id=13, username='mark', password='1111'}
        // User{id=14, username='mark', password='1111'}
        // User{id=15, username='mark', password='1111'}
    }

最后我们看一下bind标签,set标签,bind标签可以取出传入的值,并进行重新处理,赋值给另外一个值,set标签会把最后一个,号去掉

看一下mapper文件

    // set 与 bind
    void updateUser(User user);

看一下我们的映射文件

    
    <update id="updateUser">
        <bind name="username" value="username+'bind'" />
        update `user`
        <set>
            <if test="username != null and username != ''">
                username=#{username},
            if>
            <if test="password != null and password != ''">
                password=#{password},
            if>
        set>
         where id=#{id}
    update>

看一下测试类

    @Test
    public void update() {
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("yang");
        user.setPassword("1234");
        user.setId(2);
        // 通过sql语句可以看出,虽然名称设置的是yang,但是我们在bind标签中拦截了username并且在最后加了一个bind,最终他们会添加上。
        userMapper.updateUser(user);  // ==>  Preparing: update `user` SET username=?, password=? where id=?   ==> Parameters: yangbind(String), 1234(String), 2(Integer)
        sqlSession.commit();
        sqlSession.close();
    }

我们来看一下include,sql标签,对于重复性语句,我们可以提出封装成一个sql语句块,然后使用include进行引用,并且可以传值

看一下mapper,定义了两个接口

    // sql 与include语句
    User getUserById(@Param("id") Integer id);
    User getUser(@Param("id") Integer id);

看一下UserMapper.xml

    
    <select id="getUserById" resultType="com.yang.domain.User">
        <include refid="selectUser">
            <property name="lk" value="2" />
        include>
        where id=#{id}
    select>
    <select id="getUser" resultType="com.yang.domain.User">
        <include refid="selectUser" />
        where id=#{id}
    select>

    
    <sql id="selectUser">
        <choose>
            <when test="${lk} ==2">
                select `username` from `user`
            when>
            <otherwise>
                select * from `user`
            otherwise>
        choose>
    sql>

看一下测试类

    @Test
    public void getUser() {
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 可以看出使用这个之后就会把之前的定义的sql进行拼接,并且通过property进行传值,sql语句块也接受到了,只查询username
        User user2 = userMapper.getUserById(2);  // ==>  Preparing: select `username` from `user` where id=?
        System.out.println(user2);  // User{id=null, username='yangbind', password='null'}
    }

基本动态语句够用了,接下来看一下mybatis的缓存,mybatis缓存分为

一级缓存,只存在同一个sqlSession,对于用一个sqlsession,如果参数和sql完全一样的情况下,并且中间没有增删改,没有关闭sqlSession,没有删除缓存,并且没有超时,那么同一个sqlsession调用一个mappper对象,只会执行一次sql,剩余会走缓存取,并不会再次查询数据库,一级缓存默认就是开启的。

 

mybatis入门篇3 ---- 动态sql,缓存,以及分页jar包的使用_第1张图片

 

二级缓存,是mapper级别的缓存,顾名思义缓存只存在与yigemapper中,并且二级缓存就在命名空间中命名,二级缓存默认是不开启的,二级缓存需要配置,并且二级缓存中实现返回的PoJo必须是可序列化,也就是必须实现Serializable接口。二级缓存我们一般使用第三方,因为mybatis并不是专业做缓存的。

 

 mybatis入门篇3 ---- 动态sql,缓存,以及分页jar包的使用_第2张图片

首先我们先来配置一下,把一级缓存,二级缓存的配置一下

先看一下配置文件,直接在settings李 main开启二级缓存,一级缓存是开启的

    <settings>
     ...
        
        <setting name="cacheEnabled" value="true" />
        
        <setting name="localCacheScope" value="SESSION" />

    settings>

看一下pojo类,如果需要开启二级缓存,这个类需要实现接口implements

public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
.....
}

接下来看一下我我们的marpper

package com.yang.mapper;

import com.yang.domain.User;

public interface UserMapper2 {

    User getUserById(Integer id);

    void insertUser(User user);
}
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="com.yang.mapper.UserMapper2">
    
    <cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024" />

    
    <select id="getUserById" resultType="com.yang.domain.User" useCache="true">
        select * from `user` where id=#{id}
    select>

    <insert id="insertUser">
        insert into `user`(username, password) values (#{username}, #{password})
    insert>
mapper>

首先我们做一下一级缓存的测试

    /**
     * 测试一级缓存
     * 从这个测试缓存可以看出两次查询相同的mapper对象,
     * 只查询了一次数据库,并且两个对象是相同的,这个是以及缓存
     */
    @Test
    public void testCache(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper2 userMapper = sqlSession.getMapper(UserMapper2.class);
        User user1 = userMapper.getUserById(2);
        System.out.println(user1);  // User{id=2, username='yangbind', password='1234'}
        User user2 = userMapper.getUserById(2);
        System.out.println(user2);  // User{id=2, username='yangbind', password='1234'}
        System.out.println(user1 == user2);  // true
        /*
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@66ea810]
        ==>  Preparing: select * from `user` where id=?
        ==> Parameters: 2(Integer)
        <==    Columns: id, username, password
        <==        Row: 2, yangbind, 1234
        <==      Total: 1
        User{id=2, username='yangbind', password='1234'}
        Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.0
        User{id=2, username='yangbind', password='1234'}
        true
         */
    }

接下来看一下中间插入数据,一级缓存会失效,执行了两次查询数据库操作

/**
     * 通过打印结果可以看出,如果中间执行了一次数据库改变操作,那么就是是一级缓存失效
     * 并且同时我们也可以发现,二级缓存也没有作用,这是因为,只有关闭sqlSEssion之后,一级缓存才会将内容写入二级缓存
     */
    @Test
    public void testCache2(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper2 userMapper = sqlSession.getMapper(UserMapper2.class);
        User user1 = userMapper.getUserById(2);
        System.out.println(user1);  // User{id=2, username='yangbind', password='1234'}
        User user = new User();
        user.setUsername("yang");
        user.setPassword("13456");
        userMapper.insertUser(user);
        User user2 = userMapper.getUserById(2);
        System.out.println(user2);  // User{id=2, username='yangbind', password='1234'}
        System.out.println(user1 == user2);  // false
        /*
        ==>  Preparing: select * from `user` where id=? 
        ==> Parameters: 2(Integer)
        <==    Columns: id, username, password
        <==        Row: 2, yangbind, 1234
        <==      Total: 1
        User{id=2, username='yangbind', password='1234'}
        ==>  Preparing: insert into `user`(username, password) values (?, ?) 
        ==> Parameters: yang(String), 13456(String)
        <==    Updates: 1
        Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.0
        ==>  Preparing: select * from `user` where id=? 
        ==> Parameters: 2(Integer)
        <==    Columns: id, username, password
        <==        Row: 2, yangbind, 1234
        <==      Total: 1
        User{id=2, username='yangbind', password='1234'}
     false
         */
    }

通过上述例子,大体可以分析出缓存的查询顺序,先查询一级缓存,在查询二级缓存。

并且一开始二级缓存是没有东西的,只有关闭sqlSession之后,才会将一级缓存对象存入二级缓存,接下来看一下二级缓存例子

 /**
     * 这个是关闭了一级缓存,这个我们没有关闭sqlSession,使用同一个sqlSession,
     * 执行结果显示是查询了两次数据库
     */
    @Test
    public void testSecond(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper2 userMapper = sqlSession.getMapper(UserMapper2.class);
        User user1 = userMapper.getUserById(2);
        System.out.println(user1);  // User{id=2, username='yangbind', password='1234'}
        User user2 = userMapper.getUserById(2);
        System.out.println(user2);  // User{id=2, username='yangbind', password='1234'}
        System.out.println(user1 == user2);  // false
        sqlSession.close();
        /*
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@508dec2b]
        ==>  Preparing: select * from `user` where id=?
        ==> Parameters: 2(Integer)
        <==    Columns: id, username, password
        <==        Row: 2, yangbind, 1234
        <==      Total: 1
        User{id=2, username='yangbind', password='1234'}
        Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.0
        ==>  Preparing: select * from `user` where id=?
        ==> Parameters: 2(Integer)
        <==    Columns: id, username, password
        <==        Row: 2, yangbind, 1234
        <==      Total: 1
        User{id=2, username='yangbind', password='1234'}
        false
         */
    }

    /**
     * 这个是关闭了一级缓存,这个我们关闭sqlSession,使用同一个sqlSession,
     * 执行结果显示是查询了一次数据库,并且两个对象都是一样的
     */
    @Test
    public void testSecond2(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper2 userMapper = sqlSession.getMapper(UserMapper2.class);
        User user1 = userMapper.getUserById(2);
        System.out.println(user1);  // User{id=2, username='yangbind', password='1234'}
        sqlSession.close();
        SqlSession sqlSession2 = MyBatisUtils.openSession();
        UserMapper2 userMapper2 = sqlSession2.getMapper(UserMapper2.class);
        User user2 = userMapper2.getUserById(2);
        System.out.println(user2);  // User{id=2, username='yangbind', password='1234'}
        System.out.println(user1 == user2);  // true
        sqlSession2.close();
        /*
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@798162bc]
        ==>  Preparing: select * from `user` where id=? 
        ==> Parameters: 2(Integer)
        <==    Columns: id, username, password
        <==        Row: 2, yangbind, 1234
        <==      Total: 1
        User{id=2, username='yangbind', password='1234'}
        Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@798162bc]
        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@798162bc]
        Returned connection 2038522556 to pool.
        Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.5
        User{id=2, username='yangbind', password='1234'}
        true
         */
    }

这个大体上就是缓存,最终看一下分页插件的使用

先看一下引用的jar包

 

 

我们之前在看mybats的配置文件是,看到了plugins这个配置,我们就是在这里面进行引用第三方插件,看一下引用

    <plugins>
        
        <plugin interceptor="com.github.pagehelper.PageInterceptor" />
    plugins>

接下来看一下使用方法

@Test
    public void testPage() {
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper2 userMapper = sqlSession.getMapper(UserMapper2.class);
        // 这个就是指定使用分页器,在查询之前声明,否则不起作用,第一个参数是第几页,第二个参数是一页几条
        Page page = PageHelper.startPage(1,2);
        List users = userMapper.getUsers();
        for (User user : users) {
            System.out.println(user);
            /*
            User{id=2, username='yangbind', password='1234'}
            User{id=3, username='xiong', password='9012'}
             */
        }
        System.out.println(page.getPageNum());  // 1  获取当前页吗
        System.out.println(page.getPageSize());  // 2 获取当前页的条数
        System.out.println(page.getPages());  // 6  获取总页数
        System.out.println(page.getTotal());  // 11 获取总条数
    }

    @Test
    public void testPage2() {
        SqlSession sqlSession = MyBatisUtils.openSession();
        UserMapper2 userMapper = sqlSession.getMapper(UserMapper2.class);
        // 这个就是指定使用分页器,在查询之前声明,否则不起作用,第一个参数是第几页,第二个参数是一页几条
        Page page = PageHelper.startPage(1,2);
        List users = userMapper.getUsers();
        // 将查询的结果做进一步封装,可以获取是否有下一页以及是否有上一页,并且可以返回页码,第二个参数是指定页码
        PageInfo pageInfo = new PageInfo<>(users, 2);
        for (User user : users) {
            System.out.println(user);
            /*
            User{id=2, username='yangbind', password='1234'}
            User{id=3, username='xiong', password='9012'}
             */
        }
        for (User user : pageInfo.getList()) {
            System.out.println(user);
            /*
            User{id=2, username='yangbind', password='1234'}
            User{id=3, username='xiong', password='9012'}
             */
        }
        System.out.println(pageInfo.getPageNum());  // 1  获取当前页吗
        System.out.println(pageInfo.getPageSize());  // 2 获取当前页的条数
        System.out.println(pageInfo.getPages());  // 6  获取总页数
        System.out.println(pageInfo.getTotal());  // 11 获取总条数
        System.out.println(pageInfo.isHasPreviousPage());  // false 获取是否有上一页
        System.out.println(pageInfo.isHasNextPage());  // true 获取是否有下一页
        System.out.println(Arrays.toString(pageInfo.getNavigatepageNums()));  // [1, 2] 获取展示的页码
    }

 

源码可以在github上看:https://github.com/yang-shixiong/springDemo

 

你可能感兴趣的:(mybatis入门篇3 ---- 动态sql,缓存,以及分页jar包的使用)