MyBatis的动态Sql查询(七)

本文深入探讨MyBatis框架中的动态SQL特性,包括if、choose、trim、where、set、foreach、bind等元素的使用,展示如何灵活构建SQL查询语句,实现高效的数据操作。

年轻的时候,遇见了一个人,便以为余生再没有江湖,后来,才懂,她才是江湖的起源。

上一章简单介绍了MyBatis的XxxMapper.xml 映射器的详解(六),如果没有看过,请观看上一章

一. 动态Sql 查询

在数据库的持久化操作的过程中,最复杂最常用的就是查询 select 语句, 主要是后面跟着各种各样的 条件判断语句。而MyBatis很好的提供了这一点。 体现了Mybatis的灵活性,高度可配置性和可维护性。
具有的sql动态元素有:

  1. if 判断 单分支的语句
  2. choose (when otherwise) 多条件分支
  3. trim 去除前缀和后缀的第一个符号
  4. where 辅助元素, 代替 sql中的 1=1
  5. set 更新元素
  6. foreach 循环判断
  7. bind 绑定元素

下面 老蝴蝶 会讲解这些元素的使用。

二. if元素

二.一 以前不带参数的查询

老蝴蝶 以前常写的 查询全部:

接口:

User getById(int id);
List<User> findAllF1();

sql 语句配置:

<!-- 定义map形式 -->
	
	<resultMap type="user" id="userResultMap">
		<id property="id" column="id"/>
		<result property="name" column="name"/>
		<result property="sex" column="sex"/>
		<result property="age" column="age"/>
		<result property="description" column="description"/>
	</resultMap>
	
	<select id="getById"  parameterType="int" resultType="user">
		<!-- 设置别名 -->
		select * from user where id=#{id}
	</select>
	
	<select id="findAllF1" resultMap="userResultMap">
		select * from user t
	</select>

测试方法:

	@Test
	public void findAllF1Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		List<User> allList=userMapper.findAllF1();
		allList.forEach(n ->System.out.println(n));
	}

在这里插入图片描述

二.二 带参数的简单查询

上面的都是没有传入条件的,下面传入条件。 按照 名称进行查询,其中,名称可填可不填。 即,如果名称有值,则按照名称进行查询,如果没有传入名称值,则查询全部。

接口:

List<User> findAllF2(User user);

sql 元素: 用mybatis提供的 if 元素 进行单分支判断。

<select id="findAllF2" parameterType="user" resultMap="userResultMap">
		select * from user t where 
		<if test="name!=null and name!=''">
			t.name like concat('%',#{name},'%')
		</if>
	</select>

测试方法:

@Test
	public void findAllF2Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		User user=new User();
		user.setName("蝴蝶飞");  
		List<User> allList=userMapper.findAllF2(user);
		allList.forEach(n ->System.out.println(n));
	}

可以填写查询的值为 蝴蝶飞, 可以查询出结果。

在这里插入图片描述
但如果此时 没有传入 name值呢?
即将 user.setName(“蝴蝶飞”); 注释掉:
在这里插入图片描述
是错误的。

二.三 添加1=1 避免sql语句组装不正确

所以,常见的写法就是在where 后面添加一个 1=1, 避免在没有传入参数时,sql 语句组装不正确。

<select id="findAllF2" parameterType="user" resultMap="userResultMap">
		select * from user t where 1=1
		<if test="name!=null and name!=''">
			and t.name like concat('%',#{name},'%')
		</if>
	</select>

当然,也可以传入多个参数, 如按照name 和age 进行查询。 接口是不变的。
sql 语句也按照 上面 if 的写法。 后面加上1=1 就避免了sql语句出现错误

<select id="findAllF2" parameterType="user" resultMap="userResultMap">
		select * from user t where 1=1
		<if test="name!=null and name!=''">
			and t.name like concat('%',#{name},'%')
		</if>
		<if test="age!=null">
			and t.age>#{age}
		</if>
	</select>

测试方法:

@Test
	public void findAllF2Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		User user=new User();
		//两个条件时
		user.setName("蝴蝶飞");
		user.setAge(24);
		List<User> allList=userMapper.findAllF2(user);
		allList.forEach(n ->System.out.println(n));
	}

在这里插入图片描述
传入一个name 参数,传入一个age 参数, 不传参数, 都是正确的, 只是拼接的sql 语句不一样而已。

三. where 语句

在if 中 写的 1=1 虽然避免了错误,但总是不合理的, 应该直接对参数进行相应的解析。如果没有传入值,应该是 select * from user, 而不是 后面再跟上 where 1=1 , 故可以用where 语句进行。 where 语句中条件正确成立后,才会加上 where 语句。

故 接口为:

List<User> findAllF3(User user);

sql语句:

 <select id="findAllF3" parameterType="user" resultMap="userResultMap">
		select * from user t
		<where>
			<if test="name!=null and name!=''">
				and t.name like concat('%',#{name},'%')
			</if>
			<if test="age!=null">
				and t.age>#{age}
			</if>
		</where>
	</select>

测试方法:

@Test
	public void findAllF3Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		User user=new User();
		//user.setName("蝴蝶飞");  //填写时, 不填写时不又其作为查询条件。
		//两个条件时
		user.setName("蝴蝶飞");
		user.setAge(24);
		List<User> allList=userMapper.findAllF3(user);
		allList.forEach(n ->System.out.println(n));
	}

传入两个参数是:
在这里插入图片描述
传入一个 name 参数为:
在这里插入图片描述
传入一个 age 参数为:
在这里插入图片描述
不传入参数为:
在这里插入图片描述
这样,就可以达到 去除 1=1 的效果了。

四. trim() 去除前后缀

<select id="findAllF3" parameterType="user" resultMap="userResultMap">
		select * from user t  <!-- 去掉第一个and -->
		<trim prefix="where" prefixOverrides="and">
			<if test="name!=null and name!=''">
				and t.name like concat('%',#{name},'%')
			</if>
			<if test="age!=null">
				and t.age>#{age}
			</if>
		</trim>
	</select>

前缀 prefix 是 where , 去除前缀注解 prefixOverrides 的and , 即去除where 后的第一个and 关键字, 这样就可以在每一个 语句后面跟上 and 而不出错了。
除了前缀之外,还有后缀 suffix 和对应的suffixOverrides 。

常见的是前缀, 去除的是 and 或者是 or.

五. choose (when,otherwise) 多分支选择

在前面的两个参数 name,age 时 查询,是多条件查询, name 和sex 是没有先后顺序的。 而在业务当中,常常是有先后顺序的。 如 name 有值,则查询name, name 无值 则去查询age, 如果age 仍然没有值,则保证 description 描述不能为空, 查询的条件之间是有明显的先后顺序的。 这个时候,就需要用 choose 了。

接口:

//有顺序的查询
	List<User> findAllF4(User user);

sql 语句: (先用以前的 1=1 模式)

<select id="findAllF4" parameterType="user" resultMap="userResultMap">
		select * from user t where 1=1
		<!--总的选择开头-->
		<choose>
			<!--对每一种情况进行判断-->
			<when test="name!=null and name!=''">
				and t.name like concat('%',#{name},'%')
			</when>
			<when test="age!=null">
				and t.age>#{age}
			</when>
			<!--所有条件都不符合,执行 otherwise -->
			<otherwise>
				and t.description is not null
			</otherwise>
		</choose>
	</select>

测试方法:

@Test
	public void findAllF4Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		User user=new User();
		//user.setName("蝴蝶飞");  //填写时, 不填写时不又其作为查询条件。
		//两个条件时  有一定的先后顺序。
		user.setName("蝴蝶飞");
		user.setAge(24);
		List<User> allList=userMapper.findAllF4(user);
		allList.forEach(n ->System.out.println(n));
	}

如果name 有值的话:
在这里插入图片描述
如果 name 无值, age 有值的话:
在这里插入图片描述
如果 name 和age 均没有值的话:
在这里插入图片描述这样,就达到了 有先后顺序的查询了。

转换成 where 语句为:

<select id="findAllF4" parameterType="user" resultMap="userResultMap">
		select * from user t
		<where>
			<choose>
				<when test="name!=null and name!=''">
					and t.name like concat('%',#{name},'%')
				</when>
				<when test="age!=null">
					and t.age>#{age}
				</when>
				<otherwise>
					and t.description is not null
				</otherwise>
			</choose>
		</where>
	</select>

六. set 更新部分字段

关于update 语句的用法,可以看第三章, 那个时候,更新都是更新全部的字段,无法进行部分字段的更新。 而以前所讲的Hibernate 只能进行全部字段的更新,这也是Hibernate 效率低的原因之一。

<update id="updateUser" parameterType="user">
		update user set name=#{name},sex=#{sex},age=#{age},description=#{description}
		where id=#{id}
	</update>

而MyBatis 通过 set 语句,很好的解决了这一点。

接口:

int updateF5(User user);

sql 语句:

<update id="updateF5" parameterType="user">
		update user 
		<set>
			<if test="name!=null and name!=''">
				name=#{name},
			</if>
			<if test="age!=null">
				age=#{age},
			</if>
			<if test="description !=null">
				description=#{description}
			</if>
		</set>
		where id=#{id}
	</update>

六.一 查询数据库更新

@Test
	public void findAllF51Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		User user=userMapper.getById(1);
		user.setName("部分更新字段123");
		user.setDescription("只修改部分字段");
		userMapper.updateF5(user);
		sqlSession.commit();
	}

在这里插入图片描述
会将数据库中查询出来的值进行判断,如果有值,那么就进行更新,没有值,就不更新。 如果将此时的 age字段在数据库中设置为null 的话.

在这里插入图片描述
再次运行的话:
在这里插入图片描述
发现,这个时候 就不会更新 age 字段了。

六.二 设置对象bean 更新

@Test
	public void findAllF52Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		User user=new User();
		user.setId(1);
		user.setName("部分更新字段");
		user.setDescription("只修改部分字段");
		userMapper.updateF5(user);
		sqlSession.commit();
	}

这个时候,运行的话,是不会更新其他的字段的。

在这里插入图片描述

七. foreach 查询

在数据库中查询的话, 常常会查询id 的集合,进行批量的查询。 常见的形式有 数组 和List 集合的形式。 遍历查询,用的是 foreach .
属性有:
collection 为集合, item 为循环的变量, index 为索引, open 为开始, close 为结束,separator 为分隔符。
如果是单个的数组, 那么可以 collection=“array”,
如果是单个的集合,那么可以 collection=“list”
如果是多个的话,那么可以用Map 或者是@Param 注解进行传参。

七.一 数组形式

接口:

//数组形式
	List<User> findAllF6(Integer[] ids);

sql语句:

<select id="findAllF6" parameterType="list" resultMap="userResultMap">
		select * from user t
		<where>
			id in 
			<foreach collection="array" item="id" index="index" open="(" separator="," close=")">
				#{id}
			</foreach>
		</where>
	</select>

测试方法:

@Test
	public void findAllF61Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		List<User> allList=userMapper.findAllF6(new Integer[]{1,3,4,5});
		allList.forEach(n ->System.out.println(n));
	}

在这里插入图片描述

七.二 集合形式

接口:

List<User> findAllF6(List<Integer> ids);

sql 语句:

 <select id="findAllF6" parameterType="list" resultMap="userResultMap">
		select * from user t
		<where>
			id in 
			<foreach collection="list" item="id" index="index" open="(" separator="," close=")">
				#{id}
			</foreach>
		</where>
	</select>

测试方法:

@Test
	public void findAllF6Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		List<Integer> ids=Arrays.asList(1,3,4);
		List<User> allList=userMapper.findAllF6(ids);
		allList.forEach(n ->System.out.println(n));
	}

在这里插入图片描述

七.三 多值时 用@Param 注解

接口:

List<User> findAllF6(@Param(value="ids") List<Integer> ids,
			@Param(value="sexs") List<String> sexs);

sql 语句:

<select id="findAllF6" parameterType="list" resultMap="userResultMap">
		select * from user t
		<where>
			id in 
			<foreach collection="ids" item="id" index="index" open="(" separator="," close=")">
				#{id}
			</foreach>
			and sex in 
			<foreach collection="sexs" item="sex" index="index" open="(" separator="," close=")">
				#{sex}
			</foreach>
		</where>
	</select>

测试方法:

@Test
	public void findAllF62Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		List<Integer> ids=Arrays.asList(1,3,4);
		List<String> sexs=Arrays.asList("男","女");
		List<User> allList=userMapper.findAllF6(ids,sexs);
		allList.forEach(n ->System.out.println(n));
	}

在这里插入图片描述
foreach 的用法很重要。

八. bind 绑定

通过OGNL 表达式定义一个上下文变量,方便我们使用。 如字符串拼接时, mysql 中拼接是 concat() 函数,而oracle 是 || 进行拼接, 这样可以在用 like 语句时要根据不同的数据库来更新语句。 而 bind 语句则解决了这种问题。

接口:

List<User> findAllF7(@Param(value="name") String name,@Param(value="description") String description);

sql 语句:

<select id="findAllF7" parameterType="list" resultMap="userResultMap">
		select * from user t
		<where>
			<!-- 先定义变量 pattern_name 和pattern_description -->
			<bind name="pattern_name" value="'%'+name+'%'"/>
			<bind name="pattern_description" value="'%'+description+'%'"/>
			
			<if test="name!=null and name!=''">
				<!-- 引入变量 pattern_name -->
				and name like #{pattern_name}
			</if>
			<if test="description!=null and description!=''">
				<!-- 引入变量 pattern_description -->
				and description like #{pattern_description}
			</if>
		</where>
	</select>

测试方法:

@Test
	public void findAllF7Test(){
		SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
		UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
		List<User> allList=userMapper.findAllF7("蝴蝶","蝴蝶");
		allList.forEach(n ->System.out.println(n));
	}

在这里插入图片描述这就是 bind 的常见用法。

谢谢!!!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

两个蝴蝶飞

你的鼓励,是老蝴蝶更努力写作的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值