mybatis笔记-8、动态sql

本文深入探讨MyBatis框架中的动态SQL特性,包括if判断、sql片段复用、foreach处理集合参数及choose多条件选择,通过实例演示如何增强SQL语句的灵活性和重用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


# 动态sql

标签: mybatis


Contents


什么是动态sql?:

mybatis核心,对sql语句进行灵活操作,通过表达式对sql进行判断,对sql进行灵活拼接、组装。

一、if判断

mapper.xml
<!-- 用户信息综合查询
    #{userCustom.sex}:取出pojo包装对象中性别值
    ${userCustom.username}:取出pojo包装对象中用户名称
 -->
<select id="findUserList" parameterType="com.iot.mybatis.po.UserQueryVo"
        resultType="com.iot.mybatis.po.UserCustom">
    SELECT * FROM user
    <where><!--  where 可以自动去掉条件中的第一个and -->
        <if test="userCustom!=null"><!--类.属性不为空-->
            <if test="userCustom.sex!=null and userCustom.sex != '' "><!--类.属性.属性不为空-->
               AND user.sex=#{userCustom.sex}
            </if>
            <if test="userCustom.username!=null and userCustom.username != '' ">
               AND user.username LIKE '%${userCustom.username}%'
            </if>
        </if>
    </where>
</select>

<!-- 用户信息综合查询总数
    parameterType:指定输入类型和findUserList一样
    resultType:输出结果类型
-->
<select id="findUserCount" parameterType="com.iot.mybatis.po.UserQueryVo" resultType="int">
    SELECT count(*) FROM user
    <where>
        <if test="userCustom!=null">
            <if test="userCustom.sex!=null and userCustom.sex != '' ">
                AND user.sex=#{userCustom.sex}
            </if>
            <if test="userCustom.username!=null and userCustom.username != '' ">
                AND user.username LIKE '%${userCustom.username}%'
            </if>
        </if>
    </where>
</select>

测试结果

1.注释掉testFindUserList()方法中的userCustom.setUsername("张三");

public class UserMapperTest {  
    @Before  
    public void setup() throws Exception{  ...}
    	
    @Test
    public void testFindUserList() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();

		//创建UserMapper对象,mybatis自动生成mapper代理对象
		UserMapper userMapper  sqlSession.getMapper(UserMapper.class);

		//创建包装对象,设置查询条件
		UserQueryVo userQueryVo = new UserQueryVo();
		UserCustom userCustom = new UserCustom();
        
		//由于这里使用动态sql,如果不设置某个值,条件不会拼接在sql中
        userCustom.setSex("1");
        //userCustom.setUsername("张三");
        userQueryVo.setUserCustom(userCustom);
        
        List<UserCustom> list = userMapper.findUserList(userQueryVo);
		System.out.println(list);
    }
}

输出

DEBUG [main] - Checking to see if class com.iot.mybatis.mapper.UserMapper matches criteria [is assignable to Object]
DEBUG [main] - Checking to see if class com.iot.mybatis.mapper.UserMapperTest matches criteria [is assignable to Object]
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 352359770.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1500955a]
DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE user.sex=? 
DEBUG [main] - ==> Parameters: 1(String)
DEBUG [main] - <==      Total: 6
[User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市], User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州], User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州], User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=28, username=王小军, sex=1, birthday=Tue Feb 23 00:00:00 CST 2016, address=河南郑州]]

可以看到sql语句为reparing: SELECT * FROM user WHERE user.sex=?,没有username的部分

2.userQueryVo设为null,则userCustom为null

//List<UserCustom> list = userMapper.findUserList(userQueryVo);
List<UserCustom> list = userMapper.findUserList(null);

输出

DEBUG [main] - ==>  Preparing: SELECT * FROM user 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 9
[User [id=1, username=王五, sex=2, birthday=null, address=null], User [id=10, username=张三, sex=1, birthday=Thu Jul 10 00:00:00 CST 2014, address=北京市], User [id=16, username=张小明, sex=1, birthday=null, address=河南郑州], User [id=22, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=24, username=张三丰, sex=1, birthday=null, address=河南郑州], User [id=25, username=陈小明, sex=1, birthday=null, address=河南郑州], User [id=26, username=王五, sex=null, birthday=null, address=null], User [id=27, username=王大军, sex=2, birthday=Tue Feb 23 00:00:00 CST 2016, address=河南郑州], User [id=28, username=王小军, sex=1, birthday=Tue Feb 23 00:00:00 CST 2016, address=河南郑州]]

可以看到sql语句变为了SELECT * FROM user

二、sql片段(重点)

解决代码重复的问题。

将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。

2.1 定义sql片段
<!-- 定义sql片段
    id:sql片段的唯 一标识

    经验:1.是基于单表来定义sql片段,这样话这个sql片段可重用性才高
    	 2.在sql片段中不要包括 where
 -->
<sql id="query_user_where">
    <if test="userCustom!=null">
        <if test="userCustom.sex!=null and userCustom.sex!=''">
            AND user.sex = #{userCustom.sex}
        </if>
        <if test="userCustom.username!=null and userCustom.username!=''">
            AND user.username LIKE '%${userCustom.username}%'
        </if>
    </if>
</sql>
2.2引用sql片段
<!-- 用户信息综合查询
    #{userCustom.sex}:取出pojo包装对象中性别值
    ${userCustom.username}:取出pojo包装对象中用户名称
 -->
<select id="findUserList" parameterType="com.iot.mybatis.po.UserQueryVo"
        resultType="com.iot.mybatis.po.UserCustom">
    SELECT * FROM user
    <!--  where 可以自动去掉条件中的第一个and -->
    <where>
        <!-- 引用sql片段 的id,如果refid指定的id不在本mapper文件中,需要前边加namespace -->
        <include refid="query_user_where"></include>
        <!-- 在这里还要引用其它的sql片段  -->
    </where>
</select>

三、foreach标签

向sql传递数组或List,mybatis使用foreach解析

在用户查询列表和查询总数的statement中增加多个id输入查询。两种方法,sql语句如下:

  • SELECT * FROM USER WHERE id=1 OR id=10 OR id=16
  • SELECT * FROM USER WHERE id IN(1,10,16)

一个使用OR,一个使用IN

  • 在输入参数类型中添加List<Integer> ids传入多个id
public class UserQueryVo {

    //传入多个id
    private List<Integer> ids;
    
    getter、setter方法
    。。。
}
  • 修改mapper.xml
<if test="ids!=null">
    <!-- 使用 foreach遍历传入ids
    collection:指定输入 对象中集合属性
    item:每个遍历生成对象中
    open:开始遍历时拼接的串
    close:结束遍历时拼接的串
    separator:遍历的两个对象中需要拼接的串
     -->
    
    <!-- 使用实现下边的sql拼接:
     AND (id=1 OR id=10 OR id=16)
     -->
    <foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
        <!-- 每个遍历需要拼接的串 -->
        id=#{user_id}
    </foreach>
    
    <!-- 实现  “ and id IN(1,10,16)”拼接 -->
    <!-- <foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
        每个遍历需要拼接的串
        #{user_id}
    </foreach> -->

</if>
--------最终结果如下--------
<sql id="query_user_where">
    <if test="userCustom!=null">
        <if test="userCustom.sex!=null and userCustom.sex!=''">
            AND user.sex = #{userCustom.sex}
        </if>
        <if test="userCustom.username!=null and userCustom.username!=''">
            AND user.username LIKE '%${userCustom.username}%'
        </if>
        <if test="ids!=null">
            <foreach collection="ids" item="user_id" open="AND (" close=")" separator="or">
                <!-- 每个遍历需要拼接的串 -->
                id=#{user_id}
            </foreach>
        </if>
    </if>
</sql>
  • 测试代码

testFindUserList中加入

//传入多个id
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(10);
ids.add(16);
//将ids通过userQueryVo传入statement中
userQueryVo.setIds(ids);

四、 Choose(when , otherwise )

Choose(when , otherwise )相当swtich

when元素执行SQL代码

<select id="findCustomerByNameOrJobs" parameterType="Customer" resultType="Customer">
       select *from customer where 1=1
        <choose>
            <when test="username!=null and username!=''">
                and username like concat('%',#{username},'%')
            </when>
            <when test="jobs !=null and jobs!=''">
                and jobs=#{jobs}
            </when>
             <otherwise>
                 and phone is not null
             </otherwise>
        </choose>
    </select>

当第一个when元素中的执行条件为真,则只动态组装第一个when元素的SQL,否则就继续判断第二个when元素中的条件是否为真。

when元素测试类

 	@Test
    public void findCustomerBy() {
        Customer customer =new Customer();
       /* customer.setUsername("jack");*/
        customer.setJobs("teacher");
		List<Customer> customers =
 sqlSession.selectList("com.itheima.mapper"+".CustomerMapper.findCustomerByNameOrJobs",customer);
        for(Customer customer2:customers) {
            System.out.println(customer2);
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值