目录
前言
一、MyBatis动态SQL
1.动态SQL是什么
2.动态SQL的作用
3.常用动态SQL元素
1. where + if 元素
2. set + if 元素
3. choose + when + otherwise 元素
4. 自定义 trim 元素
<1>. 自定义 trim 元素改写上面的 where + if 语句
<2>. 自定义 trim 元素改写上面的 set + if 语句
5. foreach 元素
6.SQL片段重用
二、模糊查询的三种SQL方式、#和$的区别
三种模糊查询的方法是:
1.使用#{字段名}
2.使用${字段名}
3.使用concat{'%',#{字段名},'%'}
#与$的区别
1.$ 符号(sql拼接符号)
2. # 符号(占位符)
四、MyBatis结果映射
映射场景
resultType进行结果映射
resultMap进行结果映射
总结
前言
在我们编写的sql语句的内容并不是固定的,会通过一些条件判断拼接成最终符合要求的sql语句。
本篇所讲的动态SQL,是mybatis通过标签元素的形式, 如if, choose, when, otherwise, trim, where, set, foreach等标签完成对sql的拼接功能,使用起来也非常灵活方便,大大提高了开发人员的工作效率!
一、MyBatis动态SQL
1.动态SQL是什么
动态SQL是一种根据不同条件动态生成SQL语句的技术。在MyBatis中,我们可以使用动态SQL来根据不同的查询条件生成不同的SQL语句,从而实现更灵活的查询。
2.动态SQL的作用
1. 条件查询:通过动态SQL,我们可以根据用户输入的条件动态生成查询语句,从而实现灵活的条件查询。例如,根据用户选择的不同筛选条件,我们可以动态地拼接SQL语句,实现根据不同条件查询不同的结果。
2. 动态排序:有时候我们需要根据用户的选择对查询结果进行排序。通过动态SQL,我们可以根据用户选择的排序字段和排序方式动态生成排序语句,从而实现按照不同的规则对查询结果进行排序。
3. 动态更新:有时候我们需要根据不同的条件更新数据库中的数据。通过动态SQL,我们可以根据不同的条件动态生成更新语句,从而实现根据不同的条件更新不同的数据。
4. 动态插入:有时候我们需要根据不同的条件插入数据到数据库中。通过动态SQL,我们可以根据不同的条件动态生成插入语句,从而实现根据不同的条件插入不同的数据。
3.常用动态SQL元素
下面我们将以用户表为例举例说明:
1. where + if 元素
先来个简单的需求,根据 username 和 sex 来查询user用户,首先看下普通的sql:
<select id="selectUserByNameAndSex" resultType="com.ctb.model.User" parameterType="com.kzy.entity.User">select * from user where username=#{username} and sex=#{sex}
</select>
这种方式如果其中一个参数为空,可能就会导致最终查询不到数据。
我们现在想实现如果username为空,将只根据sex来查询;如果sex为空,我们将只根据username来查询。那么可以使用 where + if 标签进行判断,sql如下:
<select id="selectUserByNameOrSex" resultType="com.ctb.model.User" parameterType="com.ctb.model.User">select * from user <where><if test="username != null">username=#{username}</if><if test="sex != null">and sex=#{sex}</if></where>
</select>
if 元素:即根据条件判断是否显示其里面的内容。 where 元素:自行判断若下面的子元素有内容,则此处会添加一个'where',如果下面的子元素无内容,即条件判断都为空,则此处不添加'where'。此外,如果where标签内容以'and' 或 'or'开头的话,会将and/or自动剔除(否则 'where and/or' 连在一起会报语法错误)。
2. set + if 元素
不仅查询操作可能会用到动态sql,有时一些更新操作也会需要根据前端传来的参数进行判断,拼接符合条件的sql语句,如下:
<update id="updateUserById" parameterType="com.ctb.model.User">update user u<set><if test="username != null and username != ''">u.username = #{username},</if><if test="sex != null and sex != ''">u.sex = #{sex}</if></set>where id=#{id}
</update>
若第一个条件 username 为空,那么 sql 语句为:update user u set u.sex=? where id=?
若第一个条件不为空,那么 sql 语句为:update user u set u.username = ? ,u.sex = ? where id=?
多个条件以此类推,最后一个if标签里的内容结尾不用加逗号,是为了防止出现 '... , where ...' 语法错误。
3. choose + when + otherwise 元素
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="selectUserByChoose" resultType="com.ctb.model.User" parameterType="com.ctb.model.User">select * from user<where><choose><when "id != null and test=id !='' ">id=#{id}</when><when test="username != null and username !='' ">and username=#{username}</when><otherwise>and sex=#{sex}</otherwise></choose></where>
</select>
业务还是查询user,三个条件分别为 id,username,sex,但是策略变为只选择一个作为查询条件:传入 “id” ,对应的sql语句就是 select * from user where id=?;
传入 “username” ,对应的sql语句就是 select * from user where username=?;
如果前两者都没有传入,那么默认选择<otherwise>标签里的内容,对应的查询语句为 select * from user where sex=?;
4. 自定义 trim 元素
如果 where 或者 set 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 或 set 的功能,非常实用!
<1>. 自定义 trim 元素改写上面的 where + if 语句
<select id="selectUserByNameOrSex" resultType="com.ctb.model.User" parameterType="com.ctb.model.User">select * from user<!-- <where><if test="username != null">username=#{username}</if><if test="sex != null">and sex=#{sex}</if></where> --><!-- 改写后的效果 --><trim prefix="where" prefixOverrides="and | or"><if test="username != null">username=#{username}</if><if test="sex != null">and sex=#{sex}</if></trim>
</select>
prefix:插入 prefix 属性中指定的内容,即前缀。
prefixoverride:前缀覆盖,去掉第一个and 或者or,即 prefixoverride 属性中的内容,此处为了防止拼接sql时出现" where and "这种情况。
<2>. 自定义 trim 元素改写上面的 set + if 语句
<update id="updateUserById" parameterType="com.ctb.model.User">update user u<!-- <set><if test="username != null and username != ''">u.username = #{username},</if><if test="sex != null and sex != ''">u.sex = #{sex}</if></set> --><!-- 修改后的内容 --><trim prefix="set" suffixOverrides=","><if test="username != null and username != ''">u.username = #{username},</if><if test="sex != null and sex != ''">u.sex = #{sex},</if></trim>where id=#{id}
</update>
suffixoverride:后缀覆盖,去掉最后一个逗号,即 suffixoverride 属性中的内容,此处为了防止拼接sql时出现尾部多个逗号这种情况。
5. foreach 元素
foreach 元素的非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量,它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符,不会有不必要的语法错误。
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach 。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
例如:现在我们有个简单的sql语句:select * from user where id in (1,2,3);
现在我们来对它进行改写 :
<select id="selectUserByListId" parameterType="java.util.List" resultType="com.ctb.model.User">select * from userwhere id in<!--collection:指定输入对象中的集合属性,可以是array数组,也可是list集合item:每次遍历生成的对象open:开始遍历时的拼接字符串close:结束时拼接的字符串separator:遍历对象之间需要拼接的字符串select * from user where id in (1,2,3)--><foreach collection="list" item="id" open="(" close=") " separator=",">#{id}</foreach>
</select>
foreach 元素内各个属性对应的含义:
collection:指定输入对象中的集合属性,可以是array数组,也可是list集合
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
6.SQL片段重用
该作用于当我们经常需要使用某表的某些字段时,我们可以它分装起来,便于直接引用。
<sql id="userColumns">id, name, sex,birthday
</sql><select id="selectUsers" resultMap="BaseResultMap">SELECT <include refid="userColumns" />FROM t_user
</select><select id="selectUsersWithPrice" resultMap="BaseResultMap">SELECT <include refid="userColumns" />, infoFROM t_user
</select>
在这个示例中,通过<sql>标签定义了一个名为bookColumns的SQL片段,包含了书籍表的列名。然后,在不同的查询语句中通过<include>标签引用了该SQL片段,实现了列名的重用。
二、模糊查询的三种SQL方式、#和$的区别
三种模糊查询的方法是:
1.使用#{字段名}
<select id="like1" resultType="com.ctb.model.Book" parameterType="java.lang.String" >select<include refid="Base_Column_List" />from t_mvc_bookwhere bname like #{bname}</select>
测试结果
2.使用${字段名}
<select id="like2" resultType="com.ctb.model.Book" parameterType="java.lang.String" >select<include refid="Base_Column_List" />from t_mvc_bookwhere bname like ${bname}</select>
测试结果:
3.使用concat{'%',#{字段名},'%'}
<select id="like3" resultType="com.ctb.model.Book" parameterType="java.lang.String" >select<include refid="Base_Column_List" />from t_mvc_bookwhere bname like concat('%',#{bname},'%')
</select>
测试结果:
#与$的区别
1.$ 符号(sql拼接符号)
- $符号占位符是简单的字符串替换,不进行预编译和参数类型处理,也不会进行转义。
- $符号占位符直接将参数的值替换到SQL语句中,可以用于动态拼接SQL语句的部分内容。
- $符号占位符存在SQL注入的风险,因为参数值直接替换到SQL语句中,可能导致恶意注入攻击。
- 没有 '引号'
2. # 符号(占位符)
- #符号占位符是预编译的占位符,会对参数进行类型处理和安全处理。
- #符号占位符将参数值作为预编译参数传递给数据库,可以防止SQL注入攻击。
- #符号占位符可以用于动态生成SQL语句的条件部分,例如WHERE子句、ORDER BY子句等。
- 有引号
注:
1) mybatis中使用OGNL表达式传递参数
2) 优先使用#{...}
3) ${...}方式存在SQL注入风险
#{...}
更加安全可靠,适用于大多数情况下。而${...}
则更加灵活,但需要注意潜在的安全风险。在使用${...}
时,要确保参数值的来源可信,避免直接将用户输入的数据作为参数值传递进去。
四、MyBatis结果映射
Mybatis中结果集的处理分为两种:
resultMap:适合使用返回值是自定义实体类的情况
resultType:适合使用返回值的数据类型是非自定义的,即jdk的提供的类型
映射场景
①返回单表的对应的实体类,仅有一个查询结果,可以用resultType/resultMap。
②返回单表的对应的实体类,有多个查询结果,可以用resultType/resultMap。
③返回多表对应结果,仅有一个查询结果,通常用resultType也可以用resultMap。
④返回多表对应结果,有多个查询结果,通常用resultType也可以用resultMap。
⑤返回单个列段,仅有一个查询结果,就用resultType。
⑥返回单个列段,有多个查询结果,就用resultType。
resultType进行结果映射
首先,我们需要定义一个User类,用于存储查询结果:
public class User {private int id;private String name;private int age;private String gender;private String email;// getter和setter方法省略
}
然后,在MyBatis的mapper文件中,我们可以使用resultType来映射查询结果到User对象上。具体配置如下:
<select id="getUserById" resultType="com.ctb.model.User">SELECT id, name, age, gender, emailFROM userWHERE id = #{id}
</select>
在上面的配置中,我们使用了resultType属性来指定查询结果映射到的Java对象的全限定名。这样,当执行查询语句时,MyBatis会自动将查询结果映射到指定的Java对象上。
resultMap进行结果映射
首先,我们需要定义一个Order类,用于存储查询结果:
public class Order {private int id;private int userId;private int productId;private double price;private int quantity;// getter和setter方法省略
}
然后,在MyBatis的mapper文件中,我们可以使用resultMap来映射查询结果到Order对象上。具体配置如下:
<resultMap id="OrderResultMap" type="com.example.Order"><id property="id" column="id" /><result property="userId" column="user_id" /><result property="productId" column="product_id" /><result property="price" column="price" /><result property="quantity" column="quantity" />
</resultMap><select id="getOrderById" resultMap="OrderResultMap">SELECT id, user_id, product_id, price, quantityFROM orderWHERE id = #{id}
</select>
在上面的配置中,我们定义了一个名为"OrderResultMap"的resultMap,它的type属性指定了要映射的Java对象的全限定名。接下来,我们为每个字段指定了对应的属性名和数据库表中的列名。最后,在查询语句中引用这个resultMap即可将查询结果映射到Order对象上。
需要注意的是,如果查询结果中的某个字段在Java对象中没有对应的属性,那么该字段将被映射为null。此外,如果查询结果中的某个字段在Java对象中有多个对应的属性,那么该字段的值将被映射为一个列表或数组。
总结
resultType
用于指定查询结果的类型。它可以是Java的基本数据类型(如int、String等),也可以是自定义的Java对象。当查询结果只有一列时,可以使用resultType
来指定该列的类型。例如,如果查询结果只有一个整数列,可以将resultType
设置为int
。
resultMap
用于定义复杂的查询结果映射关系。当查询结果包含多个列,并且需要将这些列映射到一个Java对象中时,可以使用resultMap
来定义映射规则。resultMap
可以指定每个列与Java对象的属性之间的映射关系,以及一些其他的映射规则,如类型转换、关联关系等。
resultType
适用于简单的查询结果映射,而resultMap
适用于复杂的查询结果映射。
MyBatis动态SQL、模糊查询与结果映射到这就结束了,
祝大家在编程之路越走越顺,越走越宽