mybatis-plus

Mybatis-plus

  • 简化数据库操作,帮你完成简单的增删改

第一个mybatis-plus(springboot框架)

  1. 创建springboot程序,勾选mysqlDriver

  2. 导入依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.2</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.23</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    
  3. 写数据库对应实体类,注意命名规则,数据库_,实体类驼峰,或者直接全部小写

  4. 写dao继承basemapper,@mapper注解(注入用)

    package com.zhm.dao;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.zhm.domian.Book;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface BookDao extends BaseMapper<Book> {
    }
    
  5. 测试

    package com.zhm;
    
    import com.zhm.dao.BookDao;
    import com.zhm.domian.Book;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.util.List;
    
    @SpringBootTest
    class Mybatisplus01QuickstartApplicationTests {
    
        @Autowired
        private BookDao bookDao;
    
        @Test
        void getAllTest() {
            List<Book> bookList = bookDao.selectList(null);
            for (Book book : bookList) {
                System.out.println(book);
            }
        }
    
    }
    

优势区间

image-20250327163013544

测试CRUD

  • 都是继承BaseMapper的方法

    image-20250327165508084

package com.zhm;

import com.zhm.dao.BookDao;
import com.zhm.domian.Book;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {

    @Autowired
    private BookDao bookDao;

    @Test
    void testInsert(){
        Book book = new Book();
        book.setName("mb-sb");
        book.setCount(999);
        book.setDetail("speed");
        int i = bookDao.insert(book);
    }

    @Test
    void testDelete(){
        int i = bookDao.deleteById(24);
    }

    @Test
    void testUpdate(){
        Book book = new Book();
        book.setId(19L);
        book.setName("mb-sb");
        book.setDetail("speed");
        int i = bookDao.updateById(book);
    }

    @Test
    void testGetById(){
        Book book = bookDao.selectById(19L);
        System.out.println(book);
    }

    @Test
    void testGetAll() {
        List<Book> bookList = bookDao.selectList(null);
        for (Book book : bookList) {
            System.out.println(book);
        }
    }

}

分页

  1. 配置分页拦截器

    package com.zhm.config;
    
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class PageConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            // 定义mp拦截器容器,放拦截器
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            // 放入page拦截器使其生效
            mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }
    
  2. 测试

    @Test
    void testPage(){
        IPage iPage = new Page(2,3);// 第几页,一页多少
        bookDao.selectPage(iPage,null);
    
        System.out.println(iPage.getCurrent());// 当前第几页
        System.out.println(iPage.getSize());// 页面大小
        System.out.println(iPage.getPages());// 多少页
        System.out.println(iPage.getTotal());// 多少数据
        System.out.println(iPage.getRecords());// 结果
    }
    
  3. 配置拦截器才能生效分页,因为分页实现是在执行语句后面加limit,相当于拦截分页sql,再加上limit

    MyBatis-Plus 的分页实现原理

    MyBatis-Plus 的分页功能是通过拦截器(插件)机制实现的,具体来说:

    • 当调用分页方法(如 baseMapper.selectPage(page, queryWrapper))时
    • PaginationInnerInterceptor 会拦截 SQL 执行过程
    • 自动在原始 SQL 后追加 LIMIT 分页语句
    • 同时自动生成并执行 COUNT 查询 获取总记录数

条件查询

  • bookDao.selectList(lqw) lqw为查询条件,自己选择需要的条件加上

  • 一般lqw都是用LambdaQueryWrapper,可以省略lambda()

  • lambda优势

    避免硬编码字段名(如 "count"),直接引用实体类的 get 方法,编译器会检查字段是否存在

    @Test
    void testDQL() {
//        QueryWrapper<Book> lqw = new QueryWrapper<Book>();
        LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
        // 链式 默认就是且 &&
//        lqw.gt(Book::getCount,100).lt(Book::getCount,800);
//        lqw.lambda().gt(Book::getCount,800).or().lt(Book::getCount,100);
        // 加or(). 就是或 ||
        lqw.gt(Book::getCount,800).or().lt(Book::getCount,100);
        List<Book> books = bookDao.selectList(lqw);
        for (Book book : books) {
            System.out.println(book);
        }
    }
  • lt:less than 小于
  • le:less than or equal to 小于等于
  • eq:equal to 等于
  • ne:not equal to 不等于
  • ge:greater than or equal to 大于等于
  • gt:greater than 大于

处理null

  • 如果传入值为null就不执行,前面条件判断为true就加上这个条件

  •     @Test
        void testDQL() {
    //        QueryWrapper<Book> lqw = new QueryWrapper<Book>();
            LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
            // 链式 默认就是且 &&
    //        lqw.gt(Book::getCount,100).lt(Book::getCount,800);
    //        lqw.lambda().gt(Book::getCount,800).or().lt(Book::getCount,100);
            // 加or(). 就是或 ||
            Integer min = 101;
            Integer max = 800;
            // 前面条件判断为true就加上这个条件
            lqw.gt(max!=null,Book::getCount,max)
                .or()
                .lt(min!=null,Book::getCount,min);
            List<Book> books = bookDao.selectList(lqw);
            for (Book book : books) {
                System.out.println(book);
            }
        }
    

查询映射投影

  • 只查某些结果,加select条件

    @Test
    void testDQLAS() {
    //        QueryWrapper<Book> qw = new QueryWrapper<>();
    //        lqw.select("name","count");
        LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
        lqw.select(Book::getName,Book::getDetail);
        List<Book> books = bookDao.selectList(lqw);
        for (Book book : books) {
            System.out.println(book);
        }
    }
    
  • 查询某些条件,映射,分组

    用selectmap存结果,键值对可以存所有类型

        @Test
        void testDQLAS2() {
            QueryWrapper<Book> qw = new QueryWrapper<>();
    //        lqw.select("name","count");
            qw.select("count(*) as count_sum,count")
                .groupBy("count");
            List<Map<String, Object>> books = bookDao.selectMaps(qw);
            System.out.println(books);
        }
    

查询条件

@Test
    void testDQLWrapper() {
        LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
        // eq= lt< gt> le<= ge>= between[a,b](含ab)
        
//        lqw.eq(Book::getName,"mysql").eq(Book::getCount,200);
//        lqw.between(Book::getCount,100,200);
        
        // like likeLeft likeRight 代表%位置  %j%和%j和j%
        lqw.like(Book::getName,"j");
//        lqw.likeLeft(Book::getName,"j");
//        lqw.likeRight(Book::getName,"j");
        
        // 查结果只有一个就用 selectone 一般登录验证
//        Book book = bookDao.selectOne(lqw);
        List<Book> books = bookDao.selectList(lqw);
        System.out.println(books);
    }

映射相关问题--数据库和实体类不匹配

  • @TableName("book") 数据库表名改了
  • @TableField("name") 数据库属性名改了
package com.zhm.domian;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
// 数据库表名改了
@TableName("book")
public class Book {
    private Long id;
    // 数据库属性改名了
    @TableField("name")
    private String name;
    // 不查这个,一般用于密码
    @TableField(select = false)
    private Integer count;
    private String detail;

    // 实体类有,数据库没有的
    @TableField(exist = false)
    private Integer online;
}

Id生成策略

  • 有5种,用@TableId(type=IdType.xxx),在实体类id上面写注解

    AUTO(0),
    NONE(1),
    INPUT(2),
    ASSIGN_ID(3),
    ASSIGN_UUID(4),
    
    // auto 数据库默认
    // input 不自增,要用户传id
    // ASSIGN_ID 雪花算法 64位2进制 是一个long
    // 第1位0 表示正 2-42 时间戳精确ms 43-52 机器码表示电脑标识符号 53-64 某一时间段内的第几个请求
    // 用户传入大于系统生成
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    
  • 全局控制

    在配置文件设置,id-type所有id生成策略,table-prefix所有表名前缀,user和tbl_user

    #   全局设定
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        banner: off
    #    全局设定
        db-config:
          id-type: assign_id
          table-prefix: tbl_
    

删除相关

多数据删除,多数据查询

  • deleteBatchIds放集合,集合放long类型数据
@Test
void testDeletes(){
    List<Long> list = new ArrayList<Long>();
    list.add(678L);
    list.add(679L);

    bookDao.deleteBatchIds(list);
}

@Test
void testSelects(){
    List<Long> list = new ArrayList<Long>();
    list.add(1L);
    list.add(3L);
    list.add(5L);

    bookDao.selectBatchIds(list);
}

逻辑删除

  • 某些情况为了保护数据又要删除数据,只是加一个逻辑判断字段,0未删除,1删除,实际数据库内数据不删除

  • 数据库加deleted,实体类属性deleted,

  • @TableLogic(value = "0",delval = "1"),value代表没被删除显示,delval代表被删除后显示

        @TableLogic(value = "0",delval = "1")
        private Integer deleted;
    
  • SELECT id,name,detail,deleted FROM book WHERE deleted=0

    之后查询会忽略deleted=1的,如果要获取deleted=1被删的,自己写sql就行

  • 全局配置

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        banner: off
        #全局配置 删除字段和值 1删 0不删
        db-config:
          logic-delete-field: deleted
          logic-delete-value: 1
          logic-not-delete-value: 0
    

乐观锁 并发问题

  • 并发,太多个人抢票,出现-1,-2数据非法数据

  • 乐观锁,用一个字段version标识用户版本,默认都是1

    1. 数据库字段version,实体类属性version,实体类加上@Version注解

    2. 加入乐观锁拦截器,拦截器就是利用aop切面编程,当遇到某种条件执行sql时候,加上sql语句

      package com.zhm.config;
      
      import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
      import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
      import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class PageConfig {
      
          @Bean
          public MybatisPlusInterceptor mybatisPlusInterceptor(){
              // 定义mp拦截器容器,放拦截器
              MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
              // 放入page拦截器使其生效
              mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
              mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
              return mybatisPlusInterceptor;
          }
      }
      
    3. 测试,只要不是同时,就一定会失败一个,因为成功后会version+1,判断不满足

      测试要先获取version,设置或者selectbyid获取整个对象

      @Test
      void testUpdateO(){
          Book book1 = bookDao.selectById(2);// 第一个用户 获取version=1
          Book book2 = bookDao.selectById(2);// 第二个用户 获取version=1
      
          book1.setName("mybatis-plus");
          book2.setName("mybatis-nb");
      
          // 判断条件从上面获取到就不变,执行成功会+1,乐观锁解决并发
          bookDao.updateById(book1);// 修改条件 where version=1 满足之后 version+1
          bookDao.updateById(book2);// 修改条件 where version=1 不满足
      }
      
posted @ 2025-04-22 22:10  学习java的白菜  阅读(19)  评论(0)    收藏  举报