动态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接口来自定义二级缓存。
一级缓存
一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据库会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中,没必要到数据库中去拿了。
- 一级缓存无法关闭。
测试:
查询两次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();
}
结果:
另外,出现以下四种情况,则一级缓存失败:
- 开启了两个sqlSession(sqlSession不同)
- sqlSession相同,查询条件不同(一个是查询的id为1,一个查询的是id为2的)
- sqlSession相同,两次查询之间执行了增删改的操作(增删改操作可能会对当前数据产生影响)
- 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。则代表二级缓存开启,无需经过数据库,直接在缓存中拿到同一条数据。
结论
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
- 查出的数据都会被默认先放在一级缓存中
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
缓存底层原理如下:
总结:合理的运用缓存则会提高查询效率,方便程序执行。