mybatis问题合集:#{}与${}区别、动态sql语句、缓存机制

摘要:
因此,为了防止SQL注入,如果可以使用#{},请不要使用${}。如果必须使用${},请注意防止SQL注入问题。您可以手动确定传入变量并过滤它们。通常,SQL注入将输入一个长SQL语句。2.动态SQL语句1.if:动态SQL通常有条件地包含where子句的一部分。看看通过设计修剪、位置和设置解决的问题。

一、MyBatis 中#{}和${}区别

  #{} 是预编译处理,像传进来的数据会加个" "(#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号)

  ${} 就是字符串替换。直接替换掉占位符。$方式一般用于传入数据库对象,例如传入表名.

  使用 ${} 的话会导致 sql 注入。什么是 SQL 注入呢?比如 select * from user where id = ${value}。value 应该是一个数值吧。然后如果对方传过来的是 001  and name = tom。这样不就相当于多加了一个条件嘛?把SQL语句直接写进来了。如果是攻击性的语句呢?001;drop table user,直接把表给删了。

  所以为了防止 SQL 注入,能用 #{} 的不要去用 ${}

  如果非要用 ${} 的话,那要注意防止 SQL 注入问题,可以手动判定传入的变量,进行过滤,一般 SQL 注入会输入很长的一条 SQL 语句

二、动态sql语句

1、if:动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。

<select id="select" resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
 <if test="name!= null">
    AND name like #{title}
  </if>
</select>

2、where:像上面的那种情况,如果where后面没有条件,然后需要直接写if判断(开头如果是 and / or 的话,会去除掉)

<select id="select" resultType="Blog">
  SELECT * FROM BLOG
  <where>
      <if test="title != null">
        AND title like #{title}
      </if>
      <if test="name!= null">
        AND name like #{title}
      </if>
  <where>
</select>

3、choose(when、otherwise):choose 相当于 java 里面的 switch 语句,otherwise(其他情况)

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

4、set:set 元素主要是用在更新操作的时候,如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。

<update id="dynamicSetTest" parameterType="Blog">  
    update t_blog  
    <set>  
        <if test="title != null">  
            title = #{title},  
        </if>  
        <if test="content != null">  
            content = #{content},  
        </if>  
        <if test="owner != null">  
            owner = #{owner}  
        </if>  
    </set>  
    where id = #{id}  
</update> 

5、foreach:主要用在构建in条件中

  foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

  注意:你可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以"list"作为键,而数组实例的键将是"array"。

<select id="dynamicForeachTest" resultType="Blog">  
  select * from t_blog where id in  
  <foreach collection="list" index="index" item="item" open="(" separator="," close=")">  
    #{item}  
  </foreach>
</select>  

  open separator close 相当于是 in (?,?,?)

  如果是个map怎么办

<select id="dynamicForeach3Test" resultType="Blog">  
  select * from t_blog where title like "%"#{title}"%" and id in  
  <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">  
    #{item}  
  </foreach>  
</select>  
// collection对应map的键,像这样
List<Integer> ids = new ArrayList<Integer>();  
ids.add(1);
ids.add(2);
ids.add(3);
Map<String, Object> params = new HashMap<String, Object>();  
params.put("ids", ids);  

6、trim、where、set解决的问题

  前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在考虑回到"if"示例,这次我们将"ACTIVE = 1"也设置成动态的条件,看看会发生什么。看看设计 trim、where、set 所解决的问题。

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG 
  WHERE 
  <if test="state != null">
    state = #{state}
  </if> 
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

  如果这些条件没有一个能匹配上将会怎样?最终这条 SQL 会变成这样:SELECT * FROM BLOG WHERE

  这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样:SELECT * FROM BLOG WHERE AND title like 'someTitle'

  MyBatis 有一个简单的处理,这在90%的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能得到想要的效果。

  where 元素知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除。

  如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:trim标记是一个格式化的标记,可以完成set或者是where标记的功能,

// prefix:前缀   prefixoverride:去掉第一个and或者是or
select * from test
<trim prefix="WHERE" prefixoverride="AND丨OR">
      <if test="a!=null and a!=' '">AND a=#{a}<if>
      <if test="b!=null and b!=' '">AND a=#{a}<if>
</trim>

  类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

  这里,set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。

  若你对等价的自定义 trim 元素的样子感兴趣,那这就应该是它的真面目:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

三、缓存机制

  缓存机制减轻数据库压力,提高数据库性能。mybatis的缓存分为两级:一级缓存、二级缓存

1、一级缓存:

  一级缓存为 sqlsesson 缓存,缓存的数据只在 SqlSession 内有效。在操作数据库的时候需要先创建 SqlSession 会话对象,在对象中有一个 HashMap 用于存储缓存数据,此 HashMap 是当前会话对象私有的,别的 SqlSession 会话对象无法访问。

具体流程:

    第一次执行 select 完毕会将查到的数据写入 SqlSession 内的 HashMap 中缓存起来

    第二次执行 select 会从缓存中查数据,如果 select 相同切传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率

注意:

    1、如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 SqlSession 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读

   2、当一个 SqlSession 结束后那么他里面的一级缓存也就不存在了, mybatis 默认是开启一级缓存,不需要配置

   3、 mybatis 的缓存是基于 [namespace:sql语句:参数] 来进行缓存的,意思就是, SqlSession 的 HashMap 存储缓存数据时,是使用 [namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的

2、二级缓存:

  二级缓存是 mapper 级别的缓存,也就是同一个 namespace 的 mappe.xml ,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域

  二级缓存默认是没有开启的。需要在 setting 全局参数中配置开启二级缓存

  开启二级缓存步骤:

  1、conf.xml 配置全局变量开启二级缓存

<settings>
    <setting name="cacheEnabled" value="true"/> // 默认是false:关闭二级缓存
<settings>

  2、在userMapper.xml中配置

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
// 当前mapper下所有语句开启二级缓存

// 这里配置了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象是只读的

// 若想禁用当前select语句的二级缓存,添加useCache="false"修改如下:
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">

具体流程:

  1.当一个 sqlseesion 执行了一次 select 后,在关闭此 session 的时候,会将查询结果缓存到二级缓存

  2.当另一个 sqlsession 执行 select 时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能。

免责声明:文章转载自《mybatis问题合集:#{}与${}区别、动态sql语句、缓存机制》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇usb2.0、usb3.0、usb3.1、type-c 接口含义与区别golang之vscode环境配置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux学习之路--shell学习

shell基础知识 什么是Shell Shell是命令解释器(command interpreter),是Unix操作系统的用户接口,程序从用户接口得到输入信息,shell将用户程序及其输入翻译成操作系统内核(kernel)能够识别的指令,并且操作系统内核执行完将返回的输出通过shell再呈现给用户,下图所示用户、shell和操作系统的关系: Shell也...

NLP(二十六):如何微调 GPT-2 以生成文本

近年来,自然语言生成 (NLG) 取得了令人难以置信的进步。 2019 年初,OpenAI 发布了 GPT-2,这是一个巨大的预训练模型(1.5B 参数),能够生成类似人类质量的文本。Generative Pretrained Transformer 2 (GPT-2) 顾名思义,基于 Transformer。 因此,它使用注意力机制,这意味着它会学习关...

sql 二进制文件的导入导出

/*--bcp-二进制文件的导入导出 支持image,text,ntext字段的导入/导出image适合于二进制文件;text,ntext适合于文本数据文件 注意:导入时,将覆盖满足条件的所有行导出时,将把所有满足条件的行也出到指定文件中 此存储过程仅用bcp实现邹建   2003.08-----------------*/ /*--调用示例--数据导出e...

CRL快速开发框架系列教程十三(嵌套查询)

本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框架系列教程四(删除数据) CRL快速开发框架系列教程五(使用缓存) CRL快速开发框架系列教程六(分布式缓存解决方案) CRL快速开发框架系列教程七(使用事...

Python 3 进阶 —— 使用 PyMySQL 操作 MySQL

PyMySQL 是一个纯 Python 实现的 MySQL 客户端操作库,支持事务、存储过程、批量执行等。 PyMySQL 遵循 Python 数据库 API v2.0 规范,并包含了 pure-Python MySQL 客户端库。 安装 pip install PyMySQL 创建数据库连接 import pymysql connection = p...

MyBatis模糊查询和多条件查询

一、ISmbmsUserDao层 //根据姓名模糊查询 public List<Smbms> getUser(); //多条件查询 public List<Smbms> getLikeUser(@Param("userName") String userName , @Param("userCode"...