Mybatis:缓存,动态SQL,注解SQL以及动态标签使用
1 转义字符
字符 | 转义 | 描述 |
---|---|---|
< | < |
小于 |
<= | <= |
小于等于 |
> | > |
大于 |
>= | >= |
大于等于 |
<> | <> |
不等于 |
& | & |
|
' | ' |
|
" | " |
2 一级缓存以及二级缓存
学习Mybatis
缓存的过程中,发现一篇美团的优秀文章: 聊聊MyBatis缓存机制.
此处对一级缓存以及二级缓存的使用进行总结.
2.1 一级缓存
2.1.1 小结
(1) MyBatis
一级缓存的生命周期和SqlSession
一致;
(2) MyBatis
一级缓存内部设计简单,只是一个没有容量限定的HashMap
,在缓存的功能性上有所欠缺;
(3) MyBatis
的一级缓存最大范围是SqlSession
内部,有多个SqlSession
或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement
.
2.1.2 一级缓存脏读现象
(1) 在使用MyBatis
的项目中,被@Transactional
注解的函数中,一个单独的SqlSession
对象将会被创建和使用,所有数据库操作会共用这个sqlSession
,当事务完成时,这个``sqlSession会以合适的方式提交或回滚; (2)
SELECT语句默认开启查询缓存,并且不清除缓存,所以使用同一个 SqlSession 多次用相同的条件查询数据库时,只有第一次真实访问数据库,后面的查询都直接读取缓存返回; (3) 此时,如果其余
SqlSession更新了带待查询数据,就会造成脏读现象; (4) 或者同一次
SqlSession中
多次查询数据`,例如多次查询分表数据(查询结果和分表查询数据相关),就会造成查询结果失效.
2.2 二级缓存
(1) MyBatis
的二级缓存相对于一级缓存来说,实现了SqlSession
之间缓存数据的共享,同时粒度更加的细,能够到namespace
级别,通过Cache
接口实现类不同的组合,对Cache
的可控性也更强。
(2) MyBatis
在多表查询
时,极大可能会出现脏数据
,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
(3) 在分布式环境下,由于默认的MyBatis Cache
实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis
、Memcached
等分布式缓存可能成本更低
,安全性
也更高。
3 Myabtis
枚举值转换与驼峰转换配置
3.1 枚举值转换配置
mybatis.configuration.default-enum-type-handler = org.apache.ibatis.type.EnumOrdinalTypeHandler
定义如上配置,则Mybatis
存储枚举值时,添加/更新枚举值转换为Integer
,查询时会自动将Integer
转换为相应的枚举值.
3.2 驼峰转换配置
mybatis.configuration.map-underscore-to-camel-case = true
4.动态注解SQL
4.1 查询
4.1.1 单条查询
@Select("SELECT * FROM `attachment` WHERE `id` = #{id}")
Attachment getAttachment(@Param("id") Integer id);
4.1.2 列表查询
此处为了避免在注解
的动态SQL
中写foreach
,使用辅助类完成字符串替换的工作.
查询出的结果Mybatis
会自动将结果映射到返回值上,支持批量查询结果.
@Lang(SimpleSelectInExtendedLanguageDriver.class)
@Select("SELECT id, created_time FROM `change` WHERE id in (#{ids})")
List<Change> getChangeTimeInfo(@Param("ids") List<Integer> ids);
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SimpleSelectInExtendedLanguageDriver extends XMLLanguageDriver implements LanguageDriver {
private final Pattern inPattern = Pattern.compile("\\(#\\{(\\w+)\\}\\)");
@Override
public SqlSource createSqlSource(Configuration configuration,
String script, Class<?> parameterType) {
Matcher matcher = inPattern.matcher(script);
if (matcher.find()) {
script = matcher.replaceAll(
"(<foreach collection=\"$1\" item=\"__item\" separator=\",\" >#{__item}</foreach>)");
}
script = "<script>" + script + "</script>";
return super.createSqlSource(configuration, script, parameterType);
}
}
4.2 插入数据
以下为示例使用的实体类:
import lombok.Data;
@Data
public class TaskTreeItem {
private Integer id;
/**
* 当前节点的祖先
*/
private Integer ancestor;
//
/**
* 当前节点
*/
private Integer descendant;
}
4.2.1 单条插入
单条插入数据,如果需要返回生成的主键值,可以设置useGeneratedKeys
为true
,指定id
为返回的主键值.
@Insert("INSERT INTO task_tree (ancestor,descendant,depth) VALUES (#{ancestor},#{descendant})")
@Options(useGeneratedKeys = true, keyColumn = "id")
int insert(TaskTreeItem taskTreeItem);
4.2.2 批量插入
批量插入需要写动态SQL
(此外需要确保数据库支持),此处使用到<foreach>
.
@Insert({"<script>",
"INSERT INTO `task_tree` (`ancestor`, `descendant`) VALUES ",
"<foreach item='item' index='index' collection='list' open='' separator=',' close=''>",
"(#{item.ancestor}, #{item.descendant})",
"</foreach>",
"</script>"})
Integer addTreeItems(List<TaskTreeItem> taskTreeItems);
4.3 更新数据
4.3.1 简单更新
@Update("UPDATE `change` SET `status` = #{status}, `finish_time` = #{finishTime} WHERE `id` = #{id}")
int finishChange(@Param("id") int id, @Param("status") Change.Status status, Instant finishTime);
在不涉及变量判断的情况下,通过@Param
注解将参数映射到SQL
语句中,即可实现数据的更新.
4.3.2 含逻辑判断更新
业务的数据表更新中,经常含有复杂的判断(诸如非空判断,时间戳比较等判断),因此在此处进行展示操作.
(业务场景中,在CRUD
的各个环节均可能存在逻辑判断,此处只是节选作为说明).
@Update("<script>" +
"UPDATE `change` " +
"<set>" +
"<if test=\"description != null\">" +
" description = #{description}," +
"</if> " +
"<if test=\"executePlan != null\">" +
" execute_plan = #{executePlan}," +
"</if> " +
"<if test=\"status != null\">" +
" status = #{status}," +
"</if> " +
" id = #{id}, " +
"</set>" +
" WHERE id = #{id}" +
"</script>")
int updateChange(Change change);
在注解中使用动态SQL
要比在XML中使用困难,主要在于维护字符串拼接以及字符串格式化.
动态SQL
需要在开头以及结尾添加标签,告知MyBatis
当前语句为动态SQL
.
此处,标签的使用,添加了id = #{id},
一行,这是为了避免所有属性均为空时, SQL
语句不规范导致业务执行异常.
4.4 删除数据
@Delete("DELETE FROM milestone where id = #{milestoneId} AND task_id = #{taskId}")
int deleteTaskMilestone(@Param("taskId") int taskId, @Param("milestoneId") int milestoneId);
删除数据较为简单,此处不再赘述.
5 标签
此处列举经常使用的标签作为记录.
5.1
@Select("<script>" +
"<if test=\"incidentId != null\">" +
"(SELECT * FROM `change` WHERE `id` IN (SELECT `change_id` FROM `incident_change` WHERE `incident_id` = #{incidentId}) ORDER BY `id` DESC)" +
" UNION " +
"</if> " +
"(SELECT * FROM `change` " +
"<where>" +
"<if test=\"status != null\">" +
"AND `status` = #{status} " +
"</if>" +
"<if test=\"description != null and description != ''\">" +
"AND `description` LIKE CONCAT('%', #{description}, '%') " +
"</if>" +
"</where>" +
"ORDER BY `id` DESC)" +
"</script>")
List<Change> listChangeByCondition(ChangeQueryRequest request);
5.2 与
@Select("<script>" +
"SELECT task.*,content_text.content 'description' FROM `task` " +
" JOIN content_text ON task.description_id = content_text.`id` " +
" WHERE " +
"<choose>" +
"<when test=\"id != null\">" +
" task.id = #{id}" +
"</when>" +
"<otherwise>" +
" task.id IN (SELECT `task_id` FROM `key_object_task`)" +
"</otherwise>" +
"</choose>" +
"<if test=\"type != null\">" +
"AND task.type = #{type} " +
"</if>" +
"<if test=\"statuses != null and statuses.size()>0\">" +
"<foreach item='item' index='index' collection='statuses' open=' AND task.status IN (' separator=',' close=')' >" +
"#{item}" +
"</foreach>"+
"</if>" +
" ORDER BY task.id DESC" +
"</script>")
List<TaskResponse> listKeyObjectTask(KeyObjectTaskQueryRequest taskQueryRequest);
此处,可以注意:
(1) 查询参数中包含statuses
参数,其实质上是一个List<Enum>
,此处使用foreach
标签就需要指明参数名称,而不是使用list
代替;
(2) TaskResponse
类中包含一个description
参数(参数对应content_text.content
字段),因此使用content_text.content 'description'
指明查询结果的映射字段.
PS:
如果您觉得我的文章对您有帮助,可以扫码领取下红包或扫码支持(随意多少,一分钱都是爱),谢谢!
支付宝红包 | 支付宝 | 微信 |
---|---|---|
![]() |
![]() |
![]() |
Mybatis:缓存,动态SQL,注解SQL以及动态标签使用的更多相关文章
- spring boot整合mybatis基于注解开发以及动态sql的使用
让我们回忆一下上篇博客中mybatis是怎样发挥它的作用的,主要是三类文件,第一mapper接口,第二xml文件,第三全局配置文件(application.properties),而今天我们就是来简化 ...
- MyBatis 注解配置及动态SQL
一.注解配置 目前MyBatis支持注解配置,用注解方式来替代映射文件,但是注解配置还是有点不完善,在开发中使用比较少,大部分的企业还是在用映射文件来进行配置.不完善的地方体现在于当数据表中的字段 ...
- Mybatis 动态SQL注解 in操作符的用法
在SQL语法中如果我们想使用in的话直接可以像如下一样使用: ,,) ; ,,) ; 但是如果在MyBatis中的使用 in 操作符,像下面这样写的话,肯定会报错: @Update("upd ...
- mybatis动态注解sql编写注意事项
最近在编写mybatis的动态注解sql遇到了不少的坑,在网上看到一篇讲的比较详细的文章,记录一下: https://mbd.baidu.com/newspage/data/landingshare? ...
- MyBatis在注解上使用动态SQL(@select使用if)
1.用script标签包围,然后像xml语法一样书写 @Select({"<script>", "SELECT * FROM tbl_order", ...
- Mybatis中多个参数的问题&&动态SQL&&查询结果与类的对应
### 1. 抽象方法中多个参数的问题 在使用MyBatis时,接口中的抽象方法只允许有1个参数,如果有多个参数,例如: Integer updatePassword( Integer id, Str ...
- Mybatis(一)Porxy动态代理和sql解析替换
JDK常用核心原理 概述 在 Mybatis 中,常用的作用就是讲数据库中的表的字段映射为对象的属性,在进入Mybatis之前,原生的 JDBC 有几个步骤:导入 JDBC 驱动包,通过 Driver ...
- mybatis(二)接口编程 、动态sql 、批量删除 、动态更新、连表查询
原理等不在赘述,这里主要通过代码展现. 在mybatis(一)基础上,新建一个dao包,并在里面编写接口,然后再在xml文件中引入接口路径,其他不变,在运用阶段将比原始方法更节约时间,因为不用再去手动 ...
- MyBatis基础入门《二十》动态SQL(foreach)
MyBatis基础入门<二十>动态SQL(foreach) 1. 迭代一个集合,通常用于in条件 2. 属性 > item > index > collection : ...
随机推荐
- Java面试题全集(上-中-下)及Java面试题集(1-50/51-70)
阅读量超百万级的文章,收藏并分享一下.感谢原创作者的总结 对初中级java开发人员有特别大的帮助,不论是技术点面试还是知识点总结上. Java面试题全集(上): https://blog.cs ...
- java文件传输之文件编码和File类的使用
---恢复内容开始--- 我们知道,在用户端和服务端之间存在一个数据传输的问题,例如下载个电影.上传个照片.发一条讯息.在这里我们 就说一下文件的传输. 1.文件编码 相信大家小时候玩过积木(没玩过也 ...
- Python Tips阅读摘要
发现了一本关于Python精通知识点的好书<Python Tips>,关于Python的进阶的技巧.摘录一些比较有价值的内容作为分享. *args and **kwargs 在函数定义的时 ...
- c#语言中的Process进程类型的使用示例
下面我们用一个简单的例子来说明如何使用 我们用vs2015新建一个解决方案,这个解决方案包含两个WINFORM窗体项目,一个是SoftWare.Test,一个是SoftWare.Update,如下图所 ...
- python笔记:#004#注释
注释 目标 注释的作用 单行注释(行注释) 多行注释(块注释) 01. 注释的作用 使用用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性 02. 单行注释(行注释) 以 # 开头,# ...
- 用Maven实现一个protobuf的Java例子
注:试验环境在Mac Idea环境下 1. 介绍Protocol Buffers Protocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化.反序列化,常用于RP ...
- SVN服务器搭建--Subversio与TortoiseSVN的配置安装(Windows)
1. Subversio和TortoiseSVN 简介 Subversio简介: Subversion是一个自由,开源的版本控制系统,可以随意地免费下载.修改.以及重新发布. 是一个通用系统,可以管 ...
- linux基础命令用法
目录管理 ls.cd.pwd.mkdir.rmdir.tree ls(list) 列出,列表 用法: ls -l:长格式 文件类型: -:普通文件 (f) d: 目录文件 b: 块设备文件 (bloc ...
- PAT1082:Read Number in Chinese
1082. Read Number in Chinese (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yu ...
- awk高级玩法
1. 程序元素 一个awk 程序是一对以模式(pattern) 与大括号框起来的操作(action) 组合而成的,或许,还会加上实现操作细节的函数(function ) .针对每个匹配于输人数据的模式 ...