1 转义字符

字符 转义 描述
< &lt; 小于
<= &lt;= 小于等于
> &gt; 大于
>= &gt;= 大于等于
<> &lt;&gt; 不等于
& &amp;
' &apos;
" &quot;

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接口实现,有一定的开发成本,直接使用RedisMemcached等分布式缓存可能成本更低安全性也更高。

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以及动态标签使用的更多相关文章

  1. spring boot整合mybatis基于注解开发以及动态sql的使用

    让我们回忆一下上篇博客中mybatis是怎样发挥它的作用的,主要是三类文件,第一mapper接口,第二xml文件,第三全局配置文件(application.properties),而今天我们就是来简化 ...

  2. MyBatis 注解配置及动态SQL

      一.注解配置 目前MyBatis支持注解配置,用注解方式来替代映射文件,但是注解配置还是有点不完善,在开发中使用比较少,大部分的企业还是在用映射文件来进行配置.不完善的地方体现在于当数据表中的字段 ...

  3. Mybatis 动态SQL注解 in操作符的用法

    在SQL语法中如果我们想使用in的话直接可以像如下一样使用: ,,) ; ,,) ; 但是如果在MyBatis中的使用 in 操作符,像下面这样写的话,肯定会报错: @Update("upd ...

  4. mybatis动态注解sql编写注意事项

    最近在编写mybatis的动态注解sql遇到了不少的坑,在网上看到一篇讲的比较详细的文章,记录一下: https://mbd.baidu.com/newspage/data/landingshare? ...

  5. MyBatis在注解上使用动态SQL(@select使用if)

    1.用script标签包围,然后像xml语法一样书写 @Select({"<script>", "SELECT * FROM tbl_order", ...

  6. Mybatis中多个参数的问题&&动态SQL&&查询结果与类的对应

    ### 1. 抽象方法中多个参数的问题 在使用MyBatis时,接口中的抽象方法只允许有1个参数,如果有多个参数,例如: Integer updatePassword( Integer id, Str ...

  7. Mybatis(一)Porxy动态代理和sql解析替换

    JDK常用核心原理 概述 在 Mybatis 中,常用的作用就是讲数据库中的表的字段映射为对象的属性,在进入Mybatis之前,原生的 JDBC 有几个步骤:导入 JDBC 驱动包,通过 Driver ...

  8. mybatis(二)接口编程 、动态sql 、批量删除 、动态更新、连表查询

    原理等不在赘述,这里主要通过代码展现. 在mybatis(一)基础上,新建一个dao包,并在里面编写接口,然后再在xml文件中引入接口路径,其他不变,在运用阶段将比原始方法更节约时间,因为不用再去手动 ...

  9. MyBatis基础入门《二十》动态SQL(foreach)

    MyBatis基础入门<二十>动态SQL(foreach) 1. 迭代一个集合,通常用于in条件 2. 属性 > item > index > collection : ...

随机推荐

  1. 新导入的eclipse项目报错,找不到java包,找不到web.xml文件报错。

    新导入的项目可能会出现报错,特别是web项目.我这里提供一种解决方法: 1.右击项目,选择“属性” 2.选择 Resource->java build path->libraries 图中 ...

  2. XAMPP重置MySQL密码

    找到XAMPP的安装位置,这里以我的为例:C:\xampp 那么MySQL的路径:C:\xampp\mysql phpMyAdmin的路径:C:\xampp\phpMyAdmin 修改MySQL密码 ...

  3. vue.js中的全局组件和局部组件

    组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能. 组件的使用有三 ...

  4. 2018年,传言QQ首次被神秘黑客DDOS攻击,Python可以实现?

    于2018-5-10日晚 网络流传黑客DDOS攻击了QQ服务器,导致大家聊天发送内容时出现感叹号.我们都知道一般情况下出现感叹号都是你的网络不稳定,或者...别人已经删除你了.然而昨晚很奇怪,发出的内 ...

  5. (ospf、rip、isis、EIGRP)常见的动态路由协议简介

    路由器要转发数据必须先配置路由数据,通常根据网络规模的大小可设置静态路由或设置动态路由.静态路由配置方便,对系统要求低,适用于拓扑结构简单并且稳定的小型网络.缺点是不能自动适应网络拓扑的变化,需要人工 ...

  6. 学习Android过程中的一些博客或工具收集

    android studio中使用SlidingMenu: 超简单Android Studio导入第三方库(SlidingMenu)教程绝对傻瓜版 android 更新sdk23以后,报错提示Floa ...

  7. 洛谷 P1613 解题报告

    P1613 跑路 题目描述 小\(A\)的工作不仅繁琐,更有苛刻的规定,要求小\(A\)每天早上在\(6:00\)之前到达公司,否则这个月工资清零.可是小\(A\)偏偏又有赖床的坏毛病.于是为了保住自 ...

  8. iframe跨域动态设置主窗口宽高

    Q:在A项目的a页面嵌入一个iframe,src是B项目的b页面,怎样让a页面的高度跟b页面的高度一样? A:解决跨域方案:增加一个A项目的c页面. 操作步骤: 一,a页面的iframe设置: 获取到 ...

  9. 【转】JavaScript的异常处理

    当 JavaScript 引擎执行 JavaScript 代码时,有可能会发生各种异常,例如是语法异常,语言中缺少的功能,由于来自服务器或用户的异常输出而导致的异常. 而 Javascript 引擎是 ...

  10. union 的两个用处

    1 节约内存: 这一功能可以参考我的其它博文: https://i.cnblogs.com/EditPosts.aspx?postid=8545190&update=1 2 测试机器大小端: ...