1、概述
在实际开发中,使用jdbc或其他持久层框架开发时,经常需要根据不同条件拼接SQL语句,拼接SQL语句时还要确保不能遗漏必要的空格、符号等,这种编程方式给开放人员带来很大的不便,而mybatis提供SQL语句动态组装功能,很好的解决了这个问题。
2、动态SQL元素
动态 SQL 是 MyBatis 框架中非常重要的特性之一,mybatis采用功能强大的基于OGNL表达式来完成动态SQL,它为开发人员提供了一种灵活、可读、易于维护和高效的 SQL 查询方式,能够有效地提升开发效率和系统性能,很大程度上避免单一SQL语句的堆砌,提高SQL语句的复用性。
-
灵活性:动态 SQL 允许根据不同的条件动态生成不同的 SQL 查询语句,从而使得应用程序可以更加灵活地处理各种业务场景。
-
可读性:通过使用动态 SQL,可以将复杂的 SQL 查询语句拆分成更小的部分,并根据需要组合这些部分,使得 SQL 查询语句更加清晰易懂。
-
维护性:动态 SQL 可以使得 SQL 查询语句的维护变得更加容易,因为可以根据需要动态地调整和修改 SQL 查询语句的组成部分,而不需要修改大量的代码。
-
性能优化:通过动态生成 SQL 查询语句,可以根据具体的业务需求来优化 SQL 查询语句,从而提高查询的性能和效率。
-
减少 SQL 注入的风险:MyBatis 提供了参数绑定和预编译功能,可以有效地防止 SQL 注入攻击,保障系统的安全性。
常用元素
元素 | 作用 |
<if> | 判断语句,单条件分支判断 |
<choose>(<when>、<otherwise>) | 相当于 Java 中的 switch ...case...default 语句,多条件分支判断 |
<where> | 简化SQL语句中where的条件判断 |
<tirm> | 灵活去除多余关键字 |
<set> | 用于SQL语句的动态更新 |
<foreach> | 循环语句,在in语句等列举条件常用 |
我的数据集数据
+-----+-------+------+
| uid | uname | uage |
+-----+-------+------+
| 1 | 张三 | 20 |
| 2 | 李四 | 18 |
+-----+-------+------+
①<if>元素
<if>
元素是动态 SQL 中的一个常用判断元素,用于根据条件动态地包含或排除 SQL 语句的部分,类似于java中的if语句。if
元素可以在 XML 映射文件中使用,通常用于动态生成 WHERE 子句或其他条件语句。
if语句的语法:
<if test="判断条件"> SQL语句</if>
<!--在使用if元素时,只要test属性中的表达式为true,就会执行元素中的条件语句-->
在select里面使用时
<select id="findById"
parameterType="org.example.pojo.User"
resultType="org.example.pojo.User">
select * from users where 1=1
<if test="uname != null and uname != ''">
<!-- 使用 like 操作符进行模糊匹配,查询条件中的 uname 的值包含在列值中 -->
and uname like concat('%',#{uname},'%')
</if>
</select>
对应的测试代码
User user1=new User();
user1.setUname("张");
//selectList 方法用于执行查询操作,第一个参数是命名空间和 SQL 语句的 ID,第二个参数是查询条件
//List<User> users:这是一个泛型列表,用于存储查询结果
List<User> users = session.selectList("org.example.pojo.User.findById", user1);
for (User user:users){
System.out.println(user);
}
//运行结果User{uid=1, uname='张三', uage=20}
②<choose><when><otherwise>元素
<choose><when><otherwise>元素是 MyBatis 中用于条件判断的动态 SQL 元素。它们通常一起使用,提供了一种类似于 Java 中的 switch-case
结构的功能,用于根据不同的条件执行不同的 SQL 语句片段。这三个元素往往组合使用,作用相当于java语言中的if...else if else.
<select id="findById"
parameterType="org.example.pojo.User"
resultType="org.example.pojo.User">
select * from users where 1=1
<choose>
<when test="uname != null and uname !=''">
and uname like concat('%',#{uname},'%')
</when>
<otherwise>
<!-- 如果上面的条件不满足,则执行这里的语句 -->
and uage is not null
</otherwise>
</choose>
</select>
测试代码
User user1=new User();
//user1.setUname("张");
//List<User> users:这是一个泛型列表,用于存储查询结果
List<User> users = session.selectList("org.example.pojo.User.findById", user1);
for (User user:users){
System.out.println(user);
}
//运行结果
//User{uid=1, uname='张三', uage=20}
//User{uid=2, uname='李四', uage=18}
User user1=new User();
user1.setUname("张");
//List<User> users:这是一个泛型列表,用于存储查询结果
List<User> users = session.selectList("org.example.pojo.User.findById", user1);
for (User user:users){
System.out.println(user);
}
//运行结果
//User{uid=1, uname='张三', uage=20}
③<where>元素
<where>元素会自动判断由组合条件拼接的SQL语句,只有where元素内的某个或多个条件成立时,才会在拼接SQL中加入where关键字,否则不会添加:即使where之后的内容有多余的and或or,where元素也会自动将他们去除。
where、if同时使用可以进行查询、模糊查询。
<select id="findById"
parameterType="org.example.pojo.User"
resultType="org.example.pojo.User">
select * from users
<where>
<if test="uname != null and uname != ''">
<!-- 使用 like 操作符进行模糊匹配,查询条件中的 uname 的值包含在列值中 -->
and uname like concat('%',#{uname},'%')
</if>
</where>
</select>
这个查询语句会根据传入的
uname
参数进行模糊查询,如果uname
参数不为空,则生成的 SQL 语句会添加相应的模糊查询条件,使得查询结果符合条件,不满足的话不会添加SQL语句。
测试代码
User user1=new User();
//user1.setUname("张");
//List<User> users:这是一个泛型列表,用于存储查询结果
List<User> users = session.selectList("org.example.pojo.User.findById", user1);
for (User user:users){
System.out.println(user);
}
//运行结果
//User{uid=1, uname='张三', uage=20}
//User{uid=2, uname='李四', uage=18}
④<trim>
trim 元素用于在生成动态 SQL 语句时进行字符串修剪或删除。它可以帮助你动态地添加 WHERE 子句或者其他条件,同时避免不必要的空格或逗号等问题,删除多余的关键字,可以直接实现<where>元素的功能。
属性
属性 | 说明 |
---|---|
prefix | 指定SQL语句增加的前缀 |
prefixOverrides | 指定SQL语句中要去掉的前缀字符串 |
suffix | 指定给SQL语句增加的后缀 |
suffixOverrides | 指定SQL语句中要去掉的后缀字符串 |
<select id="findById"
parameterType="org.example.pojo.User"
resultType="org.example.pojo.User">
select * from users where 1=1
<trim prefix="where" prefixOverrides="and">
<if test="uname != null and uname != ''">
<!-- 使用 like 操作符进行模糊匹配,查询条件中的 uname 的值包含在列值中 -->
and uname like concat('%',#{uname},'%')
</if>
</tirm>
</select>
<trim>
元素被用于动态添加 WHERE 子句。它的prefix
设置为 "WHERE",这意味着只有在至少有一个条件被添加到 WHERE 子句时才会加上 WHERE 关键字。prefixOverrides
设置为 "AND | OR ",这意味着如果在动态 SQL 语句生成时发现以 "AND " 或 "OR " 开头的内容,它们会被删除,避免出现语法错误。
测试代码
User user1=new User();
//user1.setUname("张");
//List<User> users:这是一个泛型列表,用于存储查询结果
List<User> users = session.selectList("org.example.pojo.User.findById", user1);
for (User user:users){
System.out.println(user);
}
//运行结果
//User{uid=1, uname='张三', uage=20}
//User{uid=2, uname='李四', uage=18}
⑤<set>元素
<set>
元素在 MyBatis 中用于在动态 SQL 中定义要更新的字段。通常在执行 UPDATE 语句时使用,它允许你根据需要动态地设置要更新的字段,而避免了在 SQL 语句中出现不必要的逗号。
在 UPDATE 语句中,<set>
元素内部通常包含了一系列 <if>
元素,每个 <if>
元素用于判断一个字段是否需要更新。如果某个字段需要更新,则将该字段及其新值添加到更新语句中。
<update id="UpdateById"
parameterType="org.example.pojo.User">
update users
<set>
<if test="uname != null and uname != ''">
<!-- 使用 like 操作符进行模糊匹配,查询条件中的 uname 的值包含在列值中 -->
uname=#{uname},
</if>
<if test="uage != null and uage !=''">
uage=#{uage},
</if>
</set> where uid=#{uid}
</update>
根据传入的参数动态设置了要更新的字段。如果传入的
uname
参数不为空,则更新uname
字段为传入的值;如果传入的uage
参数不为空,则更新uage
字段为传入的值。最后通过 WHERE 子句根据用户的 ID(uid
)来定位要更新的记录。
测试代码
User user1=new User();
user1.setUid(1);
user1.setUname("张");
session.update("org.example.pojo.User.UpdateById",user1);
session.commit();
session.close();
在映射文件中使用set和if元素组合进行update语句动态SQL组装时,如果set元素内包含内容为空,则会出现SQL语法错误。所以,在使用set元素更新字段信息时,要保证传入的更新字段不能为空。
⑥<foreach>元素
<foreach>
元素在 MyBatis 中用于迭代集合并生成相应的 SQL 片段。它可以循环遍历集合中的元素,并根据集合元素动态生成 SQL 片段,通常用于 IN 子句中的参数拼接。
属性
属性 | 说明 |
---|---|
item | 表示集合每一个元素进行迭代时的别名,该属性必选。 |
index | 在list和数组中,index是元素序号。在map中,index是元素key |
open | foreach语句代码的开始符号。一般和close=”)“合用。常用在in条件语句。 |
separator | 表示元素之间的分隔符。 |
close | 表示foreach语句代码的关闭符号,一般和open=”)“合用。常用在in条件语句。 |
collection | 用于指定遍历参数的类型。该属性取值有三种情况list、数组、map。 |
(1)入参为单参数且参数类型是list,collection属性值为list
<select id="findByList"
resultType="org.example.pojo.User">
select * from users where uid in
<foreach collection="list" item="uid" open="(" close=")" separator=",">
#{uid}
</foreach>
</select>
测试代码
List<Integer> uids =new ArrayList<>();
uids.add(2);
List<User> users = session.selectList("org.example.pojo.User.findByList", uids);
for (User user:users){
System.out.println(user);
}
session.close()
(2)入参为单参数且参数类型是数组,collection属性值为array
<select id="findByArray"
resultType="org.example.pojo.User">
select * from users where uid in
<foreach collection="array" item="uid" open="(" close=")" separator=",">
#{uid}
</foreach>
</select>
测试代码
Integer[] uids = {2};
List<User> users = session.selectList("org.example.pojo.User.findByArray", uids);
for (User user:users){
System.out.println(user);
}
session.close()
(3)传入参数为多参数,需把参数封装为map进行处理,collection属性值为map
<select id="selectByMap" resultType="org.example.pojo.User">
<!-- 定义查询语句的唯一标识符为 "selectByMap",结果类型为 org.example.pojo.User -->
<!--uname取的map里面对应的键uname-->
select * from users where uname=#{uname} and uid in
<!-- 在 SQL 语句中使用 foreach 元素来循环遍历 uid 集合,rolemap表示集合里面的每个uid -->
<foreach collection="uid" item="roleMap" open="(" close=")" separator=",">
<!-- roleMap 是循环中当前迭代的元素,在这里表示用户 ID -->
#{roleMap}
</foreach>
</select>
测试代码
//创建了一个 HashMap 对象 map 用于存储查询的参数
Map<String,Object> map =new HashMap<>();
//键 "uid" 对应的值是一个 ArrayList, List<Integer> 表示一个整数列表
List<Integer> uids =new ArrayList<>();
uids.add(1);
//将名为 "uid" 的键与先前创建的整数列表 uids 相关联,并将它们放入了一个称为 map 的 Map 对象中
map.put("uid",uids);
map.put("uname","张");
List<User> users = session.selectList("org.example.pojo.User.selectByMap", map);
for (User user:users){
System.out.println(user);
}
//关闭session
session.close();