170906-MyBatis续
===============================================Dynamic SQL=================================================
为什么需要动态SQL?有时候需要根据实际传入的参数来动态的拼接SQL语句。
最常用的就是:where和if标签
1.参考官方文档
? if:字符判断
? choose (when, otherwise):分支选择
? trim (where, set):字符串截取;其中where标签封装查询条件,set标签封装修改条件
? foreach
2.if案例:
1)在EmployeeMapper接口中添加一个方法:
//携带了哪个字段,查询条件就带上哪个字段的值
public List<Employee> getEmployeeByConditionIf(Employee employee);
2).如果要写下列的SQL语句,只要是不为空,就作为查询条件,如下所示,这样写实际上是有问题的,所以我们要写成动态SQL语句:
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee">
select *from tbl_employee where id = #{id} and user_name = #{userName} and email = #{email} and gender = #{gender}
</select>
3)用if标签改写为动态SQL,如下所示:
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee">
select *from tbl_employee
where
<!-- test:判断表达式(OGNL) OGNL参照PPT或者官方文档。 c:if test 从参数中取值进行判断 遇见特殊符号,应该去写转义字符:参考W3CSchool>>HTML>>ISO8859 -->
<if test="id != null">
id = #{id}
</if>
<if test="userName != null && userName !=''">
and user_name = #{userName}
</if>
<if test="email != null and email.trim() != """>
and email = #{email}
</if>
<!-- ognl会进行字符串和数字的转换判断;"0"==0,"1"==1 -->
<if test="gender == 0 or gender == 1">
and gender = #{gender}
</if>
</select>
4).测试代码:
@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setId(1);
employee.setUserName("张三丰");
employee.setEmail("sunwukong@163.com");
employee.setGender(1);
List<Employee> list = mapper.getEmployeeByConditionIf(employee);
System.out.println(list);
}
#测试结果没问题 但是仔细来说,上面的sql语句是有问题的,当我们不给动态sql语句传递id值的时候,sql语句的拼装就会有问题!
解决办法:
1.给where后面加上1=1,以后的条件都可以使用and xxx了
2.mybatis可以使用where标签来将所有的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉!//需要注意:where标签只会去掉第一个多出来的and或者or
3.也就是说使用where标签有时候还是不能解决问题的,那怎么办呢?我们这里可以使用trim标签!
2.trim标签:可以自定义字符串的截取规则
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee">
select *from tbl_employee
<!-- 后面多出的and或者or where标签不能够解决 prefix="":前缀:trim标签体重是整个字符串拼串后的结果。 prefix给拼串后的整个字符串加一个前缀 prefixOverrides="": 前缀覆盖:去掉整个字符串前面多余的字符 suffix="":后缀 suffix给拼串后的整个字符串加一个后缀 suffixOverrides="": 后缀覆盖:去掉整个字符串后面多余的字符 -->
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id = #{id} and
</if>
<if test="userName != null && userName !=''">
user_name = #{userName} and
</if>
<if test="email != null and email.trim() != """>
email = #{email} and
</if>
<!-- ognl会进行字符串和数字的转换判断;"0"==0,"1"==1 -->
<if test="gender==0 or gender==1">
gender = #{gender}
</if>
</trim>
</select>
3.choose标签:分支选择,类似于Java中的带了break的switch...case
choose (when, otherwise):如果带了id,就用id查,如果带了userName就用userName查,只会进入其中一个!
案例演示:
1.在EmployeeMapper接口中添加一个方法:
public List<Employee> getEmployeeByConditionChoose(Employee employee);
2.sql映射文件
<!-- public List<Employee> getEmployeeByConditionChoose(Employee employee); -->
<select id="getEmployeeByConditionChoose" resultType="com.neuedu.entity.Employee">
select *from tbl_employee
<where>
<!-- 如果带了id,就用id查,如果带了userName就用userName查,只会进入其中一个! -->
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="userName != null">
user_name like #{userName}
</when>
<when test="email != null">
email = #{email}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
4.trim 中的set标签(where, set):字符串截取;其中where标签封装查询条件,set标签封装修改条件 set元素会动态前置set关键字,同时也会消除无关的逗号。
1).在EmployeeMapper中添加一个更新的方法,如下所示: public void updateEmp(Employee employee);
2)在sql映射文件中,填写相应的sql语句,如下所示【set标签可以将字段后面的逗号去掉】:
<update id="updateEmp">
update tbl_employee
<set>
<if test="userName != null">
user_name = #{userName},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="gender != null">
gender = #{gender},
</if>
</set>
where id = #{id}
</update>
测试类代码为:
@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setId(1);
employee.setUserName("哈哈");
employee.setEmail("sunwukong@163.com");
employee.setGender(1);
mapper.updateEmp(employee);
}
//当然上面的set标签我们也可以使用trim标签来代替,如下所示:
<update id="updateEmp">
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="userName != null">
user_name = #{userName},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="gender != null">
gender = #{gender},
</if>
</trim>
where id = #{id}
</update>
5.foreach:遍历元素 动态SQL的另一个常用的操作是需要对一个集合进行遍历,通常在构建in条件语句的时候!
foreach元素允许指定一个集合,声明集合项和索引变量,并可以指定开闭匹配的字符串以及在迭代之间放置分隔符。
案例演示:
1.在EmployeeMapper接口中加入一个方法,如下所示:
public List<Employee> getEmpsByConditionForeach(@Param("ids") List<Integer> ids);
2.在MyBatis的sql映射文件中写相应的代码:
<!-- public List<Employee> getEmpsByConditionForeach(List<Integer> ids); -->
<select id="getEmpsByConditionForeach" resultType="com.neuedu.entity.Employee">
select * from tbl_employee where id in
<!-- collection:指定要遍历的集合 item:将当前遍历出的元素赋值给指定的变量 separator:每个元素之间的分隔符 open:遍历出所有记过拼接一个开始的字符 close:遍历出所有结果拼接一个结束的字符 -->
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
3.测试类代码为:
@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
List<Integer> asList = Arrays.asList(1,2,3,4);
List<Employee> emps = mapper.getEmpsByConditionForeach(asList);
for (Employee employee : emps) {
System.out.println(employee);
}
}
foreach标签还可以用于批量保存数据,如下所示:
1.在EmployeeMapper接口类中添加批量插入的方法:
public void addEmps(@Param("emps") List<Employee> emps);
2.在EmployeeMapper.xml的sql映射文件中添加响应的语句:
<!-- public void addEmps(@Param("emps") List<Employee> emps); -->
<!-- MySQL下批量保存:可以foreach遍历,mysql支持values(),(),()语法 -->
<insert id="addEmps">
INSERT INTO tbl_employee(user_name,gender,email,d_id) VALUES
<foreach collection="emps" item="emp" separator=",">
(#{emp.userName},#{emp.gender},#{emp.email},#{emp.depart.id})
</foreach>
</insert>
3.测试代码:
六、MyBatis-缓存机制
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。
一级缓存和二级缓存。
一级缓存:(本地缓存):SqlSession级别的缓存,一级缓存是一致开启的,没法关闭。方法之间不共用!
与数据库同一次会话期间查询到的数据放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库; 二级缓存(全局缓存):
–1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
–2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
–3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
案例: 测试一级缓存【默认是开启的本地缓存的】:
@Test
public void testGetEmployee(){
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
Employee emp2 = mapper.getEmployeeById(2);
System.out.println(emp2);
System.out.println(emp == emp2); }
一级缓存失效的情况【4种】(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询);
1.sqlSession不同。
@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
SqlSession openSession2= sessionFactory.openSession();
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
Employee emp2 = mapper2.getEmployeeById(2);
System.out.println(emp2);
System.out.println(emp == emp2);
openSession.close();
openSession2.close();
}
2.SqlSession相同,但是查询条件不一样[当前缓存中还没有这个数据]
@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
Employee emp2 = mapper.getEmployeeById(3);
System.out.println(emp2);
System.out.println(emp == emp2);
openSession.close();
}
3.SqlSession相同,但是两次查询之间执行了增删改操作【这次增删改可能对当前数据有影响】。
@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
mapper.updateEmp(new Employee(1, 1, "张三丰","zhangsanfeng@163.com", new Department(1)));
Employee emp2 = mapper.getEmployeeById(2);
System.out.println(emp2);
System.out.println(emp == emp2);
openSession.close();
}
4.SqlSession相同,手动清除了一级缓存[缓存清空]。
@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
SqlSession openSession= sessionFactory.openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
//手动清空缓存
openSession.clearCache();
Employee emp2 = mapper.getEmployeeById(2);
System.out.println(emp2);
System.out.println(emp == emp2);
openSession.close();
}
二级缓存:【全局缓存】:基于namespace级别的缓存:一个namespace对应一个二级缓存。
【一级缓存的范围还是太小了,每次SqlSession一关闭,一级缓存中的数据就消失】
所以从这个角度讲:能跨sqlSession的缓存为二级缓存!
工作机制:
1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中。
2.如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中。
3.SqlSession
=== EmployeeMapper ===> Employee
DepartmentMapper ===> Department
不同namespace查出的数据会放在自己对应的缓存中(map)
效果:数据会从二级缓存中获取
查出的数据都会被默认先放在一级缓存中。
只有会话提交或者关闭之后,一级缓存中的数据才会转移到二级缓存中。
需要注意的是:在哪个Mapper.xml文件中开启了<cache>缓存标签,哪个Mapper中就开启了二级缓存。
使用:
1).开启全局二级缓存配置:
<setting name="cacheEnabled" value="true"/>
2).去mapper.xml中配置使用二级缓存:
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024" type=""></cache>
<!-- ?eviction=“FIFO”:缓存回收策略: ?LRU –最近最少使用的:移除最长时间不被使用的对象。 ?FIFO –先进先出:按对象进入缓存的顺序来移除它们。 ?SOFT –软引用:移除基于垃圾回收器状态和软引用规则的对象。 ?WEAK –弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 ?默认的是LRU。 ?flushInterval:缓存刷新间隔 ?缓存多长时间清空一次,默认不清空,设置一个毫秒值。 ?size:引用数目,正整数 ?代表缓存最多可以存储多少个对象,太大容易导致内存溢出 ?readOnly:是否只读,true/false ?true:只读缓存;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快。 ?false:非只读:mybatis觉得获取的数据可能会被修改。 mybatis会利用序列化&反序列化的技术克隆一份。安全,速度慢。 ?type:指定自定义缓存的全类名 实现cache接口即可! -->
3).我们的POJO需要实现序列化接口[implements Serializable]
测试二级缓存【测试代码】:
@Test
public void testGetEmployee() throws IOException{
SqlSessionFactory sessionFactory = testBefore();
//开启两个会话
SqlSession openSession= sessionFactory.openSession();
SqlSession openSession2 = sessionFactory.openSession();
//利用两个openSession对象获取两个mapper对象
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
//用第一个openSession获取的mapper对象查询2号员工信息
Employee emp = mapper.getEmployeeById(2);
System.out.println(emp);
//关闭第一个openSession对象
openSession.close();
//用第二个openSession获取的mapper对象查询2号员工信息
Employee emp2 = mapper2.getEmployeeById(2);
System.out.println(emp2);
openSession2.close();
}
//可以看到只发送了一次SQL语句,第二次查询时从二级缓存中拿到的数据,并没有发送新的sql语句。
//需要注意的是:只有一级缓存中关闭的情况下,二级缓存才会被使用。
需要注意的是:在哪个Mapper.xml文件中开启了<cache>缓存标签,哪个Mapper中就开启了二级缓存。可用DepartmentMapper.xml验证
和缓存有关的设置/属性:
1)cacheEnabled="true": false:关闭缓存(二级缓存关闭)【一级缓存一直可用】
2)每个select标签都有useCache="true";
false:不使用缓存(一级缓存依然使用,二级缓存不使用)
3)每个增删改标签都有一个flushCache="true":增删改执行完成后就会清楚缓存【一级二级缓存都会被清空】
查询标签:flushCache = "false"
如果flushCache = true;每次查询之前都会清空缓存,缓存是没有被使用!
170906-MyBatis续的更多相关文章
- Spring Boot 知识笔记(整合Mybatis续-补充增删改查)
续上篇,补充数据库增删改查的其他场景. 一.Mapper中添加其他场景操作 package net.Eleven.demo.Mapper; import net.Eleven.demo.domain. ...
- sping+maven+mybatis+ehcache续之实现mapper
配置接着上一篇文章 新建UserMapper.java和UserMapper.xml 其中UserMapper.xml的namespace以及文件名要和UserMapper.java一致 <ma ...
- 深入理解MyBatis的原理(三):配置文件用法(续)
前言:前文讲解了 MyBatis 的配置文件一部分用法,本文将继续讲解 MyBatis 的配置文件的用法. 目录 1.typeHandler 类型处理器 2.ObjectFactory 3.插件 4. ...
- Mybatis 学习记录 续
项目结构如下: 1.数据库建表 表名:user 结构: 内容: 2.pom.xml文件更新如下: 注:其中build部分尤其需要重视 <?xml version="1.0" ...
- MyBatis知多少(15)数据模型
瘦数据模型是一种最为臭名昭著并且问题多多的对关系数据库系统的滥用.不幸的是,有时又的确需要瘦数据模型.所谓瘦数据模型,就是简单地将每张表都设计为一种通用数据结构,用于存储名值对的集合.这非常像Java ...
- 【MyBatis学习笔记】
[MyBatis学习笔记]系列之预备篇一:ant的下载与安装 [MyBatis学习笔记]系列之预备篇二:ant入门示例 [MyBatis学习笔记]系列之一:MyBatis入门示例 [MyBatis学习 ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(六)一定要RESTful吗?
作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载. 写在前面的话 这个问题看起来就显得有些萌,或者说类似的问题都有些不靠 ...
- MP实战系列(十二)之封装方法详解(续二)
继续MP实战系列(十一)之封装方法详解(续一)这篇文章之后. 此次要讲的是关于查询. 查询是用的比较多的,查询很重要,好的查询,加上索引如鱼得水,不好的查询加再多索引也是无济于事. 1.selectB ...
- Spring+SpringMVC+MyBatis整合基础篇(二)牛刀小试
前言 承接上文,该篇即为项目整合的介绍了. 废话不多说,先把源码和项目地址放上来,重点要写在前面. 项目展示地址,点这里http://ssm-demo.13blog.site,账号:admin 密码: ...
- MyBatis 核心配置综述之StatementHandler
目录 MyBatis 核心配置综述之StatementHandler MyBatis 四大组件之StatementHandler StatementHandler 的基本构成 StatementHan ...
随机推荐
- 前端 CSS 继承性和层叠性
CSS有两大特性:继承性和层叠性 前端 CSS的继承性 前端 CSS层叠性 CSS选择器优先级 前端 CSS 优先级 样式设置important
- 华南理工大学 “三七互娱杯” G HRY and tree
https://ac.nowcoder.com/acm/contest/874/G 题目大意:对于一个连通图,现在定义两个点的贡献为连接两点的路径上最大的权值 求任意两个点贡献的和 这个题看懂花了我很 ...
- [转帖]Docker从入门到动手实践
Docker从入门到动手实践 https://www.cnblogs.com/nsky/p/10853194.html dockerfile的图很好呢. 但是自己没有做实验 , 其实知识都挺好. do ...
- [转帖]OEM、ODM、OBM分别是什么?
OEM.ODM.OBM分别是什么? https://blog.csdn.net/liangtianmeng/article/details/83215500 感觉很多地方说明的都不对 OEM 别人的产 ...
- typescript是否可以直接编译执行?
算是个有趣的小问题,由于必须依赖node.js,typescript理论上是不能不转成js直接运行的.
- java常用类之BigDecimal
BigDecimal 小数计算丢失精度问题 在计算机中,所有文件都是以二进制存储的,数字运算也是使用二进制进行计算的,因为计算机中不存在小数点,所以我们通常说的浮点数如float.double都是计算 ...
- 从汇编到C
一. 设置栈 1.1. C语言运行时需要和栈的意义 1.1.1. “C语言运行时(runtime)”需要一定的条件,这些条件由汇编来提供.C语言运行时主要是需要栈 1.1.2. C语言与栈的关系 a. ...
- 图——图的Prim法最小生成树实现
1,运营商的挑战: 1,在下图标出的城市间架设一条通信线路: 2,要求: 1,任意两个城市间都能够通信: 2,将架设成本降至最低: 2,问题抽象: 1,如何在图中选择 n - 1 条边使得 n 个顶点 ...
- Eclipse删除已安装插件
环境:(Windows) Eclipse 1.点击菜单"Help",选择"Install New Software",在弹出的对话框中选择"alrea ...
- switch语句小练习
java有两钟选择判断语句,分别是if else和switch case语句. 下面我们做一个switch case语句的练习. // 定义一个扫描器 Scanner sacnner = new Sc ...