有時候,靜態的SQL語句並不能滿足應用程序的需求。我們可以根據一些條件,來動態地構建 SQL語句。
例如,在Web應用程序中,有可能有一些搜索界面,需要輸入一個或多個選項,然后根據這些已選擇的條件去執行檢索操作。我們可能需要根據用戶選擇的條件來構建動態的SQL語句。如果用戶提供了任何一個條件,我們需要將那個條件添加到SQL語句的WHERE子句中。
!以下內容基於自己建的表和類!
1.<if>標簽被用來通過條件嵌入SQL片段,如果條件為true,則相應地SQL片段將會被添加到SQL語句中。
例如:
假定有一個課程搜索界面,設置了講師(Tutor)下拉列表框,課程名稱(CourseName)文本輸入框,開始時間(StartDate)輸入框,結束時間(EndDate)輸入框,作為搜索條件。假定課講師下拉列表是必須選的,其他的都是可選的。當用戶點擊搜索按鈕時,需要顯示符合條件的列表數據。
對應的sql映射文件,如下所示:
<!-- 獨立的Course封裝映射 --> <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>
<!-- 查詢Course的select語句,里面加入了if條件判斷 --> <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); }
測試方法:
@Test public void test_searchCourses1(){ SqlSession sqlSession = null; try { sqlSession = MyBatisSqlSessionFactory.openSession(); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); Map<String,Object> map = new HashMap<String,Object>(); map.put("tutorId", 1); map.put("courseName", "%Java%"); LocalDate date = LocalDate.of(2019, 1, 10); map.put("startDate", date); List<Course> courses = mapper.searchCourses(map); courses.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } }
2.choose,when 和 otherwise 條件
有時候,查詢功能是以查詢類別為基礎的。首先,用戶需要先選擇是通過講師查詢,還是課程名稱查詢,還是開始時間查詢。然后根據選擇的查詢類別,輸入相應的參數,再進行查詢。
例如,頁面中有一個下拉列表,可以選擇查詢的類別,可以選擇根據講師查詢、根據課程名查詢、根據時間查詢等等,選擇了列表之后,再輸入關鍵字進行查詢。
MyBatis提供了<choose>標簽可以支持此類型的查詢處理。 假設如果用戶都沒有選擇,那么默認可以根據當前時間進行查詢。
注意:mysql中now()表示當前時間 oracle需要使用sysdate
對應的sql映射文件,如下所示:
<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>
測試方法:
@Test public void test_searchCourses2(){ SqlSession sqlSession = null; try { sqlSession = MyBatisSqlSessionFactory.openSession(); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); Map<String,Object> map = new HashMap<String,Object>(); // map.put("searchBy", "Tutor"); // map.put("tutorId", 1); map.put("searchBy", "CourseName"); map.put("courseName", "%MyBatis%"); List<Course> courses = mapper.searchCourses(map); courses.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } }
MyBatis計算<choose>中條件的值,並使用第一個值為TRUE的子句。如果沒有條件為 true,則使用<otherwise>內的子句。
3.Where 條件
有時候,所有的查詢條件應該是可選的。在需要使用至少一種查詢條件的情況下,可以直接使用WHERE子句。
如果有多個條件,我們需要在條件中添加AND或OR。MyBatis提供了<where>元素支持這種類型的動態SQL語句。
例如,在查詢課程界面,假設所有的查詢條件是可選的。
注意,<where>元素只有在其內部標簽有返回內容時才會在動態語句上插入WHERE條件語句。
並且,如果WHERE子句以AND或者OR打頭,則打頭的AND或OR將會被移除。
映射文件:
<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> </where> </select>
測試方法:
@Test public void test_searchCourses3(){ SqlSession sqlSession = null; try { sqlSession = MyBatisSqlSessionFactory.openSession(); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); Map<String,Object> map = new HashMap<String,Object>(); //map.put("tutorId", 1); //map.put("courseName", "JavaSE"); //map.put("startDate", LocalDate.of(2019, 1, 10)); List<Course> courses = mapper.searchCourses(map); courses.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } }
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語句,沒有if成立,就會去掉where直接查詢
suffix表示后綴,和prefix相反
suffixOverrides="and"表示如果最后生成的sql語句多一個and,則自動去掉.
prefixOverrides的意思是處理前綴,和suffixOverrides相反
測試方法:
@Test public void test_searchCourses4(){ SqlSession sqlSession = null; try { sqlSession = MyBatisSqlSessionFactory.openSession(); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); Map<String,Object> map = new HashMap<String,Object>(); // map.put("tutorId", 1); // map.put("courseName", "JavaSE"); List<Course> courses = mapper.searchCourses(map); courses.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } }
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> <!-- 在這里的 tutorId指的是集合中存入准備查詢的tutor_id--> <foreach item="tutorId" collection="tutorIds"> OR tutor_id = #{tutorId} </foreach> </where> </if> </select>
映射接口:
public interface DynamicSqlMapper{ List<Course> searchCoursesByTutors(Map<String,Object> map); }
測試方法:
@Test public void test_searchCoursesByTutors(){ SqlSession sqlSession = null; try { sqlSession = MyBatisSqlSessionFactory.openSession(); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); 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); List<Course> courses = mapper.searchCoursesByTutors(map); courses.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } }
和上面同樣的功能,使用<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 條件,專用於UPDATE更新操作
<set>元素和<where>元素類似,但是set元素只是針對update更新語句使用的。
<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=#{studId} </update>
這里,如果<if>條件返回了任何文本內容,<set>將會插入set關鍵字和其文本內容,並且會剔除將末尾的逗號","。
測試方法:
@Test public void test_updateStudent(){ SqlSession sqlSession = null; try { sqlSession = MyBatisSqlSessionFactory.openSession(); DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); Student student = new Student(); student.setStudId(45); student.setEmail("xx@briup.com"); mapper.updateStudent(student); sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } }