教你学会SSM框架第八步,动态SQL的使用

3 篇文章 0 订阅

前言

MyBatis提供对SQL语句动态组装的功能,可以解决在使用JDBC或者框架进行数据库开发时手动拼装SQL这一工作。

8.1 < if>元素

在MyBatis中,< if>元素是常用的判断语句,主要用于实现某些简单的条件选择。在实际应用中,我们可能会通过多个条件来精确地查询某个数据。例如,要查找某个用户信息,可以通过姓名和职业来查找用户,也可以不填写职业,直接通过姓名来查找用户,还可以都不填写而查询出所有用户,此时姓名和职业就是非必需条件。类似于这种情况,在MyBatis中就可以通过< if>元素来实现。

实例一

  1. 创建项目,引入jar包
    在这里插入图片描述
  2. 修改配置文件中的数据库信息为外部引用的形式,在src目录下创建一个名称为db_properties的配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis
jdbc.username=root
jdbc.password=root

  1. 在MyBatis配置文件mybatis-config.xml中配置< properties/>属性,修改数据库连接信息
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="db.properties" />
	<environments default="mysql">
		<environment id="mysql">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<!--数据库驱动 -->
				<property name="driver" value="${jdbc.driver}" />
				<!--连接数据库的ur1 -->
				<property name="url" value="${jdbc.url}" />
				<!--连接数据库的用户名 -->
				<property name="username" value="${jdbc.username}" />
				<!--连接数据库的密码-->
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="com/ssm/mapper/UserMapper.xml" />
	</mappers>
</configuration>
  1. 创建一个工具类包,定义获取SqlSession的方法getSession()
package com.ssm.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisUtils{
	private static SqlSessionFactory sqlSessionFactory=null;
	static{
		try {
			String resource = "mybatis-config.xml";
			InputStream inputStream = Resources.getResourceAsStream(resource);
			sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}	
	}
	public static SqlSession getSession(){
		return sqlSessionFactory.openSession();
	}
}

  1. 创建映射文件UserMapper.xml,在映射文件中使用 if元素根据username和jobs来查询用户信息列表的动态SQL
<?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.ssm.mapper.UserMapper">
<select id="findUserByNameAndJobs" parameterType="com.ssm.po.User" resultType="com.ssm.po.User">
			select * from t_user where 1=1
		<if test="username !=null and username !=''">
			and username like concat('%',#{username},'%')
		</if>
		<if test="jobs !=null and jobs !=''">
			and jobs=#{jobs}
		</if>
	</select> 
	</mapper>

使用< if>元素的test属性分别对username和jobs进行了非空判断(test属性多用于条件判断语句中,用于判断真假,大部分的场景中都是进行非空判断的,有时也需要判断字符串、数字和枚举等),如果传入的查询条件非空,就进行动态SQL组装。

  1. 创建测试类以及测试方法
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.ssm.po.User;
import com.ssm.util.MybatisUtils;
public class MybatisTest {
/*
	 * 根据用户姓名和职业组合条件查询用户信息列表
	 */
	@Test
	public void findUserByNameAndJobsTest() throws Exception {
		//通过工具类生成sqlSession对象
		SqlSession sqlSession = MybatisUtils.getSession();
		//创建User对象,封装需要组合查询的条件
		User user=new User();
		user.setUsername("zhangsan");
		user.setJobs("teacher");
		//执行sqlSession的查询方法,返回结果集
		List<User> users = sqlSession.selectList("com.ssm.mapper.UserMapper.findUserByNameAndJobs", user);
		for (User u : users) {
			System.out.println(u.toString());
		}
		sqlSession.close();
	}
}

findUserByNameAndJobsTest()方法中,首先通过MybatisUtils工具类获取了SqlSession对象,然后使用User对象封装了用户名为zhangsan且职业为teacher的查询条件,并通过SqlSession对象的selectList()方法执行多条件组合的查询操作。最后,程序执行完毕时关闭了SqlSession对象。执行findUserByNameAndJobsTest()方法。

如果将封装到User对象中的zhangsan和teacher两行代码注释掉,然后再次执行findUserByNameAndJobsTest()方法,当未传递任何参数时,会将数据表中的所有数据查出来。这就是< if>元素的使用。

8.2 < choose> < when> < otherwise>元素

  • 在使用< if>元素时,只要test属性中的表达式为true,就会执行元素中的条件语句,但是在实际应用中,有时只需要从多个选项中选择一个执行。例如,若用户姓名不为空,则只根据用户姓名进行筛选;若用户姓名为空,而用户职业不为空,则只根据用户职业进行筛选;若用户姓名和用户职业都为空,则要求查询出所有电话不为空的用户信息。
    此种情况下,可以使用< choose>、< when>、< otherwise>元素进行处理,类似于在Java语言中使用switch…case…default语句。
    以下所有元素使用时配置步骤同上if元素

第一步
首先在UserMapper.xml映射文件中编写动态的SQL

<!--<choose>(<when>、<otherwise>)元素使用 -->
	<select id="findUserByNameOrJobs" parameterType="com.ssm.po.User" resultType="com.ssm.po.User">
			select * from t_user 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>

第二步
编写测试类

/*
	 * 根据用户姓名或者职业组合条件查询用户信息列表
	 */
	@Test
	public void findUserByNameOrJobsTest() throws Exception {
		SqlSession sqlSession = MybatisUtils.getSession();
		User user=new User();
		//user.setUsername("zhangsan");
		//user.setJobs("teacher");
		List<User> users = sqlSession.selectList("com.ssm.mapper.UserMapper.findUserByNameOrJobs", user);
		for (User u : users) {
			System.out.println(u.toString());
		}
		sqlSession.close();
	}

8.3 < where> < trim>元素

映射文件中编写的SQL后面都加入了“where1=1”的条件,是为了保证当条件不成立时拼接起来的SQL语句在执行时不会报错,即使得SQL不出现语法错误。那么在MyBatis中,有没有什么办法不用加入“1=1”这样的条件,也能使拼接后的SQL成立呢?针对这种情况,MyBatis提供了< where>元素。
将where 1=1 替换为如下代码

<!--<if>、<where>元素使用 -->
	<select id="findUserByNameAndJobs" parameterType="com.ssm.po.User" resultType="com.ssm.po.User">
		select * from t_user 
	   <where>
		<if test="username !=null and username !=''">
			and username like concat('%',#{username},'%')
		</if>
		<if test="jobs !=null and jobs !=''">
			and jobs=#{jobs}
		</if>
	  </where>
	</select>

除了使用< where>元素外,还可以通过< trim>元素来定制需要的功能,上述代码可以修改为如下形式。
上述配置代码中,同样使用< trim>元素对“where 1=1”条件进行了替换,< trim>元素的作用是去除一些特殊的字符串,它的prefix属性代表的是语句的前缀(这里使用where来连接后面的SQL片段),而prefixOverrides属性代表的是需要去除的那些特殊字符串(这里定义了要去除SQL中的and),上面的写法和使用< where>元素基本是等效的。
代码如下

<!-- 
	<if>、<trim>元素使用 
	<select id="findUserByNameAndJobs" parameterType="com.ssm.po.User" resultType="com.ssm.po.User">
		select * from t_user 
	   <trim prefix="where" prefixOverrides="and">
		<if test="username !=null and username !=''">
			and username like concat('%',#{username},'%')
		</if>
		<if test="jobs !=null and jobs !=''">
			and jobs=#{jobs}
		</if>
	  </trim>
	</select> 

8.4 < set>元素

在Hibernate中,如果想要更新某一个对象,就需要发送所有的字段给持久化对象,然而实际应用中会存在只需要更新某一个或几个字段。为了让程序只更新需要更新的字段,MyBatis提供了< set>元素来完成这一工作。< set>元素主要用于更新操作,主要作用是在动态包含的SQL语句前输出一个SET关键字,并将SQL语句中最后一个多余的逗号去除。

修改映射文件

<!-- 	
	 更新用户信息
	<update id="updateUser" parameterType="com.ssm.po.User">
		update t_user
		<set>
			<if test="username !=null and username !=''">
				username=#{username}
			</if>
			<if test="jobs !=null and jobs !=''">
				jobs=#{jobs}
			</if>
			<if test="phone !=null and phone !=''">
				phone=#{phone}
			</if>
		</set>
		where id=#{id}
	</update>

在上述配置的SQL语句中,使用了< set>和< if>元素相结合的方式来组装update语句。其中< set>元素会动态前置SET关键字,同时消除SQL语句中最后一个多余的逗号;< if>元素用于判断相应的字段是否传入值,如果传入的更新字段非空,就将此字段进行动态SQL组装,并更新此字段,否则此字段不执行更新。

注意1

在映射文件中使用< set>和< if>元素组合进行update语句动态SQL组装时,如果< set>元素内包含的内容都为空,就会出现SQL语法错误。所以在使用< set>元素进行字段信息更新时,要确保传入的更新字段不能都为空。

8.5 < foreach>元素

MyBatis中已经提供了一种用于数组和集合循环遍历的方式,那就是使用< foreach>元素。假设在一个用户表中有1000条数据,现在需要将id值小于100的用户信息全部查询出来,就可以通过< foreach>元素来解决。

第一步,修改映射文件
< foreach>元素通常在构建IN条件语句时使用,其使用方式如下。

<!--<foreach>元素使用 -->
	<select id="findUserByIds" parameterType="List" resultType="com.ssm.po.User">
			select * from t_user where id in
		<foreach item="id" index="index" collection="list" open="(" separator="," close=")">
		   #{id}
		</foreach>
	</select>

在上述代码中,使用< foreach>元素对传入的集合进行遍历以及动态SQL组装。关于< foreach>元素中使用的几种属性的描述具体如下:

  • item:配置的是循环中当前的元素。
  • index:配置的是当前元素在集合中的位置下标。
  • collection:配置的list是传递过来的参数类型(首字母小写),可以是一个array、list(或collection)、Map集合的键、POJO包装类中的数组或集合类型的属性名等。
  • open和close:配置的是以什么符号将这些集合元素包装起来。
  • separator:配置的是各个元素的间隔符。
    注意
  • 可以将任何可迭代对象(如列表、集合等)和任何字典或者数组对象传递给< foreach>作为集合参数。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者MapEntry对象的集合)时,index是键,item是值

第二步
编写测试类

/*
	 * 根据用户编号批量查询用户信息
	 */
	@Test
	public void	findUserByIdsTest(){
		SqlSession sqlSession = MybatisUtils.getSession();
		List<Integer> ids=new ArrayList<Integer>();
		ids.add(1);
		ids.add(2);
		List<User> users = sqlSession.selectList("com.ssm.mapper.UserMapper.findUserByIds", ids);
		for (User user : users) {
			System.out.println(user.toString());
		}
		sqlSession.close();
		
	}

在上述代码中,执行查询操作时传入了一个客户编号集合ids。执行findUserByIdsTest()方法后,成功批量地查询出对应的用户信息。

在使用< foreach>时,最关键、最容易出错的就是collection属性,该属性是必须指定的,而且在不同情况下该属性的值是不一样的,主要有以下3种情况。

  • 如果传入的是单参数且参数类型是一个数组或者List的时候,collection属性值分别为array、list(或collection)
  • 如果传入的参数有多个,就需要把它们封装成一个Map,当然单参数也可以封装成Map集合,这时collection属性值就为Map的键
  • 如果传入的参数是POJO包装类,collection属性值就为该包装类中需要进行遍历的数组或集合的属性名
  • 在设置collection属性值的时候,必须按照实际情况配置,否则程序就会出现异常。

8.6 < bind>元素

在进行模糊查询编写SQL语句的时候,若使用“${}”进行字符串拼接,则无法防止SQL注入问题;若使用concat函数进行拼接,则只针对 MySQL数据库有效;若使用的是Oracle数据库,则要使用连接符号“||”。这样,映射文件中的SQL就要根据不同的情况提供不同形式的实现,显然是比较麻烦的,且不利于项目的移植。为此,MyBatis提供了< bind>元素来解决这一问题。我们完全不必使用数据库语言,只要使用 MyBatis的语言即可与所需参数连接。
MyBatis的< bind>元素可以通过OGNL表达式来创建一个上下文变量,其使用方式如下所示。

第一步,修改映射文件

<!--<bind>元素的使用:根据用户姓名模糊查询用户信息 -->
	<select id="findUserByName2" parameterType="com.ssm.po.User" resultType="com.ssm.po.User">
		<!--_parameter.getUsername()也可以直接写成传入的字段属性名,即username  -->
		<bind name="p_username" value="'%'+_parameter.getUsername()+'%'"/>
		select * from t_user 
		where username like #{p_username}
	</select>

上述配置代码中,使用< bind>元素定义了一个name为p_username的变量,< bind>元素中value的属性值就是拼接的查询字符串,其中_parameter.getUsername()表示传递进来的参数(也可以直接写成对应的参数变量名,如username)。在SQL语句中,直接引用< bind>元素的name属性值即可进行动态SQL组装。

第二步
编写测试类

/*
	 * 根据用户姓名模糊查询用户信息
	 */
	@Test
	public void	findUserByName2(){
		SqlSession sqlSession = MybatisUtils.getSession();
		User user=new User();
		user.setUsername("s");
		List<User> users = sqlSession.selectList("com.ssm.mapper.UserMapper.findUserByName2", user);
		for (User u : users) {
			System.out.println(u.toString());
		}
		sqlSession.close();		
	}

。。。。。。。。。。。。。。。。结束

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Devin Dever

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值