mybatis 6:動態SQL
※ 動態SQL dynamic-sql
有時候,靜態的SQL語句並不能滿足應用程式的需求。我們可以根據一些條件,來動態地構建 SQL語句。 例如,在Web應用程式中,有可能有一些搜尋介面,需要輸入一個或多個選項,然後根據這些已選擇的條件去執行檢索操作。在實現這種型別的搜尋功能,我們可能需要根據這些條件來構建動態的SQL語句。如果使用者提供了任何輸入條件,我們需要將那個條件新增到SQL語句的WHERE子句中。MyBatis通過使用<if>,<choose>,<where>,<foreach>,<trim>元素提供了對構造動態SQL語句的高級別支援。
※ 1 If 條件 <if>元素被用來有條件地嵌入SQL片段,如果測試條件被賦值為true,則相應地SQL片段將會被新增到SQL語句中。假定我們有一個課程搜尋介面,設定了講師(Tutor)下拉列表框,課程名稱(CourseName)文字輸入框,開始時間(StartDate)輸入框,結束時間(EndDate)輸入框,作為搜尋條件。假定課講師下拉列表是必須選的,其他的都是可選的。 當用戶點選搜尋按鈕時,我們需要顯示符合以下條件的成列表: 特定講師的課程 課程名 包含輸入的課程名稱關鍵字的課程;如果課程名稱輸入為空,則取所有課程 在開始時間和結束時間段內的課程 我們可以對應的對映語句,如下所示:
<resultMap type="Course" id="CourseResult"> <id column="course_id" property="courseId" /> <result column="name" property="name" /> <result column="description" property="description" /> <result column="start_date" property="startDate" /> <result column="end_date" property="endDate" /> </resultMap> <select id="searchCourses" parameterType="map" resultMap="CourseResult"> SELECT * FROM COURSES WHERE TUTOR_ID= #{tutorId} <if test="courseName != null"> AND NAME LIKE #{courseName} </if> <if test="startDate != null"> AND START_DATE >= #{startDate} </if> <if test="endDate != null"> AND END_DATE <![CDATA[ <= ]]> #{endDate} </if> </select> public interface DynamicSqlMapper{ List<Course> searchCourses(Map<String, Object> map); } public void searchCourses(){ Map<String,Object> map = new HashMap<String,Object>(); map.put("tutorId", 1); map.put("courseName", "%Java%"); map.put("startDate", new Date()); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); List<Course> courses = mapper.searchCourses(map); for (Course course : courses){ System.out.println(course); } }
此處將生成查詢語句SELECT * FROM COURSES WHERE TUTOR_ID= ? AND NAME like ? AND START_DATE >= ?。
測試程式碼:
1.實體類 student和phone
package com.briup.bean;
import java.util.Date;
import org.apache.ibatis.type.Alias;
//@Alias("stu")
@Alias(value="stu")
public class Student {
private Integer studid;
private String name;
private String email;
private Date dob;
private PhoneNumber phone;
public void setStud_id(Integer id){
System.out.println("%%%:"+id);
}
@Override
public String toString() {
return "Student [studid=" + studid + ", name=" + name + ", email=" + email + ", dob=" + dob + "]";
}
public Student() {
super();
}
public Student(Integer studid, String name, String email, Date dob) {
this.studid = studid;
this.name = name;
this.email = email;
this.dob = dob;
}
public Integer getStudid() {
System.out.println("&&&&");
return studid;
}
public void setStudid(Integer studid) {
this.studid = studid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getemail() {
return email;
}
public void setemail(String email) {
this.email = email;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
public PhoneNumber getPhone() {
return phone;
}
public void setPhone(PhoneNumber phone) {
this.phone = phone;
}
}
package com.briup.bean;
/*
*注意:此處的型別轉換器不在說明
* 1221-121-212
*/
public class PhoneNumber {
private String countrycode;
private String statecode;
private String number;
public PhoneNumber(String str){
String[] strs=str.split("[-]");
this.countrycode=strs[0];
this.statecode=strs[1];
this.number=strs[2];
}
public String getAsString(){
return countrycode+"-"+statecode+"-"+number;
}
@Override
public String toString() {
return "PhoneNumber [countrycode=" + countrycode + ", statecode=" + statecode + ", number=" + number + "]";
}
public PhoneNumber() {
super();
}
public PhoneNumber(String countrycode, String statecode, String number) {
super();
this.countrycode = countrycode;
this.statecode = statecode;
this.number = number;
}
public String getCountrycode() {
return countrycode;
}
public void setCountrycode(String countrycode) {
this.countrycode = countrycode;
}
public String getStatecode() {
return statecode;
}
public void setStatecode(String statecode) {
this.statecode = statecode;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
2.介面mapper
package com.briup.Dynalmic;
import java.util.List;
import java.util.Map;
import com.briup.bean.Student;
public interface DynamicMapper {
List<Student> findStudent_if(long studid,String name,String email);
List<Student> findStudent_ifs(Map<String, Object> map);
}
3.對映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">
<mapper namespace="com.briup.Dynalmic.DynamicMapper">
<!-- studid stud_id
if標籤相當於是if語句
if(){}
test指向boolean型別
-->
<select id="findStudent_if"
resultType="stu">
select stud_id studid,name,email,dob,phone
from students
where 1=1
<if test="param1!=0">
and stud_id=#{param1}
</if>
<if test="param2!=null">
and name=#{param2}
</if>
<if test="param3!=null">
and email=#{param3}
</if>
</select>
<!-- parameterType="map"表示引數
是一個map集合,取map中的元素
#{key} -->
<select id="findStudent_ifs"
resultType="stu" parameterType="map">
select stud_id studid,name,email,dob,phone
from students
where 1=1
<if test="id!=null">
and stud_id=#{id}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="email!=null">
and email=#{email}
</if>
</select>
</mapper>
4.測試類
package com.briup.Dynalmic;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.briup.bean.Student;
import com.briup.util.MyBatisSqlSessionFactory;
public class DynamicTest {
@Test
public void ifs_test(){
try {
SqlSession session=
MyBatisSqlSessionFactory
.openSession(true);
DynamicMapper dm=
session.getMapper(DynamicMapper.class);
Map<String, Object> map=
new HashMap<>();
// map.put("id", 4);
// map.put("name", "briup");
// map.put("email", "[email protected]");
List<Student> list=dm.findStudent_ifs(map);
System.out.println(list);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void if_test(){
try {
SqlSession session=
MyBatisSqlSessionFactory
.openSession(true);
DynamicMapper dm=
session.getMapper(DynamicMapper.class);
List<Student> stus=
dm.findStudent_if(0L, null, null);
System.out.println(stus);
for(Student s:stus){
System.out.println(s.getPhone());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
※ 2 choose,when 和 otherwise 條件 有時候,查詢功能是以查詢類別為基礎的。首先,使用者需要先選擇是否希望通過選擇講師,課程名稱,開始時間,或結束時間作為查詢條件類別來進行查詢,然後根據選擇的查詢類別,輸入相應的引數。在這樣的情景中,我們【需要只使用其中一種】查詢類別。 MyBatis提供了<choose>元素支援此型別的SQL預處理。 如果沒有選擇查詢類別,則查詢開始時間在今天之後的課程,程式碼如下: 注意:mysql中now()是當前時間 oracle需要使用sysdate
<select id="searchCourses" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<choose>
<when test="searchBy == 'Tutor'">
WHERE TUTOR_ID= #{tutorId}
</when>
<when test="searchBy == 'CourseName'">
WHERE name like #{courseName}
</when>
<otherwise>
WHERE start_date >= sysdate
</otherwise>
</choose>
</select>
MyBatis計算<choose>測試條件的值,且使用第一個值為TRUE的子句。如果沒有條件為 true,則使用<otherwise>內的子句。 相當於我們常用的java程式碼中的這個例子:
if(){
..
}
else if(){
..
}
else{
..
}
※ 3 Where 條件 有時候,所有的查詢條件應該是可選的。在需要使用至少一種查詢條件的情況下,我們應該使用WHERE子句。並且如果有多個條件,我們需要在條件中新增AND或OR。 MyBatis提供了<where>元素支援這種型別的動態SQL語句。 在我們查詢課程介面,我們假設所有的查詢條件是可選的。進而,當需要提供一個或多個查詢條件時,應該改使用WHERE子句。
<select id="searchCourses" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<where>
<if test="tutorId != null ">
TUTOR_ID= #{tutorId}
</if>
<if test="courseName != null">
AND name like #{courseName}
</if>
<if test="startDate != null">
AND start_date >= #{startDate}
</if>
<if test="endDate != null">
AND end_date <![CDATA[ <= ]]> #{endDate}
</if>
</where>
</select>
<where>元素只有在其內部標籤有返回內容時才會在動態語句上插入WHERE條件語句。並且,如果WHERE子句以AND或者OR打頭,則打頭的AND或OR將會被移除。例如:如果引數tutorId的值為null,並且courseName引數值不為null,則<where>標籤會將AND name like #{courseName}中的AND移除掉,生成的SQL WHERE子句為: where name like #{courseName}
※ 4 <trim>條件 <trim>元素和<where>元素類似,但是<trim>提供了在新增字首/字尾或者移除字首/字尾方面提供更大的靈活性。
<select id="searchCourses" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<trim prefix="WHERE" suffixOverrides="and">
<if test=" tutorId != null ">
TUTOR_ID= #{tutorId} and
</if>
<if test="courseName != null">
name like #{courseName} and
</if>
</trim>
</select>
prefix表示有一個if成立則插入where語句 suffix表示字尾,和prefix相反
suffixOverrides="and"表示如果最後生成的sql語句多一個and,則自動去掉. prefixOverrides的意思是處理字首,和suffixOverrides相反
這裡如果任意一個<if>條件為true,<trim>元素會插入WHERE,並且移除緊跟WHERE後面的AND ※ 5 foreach 迴圈 另外一個強大的動態SQL語句構造標籤即是<foreach>。它可以迭代遍歷一個數組或者列表,構造AND/OR條件或一個IN子句。 假設我們想找到tutor_id為 1,3,6的講師所教授的課程,我們可以傳遞一個tutor_id組成的列表給對映語句,然後通過<foreach>遍歷此列表構造動態SQL。
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<if test="tutorIds != null">
<where>
<foreach item="tutorId" collection="tutorIds">
OR tutor_id=#{tutorId}
</foreach>
</where>
</if>
</select>
程式碼:
public interface DynamicSqlMapper{
List<Course> searchCoursesByTutors(Map<String,Object> map);
}
public void searchCoursesByTutors(){
Map<String,Object> map = new HashMap<String,Object>();
List<Integer> tutorIds = new ArrayList<Integer>();
tutorIds.add(1);
tutorIds.add(3);
tutorIds.add(6);
map.put("tutorIds", tutorIds);
DynamicSqlMapper mapper =
sqlSession.getMapper(DynamicSqlMapper.class);
List<Course> courses = mapper.searchCoursesByTutors(map);
for (Course course : courses){
System.out.println(course);
}
}
怎樣使用<foreach>生成IN子句:
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<if test="tutorIds != null">
<where>
tutor_id IN
<foreach item="tempValue" collection="tutorIds" open="(" separator="," close=")">
#{tempValue}
</foreach>
</where>
</if>
</select>
※ 6 set 條件 <set>元素和<where>元素類似,如果其內部條件判斷有任何內容返回時,他會插入SET SQL 片段。
<update id="updateStudent" parameterType="Student">
update students
<set>
<if test="name != null">name=#{name},</if>
<if test="email != null">email=#{email},</if>
<if test="phone != null">phone=#{phone},</if>
</set>
where stud_id=#{id}
</update>
這裡,如果<if>條件返回了任何文字內容,<set>將會插入set關鍵字和其文字內容,並且會剔除將末尾的“,”。在上述的例子中,如果phone!=null,<set>將會讓會移除phone=#{phone}後的逗號“,”,生成set phone=#{phone}