教你學會SSM框架第八步,動態SQL的使用

前言

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();		
	}

。。。。。。。。。。。。。。。。結束

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章