Mybatis 的动态查询

动态SQL  dynamic-sql
        有时候,静态的SQL语句并不能满足应用程序的需求。我们可以根据一些条件,来动态地构建 SQL语句。
        例如,在Web应用程序中,有可能有一些搜索界面,需要输入一个或多个选项,然后根据这些已选择的条件去执行检索操作。在实现这种类型的搜索功能,我们可能需要根据这些条件来构建动态的SQL语句。如果用户提供了任何输入条件,我们需要将那个条件添加到SQL语句的WHERE子句中。MyBatis通过使用<if>,<choose>,<where>,<foreach>,<trim>元素提供了对构造动态SQL语句的高级别支持。

        
        3.6.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 >= ?。
 
        
        3.6.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.6.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}
 

        3.6.4 <trim>条件
        <trim>元素和<where>元素类似,但是<trim>提供了在添加前缀/后缀或者移除前缀/后缀方面提供更大的灵活性。
        <select id="searchCourses" parameterType="map" resultMap="CourseResult"> 
            SELECT * FROM COURSES 
            <trim prefix="WHERE" suffixOverrides="or | 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 

        3.6.5 foreach 循环
        另外一个强大的动态SQL语句构造标签即是<foreach>。它可以迭代遍历一个数组或者列表,构造AND/OR条件或一个IN子句。
        假设我们想找到tutor_id为 1,3,6的讲师所教授的课程,我们可以传递一个tutor_id组成的列表给映射语句,然后通过<foreach>遍历此列表构造动态SQL。
        <select id="searchCourses" 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> 



        3.6.6 set 条件
        <set>元素和<where>元素类似,如果其内部条件判断有任何内容返回时,他会插入SET SQL 片段。
         
            <update id="updateCourse" parameterType="Course"> 
                update courses  
                <set> 
                <if test="name != null">name=#{name},</if> 
                <if test="description != null">description=#{description},</if> 
                <if test="startDate != null">start_date=#{startDate},</if> 
                <if test="endDate != null">end_date=#{endDate},</if> 
                </set> 
                where course_id=#{courseId} 
            </update> 

        这里,如果<if>条件返回了任何文本内容,<set>将会插入set关键字和其文本内容,并且会剔除将末尾的“,”。在上述的例子中,如果phone!=null,<set>将会让会移除phone=#{phone}后的逗号“,”,生成set phone=#{phone} 


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容