04-Mybatis 动态SQL

动态SQL

  • MyBatis的强大特性之一是它的动态SQL。使用JDBC或其它类似框架,根据不同条件拼接SQL语句是痛苦的。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号等着用的诸多语法细节。利用动态SQL这一特性可以彻底摆脱这种痛苦,让我们更加关注sql本身的逻辑语义。虽然在以前使用动态SQL并非一件易事,但正是MyBatis 提供了可以被用在任意SQL映射语句中的强大的动态SQL语言得以改进这种情形。动态SQL元素和JSTL或基于类似XML的文本处理器相似。在 MyBatis 之前的版本中,有很多元素
    需要花时间了解。MyBatis3大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis采用功能强大的基于OGNL的表达式来淘汰其它大部分元素。

一、if语句

1.1 新增语句

  • 新增语句中对入参进行判断。这样当插入的属性没有赋值的话,就会使用默认值,默认是空的,数据库中不会有记录
<!--if动态标签-->
    <insert id="addPeopleIf" parameterType="com.intellif.mozping.entity.People">
		insert into tb_people(
        <if test="id != null">
            id,
        </if>
        <if test="name != null">
            name,
        </if>
        <if test="age != null">
            age,
        </if>
        <if test="address != null">
            address,
        </if>
        <if test="edu != null">
            edu
        </if>
		)
		values(
        <if test="id != null">
            #{id,jdbcType=INTEGER},
        </if>
        <if test="name != null">
            #{name,jdbcType=VARCHAR},
        </if>
        <if test="age != null">
            #{age,jdbcType=INTEGER},
        </if>
        <if test="address != null">
            #{address,jdbcType=VARCHAR},
        </if>
        <if test="edu != null">
            #{edu,jdbcType=VARCHAR}
        </if>
		)
	</insert>

1.2 查询语句

  • 查询语句中对入参进行判断,这里原本有2个条件,经过判断之后可以输入任意一个条件或者都为null。
  • 映射文件
  <!--动态if标签查询-->
    <select id="findByNameAndAddressIf" resultType="com.intellif.mozping.entity.People">
        select *
        from tb_people p
        <where>
            <if test="name != null and name != ''">
                and p.name = #{name}
            </if>
            <if test="address != address and address != ''">
                and p.address = #{address}
            </if>
        </where>
    </select>
  • 测试
    @Test
    public void query() {
        SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstaceByConfig(CONFIG_FILE_PATH).openSession();
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);
        List<People> byNameAndAddressIf = peopleMapper.findByNameAndAddressIf("Ma yun", null);
        for (People p : byNameAndAddressIf) {
            System.out.println(p);
        }
    }

二、choose + when + otherwise

  • 如果不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了choose 元素,它有点像 Java 中的 switch语句
  • 映射文件
<!--choose + when + otherwise-->
    <select id="findByNameAndAddressChooseWhenOtherwise" resultType="com.intellif.mozping.entity.People">
        select *
        from tb_people p where
        <choose>
            <when test="name != null and name != ''">
                p.name = #{name}
            </when>
            <when test="address != null and address != ''">
                p.address = #{address}
            </when>
            <otherwise>
                1 = 1
            </otherwise>
        </choose>
    </select>
  • 测试
@Test
    public void queryChooseWhenOtherwise() {
        SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstaceByConfig(CONFIG_FILE_PATH).openSession();
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);

        //List<People> byNameAndAddressIf = peopleMapper.findByNameAndAddressChooseWhenOtherwise("Ma yun", "tianjing");
        //List<People> byNameAndAddressIf = peopleMapper.findByNameAndAddressChooseWhenOtherwise(null, "hangzhou");
        List<People> byNameAndAddressIf = peopleMapper.findByNameAndAddressChooseWhenOtherwise(null, null);
        for (People p : byNameAndAddressIf) {
            System.out.println(p);
        }
    }
  • 测试效果:我们在测试程序里面的3个查询,第一个传了name和address,但是会查出name是"Ma yun"的全部记录,和address无关,第二个语句当name为null的时候,会查出address为"hangzhou"的全部记录,如果2个参数都是null,那么会查出全部数据库记录,这就是这三个标签的作用,他可以做一定逻辑上的优先级。

三、where

  • 当我们做where条件的动态拼接时,比如按照之前的使用if,如果我们按照第一种写法,在条件缺失的时候会有问题,第二种则不会有问题。
    第一种的where是写死的,如果if全部为null,那么就没有条件,语法上就是错的,有时候会写1=1,但是那样是可能造成sql注入的,并且也不
    是很好的处理,使用where标签的话,即使条件全部都没有,mybatis也能够正确的处理
  • 映射文件
    <!--第一种:-->
    <select id="findByNameAndAddressIf" resultType="com.intellif.mozping.entity.People">
        select *
        from tb_people p 
        where
            <if test="name != null and name != ''">
                and p.name = #{name}
            </if>
            <if test="address != address and address != ''">
                and p.address = #{address}
            </if>
    </select>
    <!--条件都为null时,打印出来的预编译语句是:select * from tb_people p where ,语法会报错-->
     
    <!--第二种:-->
    <select id="findByNameAndAddressIf" resultType="com.intellif.mozping.entity.People">
        select *
        from tb_people p
        <where>
            <if test="name != null and name != ''">
                and p.name = #{name}
            </if>
            <if test="address != address and address != ''">
                and p.address = #{address}
            </if>
        </where>
    </select>
    <!--条件都为null时,打印出来的预编译语句是:select * from tb_people p -->

四、set

  • set主要用在更新的场景。
  • 映射文件
    <!--使用set更新-->
    <update id="updateWithSet" parameterType="com.intellif.mozping.entity.People">
        update tb_people
        <set>
            <if test="name!=null"> name=#{name},</if>
            <if test="age!=null"> age=#{age},</if>
            <if test="address!=null"> address=#{address},</if>
            <if test="edu!=null"> edu=#{edu},</if>
        </set>
        where id=#{id};
    </update>
  • 测试
//测试set标签
    @Test
    public void updateWithSet() {
        SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstaceByConfig(CONFIG_FILE_PATH).openSession();
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);
        People people = new People();
        people.setId(11);
        people.setAge(54);
        people.setName("Ma hua teng");
        people.setAddress("shenzhen");
        people.setEdu("Bachelor");

        int rowAffected = peopleMapper.updateWithSet(people);
        System.out.println("rowAffected: "+rowAffected);
        //显示提交事务
        sqlSession.commit();
        sqlSession.close();
    }
打印:
15:25:31.027 [main] DEBUG c.i.m.dao.PeopleMapper.updateWithSet - ==>  Preparing: update tb_people SET name=?, age=?, address=?, edu=? where id=?; 
15:25:31.071 [main] DEBUG c.i.m.dao.PeopleMapper.updateWithSet - ==> Parameters: Ma hua teng(String), 54(Integer), shenzhen(String), Bachelor(String), 11(Integer)
15:25:31.073 [main] DEBUG c.i.m.dao.PeopleMapper.updateWithSet - <==    Updates: 1
rowAffected: 1

五、trim

  • trim标记是一个格式化的标记,可以完成set或者是where标记的功能,主要包含前缀/后缀的处理,去掉第一个指定内容或者去掉最后一个指定内容
属性功能
prefix前缀
prefixOverrides去掉第一个指定内容
suffix后缀
suffixoverride去掉最后一个指定内容
  • 映射文件
 <!--trim标签,-->
    <select id="findByNameAndAddressTrim" resultType="com.intellif.mozping.entity.People">
        select *
        from tb_people p
        <trim prefix="where" prefixOverrides="AND|OR">
            <if test="name != null and name != ''">
                and p.name = #{name}
            </if>
            <if test="address != address and address != ''">
                and p.address = #{address}
            </if>
        </trim>
    </select>
  • 代码
     @Test
    public void queryTrim() {
        SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstaceByConfig(CONFIG_FILE_PATH).openSession();
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);
        List<People> byNameAndAddressIf = peopleMapper.findByNameAndAddressTrim("Parker", "tianjing");
        for (People p : byNameAndAddressIf) {
            System.out.println(p);
        }
    }
    
    打印:
    15:41:58.869 [main] DEBUG c.i.m.d.P.findByNameAndAddressTrim - ==>  Preparing: select * from tb_people p where p.name = ? 
    15:41:58.889 [main] DEBUG c.i.m.d.P.findByNameAndAddressTrim - ==> Parameters: Parker(String)
    15:41:58.906 [main] DEBUG c.i.m.d.P.findByNameAndAddressTrim - <==      Total: 1
    People(id=2, name=Parker, age=20, address=tianjing, edu=Bachelor)
        
    
    注意:
    prefix="where" 会自动给我们加上where前缀
    prefixOverrides="AND|OR"的作用是去除第一个And或者OR,为什么呢,在这个测试中传了2个条件,因此没有这个的话,是where and name = "Parker" and address = "tianjing" ,sql语法是错误的。
    假如这里连续多个条件,那么每一个条件都有可能不传,不传的那个会被忽略,因此其实并不知道哪一个if里面的and是第一个and,所以无法确定哪一个里面的and不写,所以只能都写了,让框架去去除
    第一个and。
  • 在set赋值更新语句中也是一样,需要将最后面的逗号给去掉,原理和前缀类似
    <trim  suffixOverrides=",">
		<if test="username!=null">
			name = #{username},
		</if>
		<if test="age != 0">
			age = #{age},
		</if>
	</trim> 

六、foreach

  • 用于遍历数组或者集合,比如一个集合中包含很多主键id,要查询这些id对应的数据。
属性说明
collectioncollection属性的值有三个分别是list、array、map三种
open前缀
close后缀
separator分隔符,表示迭代时每个元素之间以什么分隔
item表示在迭代过程中每一个元素的别名
index用一个变量名表示当前循环的索引位置

6.1 查询

  • 映射配置:
    <!--forEach-->
    <select id="findByIdForEach" resultType="com.intellif.mozping.entity.People">
        select *
        from tb_people  where id  in
        <foreach collection="ids" open="(" close=")" separator="," item="id" >
            #{id}
        </foreach>
    </select>
  • 测试
    //Java接口:
    List<People> findByIdForEach(@Param("ids")List<Integer> ids);
    
    //测试:
     @Test
    public void queryForEach() {
        SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstaceByConfig(CONFIG_FILE_PATH).openSession();
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);
        ArrayList<Integer> ids = new ArrayList<>();
        for (int i = 1; i < 10; i++) {
            ids.add(i);
        }
        List<People> peopleList = peopleMapper.findByIdForEach(ids);
        for (People p : peopleList) {
            System.out.println(p);
        }
    }

6.2 插入

  • 插入操作也类似,比如插入一个多个对象,插入一个集合
<insert id="insertPeopleList">
	insert into t_people(name,age) values
	<foreach collection="users" item="user" separator=",">
		(#{user.name},#{user.age})
	</foreach>
</insert> 

七、bind

  • bind 元素可以从OGNL表达式中创建一个变量并将其绑定到上下文。说起来比较抽象,看示例。
  • 映射文件
 <!--forEach-->
    <select id="findByBind" resultType="com.intellif.mozping.entity.People">
        <!-- 声明了一个参数queryId,在后面就可以使用了 -->
        <bind name="queryId" value="1"/>
        select * from tb_people where id = ${queryId}
    </select>
  • 代码
    //测试bind
    @Test
    public void queryByBind() {
        SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstaceByConfig(CONFIG_FILE_PATH).openSession();
        PeopleMapper peopleMapper = sqlSession.getMapper(PeopleMapper.class);

        List<People> peopleList = peopleMapper.findByBind();
        for (People p : peopleList) {
            System.out.println(p);
        }
    }
    //这里不知道为什么,我用字符串,按照name查找就报错了,不太清楚OGNL

八、sql

  • sql块是可以重复使用的sql语句块,如下:
    <!--sql-->
    <sql id="baseSql">
        name, age
    </sql>

    <select id="findBySql" resultType="com.intellif.mozping.entity.People">
        select
        <include refid="baseSql"/>
        from tb_people
    </select>
    
  • 相当于定义一个可以重复使用的语句块,减少重复工作。

九、参考

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis-plus是在Mybatis基础上进行封装的一个框架,它简化了平时开发过程中对常用接口的调用,可以省去一些繁琐的操作。然而,对于一些更为复杂的查询,Mybatis-plus可能无法满足需求,此时就需要我们自定义SQL语句来实现。通过在入口类的MybatisSqlSessionFactoryBuilder#build方法中注入mybatis-plus自定义的动态配置xml文件,可以实现自定义SQL语句和动态SQL的功能。具体的实现步骤如下: 1. 在应用启动时,在入口类的MybatisSqlSessionFactoryBuilder#build方法中将mybatis-plus的自定义动态配置xml文件注入到Mybatis中。 2. 在自定义的动态配置xml文件中,可以使用各种Mybatis-plus提供的方法来实现动态SQL的功能,比如IF标签、CHOOSE标签、FOREACH标签等。 3. 在自定义SQL语句中,可以结合Mybatis-plus的Wrapper类来实现条件查询,例如使用LambdaQueryWrapper来构建查询条件。 总结起来,Mybatis-plus提供了简化开发的接口,但对于一些更为复杂的查询,仍然需要我们自定义SQL语句和动态SQL来实现。通过注入自定义的动态配置xml文件,并结合Mybatis-plus提供的方法和Wrapper类,可以实现更加灵活和高效的数据查询。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [mybatis-plus/mybatis 自定义 sql 语句、动态 sql](https://blog.csdn.net/CREATE_17/article/details/109117091)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Mybatis Plus实现动态SQL语句的原理,你知道吗?](https://blog.csdn.net/weixin_38405253/article/details/119880820)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值