动态 SQL 是 MyBatis 的强大特性之一。MyBatis提供的对SQL语句动态组装的功能解决了开发人员在使用JDBC或其他的框架进行数据库开发时,需要手动拼装SQL的繁琐问题.

动态SQL元素

MyBatis 3采用了功能强大的基于OGNL的表达式来完成动态SQL,其主要元素如下:

  • if :判断语句,用于单条分支判断.
  • choose,when,otherwise :相当于Java中的swith…case…default语句,用于多条件分支判断。
  • where,trim,set: 辅助元元素,用于处理一些SQL拼装,特殊字符问题。
  • foreach :循环语句,常用于in语句等列举条件中。
  • script:要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。
  • bind : 从OGNL表达式中创建一个变量,并将其绑定到上下文,常用于模糊查询的sql中。
    扩展 : OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能.它使用相同的表达式去存取对象的属性.这样可以更好的取得数据.

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

public List<User> selectUserByGenderAndCid(User user);
<select id="selectUserByGenderAndCid" parameterType="user" resultType="user">SELECT *FROM t_userWHERE u_gender = #{u_gender}<if test="u_cid != null">AND u_cid = #{u_cid}</if></select>

这条语句提供了可选的查找文本功能。如果不传入 “u_cid ”,那么所有符合 “u_gender”条件都会返回;如果传入了 “u_cid ” 参数,那么就会对 “u_cid ” 一列进行查找并返回对应的user结果。

 @Testpublic void Test3() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactoryBuilder ssb = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = ssb.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setU_gender(0);List<User> userList = mapper.selectUserByGenderAndCid(user);for (User u : userList) {System.out.println(u);}user.setU_cid(1);userList = mapper.selectUserByGenderAndCid(user);for (User u : userList) {System.out.println(u);}}

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

还是上面的例子,但是策略变为:传入了 “u_gender” 就按 “u_gender” 查找,传入了 “u_cid” 就按 “u_cid” 查找的情形。若两者都没有传入,就返回表中所有数据。

public List<User> selectUserByGenderOrCid(User user);
<select id="selectUserByGenderOrCid" parameterType="user" resultType="user">SELECT *FROM t_userWHERE 1=1<choose><when test="u_gender != null">AND u_gender = #{u_gender}</when><when test="u_cid != null">AND u_cid = #{u_cid}</when><otherwise></otherwise></choose></select>
@Testpublic void Test4() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactoryBuilder ssb = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = ssb.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> userList = null;//传入u_genderUser user = new User();user.setU_gender(0);userList = mapper.selectUserByGenderOrCid(user);for (User u : userList) {System.out.println(u);}//传入u_cidUser user2 = new User();user2.setU_cid(1);userList = mapper.selectUserByGenderOrCid(user2);for (User u : userList) {System.out.println(u);}//u_gender、u_cid均不传入User user3 = new User();userList = mapper.selectUserByGenderOrCid(user3);for (User u : userList) {System.out.println(u);}//传入u_gender、u_cidUser user4 = new User();user4.setU_gender(0);user4.setU_cid(1);userList = mapper.selectUserByGenderOrCid(user4);for (User u : userList) {System.out.println(u);}  }

trim、where、set

前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “ u_gender = #{u_gender}” 设置成动态条件,看看会发生什么。

<select id="selectUserByGenderAndCid" parameterType="user" resultType="user">SELECT *FROM t_userWHERE<if test="u_gender != null">u_gender = #{u_gender}</if><if test="u_cid != null">AND u_cid = #{u_cid}</if></select>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT *FROM t_userWHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT *FROM t_userWHEREAND u_cid = #{u_cid}

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

where

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

<select id="selectUserByGenderAndCid" parameterType="user" resultType="user">SELECT *FROM t_user<where><if test="u_gender != null">u_gender = #{u_gender}</if><if test="u_cid != null">AND u_cid = #{u_cid}</if></where></select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

@Testpublic void Test3() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactoryBuilder ssb = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = ssb.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> userList = null;User user = new User();//不设置u_gender u_ciduserList = mapper.selectUserByGenderAndCid(user);for (User u : userList) {System.out.println(u);}//先设置u_ciduser.setU_cid(1);userList = mapper.selectUserByGenderAndCid(user);for (User u : userList) {System.out.println(u);}//在设置u_genderuser.setU_gender(0);userList = mapper.selectUserByGenderAndCid(user);for (User u : userList) {System.out.println(u);}   }

trim

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

set

用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateUserIfNecessary" parameterType="user">UPDATE t_user<set><if test="u_username != null">u_username=#{u_username},</if><if test="u_password != null">u_password=#{u_password}</if></set>where u_id=#{u_id}</update>
@Testpublic void Test5() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactoryBuilder ssb = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = ssb.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setU_id(1);user.setU_username("隔壁老王");user.setU_password("6666666");mapper.updateUserIfNecessary(user);//提交事务sqlSession.commit();User user2 = mapper.selectUserById(1);System.out.println(user2);}


这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

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

注意,我们覆盖了后缀值设置,并且自定义了前缀值。

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

 public List<User> selectCidIn(List<Integer> list);
 <select id="selectCidIn" resultType="user">SELECT *FROM t_userWHERE u_cidIN<foreach item="item" index="index" collection="list" open="(" separator="," close=")">#{item}</foreach></select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

@Testpublic void Test6() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactoryBuilder ssb = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = ssb.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<Integer> list = new ArrayList<Integer>();list.add(1);list.add(3);list.add(5);List<User> userlist = mapper.selectCidIn(list);for (User user : userlist) {System.out.println(user);}}

script

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:

    @Update({"<script>","update t_user","  <set>","    <if test='u_username != null'>u_username=#{u_username},</if>","    <if test='u_password != null'>u_password=#{u_password},</if>","  </set>","where u_id=#{u_id}","</script>"})void updateUserValues(User user);

bind

元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:

public List<User> selectNameLike(User user);
<select id="selectNameLike" resultType="user"><bind name="pattern" value="'%' + u_username + '%'" />select * from t_user where u_username like #{pattern}
</select>
@Testpublic void Test7() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactoryBuilder ssb = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = ssb.build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setU_username("王");List<User> userlist = mapper.selectNameLike(user);for (User user1 : userlist) {System.out.println(user1);}}

配置文件

db.properties

#database configuration information
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_ssm_mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=123456

log4j.properties

#Global configuration
log4j.rootLogger=DEBUG,stdout
#Console configuration
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.layout.ConversionPattern=%5p [%t] - %m%n

sqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 读取数据库配置文件 --><properties resource="db.properties" /><typeAliases><!-- <typeAlias alias="user" type="pers.goodwin.mybatis.bean.User"/> --><!-- 推荐使用扫描包的形式来配置别名 包的形式会扫描包及子包下的所有文件, 以对象类名为别名,大小写不敏感,推荐使用小写 --><package name="pers.goodwin.mybatis.bean" /></typeAliases><!-- 配置环境-默认环境id为MySQL --><environments default="MySQL"><environment id="MySQL"><!-- 使用JDBC事务管理 --><transactionManager type="JDBC" /><!-- 数据库连接池 --><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></dataSource></environment></environments><!-- 将sql映射文件注册到全局配置文件中 --><mappers><!-- 使用相对于类路径的资源引用 --><!-- <mapper resource="pers/goodwin/mybatis/mapper/UserMapper.xml" /> --><!-- 使用完全限定资源定位符(URL) --><!-- <mapper url="file:///G:\Java\ssm_mybatis_part1\src\pers\goodwin\mybatis\mapper\UserMapper.xml"/> --><!-- 使用映射器接口实现类的完全限定类名 --><!-- <mapper class="pers.goodwin.mybatis.mapper.UserMapper"/> --><!-- 将包内的映射器接口实现全部注册为映射器 --><package name="pers.goodwin.mybatis.mapper" /></mappers>
</configuration>

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} </select> </mapper> --><mapper namespace="pers.goodwin.mybatis.mapper.UserMapper"><select id="selectUserById" parameterType="Integer"resultType="user">select * from t_user where u_id = #{id}</select><!-- #{}占位符 尽量使用占位符来解决问题 --><!-- ${}字符串拼接 容易产生SQL注入问题 --><select id="selectUserByName" parameterType="String"resultType="pers.goodwin.mybatis.bean.User"><!-- select * from t_user where u_username like '%${value}%' -->select * from t_user where u_username like "%"#{id}"%"</select><!-- 添加用户 --><insert id="insertUser"parameterType="pers.goodwin.mybatis.bean.User">insert into t_user values (null, #{u_username}, #{u_password}, #{u_gender},#{u_cid})</insert><update id="updateUser"parameterType="pers.goodwin.mybatis.bean.User">update t_user set u_username = #{u_username} where u_id = #{u_id}</update><delete id="deleteUserById" parameterType="Integer">delete from t_user where u_id = #{id}</delete><select id="selectUserByGenderAndCid" parameterType="user" resultType="user">SELECT *FROM t_user<where><if test="u_gender != null">u_gender = #{u_gender}</if><if test="u_cid != null">AND u_cid = #{u_cid}</if></where></select><select id="selectUserByGenderOrCid" parameterType="user" resultType="user">SELECT *FROM t_userWHERE 1=1<choose><when test="u_gender != null">AND u_gender = #{u_gender}</when><when test="u_cid != null">AND u_cid = #{u_cid}</when><otherwise></otherwise></choose></select><update id="updateUserIfNecessary" parameterType="user">UPDATE t_user<set><if test="u_username != null">u_username=#{u_username},</if><if test="u_password != null">u_password=#{u_password}</if></set>where u_id=#{u_id}</update><select id="selectCidIn" resultType="user">SELECT *FROM t_userWHERE u_cidIN<foreach item="item" index="index" collection="list" open="(" separator="," close=")">#{item}</foreach></select><select id="selectNameLike" resultType="user"><bind name="pattern" value="'%' + u_username + '%'" />select * from t_user where u_username like #{pattern}</select></mapper>

Mybatis学习笔记_5、Mybatis动态SQL相关推荐

  1. mybatis学习笔记四(动态sql)

    直接贴图,注解在代码上,其他的配置文件在学习一中就不贴了 1 数据库 2 实体类 package com.home.entity;/*** 此类是: 用户实体类* @author hpc* @2017 ...

  2. MyBatis学习 之 三、动态SQL语句

    2019独角兽企业重金招聘Python工程师标准>>> 有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询出的结果很可能是空的, ...

  3. 【最全Mybatis学习笔记(导入mybatis相关jar包)】

    目录 1. 什么是Mybatis 2. 如何引入Mybatis? 3.编写Mybatis工具类 4. 万能Map 5. 模糊查询怎么写? 6. 作用域(Scope)和生命周期SqlSessionFac ...

  4. xml模糊查询语句_2Mybatis学习笔记07:动态SQL语句(原创,转载请注明来源)

    开发环境: 硬件环境:Windows10+JDK 1.8: 软件环境:Java+Eclipse+Mybatis+maven3.6+tomcat8.0+Postgresql 10.6: 用到的jar包: ...

  5. Mybatis学习笔记(一) —— mybatis介绍

    一.Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名 ...

  6. 【Mybatis学习笔记】Mapper动态代理四项原则和注意事项

    如果使用原始的DAO,要自己对其进行实现,而如果使用Mybatis的Mapper动态代理,就可以让Mybatis自动帮你实现DAO接口. Mapper动态代理的四项原则 1.接口方法名需要与Mappe ...

  7. mybatis 学习笔记:mybatis 初认识

    简介 MyBatis是一个Java持久层框架,它通过XML描述符或注解把对象与存储过程或SQL语句关联起来.mybatis 可以将 preparedStatement 中的输入参数自动进行映射,将查询 ...

  8. MyBatis学习笔记(一)——MyBatis快速入门

    转自孤傲苍狼的博客:http://www.cnblogs.com/xdp-gacl/p/4261895.html 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优 ...

  9. MyBatis:学习笔记(4)——动态SQL

    MyBatis:学习笔记(4)--动态SQL 转载于:https://www.cnblogs.com/MrSaver/p/7453949.html

最新文章

  1. java任何表达式都可以当作语句_在Java语言中语句用分号终止,并不是所有的表达式都可以构成语句...
  2. 国内是否可以安装alexa_Alexa可以听到您无法听到的命令,哪些黑客可以利用
  3. *【CodeForces - 122C 】Lucky Sum (bfs记录状态,二分查找,有坑)(或分块)
  4. Vue笔记-Ant Design Vue构建前端连接后端WebSocket
  5. [ES6] 细化ES6之 -- 块级作用域
  6. antdesign 柱状图_ant design pro (十)advanced 图表
  7. 目标检测——Anchor-Based算法的学习笔记
  8. Script:Diagnostic Resource Manager
  9. TP-LINK无线上网短信Wifi认证配置流程
  10. 恒生ufx接口转变成CTP接口
  11. 如何制作macOS Big Sur 系统启动U盘
  12. Linux内核4.4 init,linux4.4内核启动到INIT: version 2.88 booting 卡住
  13. 停车位检测方法研究综述
  14. win10自动更新导致显卡驱动出问题,No AMD Graphics driver is installed or.......的解决方案
  15. selenium的工作原理
  16. 百度地图api实现轨迹运动效果
  17. android微信支付吊不起微信,安卓系统微信支付失败原因
  18. python实现去重_Python列表去重的4种实现方法
  19. 三万字速通JavaScript
  20. 头肩模拟器在免提或头戴式终端测试中的应用

热门文章

  1. LWIP协议与TCP/IP
  2. Android 架构之长连接技术
  3. 387. 字符串中的第一个唯一字符(javascript)387. First Unique Character in a String
  4. Java里面的同步和异步
  5. STM32之ADC的理解及运用
  6. java代理模式解析
  7. 【5G系列】MICO学习总结(2)
  8. 什么是Openflow?
  9. C语言中的%d、%u、%p、%f、%lu...
  10. php毕业论文选题系统,php毕业论文选题管理系统