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. Django Channels 入门指南

    http://www.oschina.NET/translate/in_deep_with_django_channels_the_future_of_real_time_apps_in_django ...

  2. MAC OSX下用pip安装lxml时遇到xmlversion.h not found的解决办法

    http://blog.csdn.NET/wave_1102/article/details/37730589 今天在Mac下用pip安装lxml,总是报如下错误: etree_defs.h::: f ...

  3. R语言学习 第十篇:操作符

    运算符是R语言中最基础的存在,熟悉运算符的使用,是熟练使用R处理数据的基础,操作符,顾名思义,是对数据进行运算的符号,R有自己的一套操作符,实现变量的赋值,引用,运算等功能. 一,赋值符号 为变量赋值 ...

  4. C++中,用类和重载运算符写高精模板

    先放代码: #include<iostream> #include<cstdio> #include<cstring> using namespace std; s ...

  5. django项目部署上线

    前言 完善的django项目上线,有很多种上线的方法,比如apache, uwsgi, nginx等.这里只介绍2种,一种是django自带的,另外一种则是nginx + uwsgi完成介绍.这里的系 ...

  6. 使用pypi-server搭建简单的PyPI源

    pypiserver 是一个最基本的PyPI服务器实现, 可以用来上传和维护python包. 本文介绍 pypiserver 在ubuntu上的基本安装, 配置和使用. 1. 基本安装和使用 1.1 ...

  7. Python(Django)项目与Apache的管理

    (开开心心每一天~ ---虫瘾师) Python(Django)项目交给Apache的管理(一) 准备:Django的环境(Python).Apache.Wsgi(必须文件) 首先需要电脑有Pytho ...

  8. 转 Web用户的身份验证及WebApi权限验证流程的设计和实现

    前言:Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能,一个功能复杂的业务应用系统,通过角色授权来控制用户访问,本文通过Form认证,Mvc的Controller基类及Action的权 ...

  9. JS方法:数字转换为千分位字符

    /** * 数字转为千分位字符 * @param {Number} num * @param {Number} point 保留几位小数,默认2位 */ function parseToThousan ...

  10. C#学习(一):委托和事件

    预备知识 在学习委托和事件之前,我们需要知道的是,很多程序都有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知. 而发布者/订阅者模式可以满足这种需求.简单来说 ...