• Mybatis——动态sql和分页


    目录

    一、mybatis动态sql

    二、模糊查询

    三、查询返回结果集的处理

    四、分页查询

    五、特殊字符处理


    一、mybatis动态sql

            关于Mybatis的动态sql,我们用两个例子来说明一下:

          ①  if

    我们知道我们一般在写sql语句时比如:

    修改的SQL语句:

    update t_oa_meeting_info set id=?,title=?,content=?,zhuchiren=?,author=?,..... where id=?

    我们会写成这样 ,但是会有弊端就是在传值时必须将你所需的参数全部传入到后端,如果没有传递的话,那么sql语句将执行并且修改数据库中的信息为NULL,则出现了数据错误的情况!

    但是我们使用Mybatis动态sql的话,就立马解决了这个问题:

    1. <update id="updateByPrimaryKeySelective" parameterType="com.zq.model.Book" >
    2. update t_mvc_book
    3. <set >
    4. <if test="bname != null" >
    5. bname = #{bname,jdbcType=VARCHAR},
    6. if>
    7. <if test="price != null" >
    8. price = #{price,jdbcType=REAL},
    9. if>
    10. set>
    11. where bid = #{bid,jdbcType=INTEGER}
    12. update>

    在这里,首先我们的sql语句便成为了XML配置文件的形式 ,其次if标签使其加入了判断,如果没有传递该参数则不会改变数据库中此参数的值。

           ② foreach

    我们举个例子:

    1. @Test
    2. public void test3() {
    3. int[] ints={1,2,3,4,5,6};
    4. // 将数据编程字符串 1,2,3,4,5,6
    5. StringBuffer sb=new StringBuffer();
    6. for (int i:ints){
    7. sb.append(",").append(i);
    8. }
    9. String s=sb.toString();
    10. System.out.println(s);
    11. }

     问题1:我们用一个数组里面存放了书籍的id,但是他们有逗号“,”分隔我们所以我们不用subString的话就会多出一个逗号:

     问题2:如果传过来的数组没有值,则会出现数组下标越界异常:

     那怎么样才能够固定一种方式,能够解决这两种问题呢?

    Mybatis动态sql  foreach就来了,一样的传入这个数组1,2,3,4,5,6放入bookIds

    1. <select id="selectByIn" resultMap="BaseResultMap" parameterType="java.util.List" >
    2. select
    3. <include refid="Base_Column_List" />
    4. from t_mvc_book
    5. where bid in
    6. <foreach collection="bookIds" open="(" close=")" separator="," item="bid">
    7. #{bid}
    8. foreach>
    9. select>

    我们这里是在新建一个查询的方法,所以我们还需要在biz层也要添加这个方法selectByIn 

    BookMapper.xml:

    1. <select id="selectByIn" resultMap="BaseResultMap" parameterType="java.util.List" >
    2. select
    3. <include refid="Base_Column_List" />
    4. from t_mvc_book
    5. where bid in
    6. <foreach collection="bookIds" open="(" close=")" separator="," item="bid">
    7. #{bid}
    8. foreach>
    9. select>

      BookMapper :

    如果说参数是非实体类(book,Order,....),那么记得加上注解 @param,bookIds是对应collection属性的

    1. package com.zq.mapper;
    2. import com.zq.model.Book;
    3. import org.apache.ibatis.annotations.Param;
    4. import java.util.List;
    5. public interface BookMapper {
    6. int deleteByPrimaryKey(Integer bid);
    7. int insert(Book record);
    8. int insertSelective(Book record);
    9. Book selectByPrimaryKey(Integer bid);
    10. int updateByPrimaryKeySelective(Book record);
    11. int updateByPrimaryKey(Book record);
    12. // 通过in关键字进行查询,讲解foreach标签的使用
    13. // 如果说参数是非实体类(book,Order,....),那么记得加上注解 @param,bookIds是对应collection属性的
    14. List selectByIn(@Param("bookIds") List bookIds);
    15. }

     BookBizImpl : 

    1. package com.zq.biz.impl;
    2. import com.zq.biz.BookBiz;
    3. import com.zq.mapper.BookMapper;
    4. import com.zq.model.Book;
    5. import java.util.List;
    6. /**
    7. * @author张强
    8. * @site www.zq.com
    9. * @create  2022-08-11 1:38
    10. */
    11. public class BookBizImpl implements BookBiz {
    12. private BookMapper bookMapper;
    13. public BookMapper getBookMapper() {
    14. return bookMapper;
    15. }
    16. public void setBookMapper(BookMapper bookMapper) {
    17. this.bookMapper = bookMapper;
    18. }
    19. @Override
    20. public int deleteByPrimaryKey(Integer bid) {
    21. return bookMapper.deleteByPrimaryKey(bid);
    22. }
    23. @Override
    24. public Book selectByPrimaryKey(Integer bid) {
    25. return bookMapper.selectByPrimaryKey(bid);
    26. }
    27. @Override
    28. public List selectByIn(List bookIds) {
    29. return bookMapper.selectByIn(bookIds);
    30. }
    31. }

    BookBiz : 

    1. package com.zq.biz;
    2. import com.zq.model.Book;
    3. import java.util.List;
    4. /**
    5. * @author张强
    6. * @site www.zq.com
    7. * @create  2022-08-11 1:35
    8. */
    9. public interface BookBiz {
    10. int deleteByPrimaryKey(Integer bid);
    11. Book selectByPrimaryKey(Integer bid);
    12. public List selectByIn(List bookIds);
    13. }

    BookBizImplTest : 

    1. package com.zq.biz.impl;
    2. import com.zq.mapper.BookMapper;
    3. import com.zq.util.SessionUtil;
    4. import org.apache.ibatis.session.SqlSession;
    5. import org.junit.After;
    6. import org.junit.Before;
    7. import org.junit.Test;
    8. import java.util.Arrays;
    9. import java.util.List;
    10. /**
    11. * @author张强
    12. * @site www.zq.com
    13. * @create  2022-08-11 1:41
    14. */
    15. public class BookBizImplTest {
    16. private BookBizImpl bookBiz = new BookBizImpl();
    17. SqlSession sqlSession;
    18. @Before
    19. public void setUp() throws Exception {
    20. System.out.println("初始换方法。。。");
    21. //工具类中获取session对象
    22. sqlSession = SessionUtil.openSession();
    23. //从session对象中获取mapper对象
    24. BookMapper mapper = sqlSession.getMapper(BookMapper.class);
    25. bookBiz.setBookMapper(mapper);
    26. }
    27. @After
    28. public void tearDown() throws Exception {
    29. System.out.println("方法测试结束。。");
    30. }
    31. @Test
    32. public void deleteByPrimaryKey() {
    33. }
    34. @Test
    35. public void selectByPrimaryKey() {
    36. System.out.println("测试的业务方法。。。");
    37. //System.out.println(bookBiz.getBookMapper());
    38. System.out.println(bookBiz.selectByPrimaryKey(44));
    39. }
    40. @Test
    41. public void test3() {
    42. int[] ints={};
    43. // 将数据编程字符串 1,2,3,4,5,6
    44. StringBuffer sb=new StringBuffer();
    45. for (int i:ints){
    46. sb.append(",").append(i);
    47. }
    48. String s=sb.toString();
    49. System.out.println(s.substring(1));
    50. }
    51. @Test
    52. public void selectByIn(){
    53. List bookIds = Arrays.asList(new Integer[]{31, 32, 33, 34});
    54. bookBiz.selectByIn(bookIds).forEach(System.out::println);
    55. }
    56. }

     运行测试方法结果展示:


    二、模糊查询

     我们在写模糊查询的时候一般是这样写的:

    select * from t_mvc_book where bname like '%?%'

    然后我们便使用占位符在后台进行传值进行查询

    那么使用Mybatis只后会有所不同,他有三种方式改变这个模糊查询的方式:

    1. <select id="selectBooksLike1" resultType="com.zq.model.Book" parameterType="java.lang.String">
    2. select * from t_mvc_book where bname like #{bname}
    3. select>
    4. <select id="selectBooksLike2" resultType="com.zq.model.Book" parameterType="java.lang.String">
    5. select * from t_mvc_book where bname like '${bname}'
    6. select>
    7. <select id="selectBooksLike3" resultType="com.zq.model.Book" parameterType="java.lang.String">
    8. select * from t_mvc_book where bname like concat('%',#{bname},'%')
    9. select>

     写完这些xml文件的配置后我们将其他的biz层也加入进来

    BookMapper.xml:

    1. <select id="selectBooksLike1" resultType="com.zq.model.Book" parameterType="java.lang.String">
    2. select * from t_mvc_book where bname like #{bname}
    3. select>
    4. <select id="selectBooksLike2" resultType="com.zq.model.Book" parameterType="java.lang.String">
    5. select * from t_mvc_book where bname like '${bname}'
    6. select>
    7. <select id="selectBooksLike3" resultType="com.zq.model.Book" parameterType="java.lang.String">
    8. select * from t_mvc_book where bname like concat('%',#{bname},'%')
    9. select>

       BookMapper :

    1. List selectBooksLike1(@Param("bname") String bname);
    2. List selectBooksLike2(@Param("bname") String bname);
    3. List selectBooksLike3(@Param("bname") String bname);

      BookBizImpl : 

    1. @Override
    2. public List selectBooksLike1(String bname) {
    3. return bookMapper.selectBooksLike1(bname);
    4. }
    5. @Override
    6. public List selectBooksLike2(String bname) {
    7. return bookMapper.selectBooksLike2(bname);
    8. }
    9. @Override
    10. public List selectBooksLike3(String bname) {
    11. return bookMapper.selectBooksLike3(bname);
    12. }

    BookBiz : 

    1. List selectBooksLike1(String bname);
    2. List selectBooksLike2(String bname);
    3. List selectBooksLike3(String bname);

    BookBizImplTest : 

    1. @Test
    2. public void selectBooksLike1(){
    3. bookBiz.selectBooksLike1("%圣墟%").forEach(System.out::println);
    4. }
    5. @Test
    6. public void selectBooksLike2(){
    7. bookBiz.selectBooksLike2("%圣墟%").forEach(System.out::println);
    8. }
    9. @Test
    10. public void selectBooksLike3(){
    11. bookBiz.selectBooksLike2("圣墟").forEach(System.out::println);
    12. }

    那么我们将三种方法都加入到测试类中后进行测试来看看不同的结果吧: 

    ①#{模糊查询字段名} 

     ②‘${模糊查询字段名} ’

     但是如果我们去掉了xml文件中第二种方式的‘’:

    ③concat('%',#{模糊查询字段名} ,'%')

             总结:

    同样是传入参数,三种方式在sql语句方面有不同的效果:
    重点是第一种和第二种的对比区别:

    1. # 将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
       如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by '111', 
           如果传入的值是id,则解析成的sql为order by "id".

    2. $ 将传入的数据直接显示生成在sql中。
       如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id,
           如果传入的值是id,则解析成的sql为order by id.
     
    3. # 方式能够很大程度防止sql注入。
     
    4. $ 方式无法防止Sql注入。
     
    5. $ 方式一般用于传入数据库对象,例如传入表名。
     
    6. 一般能用 # 的就别用 $ 。

    7.最常用的是第三种。


    三、查询返回结果集的处理

            在Mybatis中结果集的处理分为两种:

    resultMap:适合使用返回值是自定义实体类的情况

    resultType:适合使用返回值的数据类型是非自定义的,即jdk的提供的类型

     如果是单表的情况下,resultType与resultMap都可以使用。
    1 使用resultMap返回自定义类型集合

        

    2 使用resultType返回List

    3 使用resultType返回单个对象

    4 使用resultType返回List,适用于多表查询返回结果集

    5 使用resultType返回Map,适用于多表查询返回单个结果集


    四、分页查询

    为什么要重写mybatis的分页?

       Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量offset和边界limit取结果),在大数据量的情况下这样的分页基本上是没有用的,所以我们在用Mybatis分页时需要依靠外部插件

    使用分页插件步奏:

    1、导入pom依赖

    1. <dependency>
    2.     <groupId>com.github.pagehelpergroupId>
    3.     <artifactId>pagehelperartifactId>
    4.     <version>5.1.2version>
    5. dependency>

    2、Mybatis.cfg.xml配置拦截器

    1. <plugins>
    2. <plugin interceptor="com.github.pagehelper.PageInterceptor">
    3. plugin>
    4. plugins>

    3、使用PageHelper进行分页

    1. package com.zq.util;
    2. import javax.servlet.http.HttpServletRequest;
    3. import java.io.Serializable;
    4. import java.util.Map;
    5. /**
    6. * @author张强
    7. * @site www.zq.com
    8. * @create  2022-08-12 11:44
    9. */
    10. public class PageBean implements Serializable {
    11. private static final long serialVersionUID = 2422581023658455731L;
    12. //页码
    13. private int page=1;
    14. //每页显示记录数
    15. private int rows=10;
    16. //总记录数
    17. private int total=0;
    18. //是否分页
    19. private boolean isPagination=true;
    20. //上一次的请求路径
    21. private String url;
    22. //获取所有的请求参数
    23. private Map map;
    24. public PageBean() {
    25. super();
    26. }
    27. //设置请求参数
    28. public void setRequest(HttpServletRequest req) {
    29. String page=req.getParameter("page");
    30. String rows=req.getParameter("rows");
    31. String pagination=req.getParameter("pagination");
    32. this.setPage(page);
    33. this.setRows(rows);
    34. this.setPagination(pagination);
    35. this.url=req.getContextPath()+req.getServletPath();
    36. this.map=req.getParameterMap();
    37. }
    38. public String getUrl() {
    39. return url;
    40. }
    41. public void setUrl(String url) {
    42. this.url = url;
    43. }
    44. public Map getMap() {
    45. return map;
    46. }
    47. public void setMap(Map map) {
    48. this.map = map;
    49. }
    50. public int getPage() {
    51. return page;
    52. }
    53. public void setPage(int page) {
    54. this.page = page;
    55. }
    56. public void setPage(String page) {
    57. if(null!=page&&!"".equals(page.trim()))
    58. this.page = Integer.parseInt(page);
    59. }
    60. public int getRows() {
    61. return rows;
    62. }
    63. public void setRows(int rows) {
    64. this.rows = rows;
    65. }
    66. public void setRows(String rows) {
    67. if(null!=rows&&!"".equals(rows.trim()))
    68. this.rows = Integer.parseInt(rows);
    69. }
    70. public int getTotal() {
    71. return total;
    72. }
    73. public void setTotal(int total) {
    74. this.total = total;
    75. }
    76. public void setTotal(String total) {
    77. this.total = Integer.parseInt(total);
    78. }
    79. public boolean isPagination() {
    80. return isPagination;
    81. }
    82. public void setPagination(boolean isPagination) {
    83. this.isPagination = isPagination;
    84. }
    85. public void setPagination(String isPagination) {
    86. if(null!=isPagination&&!"".equals(isPagination.trim()))
    87. this.isPagination = Boolean.parseBoolean(isPagination);
    88. }
    89. /**
    90. * 获取分页起始标记位置
    91. * @return
    92. */
    93. public int getStartIndex() {
    94. //(当前页码-1)*显示记录数
    95. return (this.getPage()-1)*this.rows;
    96. }
    97. /**
    98. * 末页
    99. * @return
    100. */
    101. public int getMaxPage() {
    102. int totalpage=this.total/this.rows;
    103. if(this.total%this.rows!=0)
    104. totalpage++;
    105. return totalpage;
    106. }
    107. /**
    108. * 下一页
    109. * @return
    110. */
    111. public int getNextPage() {
    112. int nextPage=this.page+1;
    113. if(this.page>=this.getMaxPage())
    114. nextPage=this.getMaxPage();
    115. return nextPage;
    116. }
    117. /**
    118. * 上一页
    119. * @return
    120. */
    121. public int getPreivousPage() {
    122. int previousPage=this.page-1;
    123. if(previousPage<1)
    124. previousPage=1;
    125. return previousPage;
    126. }
    127. @Override
    128. public String toString() {
    129. return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", isPagination=" + isPagination
    130. + "]";
    131. }
    132. }

    4、处理分页结果

    ①BookBiz

    List listPager(Map map, PageBean pageBean);
    

    ②BookMapper.java

    1. // 利用第三方插件进行分页
    2. List listPager(Map map);

    ③BookMapper.xml

    1. <select id="listPager" resultType="java.util.Map" parameterType="java.util.Map">
    2. select * from t_mvc_book where bname like concat(concat('%',#{bname}),'%')
    3. select>

    ④BookBizImpl

    1. @Override
    2. public List listPager(Map map, PageBean pageBean) {
    3. // pageHelper分页插件相关的代码
    4. if(pageBean!=null&&pageBean.isPagination()){
    5. PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
    6. }
    7. List maps = bookMapper.listPager(map);
    8. if(pageBean!=null&&pageBean.isPagination()){
    9. // 处理查询结果的前提是需要分页,是需要分页的
    10. PageInfo info = new PageInfo(maps);
    11. pageBean.setTotal(info.getTotal()+"");
    12. }
    13. return maps;
    14. }

    ⑤BookBizImplTest

    1. @Test
    2. public void listPager() {
    3. Map map=new HashMap();
    4. map.put("bname","圣墟");
    5. // bookBiz.listPager(map).forEach(System.out::println);
    6. // 查询出第二页的20条数据
    7. PageBean pageBean = new PageBean();
    8. pageBean.setPage(2);
    9. pageBean.setRows(20);
    10. bookBiz.listPager(map,pageBean).forEach(System.out::println);
    11. }

    五、特殊字符处理

            我们在使用myBatis的时候,在做sql的配置文件的时候会发现一个问题,就是会出现特殊字符和标签括号冲突问题

    比如:<  和<

    什么意思?就是第一个是判断符号小于,第二个是标签的左括号,那么怎么区别呢?有两种方式:

    ①:  

    ②:    <(<)  、    >(>)       &(&) 

    举例说明:

      ①BookBiz

    1. List list6(BookVo bookVo);
    2. List list7(BookVo bookVo);

    ②BookMapper.java

    1. /**
    2. * 处理特殊字符
    3. * @param bookVo
    4. * @return
    5. */
    6. List list6(BookVo bookVo);
    7. /**
    8. * 处理特殊字符
    9. * @param bookVo
    10. * @return
    11. */
    12. List list7(BookVo bookVo);

    ③BookMapper.xml

    1. <select id="list6" resultType="com.zq.model.Book" parameterType="com.zq.model.BookVo">
    2. select * from t_mvc_book
    3. <where>
    4. <if test="null != min and min != ''">
    5. if>
    6. <if test="null != max and max != ''">