1. 程式人生 > >mybatis 6:動態SQL

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}