目錄
一、 動態SQL
-
MyBatis的強大特性之一便是它的
動態SQL
。 -
如果你有使用JDBC或其他類似框架的經驗,你就能體會到根據不同條件拼接SQL語句有多麼痛苦。
-
拼接的時候要確保不能忘了必要的空格, 還要注意省掉列名列表最後的逗號。利用動態SQL這一特性可以徹底擺脫這種痛苦。
-
通常使用動態SQL不可能是獨立的一部分, MyBatis當然使用一-種強大的動態SQL語言來改進這種情形,這種語言可以被用在任意的SQL映射語句中。
-
和標籤庫JSTL很像
1、if
跳轉到目錄
<if test="boolean表達式"></if>
- if元素用於判斷,一般用作
是否應該包含某一個查詢條件
測試類
/**
* 查詢工資大於等於1000的員工
*/
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
BigDecimal minSalary = new BigDecimal("1001");
List<Employee> employees = mapper.query1(minSalary);
for (Employee employee : employees) {
System.out.println(employee);
}
sqlSession.close();
}
/**
* 查詢工資在1000-2000之間的員工
*/
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
BigDecimal minSalary = new BigDecimal("1000");
BigDecimal maxSalary = new BigDecimal("2000");
List<Employee> employees = mapper.query2(minSalary, maxSalary);
for (Employee employee : employees) {
System.out.println(employee);
}
sqlSession.close();
}
Employee接口
/**
* 查詢工資大於1000的員工
*/
List<Employee> query1(@Param("minSalary") BigDecimal minSalary);
/**
* 查詢工資在1000-2000之間
* @param minSalary
* @param maxSalary
* @return
*/
List<Employee> query2(
@Param("minSalary") BigDecimal minSalary,
@Param("maxSalary") BigDecimal maxSalary
);
EmployeeMapper.xml
<!--查詢工資大於等於1000的員工-->
<select id="query1" resultType="Employee">
SELECT * FROM employee
<if test="minSalary!=null">
WHERE salary >= #{minSalary}
</if>
</select>
<!--查詢工資在1000-2000之間的員工-->
<select id="query2" resultType="Employee">
SELECT * FROM employee WHERE 1 = 1
<if test="minSalary!=null">
AND salary >= #{minSalary}
</if>
<if test="maxSalary!=null">
AND salary <= #{maxSalary};
</if>
</select>
注意: 如果minSalary和maxSalary條件是可選擇的,也就是說當minSalary傳入null時,SQL就會出現問題.就不確定使用WHERE還是AND來連接查詢條件.
解決方案: 使用WHERE 1 = 1方式,其他的查詢條件都使用AND或OR連接,但是WHERE 1 = 1 會影響查詢性能.
2、choose、when、otherwise
- 相當於switch判斷
測試方法
/**
* 查詢指定部門的員工信息
*/
@Test
public void test3(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
BigDecimal minSalary = new BigDecimal("100");
BigDecimal maxSalary = new BigDecimal("1000");
List<Employee> employees = mapper.query3(minSalary, maxSalary, 20L);
for (Employee employee : employees) {
System.out.println(employee);
}
sqlSession.close();
}
Employee接口中的方法
List<Employee> query3(
@Param("minSalary") BigDecimal minSalary,
@Param("maxSalary") BigDecimal maxSalary,
@Param("deptId") Long deptId
);
EmployeeMapper.xml
<!--查詢指定部門的員工信息-->
<select id="query3" resultType="Employee">
SELECT * FROM employee WHERE 1 = 1
<if test="minSalary!=null">
AND salary >= #{minSalary}
</if>
<if test="maxSalary!=null">
AND salary <= #{maxSalary}
</if>
<!--假如下拉列表獲取的部門id,"所在部門"這個要排除,設爲-1-->
<!--<if test="deptId > 0">
AND deptId = #{deptId}
</if>-->
<choose> <!--相當於switch判斷-->
<when test="deptId > 0">AND deptId = #{deptId}</when>
<otherwise>AND deptId IS NOT NULL</otherwise>
</choose>
</select>
3、trim(where,set)
3.1、where
- where元素: 判斷查詢條件是否有WHERE關鍵字,如果沒有,則在第一個查詢條件之前,插入一個WHERE, 如果發現查詢條件AND 或者 OR開頭,也會把第一個查詢條件前的AND/OR 替換成WHERE
- 這種方式避免了 WHERE 1 = 1的形式
查詢指定部門的員工信息
<select id="query3" resultType="Employee">
SELECT * FROM employee
<where>
<if test="minSalary!=null">
AND salary >= #{minSalary}
</if>
<if test="maxSalary!=null">
AND salary <= #{maxSalary}
</if>
<choose> <!--相當於switch判斷-->
<when test="deptId > 0">AND deptId = #{deptId}</when>
<otherwise>AND deptId IS NOT NULL</otherwise>
</choose>
</where>
</select>
3.2、set
- set元素和where元素相似,也能根據set中的sql動態的去掉
最後的逗號
,並在前面添加set關鍵字
,如過沒有內容,也會選擇忽略set語句.
應用場景
因爲password沒有設置值,所以就要採用if來動態判斷password是否爲空,如果爲空,則不拼接,但是此時會出現問題,上面拼接的語句最後會存在一個,
.
這個時候就採用set元素來操作了,可以去掉後面的,
.
測試方法
/**
* 更新指定id的員工信息
*/
@Test
public void test4(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setId(6L);
employee.setSn("6238");
// employee.setName("桂陽");
employee.setSalary(new BigDecimal("8888"));
int update = mapper.update(employee);
if (update > 0){
System.out.println("成功修改"+update+"條用戶信息!");
}
sqlSession.commit();
sqlSession.close();
}
EmployeeMapper接口
int update(Employee employee);
EmployeeMapper.xml
<update id="update">
UPDATE employee
<set>
<if test="name!=null">
name = #{name},
</if>
<if test="sn!=null">
sn = #{sn},
</if>
<if test="salary!=null">
salary = #{salary},
</if>
</set>
WHERE id = #{id};
</update>
3.3、trim
跳轉到目錄
trim是更強大的格式化SQL的標籤:
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides="">
<!--trim包含的動態SQL-->
</trim>
前提如果trim元素包含內容返回一個字符串,則
-
prefix : 在這個字符串之前插入prefix屬性值
-
prefixOverrides : 字符串內容以prefixOverrides中的內容開頭(可以包含管道符號|),那麼使用prefix屬性值替換內容的開頭.
-
suffix : 在這個字符串之後插入suffix屬性值
-
suffixOverrides : 字符串的內容以suffixOverrides中的內容結尾(可以包含管道符號|),那麼使用suffix屬性值替換內容的結尾;
-
使用where等價於
<trim prefix="WHERE" prefixOverrides="AND |OR "> </trim>
<select id="query3" resultType="Employee"> SELECT * FROM employee <!--和使用where標籤效果一樣--> <trim prefix="WHERE" prefixOverrides="AND|OR"> <if test="minSalary!=null"> AND salary >= #{minSalary} </if> <if test="maxSalary!=null"> AND salary <= #{maxSalary} </if> <choose> <!--相當於switch判斷--> <when test="deptId > 0">AND deptId = #{deptId}</when> <otherwise>AND deptId IS NOT NULL</otherwise> </choose> </trim> </select>
注意: 此時AND後面有一個空格
-
使用set等價於
<trim prefix="WHERE" suffix="" suffixOverrides=","> </trim>
trim prefix="SET" suffix="" suffixOverrides=","> <if test="name!=null"> name = #{name}, </if> <if test="sn!=null"> sn = #{sn}, </if> <if test="salary!=null"> salary = #{salary}, </if> </trim>
4、foreach
跳轉到目錄
SQL中有時候使用IN
關鍵字,如WHERE id IN(10,20,30),此時可以使用${ids}直接拼接SQL ,但是會導致SQL注入問題,要避免SQL注入,只能使用#{}方式,此時就可以配合使用foreach
元素了。foreach元素用於迭代-個集合/數組, 通常是構建在IN運算符條件中。
- foreach元素:
- collection屬性:表示對哪一個集合或數組做迭代
如果參數是數組類型,此時Map的key爲array;
如果參數是List類型,此時Map的key爲list; - open屬性:在迭代集合之前,拼接什麼符號
- close屬性:在迭代集合之後,拼接什麼符號
- separactor屬性:在迭代元素時,每一個元素之間使用什麼符號分割開來
- item屬性:被迭代的每一個元素的變量
- index屬性:迭代的索引
- collection屬性:表示對哪一個集合或數組做迭代
需求: 刪除ID爲10,20,30的數據
需求: 批量插入語法
注意: 當傳遞一個List對象或數字對象參數給MyBatis時,(這裏可以看前面講MyBatis參數處理的部分),MyBatis會自動把它包裝到一個Map中,當是List對象時會以list作爲key, 數組對象會以array作爲key. 一般使用Param註解設置key名.
EmployeeMapperTest測試類
/**
* 批量刪除指定id的員工信息
*/
@Test
public void test5(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
mapper.batchDelete(new Long[]{10L,20L,30L});
sqlSession.commit();
sqlSession.close();
}
/**
* 批量插入員工信息
*/
@Test
public void test6(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> list = new ArrayList<Employee>();
list.add(new Employee(null, "周", "10001", new BigDecimal("5555.00"), 50L));
list.add(new Employee(null, "吳", "10002", new BigDecimal("6666.00"), 60L));
list.add(new Employee(null, "鄭", "10003", new BigDecimal("7777.00"), 70L));
int count = mapper.batchInsert(list);
if (count > 0){
System.out.println("成功插入了:"+count+"條用戶信息!");
}
sqlSession.commit();
sqlSession.close();
}
EmployeeMapper接口
/**
* 使用foreach元素批量刪除
* @param ids
* param註解原理還是Map,Map的key
*/
void batchDelete(@Param("ids") Long[] ids);
/**
* 批量插入用戶信息
* @param list
* @return
* 當參數是數組或集合時,一般要加上@Param註解,寫死
*/
int batchInsert(@Param("emps") List<Employee> emps);
EmployeeMapper.xml
<!--使用foreach元素_完成批量刪除-->
<delete id="batchDelete">
DELETE FROM employee WHERE id IN
<!--
foreach元素:
collection屬性:表示對哪一個集合或數組做迭代
如果參數是數組類型,此時Map的key爲array;
如果參數是List類型,此時Map的key爲list;
open屬性:在迭代集合之前,拼接什麼符號
close屬性:在迭代集合之後,拼接什麼符號
separactor屬性:在迭代元素時,每一個元素之間使用什麼符號分割開來
item屬性:被迭代的每一個元素的變量
index屬性:迭代的索引
-->
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
<!--使用foreach元素_完成批量插入-->
<insert id="batchInsert">
INSERT INTO employee(id, name, sn, salary, deptId) VALUES
<foreach collection="emps" separator="," item="e">
(#{e.id}, #{e.name}, #{e.sn}, #{e.salary}, #{e.deptId})
</foreach>
</insert>
5、sql、include、bind
- 使用sql可以把相同的sql片段起一個名字,並使用include在sql任意位置使用.
- bind: 使用OGNL表達式創建一個變量,並將其綁定在上下文中.
需求: 按照員工的關鍵字、工資範圍、所屬部門來查詢
需求: 按照查詢條件了查詢員工的人數
注意: 要封裝一個查詢條件類,用來設置條件使用
EmployeeQueryObject 封裝查詢條件的
package com.sunny.query;
import lombok.Data;
import java.math.BigDecimal;
/**
* 封裝員工的高級查詢信息--->封裝查詢條件
*/
@Data
public class EmployeeQueryObject {
private String keyword; // 根據keyword來查詢,員工名字或編號
private BigDecimal minSalary; // 最低工資
private BigDecimal maxSalary; // 最高工資
private Long deptId = -1L; // 部門ID,缺省爲-1;表示所有部門
/**
* 重寫keyword的get方法,如果
* @return
*/
public String getKeyword(){
// 防止傳的條件是空或空字符
return empty2null(keyword);
}
// 如果字符串爲空串,也應該設置爲null
private String empty2null(String str){
return hasLength(str) ? str : null;
}
// 判斷這個字符串是否有數據
private boolean hasLength(String str){
// str不爲空 並且 str trim()後不和""相等
/**
* 判斷非空,假如str爲zy
* zy!=null ---> true
* "".equals(zy.trim()) --> false
* !"".equals(zy.trim()) ---> !false ---> true
* 所以
* true && true ---> true 不爲空
*/
return str!=null && !"".equals(str.trim());
}
}
EmployeeMapper接口
public interface EmployeeMapper {
/**
* 根據查詢條件來查詢員工
* @param qo 封裝查詢條件的類對象
* @return
*/
List<Employee> queryForList(EmployeeQueryObject qo);
/**
* 根據查詢條件來查詢員工人數
* @param qo 封裝查詢條件的類對象
* @return
*/
int queryForEmpCount(EmployeeQueryObject qo);
}
EmployeeMapper.xml
<?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">
<!--命名空間,類似包的概念: namespace:綁定一個對應的Dao/Mapper接口-->
<mapper namespace="com.sunny.dao.EmployeeMapper">
<!--多個查詢共同使用的sql-->
<sql id="Base_where">
<where>
<if test="keyword!=null">
<bind name="keywordLike" value="'%' + keyword +'%'"/>
AND (name LIKE #{keywordLike} OR sn LIKE #{keywordLike})
<!--AND (name LIKE concat('%', #{keyword}, '%') OR sn LIKE concat('%', #{keyword}, '%'))-->
</if>
<if test="minSalary!=null">
AND salary >= #{minSalary}
</if>
<if test="maxSalary!=null">
AND salary <=#{maxSalary}
</if>
<if test="deptId!=null">
AND deptId = #{deptId}
</if>
</where>
</sql>
<!--根據查詢條件來查詢符合條件的查詢-->
<select id="queryForList" resultType="Employee">
SELECT * FROM employee
<include refid="Base_where"></include>
</select>
<!--查詢符合條件的員工數量-->
<select id="queryForEmpCount" resultType="int">
SELECT count(*) FROM employee
<include refid="Base_where"></include>
</select>
</mapper>
EmployeeMapperTest測試類
public class EmployeeMapperTest {
/**
* 按照員工的關鍵字、工資範圍、所屬部門來查詢
*/
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
EmployeeQueryObject qo = new EmployeeQueryObject();
qo.setKeyword("2");
qo.setMinSalary(new BigDecimal("1000"));
qo.setMaxSalary(new BigDecimal("9000"));
qo.setDeptId(30L);
List<Employee> employees = mapper.queryForList(qo);
for (Employee employee : employees) {
System.out.println(employee);
}
sqlSession.close();
}
/**
* 按照查詢條件了查詢員工的人數
*/
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
EmployeeQueryObject qo = new EmployeeQueryObject();
qo.setKeyword("2");
qo.setMinSalary(new BigDecimal("1000"));
qo.setMaxSalary(new BigDecimal("9000"));
qo.setDeptId(30L);
int i = mapper.queryForEmpCount(qo);
if (i > 0) {
System.out.println("符合條件的一共有:"+i+"人!");
}
sqlSession.close();
}
}