0
点赞
收藏
分享

微信扫一扫

javaEE(SSM)-4:动态SQL

minute_5 2022-03-12 阅读 35

title: javaEE(SSM)学习笔记4:动态SQL

动态SQL引入

【主要作用】:根据不同条件,或者不同数据,拼接SQL子句,在XML文件中实现类似编程的效果。

【主要元素(标记)】:

1.条件查询操作

1.1<if>元素

根据条件拼接SQL语句,满足条件即包含SQL子句。

(1)语法

<if test="条件表达式">
    SQL子句
</if>

(2)示例1:模糊匹配

【需求】根据输入的姓名或职业模糊查询客户表中的客户信息:

1.如果姓名不为空,则按姓名查询;
2.如果职业不为空,则按职业查询;
3.如果两者都不为空,则必须满足指定姓名和职业的条件;
4.如果两者都为空,则显示全部数据;

【分析】:该需求适合以下各种情况

条件1:select * from customer
条件2:select * from customer where username like concat('%',#{username},'%')
条件3:select * from customer where jobs like concat('%',#{jobs},'%')
条件4:select * from customer where   username like concat('%',#{username},'%') 
										AND  jobs like concat('%',#{jobs},'%')

【具体实现】:

CustomerMapper.xml内容

	<select id="getCustomer" parameterType="customer" resultType="customer">
		select * from customer  where 1=1
        
		<if test="username!=null and username!=''">
			and username like concat('%',#{username},'%')
		</if>

		<if test="jobs!=null and jobs!=''">
			and jobs like concat('%',#{jobs},'%')
		</if>
	</select>

【注意】参数类型是pojo类,test中直接使用其属性来判断

【测试核心代码】:

		  //1参数为null  :显示全部数据
		  //sqlSession.selectList("getCustomer");  
		  //2.姓名不为空
		  cust.setUsername("张三");
		  sqlSession.selectList("getCustomer",cust); 
		  //3物业不为空
		  cust.setUsername("");
		  cust.setJobs("物业");
		  sqlSession.selectList("getCustomer",cust); 
		  //4.两者不为空
		  cust.setUsername("张三");
		  cust.setJobs("老板");
		  sqlSession.selectList("getCustomer",cust); 

【运行结果,类似如下】:

DEBUG [main] - ==>  Preparing: select * from customer where 1=1 and username like concat('%',?,'%')
DEBUG [main] - ==> Parameters: 张三(String)
TRACE [main] - <==    Columns: id, username, jobs, phone, createtime
TRACE [main] - <==        Row: 58, 测试张三, 物业, 136, 2022-01-09 09:42:57
TRACE [main] - <==        Row: 59, 测试张三, 工人, 136, 2022-01-16 19:57:16
DEBUG [main] - <==      Total: 2

DEBUG [main] - ==>  Preparing: select * from customer where 1=1 and jobs like concat('%',?,'%')
DEBUG [main] - ==> Parameters: 物业(String)
TRACE [main] - <==    Columns: id, username, jobs, phone, createtime
TRACE [main] - <==        Row: 58, 测试张三, 物业, 136, 2022-01-09 09:42:57
DEBUG [main] - <==      Total: 1
DEBUG [main] - ==>  Preparing: select * from customer where 1=1 and username like concat('%',?,'%') and jobs like concat('%',?,'%')
DEBUG [main] - ==> Parameters: 张三(String), 老板(String)
DEBUG [main] - <==      Total: 0(无数据)

(3)示例2:test运算符使用 和sql运算符

【需求】:输入id,如果id<20,那么选择id<5的数据输出

【注意】:在test中的使用,以及在xml中的如何使用运算符

CustomerMapper.xml内容

	<!-- 如果id参数不为空(null) ,且小于等于20,那么选择5-该值的数据 -->
	<select id="getCustomerById" parameterType="int" resultType="customer">
		select * from customer where 1=1 

		<if test="_parameter !=null and _parameter lte 20">
			and id &gt;= 5 and id &lt;= #{id}
		</if> 
	</select>

【测试结果,类似如下】:

DEBUG [main]==> Preparing: select * from customer where 1=1 and id >= 5 and id <= ?
DEBUG [main] - ==> Parameters: 20(Integer)
TRACE [main] - <==    Columns: id, username, jobs, phone
TRACE [main] - <==        Row: 8, 孙六六, 管理员, 133333334444
TRACE [main] - <==        Row: 9, 孙六, 工人, 44444444444444444444
TRACE [main] - <==        Row: 10, 孙六, 工人, 44444444444444444444
DEBUG [main] - <==      Total: 3

1.2 <choose>元素

作用:在给出的条件中,仅仅拼接满足条件的第一个子句。
(1)语法

<choose>
	<when test="条件表达式1"> SQL子句1</when>
    <when test="条件表达式2"> SQL子句2</when>
    ...
    <otherwise>
    	SQL子句3
    </otherwise>
</choose>

按顺序判断,只要满足其中一个条件,则不再往下判断;当所有条件不满足,执行otherwise中的子句。

(2)示例:查询客户信息

【需求】:
1.如果给出客户名称,则按客户名称进行模糊查询;
2.如果给出客户职业,按照客户职业进行模糊查询;
3.如果两者都不给出,则查询所有联系电话不为空的客户信息。

CustomerMapper.xml内容

	<select id="findCustomer" 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 like concat('%',#{jobs},'%')
			</when>
			<otherwise>
				and phone is not null
			</otherwise>
		</choose>		
	</select>

1.3 where元素

【作用】根据是否有条件子句在进行拼接。根据标记内是否包含SQL子句,决定是否使用where关键字拼接查询子句,同时它会去除多余的连接运算符,比如AND 和 OR等。有了这个标记,上例的示例中的子句就不要这么写了:where 1=1

示例:使用where 元素重新实现上例

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

1.4 trim元素

【作用】能够自动添加前缀,并去除子句前面多余的、指定的连接字符(串);或者添加后缀,自动去除子句后面多余的、指定的连接字符或字符串。用法非常灵活。

其包含以下属性:

实际上,就是在子句之前、后是否要添加SQL连接关键字,是否要删除子句前后的多余关键字,目的是自动构成正确的SQL语句。

示例1:使用trim代替where元素

如果有子句,自动加上指定的where前缀,然后删除多余的、指定的连接符

【需求】:根据客户名或者职业来查询客户信息

【思路】:在子句前加入WHERE,去掉子句前面多余的OR

CustomerMapper.xml内容

	<!-- 使用trim 来代替 where -->
	<select id="getCustomer_trim" parameterType="customer"
		resultType="customer">
		select * from customer
		<trim prefix="WHERE" prefixOverrides="OR">
			<if test="username!=null and username!=''">
				OR username like concat('%',#{username},'%')
			</if>
			<if test="jobs!=null and jobs!=''">
				OR jobs like concat('%',#{jobs},'%')
			</if>
		</trim>
	</select>

【 测试】:

	@Test
	public void test3() {
		sqlSession = ssf.openSession();
		Customer c=new Customer();
		c.setJobs("工人");
		c.setUsername("孙");
		sqlSession.selectList("getCustomer_trim",c);
	}

【结果类似】:

DEBUG [main] - ==>  Preparing: select * from customer WHERE username like concat('%',?,'%') or jobs like concat('%',?,'%')
DEBUG [main] - ==> Parameters: 孙(String), 工人(String)
TRACE [main] - <==    Columns: id, username, jobs, phone, createtime
TRACE [main] - <==        Row: 8, 孙六六, 管理员, 133333334444, 2019-10-16 20:59:08
TRACE [main] - <==        Row: 9, 孙六1, 工人, 44444444444444444444, 2022-01-16 23:09:50
TRACE [main] - <==        Row: 10, 孙六2, 工人, (Null), 2022-01-16 23:10:57
TRACE [main] - <==        Row: 59, 测试张三, 工人, 136, 2022-01-16 19:57:16
DEBUG [main] - <==      Total: 4

示例2:使用trim,代替更新子句set

如果有子句,自动加上指定的set前缀,然后删除多余的、指定的连接符

【需求】:根据id更新客户信息,如果参数存在客户名称,则更新客户名称;如果存在职业,则同时更新职业。即只更新包含值的字段。
【注意】:这种情况不能一个值没有,这样无法构成正确的SQL语句(前端去判断)。

	<!-- 使用trim 代替 set -->
	<update id="updateCustomer" parameterType="customer">
		update customer 
		
		<trim prefix="set" suffixOverrides=",">
			<if test="username!=null and username!=''">
				username=#{username},
			</if>
			
			<if test="jobs!=null and jobs!=''">
				jobs=#{jobs},
			</if>
		</trim>
		
		where id=#{id}
	</update>

【测试】

	@Test
	public void test4() {
		sqlSession = ssf.openSession();
		Customer c=new Customer();
		c.setId(8);
		c.setJobs("工人");
		c.setUsername("孙明");
		sqlSession.update("updateCustomer",c);
		sqlSession.commit();//需要提交
	}

【结果类似】

DEBUG [main] - ==>  Preparing: update customer set username=?, jobs=? where id=?
DEBUG [main] - ==> Parameters: 孙明(String), 工人(String), 8(Integer)
DEBUG [main] - <==    Updates: 1

1.5 复杂查询:foreach

列举出输入参数的每一个元素。类似java的循环结果,依次从传入的数组Array、列表List或Map中取出数据,拼接成SQL子句。

【建议】:先在MySQL写出完整的sql,再去修改对应的动态SQL。

【应用场景】:

1.从一组指定的下标中查询客户信息,使用数组或列表作为输入参数。

2.输入参数同时包含多种类型,如普通类型、POJO类型、数组或列表类型,即多参数查询,使用map。例如使用用户名、职业进行模糊查询,并使用一组下标进行查询客户信息。

3.插入一组数据。

(1)语法1:使用数组和列表

<select id="" parameterType="arraylist|list" resultType="返回结果类型">
    ...
    <foreach item="循环变量" collection="array/list" index="当前循环下标" 
             open="整个子句的前缀" close="整个子句的后缀" seperator="分隔符" >
    	SQL子语句
    </foreach>
</select>

(2)语法1应用示例:查找一组id对应的数据

【需求】:从一组指定的id中查询客户信息,使用数组和列表分别实现

CustomerMapper.xml内容

	<!-- 使用数组 -->
	<select id="byArrays" parameterType="arraylist" resultType="customer">
		select * from customer where id in		
		<foreach item="id" collection="array" open="(" close=")"	separator=",">
			#{id}
		</foreach>
	</select>
	
	<!--使用列表 -->
	<select id="byList" parameterType="list" resultType="customer">
		select * from customer where id in		
		<foreach item="id" collection="list" open="(" close=")"	separator=",">
			#{id}
		</foreach>
	</select>

【测试】

	@Test
	public void test5() {
		sqlSession = ssf.openSession();
		int[] ids= {5,8,10};
		
		sqlSession.selectList("byArrays",ids);		 
	}
	@Test
	public void test6() {
		 sqlSession = ssf.openSession();
		 List<Integer> ids=new ArrayList<Integer>();	
		 ids.add(5);
		 ids.add(8);
		 ids.add(10);
        
		 sqlSession.selectList("byList",ids);		 
	}

【结果类似如下】:

DEBUG [main] - ==>  Preparing: select * from customer where id in ( ? , ? , ? )
DEBUG [main] - ==> Parameters: 5(Integer), 8(Integer), 10(Integer)
TRACE [main] - <==    Columns: id, username, jobs, phone, createtime
TRACE [main] - <==        Row: 8, 孙明, 工人, 133333334444, 2022-01-17 07:14:33
TRACE [main] - <==        Row: 10, 孙六2, 工人, (Null), 2022-01-16 23:10:57
DEBUG [main] - <==      Total: 2

DEBUG [main] - ==>  Preparing: select * from customer where id in ( ? , ? , ? )
DEBUG [main] - ==> Parameters: 5(Integer), 8(Integer), 10(Integer)
TRACE [main] - <==    Columns: id, username, jobs, phone, createtime
TRACE [main] - <==        Row: 8, 孙明, 工人, 133333334444, 2022-01-17 07:14:33
TRACE [main] - <==        Row: 10, 孙六2, 工人, (Null), 2022-01-16 23:10:57
DEBUG [main] - <==      Total: 2

(3)语法2:使用Map

当需要根据多个不同类型参数查询时,可以使用map类型参数

【语法】:

<select id="" parameterType="map" resultType="">
        ...
    <foreach item="循环变量" collection="key名" index="当前循环下标" 
             open="前缀" close="后缀" seperator="分隔符" >
    	组合的SQL语句
    </foreach>
</select>

(4)语法2应用示例:根据一组id和其他字段查询

【需求】:根据一组指定的id,和职业进行查询客户信息

CustomerMapper.xml内容

	<select id="byMap" parameterType="map" resultType="customer">
		select * from customer
		where jobs like concat('%',#{jobs},'%')
		and id in 
        
		<foreach item="id" collection="ids" open="(" close=")" separator="," >
			#{id}
		</foreach>	
	</select>

【测试】

	@Test
	public void test7() {
		sqlSession = ssf.openSession();
		List<Integer> ids=new ArrayList<Integer>();	
		ids.add(5);
		ids.add(8);
		ids.add(10);
		
		Map<String,Object> map=new HashMap<String,Object>();
		
        map.put("jobs", "工人");//普通字符串
		map.put("ids", ids);//集合或者数组 int[] ids= {5,8,10};
		
		sqlSession.selectList("byMap",map);		 
	}

1.6foreach扩展应用

【扩展内容】主要是了解foreach还有哪些用法。

(1)使用数组,传递数组范围

【需求】:传递范围id(两个整数id,上限和下限),查询id在该范围的客户信息

【分析】:使用数组 int[] ={1,10} 封装数据,在foreach中,根据下标不同,拼接SQL子句

	<select id="byRange" parameterType="int[]" resultType="customer">
		select * from customer
		<where>
			<foreach item="id" collection="array" index="index">
				<if test="index==0">
					and id &gt;= #{id}
				</if>
				<if test="index==1">
					and id &lt;=#{id}
				</if>
			</foreach>
		</where>
	</select>

【测试】

	@Test
	public void test8() {
		sqlSession = ssf.openSession();
		int[] ids= {1,10};
		sqlSession.selectList("byRange",ids); 
	}

【结果类似】

DEBUG [main] - ==>  Preparing: select * from customer WHERE id >= ? and id <=?
DEBUG [main] - ==> Parameters: 1(Integer), 10(Integer)
TRACE [main] - <==    Columns: id, username, jobs, phone, createtime
TRACE [main] - <==        Row: 2, 李四, 老板, 111111111111111, 2022-01-16 20:28:23
TRACE [main] - <==        Row: 3, 王五, 经理, 111111111111111, 2022-01-16 20:28:33
TRACE [main] - <==        Row: 8, 孙明, 工人, 133333334444, 2022-01-17 07:14:33
TRACE [main] - <==        Row: 9, 孙六1, 工人, 44444444444444444444, 2022-01-16 23:09:50
TRACE [main] - <==        Row: 10, 孙六2, 工人, (Null), 2022-01-16 23:10:57
DEBUG [main] - <==      Total: 5

(2)同时插入一组客户信息

【需求】:同时插入多条记录

【分析】:多条记录可以使用List方式封装数据,在映射SQL中,使用foreach获取逐个对象,再拼接SQL子句

	<insert id="addCustomers" parameterType="list">
		insert into customer(username,jobs,phone) values
		<foreach item="c" collection="list"  separator=",">
			( #{c.username},#{c.jobs},#{c.phone} )
		</foreach>
	</insert>

【测试】

@Test
	public void test9() {
		sqlSession = ssf.openSession();
        
		List<Customer> cs=new ArrayList<Customer>();
        
		Customer c=new Customer();
		c.setUsername("A1");
		c.setJobs("管理工人");
		c.setPhone("1111");
		cs.add(c);//插入记录1
		
		c=new Customer();
		c.setUsername("A2");
		c.setJobs("管理");
		c.setPhone("1111");
		cs.add(c);//插入记录2
		
		
		 
		sqlSession.insert("addCustomers",cs); 
		sqlSession.commit();//别忘记了
	}

结果类似

DEBUG [main] - ==>  Preparing: insert into customer(username,jobs,phone) values ( ?,?,? ) , ( ?,?,? )
DEBUG [main] - ==> Parameters: A1(String), 管理工人(String), 1111(String), A2(String), 管理(String), 1111(String)
DEBUG [main] - <==    Updates: 2

【重点记住】

1.parameterType:

2.foreach中

举报

相关推荐

0 条评论