前言
前面我们学习了如何使用Mybatis实现简单的增删改查。今天我们来学习如何使用动态语句来根据不同的条件生成不同的SQL语句。这在实际开发中非常有用,因为通常查询条件是多样化的,需要根据实际情况来拼接SQL语句,那什么是MyBatis动态语句呢,看下面详细简介
一,什么是MyBatis动态语句?
MyBatis动态语句是指在编写SQL语句时,使用MyBatis提供的动态标签和表达式,根据不同的条件生成不同的SQL语句片段。通过使用动态语句,我们可以根据不同的条件动态地拼接SQL语句,使其更灵活和可复用。
1.1 MyBatis常用的动态标签和表达式
if 标签
用于根据条件判断是否包含某个SQL语句片段。当条件成立时,会将
<if>
标签内部的SQL语句包含到最终生成的SQL语句中示例代码:
<select id="getUser" parameterType="int" resultType="User"> SELECT * FROM user WHERE 1=1 <if test="id != null"> AND id = #{id} </if> <if test="username != null"> AND username = #{username} </if> </select>
上面属性解释,具体定义一个查询语句的标签,
id
属性指定了查询语句的唯一标识,parameterType
属性指定了输入参数的类型,resultType
属性指定了返回结果的类型
<if test="id != null">:
动态语句的起始标签,用于判断条件是否成立
AND id = #{id}: :SQL语句片段 将id!=null 成立sql条件包含到最终生成的SQL语句中
AND username = #{username}
:SQL语句片段,当条件username != null
成立时,将被包含到最终生成的SQL语句中
其中的 test
属性用于指定一个表达式,用来判断条件是否成立。表达式可以是任意的合法表达式,通常使用参数的属性进行判断。#{}
是MyBatis中的占位符,可以获取参数的值。Choose 标签
使用
<choose>
标签和<when>
、<otherwise>
标签来实现多条件选择,根据条件选择对应的SQL语句片段示例代码:
<select id="getUser" parameterType="int" resultType="User"> SELECT * FROM user WHERE 1=1 <choose> <when test="id != null"> AND id = #{id} </when> <when test="username != null"> AND username = #{username} </when> <otherwise> AND age > #{age} </otherwise> </choose> </select>
在上面的代码<otherwise>
标签用于指定条件都不成立时执行的SQL语句片段。
Where 标签
使用
<where>
标签将条件拼接到WHERE子句中,如果条件不成立则不会生成WHERE关键字示例代码:
<select id="getUser" parameterType="int" resultType="User"> SELECT * FROM user <where> <if test="id != null"> AND id = #{id} </if> <if test="username != null"> AND username = #{username} </if> </where> </select>
Set 标签
使用
<set>
标签将更新的字段拼接到SET子句中,如果条件不成立则不会生成SET关键字示例代码:
<update id="updateUser" parameterType="User"> UPDATE user <set> <if test="username != null"> username = #{username}, </if> <if test="password != null"> password = #{password}, </if> </set> WHERE id = #{id} </update>
二,MaBatis模糊查询
2.1 #与$的区别
#(井号)
用于预编译阶段,也称为安全的参数替换。当我们使用#来表示一个参数时,MyBatis会自动将传入的参数进行预编译处理,并且会对参数进行类型安全检查,然后将其转义后嵌入到SQL语句中。这样可以防止SQL注入攻击。因此,#通常在我们需要传递动态参数时使用。在SQL语句中使用#表示参数的位置
例如:
SELECT * FROM user WHERE id = #{userId}
$(美元符号)
(美元符号)则是直接拼接参数值。当我们使用来表示一个参数时,MyBatis会把参数原封不动地拼接到SQL语句中,而不会进行任何转义或预编译处理。这样的参数替换是不安全的,容易受到SQL注入攻击。因此,通常在我们确保参数安全或需要传递动态的表名、列名等情况下使用。在SQL语句中使用表示参数的内容
例如:
SELECT * FROM ${tableName}
注意:使用$进行参数替换时,我们需要自己确保参数的值正确并且安全,以避免潜在的安全风险。而使用#进行参数替换时,MyBatis会帮助我们处理参数的类型安全和转义等问题,更加安全可靠。
实操案例:
为了更加#与$的区别,我们配置三种的方式来测试,#与$及concat
在我们前面一篇的Mybatis讲解中,生成增删改查的类中配置刚所写的xml中的模糊查询方法
package com.Bing.mapper; import com.Bing.model.Book; import org.apache.ibatis.annotations.Param; import java.util.List; public interface BookMapper { int deleteByPrimaryKey(Integer bid); int insert(Book record); int insertSelective(Book record); Book selectByPrimaryKey(Integer bid); int updateByPrimaryKeySelective(Book record); int updatByPrimaryKey(Book record); List<Book> selectByBids(@Param("bids") List bids); List<Book> mhcx1(@Param("bname") String bname); List<Book> mhcx2(@Param("bname") String bname); List<Book> mhcx3(@Param("bname") String bname); }
在前端和后端进行相对应的dao方法
定义一个接口并实现
接口方法:
List<Book> mhcx1(@Param("bname") String bname); List<Book> mhcx2(@Param("bname") String bname); List<Book> mhcx3(@Param("bname") String bname);
实现类:
package com.Bing.biz.impl; import com.Bing.biz.BookBiz; import com.Bing.mapper.BookMapper; import com.Bing.model.Book; import java.util.List; /** * @Name BingBing * @company zking cy * @create 2023-08-21-11:30 */ public class BookBizImpl implements BookBiz { private BookMapper bookMapper; public BookBizImpl() { } public BookMapper getBookMapper() { return bookMapper; } public void setBookMapper(BookMapper bookMapper) { this.bookMapper = bookMapper; } @Override public int deleteByPrimaryKey(Integer bid) { return bookMapper.deleteByPrimaryKey(bid); } @Override public int insert(Book record) { return bookMapper.insert(record); } @Override public int insertSelective(Book record) { return bookMapper.insertSelective(record); } @Override public Book selectByPrimaryKey(Integer bid) { return bookMapper.selectByPrimaryKey(bid); } @Override public int updateByPrimaryKeySelective(Book record) { return bookMapper.updateByPrimaryKeySelective(record); } @Override public int updatByPrimaryKey(Book record) { return bookMapper.updatByPrimaryKey(record); } @Override public List<Book> selectByBids(List bids) { return bookMapper.selectByBids(bids); } @Override public List<Book> mhcx1(String bname) { return bookMapper.mhcx1(bname); } @Override public List<Book> mhcx2(String bname) { return bookMapper.mhcx2(bname); } @Override public List<Book> mhcx3(String bname) { return bookMapper.mhcx3(bname); } }
接下来就是测试类了,测试#号
测试类 调用#号方法:
@Test public void mhcx1() { bookBiz.mhcx1("%圣墟%").forEach(System.out::println); }
运行结果:
等到的结果: #{bname}会将传入的bname值进行预编译转义处理,然后再拼接到SQL语句中
测试$号
@Test public void mhcx2() { bookBiz.mhcx2("%圣墟%").forEach(System.out::println); }
运行会报错
要在我们的xml文件中$bname加上引号' '
加好之后,再来测试类运行一遍
等到的结果:${bname}会将传入的bname值直接拼接到SQL语句中
三,MyBatis结果映射
resultType
是在 SQL 映射文件中指定查询结果的数据类型。它可以直接指定一个基本数据类型(如 String、Integer 等)或一个实体类的全限定名。当查询结果只包含单个简单数据类型时,通常使用 resultType
例如:
<select id="getUserAge" resultType="Integer"> SELECT age FROM user WHERE id = #{userId} </select>
resultMap
则是用于处理复杂的查询结果映射关系。它定义了数据库列和 Java 对象属性之间的映射规则。通过使用 resultMap,我们可以将查询结果映射到一个复杂的对象结构中,可以包含嵌套查询结果、联合查询等复杂情况。通常,在查询结果需要转换成复杂对象或者集合时使用 resultMap
<resultMap id="userMap" type="User"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> <!-- 其他属性映射 --> </resultMap> <select id="getUsers" resultMap="userMap"> SELECT * FROM user </select>
其中
<resultMap>
标签定义了 User 类中各个属性与数据库表的列之间的映射规则,并设置了唯一标识符 userMap。在<select>
标签中使用 resultMap 属性指定查询结果的映射关系。
总结来说,resultType 适用于简单的查询结果映射,而 resultMap 适用于复杂的查询结果映射。根据实际情况选择合适的方式可以提高 MyBatis 查询结果的灵活性和可读性。