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 : ...
随机推荐
- 在 Ubuntu 系统中部署 Git Server
http://blog.csdn.NET/poisonchry/article/details/11849781 虽然有很多开源的Git仓库,不过并非所有都尽人意,譬如Github,Gitlab等,不 ...
- 兼容 Android 4.4 透明状态栏与导航栏
http://www.apkbus.com/Android-163388-1-1.html?_dsign=73d41229 android 系统自4.2 开始 UI 上就没多大改变,4.4 也只是增加 ...
- python3学习笔记4---引用http://python3-cookbook.readthedocs.io/zh_CN/latest/
2018-03-01数据结构与算法(4) 1.16过滤序列元素 最简单的过滤序列元素的方法就是使用列表推导.比如: >>> mylist = [1, 4, -5, 10, -7, 2 ...
- Hystrix 熔断机制原理
相关配置 circuitBreaker.enabled 是否开启熔断 circuitBreaker.requestVolumeThreshold 熔断最低触发请求数阈值 circuitBreaker. ...
- 微信小程序-框架详解(1)
配置 -app.json文件对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.tab等 { "pages": [ //决定页面文件的路径 "pag ...
- 【转】javascript 作用域链
JavaScript函数的作用域链分为定义时作用域链和运行时作用域链: 函数被定义的时候,它有一个属性[[scope]]标明它的定义作用域链,定义时作用域链[[scope]]遵守这样的规则:一个函数的 ...
- C语言下double转char*或者std::string,可以精确转换不含多余的0
char* GetDoubleStr(double value) { char buf[32]={0};//长度可以自定义 sprintf(buf,"%.8f",value);// ...
- 一些常用的linux命令(2)
参考:http://www.cnblogs.com/laov/p/3541414.html 系统管理命令 stat 显示指定文件的详细信息,比ls更详细 who ...
- CentOS6.5系统挂载NTFS分区的硬盘
下载rpmforge,下载对应的版本,就是对应CentOS版本,还有32位与64位也要对应上.rpmforge拥有4000多种CentOS的软件包,被CentOS社区认为是最安全也是最稳定的一个软件仓 ...
- js算法初窥02(排序算法02-归并、快速以及堆排序)
上一篇,我们讲述了一些简单的排序算法,其实说到底,在前端的职业生涯中,不涉及node.不涉及后台的情况下,我目前还真的没想到有哪些地方可以用到这些数据结构和算法,但是我在前面的文章也说过了.或许你用不 ...


