栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybatis和MybatisPlus3.4的使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybatis和MybatisPlus3.4的使用

Mybatis和MybatisPlus3.4的使用

文章目录
  • Mybatis和MybatisPlus3.4的使用
    • 1 RESTFUL
    • 2 逆向工程
      • 2.1 tkMybatis逆向工程
        • 2.1.1 导入依赖
        • 2.1.2 添加插件
        • 2.1.3 添加配置文件
        • 2.1.4 执行逆向
        • 2.1.5 生成成功
        • 2.1.6 使用tkMybatis
      • 2.2 MybatisPlus使用教程
        • 2.2.1 在mapper子项目中添加依赖
        • 2.2.2 配置`application.yml`
        • 2.2.3 配置`@MapperScan`
        • 2.2.4 编写实体类`Users`
        • 2.2.5 编写`Mapper.java`继承模版`baseMapper`
        • 2.2.6 测试是否能够正常调用MP
        • 2.2.7 配置日志
        • 2.2.8 CRUD扩展
          • 2.2.8.1 数据库插入的id的默认值:全局的唯一id
          • 2.2.8.2 自动填充
        • 2.2.9 乐观锁
        • 2.2.10 查询
        • 2.2.11 删除
        • 2.2.12 性能分析插件(新版本已经移除)
        • 2.2.13 条件构造器`wrapper`
        • 2.2.14 代码生成器,`MP`逆向工程
    • 3 常见问题解决办法
      • 3.1 数据库url中的SSL问题
      • 3.2 插件没找到
      • 3.3 将数据库中的其他表都生成了
      • 3.4 users表生成了其他表

1 RESTFUL

前后端风你开发的项目中,前后端之间是接口进行请求和响应,后端向前端提供请求时就要对外暴露一个URL;URL的设计不能是随意的,需要遵从一定的设计规范----RESTFUL。

RESTful是一种web api的标准,也就是一种url设计风格/规范

  • 每个URL请求路径代表服务器上的唯一资源。

    传统的URL设计:

    ​ http://localhost:8080/goods/delete?goodsId=1 商品1

    ​ http://localhost:8080/goods/delete?goodsId=2 商品2

    RESTful设计:

    ​ http://localhost:8080/goods/delete/1 商品1

    ​ http://localhost:8080/goods/delete/1 商品1

    示例:

        @DeleteMapping("/delete/{id}")
        public ResultVO deleteGoods(@PathVariable("id") Integer goodsId) {
            return new ResultVO(10003,"请求成功",goodsId);
        }
    
  • 使用不同的请求方式表示不同的操作,保证唯一URL对应唯一资源

    • @GetMapping 查询
    • @PutMapping 修改
    • @PostMapping 添加
    • @DeleteMapping 删除

    根据id删除一个商品:

     //http://localhost:8080/goods/1	[delete]
    	@DeleteMapping("/{id}")
     public ResultVO deleteGoods(@PathVariable("id") Integer goodsId) {
         return new ResultVO(10003,"请求成功",goodsId);
     } 
    

    根据id查询一个商品:

     //http://localhost:8080/goods/1	[get]
    	@GetMapping("/{id}")
     public ResultVO getGoods(@PathVariable("id") Integer goodsId) {
         return new ResultVO(10003,"请求成功",goodsId);
     }
    
  • 接口响应的资源表现形式使用JSON

    • 可以在控制类或者需要的接口方法上添加@ResponseBody注解讲返回的对象格式化为JSON
    • 也可以在控制类上使用@RestController声明控制器
  • 前端(AndroidiosPC)通过无状态的HTTP协议与后端接口交互

2 逆向工程

mybatis官方提供了一种根据数据库表生成mybatis执行代码的工具,这个工具就是一个逆向工程

逆向工程:针对数据库单表—->生成代码(mapper.xml、mapper.java、pojo。。)

这里我最开始使用的是mybatis逆向工程,可以生成mapper.xml、mapper.java、pojo

后面我想尝试使用MP做逆向工程,所以使用了两种方式,从简洁上来看,还是MP比较方便一点.

2.1 tkMybatis逆向工程

逆向工程并且自动添加swagger注解,注意需要在beans的pom也要添加swagger依赖

2.1.1 导入依赖

    tk.mybatis
    mapper-spring-boot-starter
    2.1.5

  • 修改启动类注解@MapperScan的包,因为这个注解还有个包是org.mybatis.spring.annotation.MapperScan;
2.1.2 添加插件
    
        
            
                org.mybatis.generator
                mybatis-generator-maven-plugin
                1.3.5
                
                    true
                    true
                    ${basedir}/src/main/resources/generator/generatorConfig.xml
                
                
                    
                        mysql
                        mysql-connector-java
                        8.0.22
                    
                    
                    
                        tk.mybatis
                        mapper
                        4.1.5
                    
                    
                        com.github.misterchangray.mybatis.generator.plugins
                        myBatisGeneratorPlugins
                        1.2
                    
                
            
        
    

同步成功后会出现

2.1.3 添加配置文件

generatorConfig.xml





    
    
    
    
    
        
        
        
        
            
        
        
        
        
        
        


        
                
                    
                    
                
        

        
            
            
        
        
        
            
        

        
        
        
        
        
        

        
        
            
            
        

        
        

        
        
        
        
        
  • 调整配置信息

    • 创建GeneralDao接口

      package com.mymall.general;
      
      import tk.mybatis.mapper.common.Mapper;
      import tk.mybatis.mapper.common.MySqlMapper;
      
      
      public interface GeneralDao extends Mapper, MySqlMapper {
      }
      
      
      
      
          
      
      
    • 配置数据库连接信息

      
      
      
    • 配置实体类路径

      
          
          
      
      
    • 配置mapper.xml目录

      
      
    • 配置dao的目录和生成表的名称

       
      
      
      
      
  • 将配置文件generatorConfig.xml配置到逆向工程的maven插件中

    org.mybatis.generator
    mybatis-generator-maven-plugin
    1.3.5
    
        ${basedir}/src/main/resources/generator/generatorConfig.xml
    
    
  • 2.1.4 执行逆向 2.1.5 生成成功
    • 将生成的entity剪切到beans子项目中

      将生成的entity移到到beans目录之后会出现报错,因为使用了tkMapper的注解,所以需要将tkMapper的依赖直接剪切到beans中,mapper项目引用了beans的依赖所以就可以不再pom中声明tkMapper的依赖了.

    2.1.6 使用tkMybatis
    package com.qc.dao;
    
    import com.qc.TkMapperDemoApplication;
    import com.qc.entity.Category;
    import org.apache.ibatis.session.RowBounds;
    import org.junit.Assert;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import tk.mybatis.mapper.entity.Example;
    
    import java.util.List;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    
    @SpringBootTest(classes = TkMapperDemoApplication.class)
    @RunWith(SpringRunner.class)
    public class CategoryDaoTest {
        @Autowired
        private CategoryDao categoryDao;
    
        @Test
        public void testInsert() {
            Category category = new Category(0,"test02",1,0,"01.png","hehe","111.jpg","black");
            //将数据插入表中,返回结果,1,成功
            //int i = categoryDao.insert(category);
            //将数据插入后将生成的主键返回给对象保存
            int i = categoryDao.insertUseGeneratedKeys(category);
            System.out.println(category.getCategoryId());
            assertEquals(1,i);
        }
    
        @Test
        public void testUpdate() {
            Category category = new Category(13,"test03",1,0,"01.png","hehe","111.jpg","black");
            //根据主键修改数据
            int i = categoryDao.updateByPrimaryKey(category);
            assertEquals(1,i);
        }
    
        @Test
        public void testDelete() {
            //根据主键删除数据
            int i = categoryDao.deleteByPrimaryKey(13);
            assertEquals(1,i);
    
        }
    
        @Test
        public void testSelect1() {
            //查询所有
            List categories = categoryDao.selectAll();
            for (Category category : categories) {
                System.out.println(category);
            }
        }
    
        @Test
        public void testSelect2() {
        //    根据主键查询
            Category category = categoryDao.selectByPrimaryKey(13);
            System.out.println(category);
        }
    
        @Test
        public void testSelect3() {
        //    条件查询
        //    1.创建一个example封装,类别category查询条件
            Example example = new Example(Category.class);
            Example.Criteria criteria = example.createCriteria();
            //criteria.andEqualTo("categoryLevel", 1);
            criteria.andLike("categoryName", "%干%");
            List categories = categoryDao.selectByExample(example);
            for (Category category : categories) {
                System.out.println(category);
            }
        }
    
        @Test
        public void testSelect4() {
        //    分页查询
            int pageNum = 2;
            int pageSize= 5;
            int start =(pageNum-1)*pageSize;
            RowBounds rowBounds = new RowBounds(start,pageSize);
            List categories = categoryDao.selectByRowBounds(new Category(), rowBounds);
            for (Category category : categories) {
                System.out.println(category);
            }
        //    查询总记录数
            int i = categoryDao.selectCount(new Category());
            System.out.println(i);
        }
    
        @Test
        public void testSelect5() {
            //准备条件
            Example example = new Example(Category.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("categoryLevel", 1);
            //准备分页条件
            int pageNum = 1;
            int pageSize= 3;
            int start =(pageNum-1)*pageSize;
            RowBounds rowBounds = new RowBounds(start,pageSize);
            //条件分页显示
            List categories = categoryDao.selectByExampleAndRowBounds(example, rowBounds);
            for (Category category : categories) {
                System.out.println(category);
            }
        //    查询满足条件的记录数
            int i = categoryDao.selectCountByExample(example);
            System.out.println(i);
        }
    }
    
    2.2 MybatisPlus使用教程 2.2.1 在mapper子项目中添加依赖

    在创建mapper子项目的时候已经添加了mybatis-plus-boot-starter,所以这里不需要额外添加依赖

    2.2.2 配置application.yml

    当时用mysql8时,需要mysql的驱动同:com.mysql.cj.jdbc.Driver,同时需要增加时区设置:serverTimezone=GMT%2b8

    datasource:
        druid:
          #数据库连接驱动
          driver-class-name: com.mysql.cj.jdbc.Driver
          #数据库连接地址
          url: jdbc:mysql://localhost:3306/mymall?serverTimezone=GMT%2b8&useSSL=false&allowPublicKeyRetrieval=true
          #数据库连接用户名和密码
          username: root
          password: 123456
    
    2.2.3 配置@MapperScan
    package com.mymall;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import springfox.documentation.oas.annotations.EnableOpenApi;
    
    @SpringBootApplication
    @MapperScan("com.mymall.dao")
    @EnableOpenApi
    public class ApiApplication {
        public static void main(String[] args) {
            SpringApplication.run(ApiApplication.class, args);
        }
    }
    
    
    2.2.4 编写实体类Users
    package com.mymall.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Users {
        Integer userId;
        String username;
        String password;
    }
    
    
    2.2.5 编写Mapper.java继承模版baseMapper
    package com.mymall.dao;
    
    import com.baomidou.mybatisplus.core.mapper.baseMapper;
    import com.mymall.entity.Users;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface UsersMapper extends baseMapper {
    }
    
    
    2.2.6 测试是否能够正常调用MP
    package com.mymall;
    
    import com.mymall.entity.Users;
    import com.mymall.dao.UsersMapper;
    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 ApiApplicationTests {
        @Autowired
        private UsersMapper usersMapper;
    
        @Test
        void contextLoads() {
            List users = usersMapper.selectList(null);
            for (Users user : users) {
                System.out.println(user);
            }
            System.out.println(users);
        }
    
    }
    
    

    测试成功!

    2.2.7 配置日志
    mybatis-plus:
      configuration:
    #    配置日志
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    显示效果

    2.2.8 CRUD扩展 2.2.8.1 数据库插入的id的默认值:全局的唯一id
    • 生成策略:分布式系统唯一ID生成方案汇总 - nick hao - 博客园 (cnblogs.com)

    • 雪花算法:snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。雪花算法支持的TPS可以达到419万左右(2^22*1000)。

    • 注意:使用雪花或者uuid的时候在数据中的字段不能是自增,不然不会生效.

    • 设置要求:

      • 主键需要设置成自增

      • 实体类字段上添加MP注解@TableId(type = IdType.ASSIGN_ID)

      • IdType类型源码

        package com.baomidou.mybatisplus.annotation;
        
        import lombok.Getter;
        
        
        @Getter
        public enum IdType {
            
            AUTO(0),
            
            NONE(1),
            
            INPUT(2),
        
            
            
            ASSIGN_ID(3),
            
            ASSIGN_UUID(4),
        
            private final int key;
        
            IdType(int key) {
                this.key = key;
            }
        }
        
    • Insert自动生成id

      @Test//测试插入
      public void insertTest(){
          User user = new User();
          user.setName("wsk");
          user.setEmail("2803708553@qq.com");
          Integer result = userMapper.insert(user); //会帮我们自动生成id
          System.out.println(result); //受影响的行数
          System.out.println(user); //通过日志发现id会自动回填
      }
      

    2.2.8.2 自动填充

    创建时间、更改时间! 这些操作一般都是自动化完成,我们不希望手动更新

    阿里巴巴开发手册︰几乎所有的表都要配置 gmt_create、gmt_modified !而且需要自动化

    • 方式一:数据库级别(工作中不允许修改数据库级别)

      1、在表中增加字段:create_time,update_time

      -- auto-generated definition
      create table mp_user
      (
          user_id     bigint auto_increment comment '用户id'  primary key,
          username    varchar(32)                        not null comment '用户名',
          password    varchar(32)                        not null comment '密码',
          create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
          update_time datetime default CURRENT_TIMESTAMP not null comment '更新时间'
      );
      

      2、更新实体类中的成员变量

      package com.mymall.entity;
      
      import com.baomidou.mybatisplus.annotation.IdType;
      import com.baomidou.mybatisplus.annotation.TableId;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.util.Date;
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class MpUser {
          @TableId(type = IdType.ASSIGN_ID)
          Long userId;
          String username;
          String password;
          Date createTime;
          Date updateTime;
      }
      
      

      3、再次运行之前插入或更新一条数据的代码显示成功自动插入时间

    • 方式二:代码级别

      1、删除数据库的默认值,更新操作!

      2、实体类字段属性上需要增加注解

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class MpUser {
          @TableId(type = IdType.ASSIGN_ID)
          Long userId;
          String username;
          String password;
          //插入时注解
          @TableField(fill = FieldFill.INSERT)
          Date createTime;
          //更新时注解
          @TableField(fill = FieldFill.UPDATE)
          Date updateTime;
      }
      

      注解详情

      package com.baomidou.mybatisplus.annotation;
      
      public enum FieldFill {
          
          DEFAULT,
          
          INSERT,
          
          UPDATE,
          
          INSERT_UPDATE
      }
      
      

      3、编写处理器来处理这个注解即可!

      @Slf4j
      @Component
      public class MymetaObjectHandler implements metaObjectHandler {
      
          @Override
          public void insertFill(metaObject metaObject) {
              log.info("start insert fill ....");
              this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
              // 或者
              this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
              // 或者
              this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
          }
      
          @Override
          public void updateFill(metaObject metaObject) {
              log.info("start update fill ....");
              this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
              // 或者
              this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
              // 或者
              this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
          }
      }
      

      注意事项:

      • 填充原理是直接给entity的属性设置值!!!
      • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
      • metaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充
      • 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
      • 填充处理器MymetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
      • 要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法
      • 不需要根据任何来区分可以使用父类的fillStrategy方法

      4.测试运行插入或更新语句

    2.2.9 乐观锁

    在面试过程中经常被问到乐观锁/悲观锁,这个其实很简单

    乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不上锁!如果出现了问题,再次更新值测试

    悲观锁:顾名思义十分悲观,他总是认为出现问题,无论干什么都会上锁!再去操作!

    当要更新一条记录的时候,希望这条记录没有被别人更新

    乐观锁实现方式:

    • 取出记录时,获取当前version

    • 更新时,带上这个version

    • 执行更新时,set version = newVersion where version = oldVersion

    • 如果version不对,就更新失败

      乐观锁:先查询,获得版本号
      -- A
      update user set name = "wsk",version = version+1 
      where id = 1 and version = 1
      -- B  (B线程抢先完成,此时version=2,会导致A线程修改失败!)
      update user set name = "wsk",version = version+1 
      where id = 1 and version = 1
      
    1. 更新数据库,添加version字段

      -- auto-generated definition
      create table mp_user
      (
          user_id     bigint auto_increment comment '用户id' primary key,
          username    varchar(32)                        not null comment '用户名',
          password    varchar(32)                        not null comment '密码',
          version     int                                null comment '乐观锁',
          create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
          update_time datetime default CURRENT_TIMESTAMP not null comment '更新时间'
      );
      
    2. 实体类添加加对应的字段和注解

       @Version
      Integer version;
      
    3. 注册组件

      //扫描mapper文件夹
      @MapperScan("com.mymall.dao")//交给mybatis做的,可以让这个配置类做扫描
      @EnableTransactionManagement//自动管理事务
      @Configuration//配置类
      public class MyBatisPlusConfig {
          //注册乐观锁插件
          @Bean
      	public MybatisPlusInterceptor mybatisPlusInterceptor() {
          	MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
          	interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
          	return interceptor;
      	}
      }
      
    4. 测试

      • 测试乐观锁成功

        @Test
         void testOptimisticLocker1() {
                //1、查询用户信息
                MpUser mpUser = mpUserMapper.selectById(1L);
                //2、修改用户信息
                mpUser.setUsername("qq");
                //3、执行更新操作
                int update = mpUserMapper.updateById(mpUser);
                System.out.println(mpUser);
            }
        
      • 模拟多线程测试乐观锁插入数据失败

         @Test
        void testOptimisticLocker2() {
            //线程1
            MpUser mpUser = mpUserMapper.selectById(1L);
            mpUser.setUsername("aa");
            mpUser.setPassword("666666");
            //模拟另外一个线程执行了插队操作
            MpUser mpUser1 = mpUserMapper.selectById(1L);
            mpUser1.setUsername("bb");
            mpUser1.setPassword("12344444");
            mpUserMapper.updateById(mpUser1);
            //自旋锁来多次尝试提交!
            mpUserMapper.updateById(mpUser);//如果没有乐观锁就会覆盖插队线程的值
        }
        
    2.2.10 查询
    • 通过id查询单个用户

      @Test
      public void testSelectById(){
          User user = userMapper.selectById(1L);
          System.out.println(user);
      }
      
    • 通过id查询多个用户

      @Test
      public void testSelectBatchIds(){
          List users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
          users.forEach(System.out::println);
      }
      
    • 条件查询 通过map封装

      @Test
      public void testMap(){
          HashMap map = new HashMap<>();
          //自定义要查询的
          map.put("name","www");
          map.put("age",18);
          List users = userMapper.selectByMap(map);
          users.forEach(System.out::println);
      }
      
    • 分页查询

      分页在网站的使用十分之多!

      1、原始的limit分页

      2、PageHelper第三方插件

      3、MybatisPlus其实也内置了分页插件!

      使用步骤:

      1. 配置拦截器组件

        package com.mymall.config;
        
        import com.baomidou.mybatisplus.annotation.DbType;
        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.mybatis.spring.annotation.MapperScan;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.transaction.annotation.EnableTransactionManagement;
        @Configuration
        @MapperScan("com.mymall.dao")
        @EnableTransactionManagement
        public class MybatisPlusConfig {
            @Bean
            public MybatisPlusInterceptor mybatisPlusInterceptor() {
                MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
                //添加乐观锁
                interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
                //新的分页插件,一缓和二缓遵循mybatis的规则,
                //需要设置 MybatisConfiguration#useDeprecatedExecutor = false
                // 避免缓存出现问题(该属性会在旧插件移除后一同移除)
           //也可以不传DbType.MYSQL,为空时会通过URL判断数据库类型     
                interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
                return interceptor;
            }
            @Bean
            public ConfigurationCustomizer configurationCustomizer() {
                return configuration -> configuration.setUseDeprecatedExecutor(false);
            }
        }
        
        
      2. 测试分页查询

        @Test
        public void testPage(){
            //参数一current:当前页   参数二size:页面大小
            //使用了分页插件之后,所有的分页操作都变得简单了
            Page page = new Page<>(2,5);
        	//第二个参数是高级查询,暂设为null  
            userMapper.selectPage(page,null);
            page.getRecords().forEach(System.out::println);
            System.out.println("总页数==>"+page.getTotal());
        }
        
    2.2.11 删除
    • 基本删除操作

      @Test
      public void testDeleteById(){
          userMapper.deleteById(1);
      }
      @Test
      public void testDeleteBatchIds(){
        userMapper.deleteBatchIds(Arrays.asList(1,2));
      }
      @Test
      public void testD(){
          HashMap map = new HashMap<>();
          map.put("age","18");
          map.put("name","lol");
          userMapper.deleteByMap(map);
      }
      
    • 逻辑删除

      物理删除:从数据库中直接删除

      逻辑删除:在数据库中没有被删除,而是通过一个变量来使他失效! deleted=0 ==> deleted=1

      管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!

      实现步骤:

      1. 数据库中添加deleted字段

         deleted     int      default 0                 null comment '逻辑删除:0显示1删除',
        
      2. 添加配置application.yml

        mybatis-plus:
          global-config:
            db-config:
              logic-delete-field: deleted  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
              logic-delete-value: 1 # 逻辑已删除值(默认为 1)
              logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
        
      3. 在实体类上添加deleted字段并加上@TableLogic注解(上面配置了application.yml后可以不添加注解,但是deleted字段一定要添加)

        @TableLogic
        private Integer deleted;
        
      4. 测试删除一条数据后查询是否数据还存在,测试结果,只是deleted设置为了1

        @Test
        void testLogicDelete() {
            //删除id为2的数据
          int i = mpUserMapper.deleteById(2);
          MpUser mpUser = mpUserMapper.selectById(2);
          System.out.println(mpUser);
        }
        

    查看数据库,发现记录还在,deleted变为1


    再次测试查询被删除的用户,发现查询为空

    2.2.12 性能分析插件(新版本已经移除)

    我们在平时的开发中,会遇到一些慢Sql。测试、druid···

    MybatisPlus也提供了性能分析插件,如果超过这个时间就停止运行!

    2.2.13 条件构造器wrapper

    警告:

    不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输

    1. wrapper 很重
    2. 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
    3. 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
    1. 常用api请到网页中查看条件构造器 | MyBatis-Plus (baomidou.com)
    2. 使用示例
      • 查询不为空且比某值大的数据

        	@Test
          void testWrapper() {
            //参数是一个wrapper ,条件构造器,和刚才的map对比学习!
            //查询username不为空,password不为空,user_id大于18的用户
            //注意这里的column参数一定是字段名,不是实体类中的成员变量
            QueryWrapper wrapper = new QueryWrapper<>();
            wrapper
                    .isNotNull("username")
                    .isNotNull("password")
                    .ge("user_id",2);
            List userList = mpUserMapper.selectList(wrapper);
            userList.forEach(System.out::println);
        }
        
      • 查询某字段值为确定值的数据,返回结果有一个和多个的可能

         @Test
        public void testWrapper2() {
            //查询name=wsk的用户
            QueryWrapper wrapper = new QueryWrapper<>();
            wrapper.eq("username","MPInsertTest1");
            //查询一个数据selectOne,若查询出多个会报错
            //Expected one result (or null) to be returned by selectOne(), but found: *
            //若出现多个结果使用list或map
            MpUser mpUser = mpUserMapper.selectOne(wrapper);//查询一个数据,若出现多个结果使用list或map
            System.out.println(mpUser);
            List mpUsers = mpUserMapper.selectList(wrapper);
        }
        
      • 查询字段值范围在某个区间的数据

        @Test
        public void testWrapper3() {
            //查询age在10-20之间的用户的用户数量
            QueryWrapper wrapper = new QueryWrapper<>();
            wrapper.between("age", 10, 20);//区间
            Integer count = userMapper.selectCount(wrapper);//输出查询的数量selectCount
            System.out.println(count);
        }
        
      • 模糊查询,notLike,likeRight,likeLeft

        @Test
        public void testWrapper4() {
            //模糊查询
            QueryWrapper wrapper = new QueryWrapper<>();
            wrapper
                .notLike("name","s")
                .likeRight("email","t");//qq%  左和右?
            List> maps = userMapper.selectMaps(wrapper);
            maps.forEach(System.out::println);
        }
        
      • 子查询

        @Test
        public void testWrapper5() {
            // SELECT id,name,age,email,version,deleted,create_time,update_time 
            //FROM user 
            //WHERe deleted=0 AND id IN 
            //(select id from user where id<5)
            QueryWrapper wrapper = new QueryWrapper<>();
            //id 在子查询中查出来
            wrapper.inSql("id","select id from user where id<5");
            List objects = userMapper.selectObjs(wrapper);
            objects.forEach(System.out::println);
        }
         
      • 通过id进行排序

        @Test
        public void testWrapper6() {
            QueryWrapper wrapper = new QueryWrapper<>();
            //通过id进行降序排序
            wrapper.orderByDesc("id");
            List userList = userMapper.selectList(wrapper);
            userList.forEach(System.out::println);
        }
        
      • 2.2.14 代码生成器,MP逆向工程
        • 添加依赖

          MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加生成器和模版引擎依赖

          这里尝试使用3.5.1版本(以下教程仅适用 3.5.1 以上版本,对历史版本的不兼容)

          // 注意!!当前包未传递依赖 mp 包,需要自己引入
          
              com.baomidou
              mybatis-plus-generator
              Latest Version
          
          

          添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。这里按照官方文档选择了freemarker

          Velocity(默认):
          
              org.apache.velocity
              velocity-engine-core
              latest-velocity-version
          
          
          Freemarker:
          
          
              org.freemarker
              freemarker
              2.3.31
          
          
          Beetl:
          
              com.ibeetl
              beetl
              latest-beetl-version
          
          
        • 在mapper子项目中创建代码生成器配置类

          package com.mymall;
          
          import com.baomidou.mybatisplus.generator.FastAutoGenerator;
          import com.baomidou.mybatisplus.generator.config.OutputFile;
          import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
          
          import java.util.Collections;
          
          
          public class CodeGenerator {
              public static void main(String[] args) {
                  
                  FastAutoGenerator.create("jdbc:mysql://localhost:3306/mymall?serverTimezone=GMT%2b8&useSSL=false&allowPublicKeyRetrieval=true",
                          "root", "123456")
                          .globalConfig(builder -> {
                              builder.author("QC") // 设置作者
                                      .enableSwagger() // 开启 swagger 模式
                                      .fileOverride() // 覆盖已生成文件
                                      //禁止打开目录
                                      .disableOpenDir()
                                      // 指定输出目录,这里使用绝对路径
                                      .outputDir("F:\JavaProjcet\MyMallPractice2\mapper\src\main\java");
                              //如果使用/src/main/java会在根目录下创建文件,也就是没成功
                              //源码:this.outputDir = System.getProperty("os.name").toLowerCase().contains("windows") ? "D://" : "/tmp";
                              //System.getProperty("os.name")获取操作系统名称,如果名称包含windows就"D://"否则就"/tmp"
                              //判断是windows系统,否则就是linux系统,所以还是只能使用绝对地址
                              //.outputDir("classpath:/src/main/java");
                          })
                          .packageConfig(builder -> {
                              builder.parent("com.mymall") // 设置父包名
                                      //.moduleName("mapper") // 设置父包模块名,也就是在父包名中添加一个包
                                      //但是这里设置相对路径成功了一次,但后面还是生成了根目录,所以还是使用绝对路径
                                      //.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "/src/main/resources/mapper")); // 设置mapperXml生成路径
                                      .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "F:\JavaProjcet\MyMallPractice2\mapper\src\main\resources\mapper")); // 设置mapperXml生成路径
                          })
                          .strategyConfig(builder -> {
                              builder.addInclude("users") // 设置需要生成的表名
                                      .addInclude("category")
                                      .addInclude("index_img")
                                      .addInclude("mp_user")
                                      .addInclude("product")
                                      .addInclude("product_sku")
                                      .addInclude("product_img")
                                      .addInclude("orders")
                                      .addInclude("product_comments")
                                      .addInclude("product_params")
                                      //.setEntityLombokModel(true)    //开启lombook支持,新版本已经没有了
                                      .addTablePrefix("mp_"); // 设置过滤表前缀,也就是将前缀去掉,只保留后面对的内容作为实体名
                          })
                          .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                          .execute();
              }
          }
          
          

          生成成功

        3 常见问题解决办法 3.1 数据库url中的SSL问题

        解决办法:将gerneratorConfig.xml中的url改成useSSL=false

        connectionURL="jdbc:mysql://localhost:3306/mymall?useSSL=false&serverTimezone=GMT%2b8"&useUnicode=true&characterEncoding=utf-8
        
        3.2 插件没找到

        解决:调整mybatis-generator-maven-plugin版本

                org.mybatis.generator
                        mybatis-generator-maven-plugin
                        1.3.5
        
        3.3 将数据库中的其他表都生成了

        解决:在gerneratorConfig.xml只添加需要生成的表名

    3.4 users表生成了其他表

    解决办法:

    1. 修改我的表名称

    2. 想办法不让MySql保留表影响到我的逆向工程

      要保证只生成自己指定的 database 中的 User 表,首先要在 中的 connectionURL 中指定数据库的实例名,然后在 中添加相关配置信息,即 ,即可保证只生成自己需要的 User 类。
      配置如下:

      
                  
              
      
    转载请注明:文章转载自 www.mshxw.com
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号