• Mybatis 快速入门之 动态sql和分页


    目录

    一:动态sql的基本概念

    二:基本的动态sql标签

    if标签

    trim标签

    choose标签

    set标签

    where标签

    二:参数传递

    1.单个参数:------------>八大基础类型与String类型

    注意:

    1.传递String的参数,必须使用@Param方式,并且指明参数名

    2.在service接口,方法应该要将@param()去除掉,只留下变量名,在mapper层可以留下@param(),以防万一,防止报错

    2.多个参数

    3.实体参数

    三:模糊查询

    1.${}

    2.#{}

    3.concat()---->mysql函数

    问题:#{...}与${...}区别? 

       注:          (1)mybatis中使用OGNL表达式传递参数          (2) 优先使用#{...}          (3) ${...}方式存在SQL注入风险

    问:SQL注入风险

    四:查询返回结果集

    案例①:使用resultMap返回自定义类型集合

    案例②: 使用resultType返回单个对象

    案例③:使用resultType返回Map,适用于多表查询返回结果集,object>

    案例④: 使用resultType返回List

    五:分页查询

      为什么要重写mybatis的分页?          Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量offset和边界limit取结果),在大数据量的情况下这样的分页基本上是没有用的

    1.导入分页插件

    2. 将pagehelper插件配置到mybatis中

    3.导入pageBean文件在util包下

    4.在BookMapper类定义方法

    5.在BookMapper.xml文件中进行sql语句的编写

    6.在iBookService类编写代码

    7.实现接口所定义的方法

    8.在你需要进行分页的Mybatis方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个Mybatis查询方法会被进行分页

    9. 获取分页信息(二种方式)

    六:特殊字符处理

    案例:价格范围查询

    七:批量新增

    重点:

            allowMultiQueries=true 允许一条SQL语句包含多个执行SQL以分号;分隔,是必须添加的


    一:动态sql的基本概念

                动态sql是指在进行sql操作的时候,传入的参数对象或者参数值,根据匹配的条件,有可能需要动态的去判断是否为空,循环,拼接等情况;

    二:基本的动态sql标签

    • if标签

    ①:在BookMapper类定义包含if标签方法

    1. //if标签
    2. List queryBookByIf(Book book);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookByIf" resultType="com.zking.model.Book">
    2. select
    3. <include refid="Base_Column_List">include>
    4. from t_book where 1=1
    5. <if test="null!=btype and ''!=btype">
    6. and btype= #{btype}
    7. if>
    8. select>

      当满足test条件时,才会将if标签内的sql语句拼接上去

    ③:在iBookService类编写代码

    1. //mybatis之if标签
    2. List queryBookByIf(Book book);

    ④:进行 junit4单元测试

    ------测试带type类型的查询:

    1. @Test
    2. //mybatis之if标签
    3. public void queryBookByIf(){
    4. List bookList = iBookService.queryBookByIf(Book.builder().btype("历史").build());
    5. bookList.forEach(System.out::println);
    6. }

    测试结果如下:   

    查看sql语句:

     ---测试不type类型的查询:

    1. @Test
    2. //mybatis之if标签
    3. public void queryBookByIf(){
    4. List bookList = iBookService.queryBookByIf(Book.builder().build());
    5. bookList.forEach(System.out::println);
    6. }

    测试结果如下:

     查看sql语句:

    • trim标签

        trim标签属性:

       prefix前缀
       suffix后缀
      suffixOverride去除后缀指定的字符
     prefixOverrides去除前缀指定的字符

    ①:在BookMapper类定义包含trim标签方法

     int insertSelective(Book record);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <insert id="insertSelective" parameterType="com.zking.model.Book" >
    2. insert into t_book
    3. <trim prefix="(" suffix=")" suffixOverrides="," >
    4. <if test="bid != null" >
    5. bid,
    6. if>
    7. <if test="bname != null" >
    8. bname,
    9. if>
    10. <if test="bprice != null" >
    11. bprice,
    12. if>
    13. <if test="btype != null" >
    14. btype,
    15. if>
    16. trim>
    17. <trim prefix="values (" suffix=")" suffixOverrides="," >
    18. <if test="bid != null" >
    19. #{bid,jdbcType=INTEGER},
    20. if>
    21. <if test="bname != null" >
    22. #{bname,jdbcType=VARCHAR},
    23. if>
    24. <if test="bprice != null" >
    25. #{bprice,jdbcType=INTEGER},
    26. if>
    27. <if test="btype != null" >
    28. #{btype,jdbcType=VARCHAR},
    29. if>
    30. trim>
    31. insert>

    • foreach标签

    foreach标签属性:

    collection被遍历的集合或数组
    item每次循环遍历的集合名
    separator每次循环的分隔符
    index下标
    open开始位置
    close关闭位置

    ①:编写一个类,储存需要遍历的集合或者数组

    1. package com.zking.vo;
    2. import com.zking.model.Book;
    3. import lombok.Data;
    4. import java.util.List;
    5. /**
    6. * @author 唐渊
    7. * @create  2022-07-23 9:26
    8. */
    9. @Data //getter/setter/toString
    10. public class BookVo extends Book {
    11. //foreach标签需要遍历的list集合
    12. private List ids;
    13. }

    ②:在BookMapper类定义包含foreach标签方法

    1. //foreach标签
    2. List queryBookByForeach(BookVo bookVo);

    ③:在BookMapper.xml文件中进行sql语句的编写

        (1)方式一:

    1. <select id="queryBookByForeach" resultType="com.zking.model.Book">
    2. select
    3. <include refid="Base_Column_List">include>
    4. from t_book where 1=1 and bid in
    5. <foreach collection="ids" item="id" separator="," open="(" close=")">
    6. #{id}
    7. foreach>
    8. select>

    (2)方式二:

    1. select
    2. <include refid="Base_Column_List">include>
    3. from t_book where 1=1
    4. <foreach collection="ids" item="id" separator="," open=" and bid in (" close=")">
    5. #{id}
    6. foreach>

    (3)方式三:

    1. select
    2. <include refid="Base_Column_List">include>
    3. from t_book where 1=1 and bid in
    4. (
    5. <foreach collection="ids" item="id" separator=",">
    6. #{id}
    7. foreach>

    ④:在iBookService类编写代码

    1. //foreach标签
    2. List queryBookByForeach(BookVo bookVo);

    ⑤:进行 junit4单元测试

    1. @Test
    2. //foreach标签
    3. public void queryBookByForeach(){
    4. BookVo bookVo=new BookVo();
    5. //Arrays.asList--->将数组转换为集合
    6. bookVo.setIds(Arrays.asList(new Integer[]{13,17,3}));
    7. List books = iBookService.queryBookByForeach(bookVo);
    8. books.forEach(System.out::println);
    9. }

    测试结果如下:

    查看sql语句: 

    • choose标签

                   choose 标签作用是通过条件判断来拼接 SQL 语句,类似于 Java 中的 switch 语句,从上到下,当有匹配的条件时,跳出 choose 语句;如果所有条件都不成立则执行 otherwise 标签中的内容,使用 choose 标签的时候,一定要注意最先需要判断的条件要放在前面,当匹配到某个条件时,就不会判断后面的语句了

    语法格式:

    1. 1>
    2. ...
    3. 2>
    4. ...
    5. 3>
    6. ...
    7. ...
    8. ...

    ①:在BookMapper类定义包含choose标签方法

    1. //choose 标签
    2. List queryBookByChoose(Book book);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookByChoose" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book where 1=1
    3. <choose>
    4. <when test="null!=bname and ''!=bname">
    5. and bname like concat('%',#{bname},'%')
    6. when>
    7. <when test="null!=btype and ''!=btype">
    8. and btype like concat('%',#{btype},'%')
    9. when>
    10. <otherwise>
    11. and bid=#{bid}
    12. otherwise>
    13. choose>
    14. select>

    ③:在iBookService类编写代码

        List queryBookByChoose(Book book);
    

    ④:实现接口所定义的方法

    1. @Override
    2. public List queryBookByChoose(Book book) {
    3. return bookMapper.queryBookByChoose(book);
    4. }

    ⑤:进行 junit4单元测试

    ------测试带name类型的查询:

    1. @Test
    2. //choose 标签
    3. public void queryBookByChoose(){
    4. List books = iBookService.queryBookByChoose(Book.builder().bname("之").build());
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

     

    查看sql语句: 

     

    ------测试带type类型的查询:

    1. @Test
    2. //choose 标签
    3. public void queryBookByChoose(){
    4. List books = iBookService.queryBookByChoose(Book.builder().btype("历史").build());
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

    查看sql语句: 

     

    ------测试带id类型的查询:

    1. @Test
    2. //choose 标签
    3. public void queryBookByChoose(){
    4. List books = iBookService.queryBookByChoose(Book.builder().bid(30).build());
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

     

    查看sql语句: 

     

    • set标签

                set用于更新的sql语句中,在update时,多条件更新,每个属性后面要加逗号“,”,这个时候可能会出现多一个“,”的情况,此时我们就可以使用set去掉后边的“,”修改方法,假如实体类属性为空就不修改此属性所对于的字段

    ①:在BookMapper类定义包含set标签方法

    1. //set标签
    2. int updateByPrimaryKeySelective(Book record);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <update id="updateByPrimaryKeySelective" parameterType="com.zking.model.Book" >
    2. update t_book
    3. <set >
    4. <if test="bname != null" >
    5. bname = #{bname,jdbcType=VARCHAR},
    6. if>
    7. <if test="bprice != null" >
    8. bprice = #{bprice,jdbcType=INTEGER},
    9. if>
    10. <if test="btype != null" >
    11. btype = #{btype,jdbcType=VARCHAR},
    12. if>
    13. set>
    14. where bid = #{bid,jdbcType=INTEGER}
    15. update>

    ③:在iBookService类编写代码

     int updateByPrimaryKeySelective(Book record);

    ④:实现接口所定义的方法

    1. @Override
    2. public int updateByPrimaryKeySelective(Book record) {
    3. return bookMapper.updateByPrimaryKeySelective(record
    4. );
    5. }

    ⑤:进行 junit4单元测试

    1. public void updateByPrimaryKeySelective(){
    2. book.setBid(27);
    3. book.setBname("剑与荣光");
    4. book.setBprice(45);
    5. book.setBtype("文学");
    6. iBookService.updateByPrimaryKeySelective(book);
    7. }

    测试结果如下:

     

    查看sql语句: 

     

    • where标签

        where标签只会在至少有一个子元素返回了SQL语句时, 才会向SQL语句中添加WHERE,并且如果WHERE之后是以AND或OR开头,会自动将其删掉。

    ①:在BookMapper类定义包含where标签方法

    1. //where标签
    2. List queryBookWhere(Book book);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookWhere" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book
    3. <where>
    4. <if test="bname!=null and bname!=''">
    5. and bname like concat('%',#{bname},'%')
    6. if>
    7. <if test="btype!=null and btype!=''">
    8. and btype like concat('%',#{btype},'%')
    9. if>
    10. where>
    11. select>

    ③:在iBookService类编写代码

    1. //where标签
    2. List queryBookWhere(Book book);

    ④:实现接口所定义的方法

    1. @Override
    2. public List queryBookWhere(Book book) {
    3. return bookMapper.queryBookWhere(book);
    4. }

    ⑤:进行 junit4单元测试

    ------测试带name类型的查询:

    1. @Test
    2. //where标签
    3. public void queryBookWhere(){
    4. List books = iBookService.queryBookWhere(Book.builder().bname("的").build());
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

     

    查看sql语句:

     

    ------测试带type类型的查询:

    1. @Test
    2. //where标签
    3. public void queryBookWhere(){
    4. List books = iBookService.queryBookWhere(Book.builder().btype("文学").build());
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

     

    查看sql语句:

     

    二:参数传递

    1.单个参数:------------>八大基础类型与String类型

       ①:案例演示:Integer参数传递

    (1):在BookMapper类定义方法

     List queryBookByInteger(Integer bid);

    (2):在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookByInteger" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List">include> from t_book where 1=1
    3. <if test="null!=bid and ''!=bid ">
    4. and bid=#{bid}
    5. if>
    6. select>

    (3):在iBookService类编写代码

      List queryBookByInteger(Integer bid);

    (4):实现接口所定义的方法

    1. @Override
    2. public List queryBookByInteger(Integer bid) {
    3. return bookMapper.queryBookByInteger(bid);
    4. }

    (5):进行 junit4单元测试

    ---------不传递参数的测试演示(相当于查询所有)

    1. @Test
    2. public void queryBookByInteger(){
    3. List books = iBookService.queryBookByInteger(null);
    4. books.forEach(System.out::println);
    5. }

    测试结果如下:

     

    查看sql语句:

         ok,不带id参数的测试,通过sql语句可以看出来,相当于查询所有,那么我们再来进行带id参数的测试:

    1. @Test
    2. public void queryBookByInteger(){
    3. List books = iBookService.queryBookByInteger(3);
    4. books.forEach(System.out::println);
    5. }

    测试结果如下:

               很好,出现了bug,没有bug的程序猿,都是没有完整的程序猿人生的,我们一起来分析为什么会出现bug:

     报错提示:

     org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'bid' in 'class java.lang.Integer'
    ### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'bid' in 'class java.lang.Integer'

    分析:当前的bid没有getter方法,反射异常-----传递的是一个属性,不是一个实体类,如何给它getter方法?

    1. <if test="null!=bid and ''!=bid ">
    2. and bid=#{bid}
    3. if>

       所以,错误点在BookMapper.xml文件中的bid,如果传递的是八大基本类型,就必须使用value,value就代表当前的值

        所以,将BookMapper.xml文件中的变为:

    1. <if test="null!=value and ''!=value ">
    2. and bid=#{bid}
    3. if>

         继续测试,看效果,查看是否可以运行:

     查看sql语句:

     OK,nice,将xml文件在的bid全部转换为value(注意:数据库的字段除外),来继续尝试:

    1. <if test="null!=value and ''!=value ">
    2. and bid=#{value}
    3. if>

    测试结果如下:

      查看sql语句:

          ok,nice~~~~nice,继续往下走~~~~··

         ps:

    • 传递Integer类型的参数,相当于传递八大基础类型

       ②:案例演示:String参数传递

    (1):在BookMapper类定义方法

    1. //传递String类型的参数
    2. List queryBookByString(String btype);

    (2):在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookByString" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book where 1=1
    3. <if test="null!=value and ''!=value ">
    4. and btype=#{btype}
    5. if>
    6. select>

    (3):在iBookService类编写代码

    1. //传递String类型的参数
    2. List queryBookByString(String btype);

    (4):实现接口所定义的方法

    1. @Override
    2. public List queryBookByString(String btype) {
    3. return bookMapper.queryBookByString(btype);
    4. }

    (5):进行 junit4单元测试

    1. @Test
    2. //传递String类型的参数
    3. public void queryBookByString(){
    4. List books = iBookService.queryBookByString("历史");
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

     报错提示:

     org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: java.lang.IllegalArgumentException: invalid comparison: java.lang.String and [C
    ### Cause: java.lang.IllegalArgumentException: invalid comparison: java.lang.String and [C

    很好,又出现了bug,继续分析:

           这个异常是属于参数异常的类型,不知道是什么哪里有问题,那么我们不妨大胆猜测一下,有可能是xml文件中不能使用value,改用btype尝试尝试

    1. <if test="null!=btype and ''!=btype ">
    2. and btype=#{btype}
    3. if>

     好,继续运行,看看结果:

        

     报错提示:

    org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'btype' in 'class java.lang.String'
    ### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'btype' in 'class java.lang.String'
     

    分析:很好,出现了bug,不过这个bug是和上面出现的bug一样,说明还是要使用value,那么我们就将所有的btype统统改为value,继续运行运行尝试尝试~~~

    1. <if test="null!=value and ''!=value ">
    2. and btype=#{value}
    3. if>

    测试结果如下:

     OK,还是在报我们第一次运行时的错误,一直在报错,这就告诉我们String类型的传参不能这么直接传参,还需要额外的参数,那么我们将BookMapper类的方法用注解的方法进行额外的设参,用@param()的方式进行设参,例如我的String类型的传递参数问题解决方案如下:

    1. //bookType-->参数
    2. //btype-->变量名
    3. List queryBookByString(@Param("bookType") String btype);

       mapper的映射文件如老样子,不进行修改,进行运行,看是否会报错

    1. <if test="null!=value and ''!=value ">
    2. and btype=#{value}
    3. if>

    测试结果如下:

     报错提示:

    org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'value' not found. Available parameters are [param1, bookType]
    ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'value' not found. Available parameters are [param1, bookType]
     

    好,天意难违,bug还是出现了,我们继续来分析:这里的报错显示value没有被分析,那么我们不妨可以得出是xml映射文件的sql语句中,不能使用value,要使用我们在mapper层中定义方法所用@param()方式设置的参数,来继续进行测试一波

    1. <if test="null!=bookType and ''!=bookType">
    2. and btype=#{bookType}
    3. if>

    测试结果如下:

          好,传递String类型的参数的bug终于解决掉了,当我们遇到bug时,不要放弃,要大胆尝试,从另一个方向分析出发,可能会有意外的收获

    注意:

    1.传递String的参数,必须使用@Param方式,并且指明参数名

    2.在service接口,方法应该要将@param()去除掉,只留下变量名,在mapper层可以留下@param(),以防万一,防止报错

    2.多个参数

    ①:在BookMapper类定义方法

    1. List queryBookParams(@Param("booktype") String btype,
    2. @Param("bookid") Integer bid);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookParams" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book where 1=1
    3. <if test="null!=booktype and ''!=booktype">
    4. and btype=#{booktype}
    5. if>
    6. <if test="null!=bookid and ''!=bookid">
    7. and bid=#{bookid}
    8. if>
    9. select>

    ③:在iBookService类编写代码

      List queryBookParams(String btype, Integer bid);

    注意:方法在mapper层和service接口所定义的方法格式不一样,请认真区分并遵循

    ④:实现接口所定义的方法

    1. @Override
    2. public List queryBookParams(String btype, Integer bid) {
    3. return bookMapper.queryBookParams(btype,bid);
    4. }

    ⑤:进行 junit4单元测试

    1. public void queryBookParams(){
    2. List books = iBookService.queryBookParams("文学", 27);
    3. books.forEach(System.out::println);
    4. }

    测试结果如下:

     

    查看sql语句:

     

    3.实体参数

    ①:在BookMapper类定义方法

     List queryBookByObject(Book book);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. @Override
    2. public List queryBookByObject(Book book) {
    3. return bookMapper.queryBookByObject(book);
    4. }

    ③:在iBookService类编写代码

      List queryBookByObject(Book book);

    ④:实现接口所定义的方法

    1. @Override
    2. public List queryBookByObject(Book book) {
    3. return bookMapper.queryBookByObject(book);
    4. }

    ⑤:进行 junit4单元测试

    1. @Test
    2. public void queryBookByObject(){
    3. List books = iBookService.queryBookByObject(Book.builder().btype("文学").build());
    4. books.forEach(System.out::println);
    5. }

    测试结果如下:

     

    查看sql语句:

     

    三:模糊查询

    ①:在BookMapper类定义方法

     List queryBookLike(Book book);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookLike" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book where 1=1
    3. <if test="null!=bname and ''!=bname">
    4. and bname like #{bname}
    5. if>
    6. select>

    ③:在iBookService类编写代码

    1. //模糊查询
    2. List queryBookLike(Book book);

    ④:实现接口所定义的方法

    1. @Override
    2. public List queryBookLike(Book book) {
    3. return bookMapper.queryBookLike(book);
    4. }

    1.${}

    重点:相当于占位符,参数传递时需要手动拼接%%百分号 例如:"zs"-->'zs' 会自动拼接单引号
     

    ⑤:进行 junit4单元测试

    方式一:使用${}方式进行模糊查询

    1. @Test
    2. //模糊查询
    3. public void queryBookLike(){
    4. List books = iBookService.queryBookLike(Book.builder().bname("%与%").build());
    5. books.forEach(System.out::println);
    6. }

       

    测试结果如下:

     

    查看sql语句:

     

    2.#{}

    重点:使用$传递参数时,只会传递参数本身--->例如:"zs"-->zs 不会自动拼接单引号

    ⑤:进行 junit4单元测试

    方式二:使用#{}方式进行模糊查询

    1. @Test
    2. //模糊查询
    3. public void queryBookLike(){
    4. List books = iBookService.queryBookLike(Book.builder().bname("与").build());
    5. books.forEach(System.out::println);
    6. }

    测试结果如下:

    1. <if test="null!=bname and ''!=bname">
    2. and bname like '%${bname}%'
    3. if>

     

    查看sql语句:

    3.concat()---->mysql函数

    ⑤:进行 junit4单元测试

    方式三:使用concat()方式进行模糊查询

    1. <if test="null!=bname and ''!=bname">
    2. and bname like concat('%',#{bname},'%')
    3. if>

    测试结果如下:

     

    查看sql语句:

    问题:#{...}与${...}区别? 

    答:参数类型为字符串,#会在前后加单引号['],$则直接插入值

       注:
              (1)mybatis中使用OGNL表达式传递参数
              (2) 优先使用#{...}
              (3) ${...}方式存在SQL注入风险
     

    问:SQL注入风险

               数据库要执行 SQL 访问数据,数据库是个执行机构,它只会检查传来的 SQL 是不是合乎语法,而并不会关心这个语句是否会造成伤害(数据泄露或破坏)。正因为只要符合语法规则就会执行的机制,导致 SQL 有了注入的风险。
                SQL 本身就是个字符串,而且一般没有加密,字符串可能被黑客劫持修改,这样就可能造成数据库执行了不该执行的动作。
                SQL 注入的惯用做法是通过把 SQL 子串插入到 Web 表单项或页面请求(Url)的查询字符串中提交,最终达到欺骗服务器执行恶意操作的目的。
    常见案例包括通过植入 SQL 骗过登录验证。而之前很多影视网站泄露 VIP 会员密码的事件,很多就是通过 SQL 植入到 WEB 表单暴露的,并且这类表单特别容易受到攻击。通过 SQL 植入,不仅可以非法获取账号信息,严重的还能够篡改、删除重要数据信息。
     

    四:查询返回结果集

       返回结果集的类型:

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

    案例①:使用resultMap返回自定义类型集合

    (1):在BookMapper类定义方法

     List queryBookByResultMap();

    (2):在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookByResultMap" resultMap="BaseResultMap">
    2. /*有参构造方法*/
    3. select <include refid="Base_Column_List"/> from t_book
    4. select>

    (3):在iBookService类编写代码

     List queryBookByResultMap();

    (4):实现接口所定义的方法

    1. @Override
    2. public List queryBookLike(Book book) {
    3. return bookMapper.queryBookLike(book);
    4. }

    (5):进行 junit4单元测试

    1. @Test
    2. //查询返回结果集
    3. //resultMap:适合使用返回值是自定义实体类的情况
    4. //resultType:适合使用返回值的数据类型是非自定义的,即jdk的提供的类型
    5. public void queryBookByResultMap(){
    6. List books = iBookService.queryBookByResultMap();
    7. books.forEach(System.out::println);
    8. }

    测试结果如下:

     

    查看sql语句:

     

    案例②: 使用resultType返回单个对象

    (1):在BookMapper类定义方法

    Book querySingleBookById(Integer bid);

    (2):在BookMapper.xml文件中进行sql语句的编写

    1. <select id="querySingleBookById" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book where bid=#{bid}
    3. select>

    (3):在iBookService类编写代码

     Book querySingleBookById(Integer bid);

    (4):实现接口所定义的方法

    1. @Override
    2. public Book querySingleBookById(Integer bid) {
    3. return bookMapper.querySingleBookById(bid);
    4. }

    (5):进行 junit4单元测试

    1. @Test
    2. //使用resultType返回单个对象
    3. public void querySingleBookById(){
    4. Book book = iBookService.querySingleBookById(13);
    5. System.out.println(book);
    6. }

    测试结果如下:

    查看sql语句:

    案例③:使用resultType返回Map,适用于多表查询返回结果集

    (1):在BookMapper类定义方法

     Map querySingleBookByMap(Integer bid);

    (2):在BookMapper.xml文件中进行sql语句的编写

    1. <select id="querySingleBookByMap" resultType="java.util.Map">
    2. select <include refid="Base_Column_List"/> from t_book where bid=#{bid}
    3. select>

    (3):在iBookService类编写代码

    Map querySingleBookByMap(Integer bid);

    (4):实现接口所定义的方法

    1. @Override
    2. public Map querySingleBookByMap(Integer bid) {
    3. return bookMapper.querySingleBookByMap(bid);
    4. }

    (5):进行 junit4单元测试

    1. @Test
    2. public void querySingleBookByMap(){
    3. Map book1 = iBookService.querySingleBookByMap(13);
    4. System.out.println(book1);
    5. }

    测试结果如下:

     

    查看sql语句:

    案例④: 使用resultType返回List

    (1):在BookMapper类定义方法

      List> queryBookByMaps();

    (2):在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookByMaps" resultType="java.util.Map">
    2. select <include refid="Base_Column_List"/> from t_book
    3. select>

    (3):在iBookService类编写代码

    List> queryBookByMaps();

    (4):实现接口所定义的方法

    1. @Override
    2. public List> queryBookByMaps() {
    3. return bookMapper.queryBookByMaps();
    4. }

    (5):进行 junit4单元测试

    1. @Test
    2. //List和Map集合,适用于多表联查返回综合结果集
    3. public void queryBookByMaps(){
    4. List> maps = iBookService.queryBookByMaps();
    5. System.out.println(maps);
    6. }

    测试结果如下:

     

    查看sql语句:

     

    五:分页查询

      为什么要重写mybatis的分页?
              Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量offset和边界limit取结果),在大数据量的情况下这样的分页基本上是没有用的

     

    实现分页查询的步骤:

    1.导入分页插件

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

    例如:我的pom.xml文件

    1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    3. <modelVersion>4.0.0modelVersion>
    4. <groupId>com.zkinggroupId>
    5. <artifactId>mybatis01artifactId>
    6. <version>1.0-SNAPSHOTversion>
    7. <packaging>warpackaging>
    8. <name>mybatis01 Maven Webappname>
    9. <url>http://www.example.comurl>
    10. <properties>
    11. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    12. <maven.compiler.source>1.8maven.compiler.source>
    13. <maven.compiler.target>1.8maven.compiler.target>
    14. <junit.version>4.13junit.version>
    15. <servlet.version>4.0.1servlet.version>
    16. <mybatis.version>3.4.5mybatis.version>
    17. <mysql.version>5.1.44mysql.version>
    18. <log4j2.version>2.9.1log4j2.version>
    19. <lombok.version>1.18.24lombok.version>
    20. properties>
    21. <dependencies>
    22. <dependency>
    23. <groupId>junitgroupId>
    24. <artifactId>junitartifactId>
    25. <version>${junit.version}version>
    26. <scope>testscope>
    27. dependency>
    28. <dependency>
    29. <groupId>javax.servletgroupId>
    30. <artifactId>javax.servlet-apiartifactId>
    31. <version>${servlet.version}version>
    32. <scope>providedscope>
    33. dependency>
    34. <dependency>
    35. <groupId>org.mybatisgroupId>
    36. <artifactId>mybatisartifactId>
    37. <version>${mybatis.version}version>
    38. dependency>
    39. <dependency>
    40. <groupId>mysqlgroupId>
    41. <artifactId>mysql-connector-javaartifactId>
    42. <version>${mysql.version}version>
    43. dependency>
    44. <dependency>
    45. <groupId>org.apache.logging.log4jgroupId>
    46. <artifactId>log4j-coreartifactId>
    47. <version>${log4j2.version}version>
    48. dependency>
    49. <dependency>
    50. <groupId>org.apache.logging.log4jgroupId>
    51. <artifactId>log4j-apiartifactId>
    52. <version>${log4j2.version}version>
    53. dependency>
    54. <dependency>
    55. <groupId>org.apache.logging.log4jgroupId>
    56. <artifactId>log4j-webartifactId>
    57. <version>${log4j2.version}version>
    58. dependency>
    59. <dependency>
    60. <groupId>org.projectlombokgroupId>
    61. <artifactId>lombokartifactId>
    62. <version>${lombok.version}version>
    63. <scope>providedscope>
    64. dependency>
    65. <dependency>
    66. <groupId>com.github.pagehelpergroupId>
    67. <artifactId>pagehelperartifactId>
    68. <version>5.1.2version>
    69. dependency>
    70. dependencies>
    71. <build>
    72. <finalName>mybatis01finalName>
    73. <plugins>
    74. <plugin>
    75. <groupId>org.apache.maven.pluginsgroupId>
    76. <artifactId>maven-compiler-pluginartifactId>
    77. <version>3.7.0version>
    78. <configuration>
    79. <source>${maven.compiler.source}source>
    80. <target>${maven.compiler.target}target>
    81. <encoding>${project.build.sourceEncoding}encoding>
    82. configuration>
    83. plugin>
    84. <plugin>
    85. <groupId>org.mybatis.generatorgroupId>
    86. <artifactId>mybatis-generator-maven-pluginartifactId>
    87. <version>1.3.2version>
    88. <dependencies>
    89. <dependency>
    90. <groupId>mysqlgroupId>
    91. <artifactId>mysql-connector-javaartifactId>
    92. <version>${mysql.version}version>
    93. dependency>
    94. dependencies>
    95. <configuration>
    96. <overwrite>trueoverwrite>
    97. configuration>
    98. plugin>
    99. plugins>
    100. build>
    101. project>

    2. 将pagehelper插件配置到mybatis中

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

    例如:我的mybatis.cfg..xml文件

    1. configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    2. <configuration>
    3. <properties resource="jdbc.properties"/>
    4. <settings>
    5. <setting name="logImpl" value="LOG4J2"/>
    6. settings>
    7. <plugins>
    8. <plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
    9. plugins>
    10. <environments default="development">
    11. <environment id="development">
    12. <transactionManager type="jdbc"/>
    13. <dataSource type="POOLED">
    14. <property name="driver"
    15. value="${jdbc.driver}"/>
    16. <property name="url"
    17. value="${jdbc.url}"/>
    18. <property name="username" value="${jdbc.username}"/>
    19. <property name="password" value="${jdbc.password}"/>
    20. dataSource>
    21. environment>
    22. environments>
    23. <mappers>
    24. <mapper resource="com/zking/mapper/BookMapper.xml"/>
    25. mappers>
    26. configuration>

    3.导入pageBean文件在util包下

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

    4.在BookMapper类定义方法

    1. //分页查询
    2. List queryBookPager(Book book, PageBean pageBean);

    5.在BookMapper.xml文件中进行sql语句的编写

    1. @Override
    2. public List<Book> queryBookPager(Book book, PageBean pageBean) {
    3. return bookMapper.queryBookPager(book);
    4. }

    6.在iBookService类编写代码

    1. //分页查询
    2. List queryBookPager(Book book, PageBean pageBean);

    7.实现接口所定义的方法

    1. @Override
    2. public List queryBookPager(Book book, PageBean pageBean) {
    3. return bookMapper.queryBookPager(book);
    4. }

    8.在你需要进行分页的Mybatis方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个Mybatis查询方法会被进行分页

    1. //设置分页处理
    2. if (null != pageBean && pageBean.isPaginate()) {
    3. PageHelper.startPage(pageBean.getCurPage(), pageBean.getPageRecord());
    4. }

    9. 获取分页信息(二种方式)

             ①:使用插件后,查询实际返回的是Page,而非List,Page继承了ArrayList,同时还包含分页相关的信息

    1. Page page = (Page)list;
    2. System.out.println("页码:" + page.getPageNum());
    3. System.out.println("页大小:" + page.getPageSize());
    4. System.out.println("总记录:" + page.getTotal());

       ②:使用PageInfo

    1. PageInfo pageInfo = new PageInfo(list);
    2. System.out.println("页码:" + pageInfo.getPageNum());
    3. System.out.println("页大小:" + pageInfo.getPageSize());
    4. System.out.println("总记录:" + pageInfo.getTotal());

      我的分页获取信息,使用第二种方式:

    1. @Test
    2. //分页查询
    3. public void queryBookPager(){
    4. PageBean pageBean=new PageBean();
    5. //判断是否分页
    6. if(null!=pageBean&&pageBean.isPagination())
    7. //pageBean.getPage()---当前页数
    8. //pageBean.getRows()----每页条数
    9. PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
    10. List bookList = iBookService.queryBookPager(Book.builder().build(), pageBean);
    11. System.out.println(bookList.getClass());
    12. if(null!=pageBean&&pageBean.isPagination()) {
    13. PageInfo pageInfo = new PageInfo(bookList);
    14. System.out.println("页码:" + pageInfo.getPageNum());
    15. System.out.println("页大小:" + pageInfo.getPageSize());
    16. System.out.println("总记录:" + pageInfo.getTotal());
    17. List list = pageInfo.getList();
    18. list.forEach(System.out::println);
    19. }
    20. }

    测试结果如下:

     

    查看sql语句:

     

    六:特殊字符处理

    特殊字符相关属性
    >>
    <<
    &&
    空格 ;

    问题:为什么需要转义?

    答:因为在mybatis中的sql语句是写在xml文件中的,但是在处理范围查询的时候经常要用到<,>,$等特殊字符

    案例:价格范围查询

    ①:定义一个特殊类,用来存放范围值

    1. package com.zking.vo;
    2. import com.zking.model.Book;
    3. import lombok.Data;
    4. import java.util.List;
    5. /**
    6. * @author 唐渊
    7. * @create  2022-07-23 9:26
    8. */
    9. @Data //getter/setter/toString
    10. public class BookVo extends Book {
    11. //最小价格
    12. private Integer min;
    13. //最大价格
    14. private Integer max;
    15. }

    ②:在BookMapper类定义方法

    1. //范围查询,转义字符处理
    2. List queryBookRange(BookVo bookVo);

    ③:在BookMapper.xml文件中进行sql语句的编写

    1. <select id="queryBookRange" resultType="com.zking.model.Book">
    2. select <include refid="Base_Column_List"/> from t_book where 1=1
    3. <if test="null!=min and ''!=min">
    4. and bprice>#{min}
    5. if>
    6. <if test="null!=max and ''!=max">
    7. and bprice<#{max}
    8. if>
    9. select>

    ④:在iBookService类编写代码

    1. //范围查询,转义字符处理
    2. List queryBookRange(BookVo bookVo);

    ⑤:实现接口所定义的方法

    1. @Override
    2. public List queryBookRange(BookVo bookVo) {
    3. return bookMapper.queryBookRange(bookVo);
    4. }

    ⑥:进行 junit4单元测试

    1. @Test
    2. //范围查询,转义字符处理
    3. public void queryBookRange(){
    4. BookVo bookVo=new BookVo();
    5. bookVo.setMin(50);
    6. bookVo.setMax(200);
    7. List books = iBookService.queryBookRange(bookVo);
    8. //不使用转义字符前
    9. //元素内容必须由格式正确的字符数据或标记组成。
    10. books.forEach(System.out::println);
    11. }

    测试结果如下:

     

    查看sql语句:

     

    七:批量新增

            mybatis原则上是不能够一次批量执行多条SQL语句,比如新增、删除、更新等,但是修改jdbc url连接带参数可以实现相同效果

    1. spring:
    2. datasource:
    3. username: root
    4. password: mysql
    5. # allowMultiQueries 是否允许一条SQL语句包含多个执行SQL以分号;分隔
    6. url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&allowMultiQueries=true
    7. driver-class-name: com.mysql.cj.jdbc.Driver

    重点:

            allowMultiQueries=true 允许一条SQL语句包含多个执行SQL以分号;分隔,是必须添加的

    ①:在BookMapper类定义方法

    1. //批量新增
    2. int insertBatch(List list);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <insert id="insertBatch">
    2. insert into t_book ( bname,bprice,btype ) values
    3. <foreach collection="list" separator="," item="it">
    4. (#{it.bname},#{it.bprice},#{it.btype})
    5. foreach>
    6. insert>

    ③:在iBookService类编写代码

    1. //批量新增
    2. int insertBatch(List list);

    ④:实现接口所定义的方法

    1. @Override
    2. public int insertBatch(List list) {
    3. return bookMapper.insertBatch(list);
    4. }

    ⑤:进行 junit4单元测试

    1. @Test
    2. //批量新增
    3. public void insertBatch(){
    4. List list = new ArrayList<>();
    5. book.setBname("西罗马帝国");
    6. book.setBprice(34);
    7. book.setBtype("历史");
    8. list.add(book);
    9. book.setBname("世界大战一");
    10. book.setBprice(65);
    11. book.setBtype("历史");
    12. list.add(book);
    13. int i = iBookService.insertBatch(list);
    14. if (i>0) {
    15. System.out.println("yes");
    16. }
    17. }

    测试结果如下:

    查看sql语句:

     

    八:批量更新

    ①:在BookMapper类定义方法

    1. //批量更新
    2. int updateForNew(List List);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <update id="updateForNew">
    2. insert into t_book (bname,bprice,btype) values
    3. <foreach collection="list" separator="," item="it">
    4. (#{it.bname},#{it.bprice},#{it.btype})
    5. foreach>
    6. on duplicate key update bname=values(bname);
    7. update>

    ③:在iBookService类编写代码

    1. @Override
    2. int updateForNew(List List) ;

    ④:实现接口所定义的方法

    1. @Override
    2. public int updateForNew(List list) {
    3. return bookMapper.insertBatch(list);
    4. }

    ⑤:进行 junit4单元测试

    1. @Test
    2. //批量更新
    3. public void updateForNew(){
    4. List list = new ArrayList();
    5. book.setBname("上帝之鞭");
    6. book.setBprice(56);
    7. book.setBtype("历史");
    8. list.add(book);
    9. book.setBname("西楚霸王");
    10. book.setBprice(77);
    11. book.setBtype("历史");
    12. list.add(book);
    13. int i = iBookService.updateForNew(list);
    14. if (i>0) {
    15. System.out.println("yes");
    16. }
    17. }

    测试结果如下:

    九:批量删除

    ①:在BookMapper类定义方法

    1. //批量删除
    2. int deleteBatch(List idList);

    ②:在BookMapper.xml文件中进行sql语句的编写

    1. <delete id="deleteBatch">
    2. delete from t_book where bid in
    3. <foreach collection="list" item="it" separator="," open="(" close=")">
    4. #{it}
    5. foreach>
    6. delete>

    ③:在iBookService类编写代码

    1. //批量删除
    2. int deleteBatch(List idList);

    ④:实现接口所定义的方法

    1. @Override
    2. public int deleteBatch(List idList) {
    3. return bookMapper.deleteBatch(idList);
    4. }

    ⑤:进行 junit4单元测试

    1. @Test
    2. //批量删除
    3. public void deleteBatch(){
    4. List list = new ArrayList<>();
    5. list.add(31);
    6. list.add(32);
    7. int i = iBookService.deleteBatch(list);
    8. if (i>0) {
    9. System.out.println("yes");
    10. }
    11. }

    测试结果如下:

     

    查看sql语句:

        代码操作有点多,但全是干货

  • 相关阅读:
    U-Boot 启动流程详解
    webrtc gcc算法(1)
    【Linux篇】之常用命令
    SpringBoot2.0(mybatis-plus初始使用)
    Java面试题-0919
    网络拓扑自动扫描工具推荐-S-mart市场已上新
    大数据专业大一学生在线提问
    杂记-缺失值插值方式
    卡片层叠Banner
    gRPC入门学习之旅(六)
  • 原文地址:https://blog.csdn.net/qq_61313896/article/details/125981873