WebService xcode macos azure ssl dynamic pyspark 纯html网页模板 axure导出html文件 svn查看历史版本 python基础 pythonapi java在线学习 javaobject java类的继承 如何查看java版本 java替换字符串 java线程中断 exescope教程 御旌是什么 js获取父节点 超级力量2修改 云管家 netreflector 苹果手机添加邮箱 软件龙头股 js递归函数 dota2控制台 文件压缩工具 摇骰子表情包 冬青黑体简体中文 adb安装 如何删除注册表 edius调色 sql列转行 python去掉空格 樱牛在哪 强制删除桌面ie图标 hscan 标记宏
当前位置: 首页 > 学习教程  > 编程语言

Mybatis学习笔记3

2020/12/5 10:37:43 文章标签:

Mybatis学习笔记3动态SQL介绍搭建环境If标签Where标签Choose标签Set标签SQL片段Forech标签小结缓存简介Mybatis缓存一级缓存一级缓存失效的情况二级缓存动态SQL 介绍 什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句 之前写的 SQL 语句都比较…

Mybatis学习笔记3

  • 动态SQL
    • 介绍
    • 搭建环境
    • If标签
    • Where标签
    • Choose标签
    • Set标签
    • SQL片段
    • Forech标签
    • 小结
  • 缓存
    • 简介
    • Mybatis缓存
    • 一级缓存
    • 一级缓存失效的情况
    • 二级缓存

动态SQL

介绍

什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句

之前写的 SQL 语句都比较简单,如果有比较复杂的业务,需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。

那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

动态 SQL 是 MyBatis 的强大特性之一,与 JSTL 或基于类 XML 语言的文本处理器类似。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

搭建环境

1.创建数据库表blog

CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2.创建mybatis基础工程
在这里插入图片描述

实际项目中一般需要保持ID的唯一性,固可以编写一个IDutils,来随机生成ID

@SuppressWarnings("all")  // 抑制警告
public class IDutils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

3.编写实体类

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;  // 属性名与字段名不一致
    private int views;
}

4.编写Mapper接口及xml文件

public interface BlogMapper {
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.NATE.dao.BlogMapper">

</mapper>

5.mybatis核心配置文件,解决属性名和字段名不一致问题——下划线驼峰自动转换,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn

<settings>
    <!-- 是否开启驼峰命名自动映射 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

6.插入数据

  • 在接口里编写方法
 public interface BlogMapper {
    // 插入数据
    int addBlog(Blog blog);
}
  • 在对应xml文件进行配置
 <insert id="addBlog" parameterType="Blog">
    insert into blog (id, title, author, create_time, views)
    values (#{id}, #{title}, #{author}, #{createTime}, #{views})
</insert>
  • 在测试类中插入数据
 @Test
public void addInitBlog(){
    SqlSession sqlSession = MybatisUtils.getSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    Blog blog = new Blog();
    blog.setId(IDutils.getId());
    blog.setTitle("凡人终有一死");
    blog.setAuthor("达摩流浪者");
    blog.setCreateTime(new Date());
    blog.setViews(10);
    mapper.addBlog(blog);

    blog.setId(IDutils.getId());
    blog.setTitle("go for it");
    mapper.addBlog(blog);

    sqlSession.close();
}

If标签

测试案例:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询

1.编写接口类

   // 查询博客
    List<Blog> queryBlogIF(Map map);

2.编写SQL语句

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog where 1=1
    <!-- 若传入title,则拼接sql -->
    <if test="title != null">
        and title = #{title}
    </if>
    <!-- 若传入author,则拼接sql -->
    <if test="author != null">
        and author = #{author}
    </if>
</select>

3.测试

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

    HashMap map = new HashMap();
    map.put("title","凡人终有一死");
    map.put("author","达摩流浪者");
    List<Blog> blogs = mapper.queryBlogIF(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

Where标签

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

测试案例:修改优化上述案例

修改SQL语句:

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <!-- 若传入title,则拼接sql -->
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</select>

Choose标签

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

1.编写接口类

List<Blog> queryBlogChoose(Map map);

2.编写SQL语句

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

3.测试

@Test
public void queryBlogChoose(){
    SqlSession sqlSession = MybatisUtils.getSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    //map.put("title","凡人终有一死");
    // map.put("author","达摩流浪者");
    map.put("views",10);
    List<Blog> blogs = mapper.queryBlogChoose(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

Set标签

用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

1.编写接口方法

//更新博客
int updateBlog(Map map);

2.编写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>

3.测试

@Test
public void updateBlog(){
    SqlSession sqlSession = MybatisUtils.getSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("title","凡人终有一死1");
    map.put("author","达摩流浪者1");
    map.put("id","97213138eda644af9056fff279ac86ee");
   mapper.updateBlog(map);
    sqlSession.close();
}

SQL片段

有时候可能某个 sql 语句用的特别多,为了增加代码的重用性,简化代码,可以将这些代码抽取出来,然后使用时直接调用。

1.提取SQL片段

<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

2.引用SQL片段

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

注意点:

  • 最好基于单表来定义 sql 片段,提高片段的可重用性
  • 在 sql 片段中不要包括 where

Forech标签

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。

测试案例:
将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息

1.编写接口方法

//查询第1,2,3号记录的博客
List<Blog> queryBlogForeach(Map map);

2.编写SQL语句

  <!--
         select * from blog where 1=1 and (id=1 or id=2 or id=3)
    -->
    <select id="queryBlogForeach" parameterType="Map" resultType="blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
            </foreach>       
        </where>
    </select>

3.测试

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

    HashMap map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);

    map.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogForeach(map);

    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

小结

其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错

缓存

简介

1.什么是缓存 [ Cache ]?

  • 存在于内存中的临时数据
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3.什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据【可以使用使用缓存】

Mybatis缓存

  • MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。
  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
  • 默认情况下,只有一级缓存开启。(SqlSession【接口】级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,是基于namespace级别的缓存。
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。可以通过实现Cache接口来自定义二级缓存

一级缓存

一级缓存也叫本地缓存:SqlSession

  • 与数据库同一次会话期间查询到数据会放在本地缓存中
  • 之后若需要获得相同的数据,直接从缓存中拿,不会再连接查询数据库

测试步骤:
1.开启日志

<!-- 标准的日志工厂实现 -->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

2.编写接口方法

<select id="queryUsersById"  resultType="user">
    select * from user where id = #{id}
</select>

3.测试

在一个Session中查询两次相同记录

@Test
    public void queryUsersById(){
        SqlSession sqlSession = MybatisUtils.getSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUsersById(1);
        System.out.println(user);
        System.out.println("=====================");
        User user1 = mapper.queryUsersById(1);
        System.out.println(user1);
        System.out.println(user==user1);
        sqlSession.close();
    }

4.结果分析
在这里插入图片描述

一级缓存失效的情况

1.sqlSession不同

@Test
public void queryUsersById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

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

   session.close();
   session2.close();
}

2.增删改操作,可能会改变原来的数据,所以一点会刷新缓存

在两次查询中加入一条与查询语句无关的更新语句

@Test
public void queryUsersById(){
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUsersById(1);
    System.out.println(user);
    mapper.updateUser(new User(2,"wjz","321132")); //更新id=2的用户数据
    System.out.println("=====================");
    User user1 = mapper.queryUsersById(1);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession.close();
}

3.sqlSession相同,查询条件不同

@Test
public void queryUsersById(){
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUsersById(1);
    System.out.println(user);
    System.out.println("=====================");
    User user1 = mapper.queryUsersById(2);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession.close();
}

4.sqlSession相同,手动清除一级缓存

@Test
public void queryUsersById(){
    SqlSession sqlSession = MybatisUtils.getSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUsersById(1);
    System.out.println(user);
    session.clearCache();//手动清除缓存
    System.out.println("=====================");
    User user1 = mapper.queryUsersById(2);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession.close();
}

小结:一级缓存是默认开启的,只在一次SqlSession中有效,也就是拿到连接到关闭数据库连接。一级缓存相当于一个Map

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制:
    1.一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    2.如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是:会话关闭后,一级缓存中的数据被保存到二级缓存中
    3.新的会话查询信息,就可以从二级缓存中获取内容
    4.不同的mapper查出的数据会放在自己对应的缓存(map)中

使用步骤:
1.在mybatis-config.xml中开启全局配置

<settings>
    <!-- 显示的开启全局缓存 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

2.在mapper.xml中配置二级缓存

<!-- 在当前Mapper.xml使用二级缓存 -->
<cache/>

也可以自定义参数

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

3.测试

@Test
public void queryUsersById(){
    //创建两个Session
    SqlSession sqlSession = MybatisUtils.getSession();
    SqlSession sqlSession2 = MybatisUtils.getSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUsersById(2);
    System.out.println(user);
    sqlSession.close();

    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    User user2 = mapper2.queryUsersById(2);
    System.out.println(user2);
    
    System.out.println(user==user2);

    
    sqlSession2.close();
}

4.结果分析
在这里插入图片描述

遇见的问题:
在这里插入图片描述
原因:对应实体类没有序列化
解决方法:需要将该实体类序列化

public class User implements Serializable {
    private int id;
    private String name;
    private String pwd;
}

总结:

  • 只要开启了二级缓存,在同一个Mapper中的查询,就能在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

本文链接: http://www.dtmao.cc/news_show_450293.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?