1.MyBatis中的动态SQL是什么?
MyBatis是一种Java持久化框架,它提供了一种简单且灵活的方式来处理数据库操作。动态SQL是MyBatis中的一个重要特性,它允许开发人员根据不同的条件生成不同的SQL语句。
动态SQL可以在XML映射文件中使用,也可以在注解中使用。它通过使用一些特殊的标签和关键字来实现动态生成SQL语句。这些标签包括if、choose、when、otherwise、trim、where、set等。
2.MyBatis中的动态SQL作用
MyBatis是一个开源的持久层框架,用于将Java对象映射到关系型数据库。动态SQL是MyBatis中的一个重要特性,它允许在SQL语句中根据不同的条件动态地生成不同的SQL片段,从而实现灵活的查询和更新操作。
动态SQL在MyBatis中的作用主要体现在以下几个方面:
1. 条件判断:动态SQL可以根据不同的条件判断来生成不同的SQL语句。例如,在查询操作中,可以根据用户输入的条件动态地拼接WHERE子句,从而实现灵活的查询功能。在更新操作中,可以根据不同的条件判断来决定是否更新某些字段或执行不同的更新操作。
2. 循环迭代:动态SQL还支持循环迭代,可以对集合类型的参数进行遍历,并在SQL语句中生成对应的循环逻辑。这样可以方便地处理批量插入、批量更新等操作。
3. 动态排序:通过动态SQL,可以根据用户指定的排序字段和排序方式来生成ORDER BY子句,从而实现动态排序功能。
4. 动态表名和列名:有时候需要根据不同的场景使用不同的表名或列名,动态SQL可以根据条件来生成对应的表名或列名,从而实现动态的数据库操作。
3.代码演示
根据我们自动生成的xml,接口,实体进行增加代码进行动态SQL的演示操作 自动生成的实体代码( Book )对象
package com.sy.model; /** * @author 谌艳 * @site www.shenyan.com * @create 2023-08-19 20:49 */ public class Book { private Integer bid; private String bname; private Float price; public Book(Integer bid, String bname, Float price) { this.bid = bid; this.bname = bname; this.price = price; } public Book() { super(); } public Integer getBid() { return bid; } public void setBid(Integer bid) { this.bid = bid; } public String getBname() { return bname; } public void setBname(String bname) { this.bname = bname; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } @Override public String toString() { return "Book{" + "bid=" + bid + ", bname='" + bname + '\'' + ", price=" + price + '}'; } }
自动生成的实体接口 BookMapper
package com.sy.mapper; import com.sy.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 updateByPrimaryKey(Book record); List<Book> selectByBids(@Param("bids") List bids); List<Book> like1(@Param("bname")String bname); List<Book> like2(@Param("bname")String bname); List<Book> like3(@Param("bname")String bname); List<Book> List1(); List<Book> List2(); }
自动生成的 BookMapper.xml 配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.sy.mapper.BookMapper" > <resultMap id="BaseResultMap" type="com.sy.model.Book" > <constructor > <idArg column="bid" jdbcType="INTEGER" javaType="java.lang.Integer" /> <arg column="bname" jdbcType="VARCHAR" javaType="java.lang.String" /> <arg column="price" jdbcType="REAL" javaType="java.lang.Float" /> </constructor> </resultMap> <sql id="Base_Column_List" > bid, bname, price </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from t_mvc_book where bid = #{bid,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > delete from t_mvc_book where bid = #{bid,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="com.sy.model.Book" > insert into t_mvc_book (bid, bname, price ) values (#{bid,jdbcType=INTEGER}, #{bname,jdbcType=VARCHAR}, #{price,jdbcType=REAL} ) </insert> <insert id="insertSelective" parameterType="com.sy.model.Book" > insert into t_mvc_book <trim prefix="(" suffix=")" suffixOverrides="," > <if test="bid != null" > bid, </if> <if test="bname != null" > bname, </if> <if test="price != null" > price, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="bid != null" > #{bid,jdbcType=INTEGER}, </if> <if test="bname != null" > #{bname,jdbcType=VARCHAR}, </if> <if test="price != null" > #{price,jdbcType=REAL}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.sy.model.Book" > update t_mvc_book <set > <if test="bname != null" > bname = #{bname,jdbcType=VARCHAR}, </if> <if test="price != null" > price = #{price,jdbcType=REAL}, </if> </set> where bid = #{bid,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.sy.model.Book" > update t_mvc_book set bname = #{bname,jdbcType=VARCHAR}, price = #{price,jdbcType=REAL} where bid = #{bid,jdbcType=INTEGER} </update> <select id="selectByBids" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bid in <foreach collection="bids" item="bid" open="(" close=")" separator=","> #{bid} </foreach> </select> <select id="like1" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bname like #{bname} </select> <select id="like2" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bname like '${bname}' </select> <select id="like3" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bname like concat('%',#{bname}, '%') </select> <select id="List1" resultType="com.sy.model.Book"> select <include refid="Base_Column_List" /> from t_mvc_book where bid=#{bid,jdbcType=INTEGER} </select> <select id="List2" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from t_mvc_book where bid=#{bid,jdbcType=INTEGER} </select> </mapper>
其中的动态SQL可以根据自己的需要进行编写 :
<select id="selectByBids" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bid in <foreach collection="bids" item="bid" open="(" close=")" separator=","> #{bid} </foreach> </select>
再将以下代码增加到增加编写的接口中
封装方法
List<Book> selectByBids(@Param("bids") List bids);
然后在实现类中增加以下代码
方法实现
@Override public List<Book> selectByBids(List bids) { return bm.selectByBids(bids); }
最后我们就可以在Demo1测试类中进行方法调用测试
在Demo1中增加以下代码进行测试 :
@Test public void testByid(){ List<Integer> bids = Arrays.asList(new Integer[]{33, 36, 37}); bookBiz.selectByBids(bids).forEach(System.out::println); }
输出结果为:
4. #和 $使用
2.1 #使用
( 1 ) #占位符语法
- #占位符会将传入的参数值自动进行预编译处理,可以防止SQL注入攻击。
- 使用#占位符时,Mybatis会将参数值以安全的方式替换到SQL语句中,使用JDBC的预编译语句来执行SQL查询。
- #占位符在生成SQL语句时会对参数值进行类型处理,将参数值转换为对应的JDBC类型。
- #占位符可以防止SQL注入攻击,但是无法实现动态拼接SQL片段。
( 2 ) #优点
#占位符语法在使用动态SQL时具有以下优点:
- 1防止SQL注入攻击:#占位符语法会将传入的参数值进行预编译处理,将参数值转换为对应的JDBC类型。这样可以防止恶意输入对SQL语句造成的安全威胁,提高了系统的安全性。
- 参数值类型处理:#占位符语法会对参数值进行类型处理,将参数值转换为对应的JDBC类型。这样可以避免在SQL语句中手动进行类型转换的麻烦,提高了开发效率。
- 可读性和可维护性:使用#占位符语法可以使SQL语句更加清晰和可读,因为参数值被封装在占位符中,不会直接出现在SQL语句中。这样可以方便后续的维护和修改,减少出错的可能性。
- 兼容性:#占位符语法是Mybatis的特有语法,相对于$占位符语法更具有兼容性。如果项目需要切换到其他ORM框架,使用#占位符语法可以减少代码的修改量。
综上所述,#占位符语法具有防止SQL注入攻击、参数值类型处理、可读性和可维护性、兼容性等优点。因此,在项目中使用动态SQL时,推荐使用#占位符语法。
2.2 $使用
( 1 ) $占位符语法
- $占位符会直接将传入的参数值替换到SQL语句中,不进行预编译处理。
- 使用$占位符时,Mybatis会将参数值直接替换到SQL语句中,生成最终的SQL语句。
- $占位符在生成SQL语句时不会对参数值进行类型处理,参数值会直接拼接到SQL语句中,可能存在安全风险。
- $占位符可以实现动态拼接SQL片段,但是可能存在SQL注入攻击的风险。
( 2 ) $优点
$占位符语法在使用动态SQL时具有以下优点:
- 动态拼接SQL片段:$占位符语法允许在SQL语句中直接使用参数值,可以方便地进行动态拼接SQL片段。这样可以在某些特殊情况下,更灵活地构建SQL语句。
- 字段名动态替换:$占位符语法可以用于动态替换字段名。这在某些场景下非常有用,比如需要根据用户的选择动态查询不同的字段。
- SQL语句灵活性:$占位符语法允许在SQL语句中使用任意有效的SQL表达式。这样可以在SQL语句中进行一些复杂的计算、字符串拼接等操作。
2.3代码演示
在自动生成的 BookMapper.xml 配置文件中增加以下代码
<select id="like1" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bname like #{bname} </select> <select id="like2" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bname like '${bname}' </select> <select id="like3" resultType="com.sy.model.Book" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from t_mvc_book where bname like concat('%',#{bname}, '%') </select>
自己在配置文件中根据自己需求来进行编写动态SQL
注意 :
这里如果使用$占位符语法的话需要在${bname}的左右增加单引号 列如: '${bname}'
这已是$占位符语法的一个小缺陷,在我们日常使用中一般使用的是第三种方法( like03 )
在自动生成的 BookMapper 接口中增加以下代码
//增加模糊查询方法 List<Book> like1(@Param("bname")String bname); List<Book> like2(@Param("bname")String bname); List<Book> like3(@Param("bname")String bname);
编写动态SQL后进行封装方法就是在自己创建的接口中增加以下代码
List<Book> like1(String bname); List<Book> like2(String bname); List<Book> like3(String bname);
在创建的实现类中增加以下代码
@Override public List<Book> like1(String bname) { return bm.like1(bname); } @Override public List<Book> like2(String bname) { return bm.like2(bname); } @Override public List<Book> like3(String bname) { return bm.like3(bname); }
在测试类中进行测试
@Test public void testlike1(){ bookBiz.like1("%不死不灭%").forEach(System.out::println); } @Test public void testlike2(){ bookBiz.like1("%不死不灭%").forEach(System.out::println); } @Test public void testlike3(){ bookBiz.like1("%不死不灭%").forEach(System.out::println); }
结果展示:
like1:
like2:
like3:
5.resultType及resultMap的区别
resultType是指定查询结果的类型。它可以是任何Java类,包括基本数据类型、自定义POJO类或者集合类。当使用resultType时,MyBatis会自动将查询结果映射到指定的Java对象上。例如,如果查询结果是一个整数,则可以将resultType设置为Integer;如果查询结果是一个用户对象的列表,则可以将resultType设置为List。
<select id="getUser" resultType="com.example.User"> SELECT id, username, email FROM user WHERE id = #{id} </select>
resultMap是一种更灵活和强大的方式来映射查询结果。它允许我们通过定义映射规则来控制如何将数据库中的列映射到Java对象的属性上。通过使用resultMap,我们可以实现更复杂的映射逻辑,例如处理数据库列名和Java属性名不一致的情况,处理关联查询结果等。在resultMap中,我们可以定义多个映射规则,并且可以重用这些规则。
<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="user_name"/> <result property="email" column="user_email"/> </resultMap> <select id="getUser" resultMap="userResultMap"> SELECT id as user_id, username as user_name, email as user_email FROM user WHERE id = #{id} </select>
5.1区别:
- resultType用于简单的结果映射,适用于查询结果列与Java对象属性名一致的情况,自动进行映射。
- resultMap用于复杂的结果映射,适用于查询结果列与Java对象属性名不一致或需要自定义映射关系的情况,需要手动定义映射关系。
5.2代码演示:
在自动生成的 BookMapper.xml 配置文件中增加以下代码
<select id="List1" resultType="com.sy.model.Book"> select <include refid="Base_Column_List" /> from t_mvc_book where bid=#{bid,jdbcType=INTEGER} </select> <select id="List2" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from t_mvc_book where bid=#{bid,jdbcType=INTEGER} </select>
在自动生成的 BookMapper 接口中增加以下代码
List<Book> List1(); List<Book> List2();
在创建的接口 BookBiz 中增加以下代码
List<Book> BookList01(); List<Book> BookList02();
在创建的实现类 BookBizImpl 中增加以下代码
@Override public List<Book> List1() { return bm.List1(); } @Override public List<Book> List2() { return bm.List2(); }
在测试类 增加以下代码 进行测试
@Test public void List1(){ bookBiz.List1().forEach(System.out::println); } @Test public void List2(){ bookBiz.List2().forEach(System.out::println); }
结果展示:
List1:
List2:
5.3总结:
resultType适用于简单的结果映射,可以自动进行映射;
resultMap适用于复杂的结果映射,需要手动定义映射关系。
根据具体的需求和情况,选择合适的配置项来实现查询结果的映射。