MyBatis系列教程四一文读懂MyBatis动态SQL使用

如果使用传统的JDBC编程,很多时候需要根据具体情况拼接SQL语句,这是一件很痛苦的事情。Mybatis为开发者提供了动态SQL语句的组装能力,并且如此强大的功能只有几个元素就可以完成,十分简单明了。大量的判断都可以在Mybatis的映射XML中完成。大大减少了编码的工作量,这也体现了Mybatis的灵活性和可维护性。下面首先了解一下Mybatis中常用的动态SQL元素。

元素

作用

备注

if

判断语句

单条件分支判断

choose(when、otherwise)

相当于Java中的case when

多条件分支判断

trim(where、set)

辅助元素

用于处理SQL拼装

foreach

循环语句

在in语句等列举条件中常用

下面,就将上述元素进行深入讨论。

4.1 if元素

if元素是日常开发中较为常用的判断语句,相当于Java中的if语句,它常常与test属性联合使用。 if元素使用方法比较简单,以第3章中的数据库为例,根据学生姓名进行模糊查询。 映射器中代码如下:

 

接口中定义方法:

 public interface StudentMapper {
     List findStudentByStudentName(String studentName);
 }

在示例代码中使用了if元素,if元素的test属性中判断studentName是否有值,如果有值则在已有SQL语句后拼接if元素中的语句,如果没有值则不拼接,下面首先传递studentName值做测试,代码如下:

 public class Test {
     public static void main(String[] args) throws IOException {
         final Logger logger = Logger.getLogger(Test.class);
         InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
 
         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
         SqlSession session = sqlSessionFactory.openSession();
         StudentMapper mapper = session.getMapper(StudentMapper.class);
         //1.
         List list = mapper.findStudentByStudentName("j");
         list.forEach(e->logger.info(e));
     }
 }

运行结果如下:

可以看出,当studentName有值时拼接了SQL语句。 在上例代码中注释1处的方法调用中传入null,再次测试,运行结果如下:

从结果中可以看出SQL语句并没有拼接if元素中的语句。

4.2 choose、when、otherwise元素

在上一小节中的if元素相当于Java当中的if分支结构,是一种非此即彼的关系,但是在很多时候开发者所面对的不一定是非此即彼的情形,可能有更多的选择或者分支,此时虽说if可以满足需要,但是代码显得很蹩脚。因此Mybatis为开发者提供了类似于多分支的结构,并且这种结构在做根据条件搜索时显得尤为方便,避免了繁复的代码和判断。例如:根据学生姓名或者年龄或者性别检索数据。 在映射器中配置select元素:

 

Mybatis会根据参数的设置进行判断来动态组装SQL,以满足不同的业务需求。 接口中定义方法:

 public interface StudentMapper {
     List searchStudent(Student student);
 }

新建测试类代码如下:

 public class Test {
     public static void main(String[] args) throws IOException {
         final Logger logger = Logger.getLogger(Test.class);
         InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
 
         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
         SqlSession session = sqlSessionFactory.openSession();
         StudentMapper mapper = session.getMapper(StudentMapper.class);
 
         Student student = new Student();
         student.setStudentName("Jack");
 
         List list = mapper.searchStudent(student);
         list.forEach(e->logger.info(e));
     }
 }

运行结果如下图:

4.3 where、trim、set元素

4.3.1 where元素

在上面的代码中,每条SQL语句中都添加了一个1=1这个条件,这是因为如果不添加这个条件上一小节中的SQL语句可能会下面这样错误的SQL语句:

 SELECT STUDENT_ID,STUDENT_NAME,STUDENT_AGE,STUDENT_GENDER FROM STUDENT WHERE  AND STUDENT_NAME = ?

但是加上这个莫名其妙的条件又显得很奇怪,在这里就可以使用where元素以达到逾期的效果。对上一小节中的SQL语句进行修改,去掉1=1,并且将choose...when修改为where和if,代码如下:

 

当where元素中的条件只要有一个成立时,会在已有SQL语句后拼接where以及成立的条件,如果没有条件成立,则不会拼接SQL语句,也不会拼接where,下面的示例将演示这种情况:

public class Test {
    public static void main(String[] args) throws IOException {
        final Logger logger = Logger.getLogger(Test.class);
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);

        List list = mapper.searchStudent(null);
        list.forEach(e->logger.info(e));
    }
}

运行结果如下:

在测试类中方法内传入了null,所有的参数都为null,不满足where元素中if元素的条件,因此Mybatis并没有拼接where及条件。

4.3.2 trim元素

有时候需要去掉一些特殊的SQL语法,比如常见的and、or。使用trim元素就可以达到这种效果。

注意上面的SQL语句,如果studentName不等于null时,此时的SQL语句会变成:

SELECT STUDENT_ID,STUDENT_NAME,STUDENT_AGE,STUDENT_GENDER
FROM STUDENT
AND STUDENT_NAME = #{studentName}

这条SQL语句会产生语法错误,trim元素此时就要发挥作用了,会将and替换为where,也就是说trim元素的作用就是将prefixOverrides中配置的值替换为prefix中的值。

4.3.3 set元素

在日常开发中更新操作出现的也比较频繁,但是在更新操作中通常会只更新几个字段,而不是全部更新,如果将所有的字段都发送给数据库,这样就会带来不必要的内存和宽带开销,set元素就可以解决这种问题,例如更新学生信息。


    UPDATE STUDENT
    
        
            STUDENT_NAME = #{studentName},
        
        
            STUDENT_AGE = #{studentAge},
        
        
            STUDENT_GENDER = #{studentGender}
        
    
    WHERE STUDENT_ID = #{studentId}

接口中定义方法:

public interface StudentMapper {
    int updateStudent(Student student);
}

新建测试类:

public class Test {
    public static void main(String[] args) throws IOException {
        final Logger logger = Logger.getLogger(Test.class);
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);

        Student student = new Student();
        student.setStudentId(7);
        student.setStudentAge(20);

        mapper.updateStudent(student);
        session.commit();
    }
}

运行结果如下:

因为传入的参数中只有studentAge,因此Mybatis在拼接SQL语句时只拼接了满足studentAge的条件。

4.4 foreach元素

foreach元素是一个循环语句,作用是遍历集合,它能够很好的支持数组和List、Set接口的集合,对此提供遍历功能。例如删除多条数据,就可以使用foreach元素。


    DELETE FROM STUDENT WHERE STUDENT_ID IN
    
        #{id}
    

接口中定义方法:

public interface StudentMapper {
    int batchDeleteStudent(List list);
}

新建测试类:

public class Test {
    public static void main(String[] args) throws IOException {
        final Logger logger = Logger.getLogger(Test.class);
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);

        List list = new ArrayList<>();
        list.add(1);
        list.add(2);

        mapper.batchDeleteStudent(list);
        session.commit();
    }
}

运行结果如下:

4.5 Mybaits分页

4.5.1 RowBounds分页

Mybatis具有分页功能,其为开发者提供了一个类RowBounds,开发者可以使用RowBounds分页,但是RowBounds分页有一个很重要的缺陷,就是会将所有的数据查询出来后根据从第几条到第几条取出数据返回,当数据量比较小时,这么做没有任何问题,但是当数据量大时,这么做无疑对数据库造成了很大的负担。 RowBounds主要定义了两个参数,offset和limit,offset代表从第几行开始读取数据,limit则是限制返回的记录数。在默认情况下offset设置为0,而limit这是Java所允许的最大整数(2147483647)。 下面通过示例学习RowBounds的用法:

在接口中定义方法:

public interface StudentMapper {
    List findStudentByPage(RowBounds rowBounds);
}

新建测试类:

public class Test {
    public static void main(String[] args) throws IOException {
        final Logger logger = Logger.getLogger(Test.class);
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
		//1.
        List list = mapper.findStudentByPage(new RowBounds(0,5));
        list.forEach(e->logger.info(e));
    }
}

在代码中注释1处,调用方法是传入RowBounds对象。即可完成分页。 运行结果如下:

从结果中可以看出使用RowBounds分页时,会将所有的数据都查询出来,然后从数据中截取目标数据,也就是通常所说的逻辑分页。

4.5.2 插件分页

基于RowBounds分页存在一定缺陷,通常会推荐使用自定义插件分页,并且又第三方提供了性能不错的分页插件PageHelper,下面就改插件做简单示范: 首先,在pom.xml中添加依赖


    com.github.pagehelper
    pagehelper
    最新版本

在Mybatis全局文件中进行配置:


    
    

由于Mybatis的插件是基于代理拦截实现,所以继续使用上例中的接口及测试类,插件会自动帮助开发者在SQL中拼接limit关键字。运行结果如下:

关于该插件的详细用法,可前往PageHelper插件官网进行查阅。

展开阅读全文

页面更新:2024-05-18

标签:开发者   语句   插件   元素   接口   条件   代码   测试   方法   教程   系列   动态   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top