目錄
前言
MyBatis提供對SQL語句動態組裝的功能,可以解決在使用JDBC或者框架進行數據庫開發時手動拼裝SQL這一工作。
8.1 < if>元素
在MyBatis中,< if>元素是常用的判斷語句,主要用於實現某些簡單的條件選擇。在實際應用中,我們可能會通過多個條件來精確地查詢某個數據。例如,要查找某個用戶信息,可以通過姓名和職業來查找用戶,也可以不填寫職業,直接通過姓名來查找用戶,還可以都不填寫而查詢出所有用戶,此時姓名和職業就是非必需條件。類似於這種情況,在MyBatis中就可以通過< if>元素來實現。
實例一
- 創建項目,引入jar包
- 修改配置文件中的數據庫信息爲外部引用的形式,在src目錄下創建一個名稱爲db_properties的配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis
jdbc.username=root
jdbc.password=root
- 在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>
- 創建一個工具類包,定義獲取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();
}
}
- 創建映射文件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組裝。
- 創建測試類以及測試方法
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();
}
。。。。。。。。。。。。。。。。結束