Mybatis基础知识04:动态sql和缓存的概念

动态sql概念介绍:指的是根据不同的查询条件,生成不同的Sql语句。
根据官网详细描述:

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
如果要编写复杂的sql语句,需要拼接,但是在java中拼接则需要注意很多例如空格等问题,很麻烦,这样就诞生了动态sql,在xml文件中利用标签来拼接语句,减少了难度,提高了拼接的效率。

环境搭建和基本工具类的编写

实体类编写
@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
接口及对应的Mybatis.xml文件编写
public interface BlogMapper {
    int addBlog(Blog blog);//增加博客信息
}

mybatis核心配置文件中,添加下划线驼峰自动转换的功能

<settings>
   <setting name="mapUnderscoreToCamelCase" value="true"/>
   <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--注册Mapper.xml-->
<mappers>
 <mapper resource="mapper/BlogMapper.xml"/>
</mappers>
IDUtil工具类

该工具类的作用就是利用UUID.randomUUID()方法随机生成Blog的id字段的值,以及将所有的 “-”替换成“ ”(空值),增加其复杂性,防止他人破解id。

public class IDUtils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }
    @Test
    public void test(){
        System.out.println(IDUtils.getId());
        System.out.println(IDUtils.getId());
        System.out.println(IDUtils.getId());
    }
}

测试结果如下:
在这里插入图片描述

初始化数据

 <insert id="addBlog" parameterType="blog">
        insert into blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>

插入部分数据:

public class MyTest {
    @Test
    public void addBlog(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog=new Blog();
        blog.setId(IDUtils.getId());
        blog.setTitle("Mybatis");
        blog.setAuthor("王长军");
        blog.setCreateTime(new Date());
        blog.setViews(9999);

        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("Java");
        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("Spring");
        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("微服务");
        mapper.addBlog(blog);
        sqlSession.close();
    }
}

部分数据结果如下:
在这里插入图片描述
到这里准备部分就结束了,之后就开始我们的动态sql了。

if语句:

需求:根据作者名字或博客名字来查询整条博客的信息,若作者名字为空,则根据博客名字来查询,反之,则根据作者名字来查询。

1.添加接口方法

List<Blog> getBlog(Map map);

2.对应的xml文件中的sql语句

在sql语句中,主要注意的就是与标签内容替代了原本where title=#{title} and author=#{author};

  <select id="getBlog" parameterType="map" resultType="Blog">
        select * from blog
        <where>
        <if test="title != null">
            and title=#{title}
        </if>
        <if test="author !=null">
            and author=#{author}
        </if>
        </where>
    </select>

3.测试类:

@Test
public void testQueryBlogIf(){
   SqlSession session = MybatisUtils.getSession();
   BlogMapper mapper = session.getMapper(BlogMapper.class);

   HashMap<String, String> map = new HashMap<String, String>();
   map.put("title","Mybatis如此简单");
   map.put("author","狂神说");
   List<Blog> blogs = mapper.queryBlogIf(map);

   System.out.println(blogs);

   session.close();
}

同理得出有关set,choose的相关sql语句:

<update id="updateBlog" parameterType="map" >
update blog
         <set>
         <if test="title != null">
             title=#{title},
         </if>
         <if test="author != null">
              author=#{author}
         </if>
         </set>
         where id=#{id}
 </update>

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

<select id="chooseBlog" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <choose>
           <when test="title !=null">
               title=#{title}
           </when>
           <when test="author != null">
               and author=#{author}
           </when>
           <otherwise>
               and views=#{views}
           </otherwise>
        </choose>
    </where>
</select>

小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。

动态SQL在开发中大量的使用,一定要熟练掌握

缓存

缓存【cache】的含义:
缓存是存在内存中的临时数据,它可以将用户经常查询的数据放在内存中,这样用户去查询数据的时候就不用从磁盘上(关系型数据库的数据文件)查询,从缓存中查询,从而提高查询效率,解决了并发系统的性能问题。

缓存的意义:
减少和数据库的交互次数,减少系统开销,提高查询效率。

缓存使用的前提:
经常查询且不经常需要改变的数据。

Mybatis缓存简介:

MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)

二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。

一级缓存

一级缓存也叫本地缓存:

  1. 与数据库同一次会话期间查询到的数据库会放在本地缓存中。
  2. 以后如果需要获取相同的数据,直接从缓存中,没必要到数据库中去拿了。
  3. 一级缓存无法关闭。
    测试:
    查询两次id为1的用户,当两次对象的地址相同时,为true时则代表两次取的同一个对象,若不相同,为false时,则代表不是同一个对象。
@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
}

结果:
在这里插入图片描述另外,出现以下四种情况,则一级缓存失败:

  1. 开启了两个sqlSession(sqlSession不同)
  2. sqlSession相同,查询条件不同(一个是查询的id为1,一个查询的是id为2的)
  3. sqlSession相同,两次查询之间执行了增删改的操作(增删改操作可能会对当前数据产生影响)
  4. sqlSession相同,但是执行过手动清楚过缓存的方法(session.clear())

二级缓存

二级缓存

二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

工作机制

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

新的会话查询信息,就可以从二级缓存中获取内容;

不同的mapper查出的数据会放在自己对应的缓存(map)中;

使用步骤

1.在Mybatis.config.xml文件中<setting 标签中添加:

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

2.去每个mapper.xml中配置使用二级缓存:

<cache/>

官方示例=====>查看官方文档
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

代码测试

UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();

代码中,在关闭了session之后,再次查询了同一个数据,对比两条数据之后,依然查询到两条数据是同一个地址,为true。则代表二级缓存开启,无需经过数据库,直接在缓存中拿到同一条数据。
结论

  1. 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  2. 查出的数据都会被默认先放在一级缓存中
  3. 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
    缓存底层原理如下:
    在这里插入图片描述
    总结:合理的运用缓存则会提高查询效率,方便程序执行。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值