如果使用传统的JDBC编程,很多时候需要根据具体情况拼接SQL语句,这是一件很痛苦的事情。Mybatis为开发者提供了动态SQL语句的组装能力,并且如此强大的功能只有几个元素就可以完成,十分简单明了。大量的判断都可以在Mybatis的映射XML中完成。大大减少了编码的工作量,这也体现了Mybatis的灵活性和可维护性。下面首先了解一下Mybatis中常用的动态SQL元素。 元素 作用 备注 if 判断语句 单条件分支判断 choose(when、otherwise) 相当于Java中的casewhen 多条件分支判断 trim(where、set) 辅助元素 用于处理SQL拼装 foreach 循环语句 在in语句等列举条件中常用 下面,就将上述元素进行深入讨论。4。1if元素 if元素是日常开发中较为常用的判断语句,相当于Java中的if语句,它常常与test属性联合使用。if元素使用方法比较简单,以第3章中的数据库为例,根据学生姓名进行模糊查询。映射器中代码如下:selectidfindStudentByStudentNameresultTypecn。bytecollege。entity。StudentparameterTypeStringSELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTWHERE11ifteststudentName!nullandstudentName!ANDSTUDENTNAMELIKECONCAT(,{studentName},)ifselect 接口中定义方法:publicinterfaceStudentMapper{ListStudentfindStudentByStudentName(StringstudentName);} 在示例代码中使用了if元素,if元素的test属性中判断studentName是否有值,如果有值则在已有SQL语句后拼接if元素中的语句,如果没有值则不拼接,下面首先传递studentName值做测试,代码如下:publicclassTest{publicstaticvoidmain(String〔〕args)throwsIOException{finalLoggerloggerLogger。getLogger(Test。class);InputStreamisResources。getResourceAsStream(mybatisconfig。xml);SqlSessionFactorysqlSessionFactorynewSqlSessionFactoryBuilder()。build(is);SqlSessionsessionsqlSessionFactory。openSession();StudentMappermappersession。getMapper(StudentMapper。class);1。ListStudentlistmapper。findStudentByStudentName(j);list。forEach(elogger。info(e));}} 运行结果如下: 可以看出,当studentName有值时拼接了SQL语句。在上例代码中注释1处的方法调用中传入null,再次测试,运行结果如下: 从结果中可以看出SQL语句并没有拼接if元素中的语句。4。2choose、when、otherwise元素 在上一小节中的if元素相当于Java当中的if分支结构,是一种非此即彼的关系,但是在很多时候开发者所面对的不一定是非此即彼的情形,可能有更多的选择或者分支,此时虽说if可以满足需要,但是代码显得很蹩脚。因此Mybatis为开发者提供了类似于多分支的结构,并且这种结构在做根据条件搜索时显得尤为方便,避免了繁复的代码和判断。例如:根据学生姓名或者年龄或者性别检索数据。在映射器中配置select元素:selectidsearchStudentresultTypecn。bytecollege。entity。StudentparameterTypecn。bytecollege。entity。StudentSELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTWHERE11choosewhenteststudentName!nullandstudentName!ANDSTUDENTNAME{studentName}whenwhenteststudentAge!nullandstudentAge!ANDSTUDENTAGE{studentAge}whenwhenteststudentGender!nullandstudentGender!ANDSTUDENTGENDER{studentGender}whenotherwiseANDSTUDENTIDISNOTNULL;otherwisechooseselect Mybatis会根据参数的设置进行判断来动态组装SQL,以满足不同的业务需求。接口中定义方法:publicinterfaceStudentMapper{ListStudentsearchStudent(Studentstudent);} 新建测试类代码如下:publicclassTest{publicstaticvoidmain(String〔〕args)throwsIOException{finalLoggerloggerLogger。getLogger(Test。class);InputStreamisResources。getResourceAsStream(mybatisconfig。xml);SqlSessionFactorysqlSessionFactorynewSqlSessionFactoryBuilder()。build(is);SqlSessionsessionsqlSessionFactory。openSession();StudentMappermappersession。getMapper(StudentMapper。class);StudentstudentnewStudent();student。setStudentName(Jack);ListStudentlistmapper。searchStudent(student);list。forEach(elogger。info(e));}} 运行结果如下图: 4。3where、trim、set元素4。3。1where元素 在上面的代码中,每条SQL语句中都添加了一个11这个条件,这是因为如果不添加这个条件上一小节中的SQL语句可能会下面这样错误的SQL语句:SELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTWHEREANDSTUDENTNAME? 但是加上这个莫名其妙的条件又显得很奇怪,在这里就可以使用where元素以达到逾期的效果。对上一小节中的SQL语句进行修改,去掉11,并且将choose。。。when修改为where和if,代码如下:selectidsearchStudentresultTypecn。bytecollege。entity。StudentparameterTypecn。bytecollege。entity。StudentSELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTwhereifteststudentName!nullandstudentName!ANDSTUDENTNAME{studentName}ififteststudentAge!nullandstudentAge!ANDSTUDENTAGE{studentAge}ififteststudentGender!nullandstudentGender!ANDSTUDENTGENDER{studentGender}ifwhereselect 当where元素中的条件只要有一个成立时,会在已有SQL语句后拼接where以及成立的条件,如果没有条件成立,则不会拼接SQL语句,也不会拼接where,下面的示例将演示这种情况:publicclassTest{publicstaticvoidmain(String〔〕args)throwsIOException{finalLoggerloggerLogger。getLogger(Test。class);InputStreamisResources。getResourceAsStream(mybatisconfig。xml);SqlSessionFactorysqlSessionFactorynewSqlSessionFactoryBuilder()。build(is);SqlSessionsessionsqlSessionFactory。openSession();StudentMappermappersession。getMapper(StudentMapper。class);ListStudentlistmapper。searchStudent(null);list。forEach(elogger。info(e));}} 运行结果如下: 在测试类中方法内传入了null,所有的参数都为null,不满足where元素中if元素的条件,因此Mybatis并没有拼接where及条件。4。3。2trim元素 有时候需要去掉一些特殊的SQL语法,比如常见的and、or。使用trim元素就可以达到这种效果。selectidtrimTestparameterTypeStringSELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTtrimprefixwhereprefixOverridesandifteststudentName!nullANDSTUDENTNAME{studentName}iftrimselect 注意上面的SQL语句,如果studentName不等于null时,此时的SQL语句会变成:SELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTANDSTUDENTNAME{studentName} 这条SQL语句会产生语法错误,trim元素此时就要发挥作用了,会将and替换为where,也就是说trim元素的作用就是将prefixOverrides中配置的值替换为prefix中的值。4。3。3set元素 在日常开发中更新操作出现的也比较频繁,但是在更新操作中通常会只更新几个字段,而不是全部更新,如果将所有的字段都发送给数据库,这样就会带来不必要的内存和宽带开销,set元素就可以解决这种问题,例如更新学生信息。updateidupdateStudentparameterTypecn。bytecollege。entity。StudentUPDATESTUDENTsetifteststudentName!nullandstudentName!STUDENTNAME{studentName},ififteststudentAge!nullandstudentAge!STUDENTAGE{studentAge},ififteststudentGender!nullandstudentGender!STUDENTGENDER{studentGender}ifsetWHERESTUDENTID{studentId}update 接口中定义方法:publicinterfaceStudentMapper{intupdateStudent(Studentstudent);} 新建测试类:publicclassTest{publicstaticvoidmain(String〔〕args)throwsIOException{finalLoggerloggerLogger。getLogger(Test。class);InputStreamisResources。getResourceAsStream(mybatisconfig。xml);SqlSessionFactorysqlSessionFactorynewSqlSessionFactoryBuilder()。build(is);SqlSessionsessionsqlSessionFactory。openSession();StudentMappermappersession。getMapper(StudentMapper。class);StudentstudentnewStudent();student。setStudentId(7);student。setStudentAge(20);mapper。updateStudent(student);session。commit();}} 运行结果如下: 因为传入的参数中只有studentAge,因此Mybatis在拼接SQL语句时只拼接了满足studentAge的条件。4。4foreach元素 foreach元素是一个循环语句,作用是遍历集合,它能够很好的支持数组和List、Set接口的集合,对此提供遍历功能。例如删除多条数据,就可以使用foreach元素。deleteidbatchDeleteStudentparameterTypeListDELETEFROMSTUDENTWHERESTUDENTIDINforeachcollectionlistopen(close)separator,itemid{id}foreachdeletecollection:配置的是传递进来的参数名称,可以是一个数组或者List、set等集合item:配置的是循环中的元素,可以理解为数组或者集合中的元素的名称open和close:配置的是以什么符号将这些集合元素包装起来separator:是各个元素的分隔符 接口中定义方法:publicinterfaceStudentMapper{intbatchDeleteStudent(Listlist);} 新建测试类:publicclassTest{publicstaticvoidmain(String〔〕args)throwsIOException{finalLoggerloggerLogger。getLogger(Test。class);InputStreamisResources。getResourceAsStream(mybatisconfig。xml);SqlSessionFactorysqlSessionFactorynewSqlSessionFactoryBuilder()。build(is);SqlSessionsessionsqlSessionFactory。openSession();StudentMappermappersession。getMapper(StudentMapper。class);ListIntegerlistnewArrayList();list。add(1);list。add(2);mapper。batchDeleteStudent(list);session。commit();}} 运行结果如下: 4。5Mybaits分页4。5。1RowBounds分页 Mybatis具有分页功能,其为开发者提供了一个类RowBounds,开发者可以使用RowBounds分页,但是RowBounds分页有一个很重要的缺陷,就是会将所有的数据查询出来后根据从第几条到第几条取出数据返回,当数据量比较小时,这么做没有任何问题,但是当数据量大时,这么做无疑对数据库造成了很大的负担。RowBounds主要定义了两个参数,offset和limit,offset代表从第几行开始读取数据,limit则是限制返回的记录数。在默认情况下offset设置为0,而limit这是Java所允许的最大整数(2147483647)。下面通过示例学习RowBounds的用法:selectidfindStudentByPageresultTypecn。bytecollege。entity。StudentSELECTSTUDENTID,STUDENTNAME,STUDENTAGE,STUDENTGENDERFROMSTUDENTselect 在接口中定义方法:publicinterfaceStudentMapper{ListStudentfindStudentByPage(RowBoundsrowBounds);} 新建测试类:publicclassTest{publicstaticvoidmain(String〔〕args)throwsIOException{finalLoggerloggerLogger。getLogger(Test。class);InputStreamisResources。getResourceAsStream(mybatisconfig。xml);SqlSessionFactorysqlSessionFactorynewSqlSessionFactoryBuilder()。build(is);SqlSessionsessionsqlSessionFactory。openSession();StudentMappermappersession。getMapper(StudentMapper。class);1。ListStudentlistmapper。findStudentByPage(newRowBounds(0,5));list。forEach(elogger。info(e));}} 在代码中注释1处,调用方法是传入RowBounds对象。即可完成分页。运行结果如下: 从结果中可以看出使用RowBounds分页时,会将所有的数据都查询出来,然后从数据中截取目标数据,也就是通常所说的逻辑分页。4。5。2插件分页 基于RowBounds分页存在一定缺陷,通常会推荐使用自定义插件分页,并且又第三方提供了性能不错的分页插件PageHelper,下面就改插件做简单示范:首先,在pom。xml中添加依赖dependencygroupIdcom。github。pagehelpergroupIdpagehelperartifactIdversion最新版本versiondependency 在Mybatis全局文件中进行配置:pluginsplugininterceptorcom。github。pagehelper。PageInterceptorpluginplugins 由于Mybatis的插件是基于代理拦截实现,所以继续使用上例中的接口及测试类,插件会自动帮助开发者在SQL中拼接limit关键字。运行结果如下: 关于该插件的详细用法,可前往PageHelper插件官网进行查阅。