一篇搞定MyBatisPlus!

2年前 (2022) 程序员胖胖胖虎阿
279 0 0

前言:该文章是以狂神说MyBatisPlus为参考,用以复习!初学者同样可以在这篇文章学到东西!
B站:狂神说MyBatisPlus地址:https://www.bilibili.com/video/BV17E411N7KN
狂神牛逼,强烈安利狂神!

配置环境

导入依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!--MyBatisPlus依赖-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>Latest Version</version>
    </dependency>
    
    <!--mysql驱动依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
    
    <!--lombok依赖-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
  1. 配置数据库等

    yaml:

#数据库配置
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
#日志配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

properties:

# 数据库配置
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8

# 日志配置 (默认控制台输出)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

★CRUD扩展

1.插入数据

@Test
public void testInsert(){
    User user = new User();
    user.setName("codeyuaiiao");
    user.setAge(3);
    user.setEmail("747557612@qq.com");
    int result = userMapper.insert(user);
    System.out.println(result);
    System.out.println(user);

}

注意:插入的Id要是全局唯一Id

2.主键自增策略

默认 ID_WORKER 全局唯一id

对应数据库中的主键(uuid.自增id.雪花算法.redis.zookeeper)

★雪花算法(Twitter的snowflake算法)

snowflake是Twitter开源的分布式ID生成算法结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0.可以保证几乎全球唯一

★主键自增

我们需要配置主键自增:

  1. 实体类字段上@TableId(type = IdType.AUTO)

一篇搞定MyBatisPlus!

  1. 数据库字段一定要是自增! 数据库和实体类一定要对应!!!

一篇搞定MyBatisPlus!

3.其余的自增方式解读:

public enum IdType {
     
    AUTO(0),//数据库ID自增  
    NONE(1),//该类型为未设置主键类型      
    INPUT(2),//用户输入ID
      		 //该类型可以通过自己注册自动填充插件进行填充  

//以下3种类型、只有当插入对象ID 为空,才自动填充。     
    ID_WORKER(3),//全局唯一ID (idWorker)      
    UUID(4),//全局唯一ID (UUID)          
    ID_WORKER_STR(5);//字符串全局唯一ID (idWorker 的字符串表示)    

2.更新数据

一篇搞定MyBatisPlus!

注意这里的update的参数都是一个实体类,并不是Id!

@Test
public void testUpdate(){
    User user = new User();
    user.setId(2L);
    user.setEmail("32134@qq.com");
    user.setName("CJ");
    int update = userMapper.updateById(user);
    System.out.println(update);
}

一篇搞定MyBatisPlus!

传入的是user,但发现它确实是按照Id来更新数据的!

★3.自动填充

创建时间 . 修改时间! 这些个操作都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据库表:gmt_create .gmt_modified几乎所有的表都要配置上!而且需要自动化!

方式一:数据库级别

不推荐使用!因为要修改数据库!

1.在表中新增字段 create_time , update_time

一篇搞定MyBatisPlus!

一篇搞定MyBatisPlus!

2.实体类要对应上

一篇搞定MyBatisPlus!

重新新增,时间出来了!

方式二:代码级别

1.删除数据库默认值
一篇搞定MyBatisPlus!

2.实体类增加注解

@TableField(fill = FieldFill.INSERT)   //在插入时自动执行填充创建时间
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

3.处理器处理注解

@Slf4j    //打印一下日志  
@Component      //注入到IOC容器中,别忘了!!!
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill...");
//        default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill...");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

新增测试,成功!

★4.乐观锁

乐观锁就是什么时候都认为不会出问题,干啥都不加锁,等出了问题再次更新值测试

悲观锁就是认为啥时候都会出问题,无论干啥都会上锁,然后操作

MP乐观锁实现

如下:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

1.添加version字段
一篇搞定MyBatisPlus!

2.同步实体类

//乐观锁注解
@Version
private Integer version;

3.注册组件 创建config包

// 这个扫描本来是在我们MybatisPlusApplication 主启动类中,现在我们把它放在配置类中
// 扫描mapper文件夹
@MapperScan("com.chen.mapper")
@EnableTransactionManagement  // 自动开启事务管理
@Configuration // 配置类
public class MybatisPlusConfig {

    //新版乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

4.测试一下单线程中的乐观锁是否生效

// 测试乐观锁单线程成功
@Test
public void testOptimisticLock(){
    // 1、查询用户信息
    User user = userMapper.selectById(1);
    // 2、修改用户信息
    user.setName("xiaoshaung");
    user.setEmail("123123132@qq.com");
    // 3、执行更新操作
    userMapper.updateById(user);
}

看一下SQL语句:

一篇搞定MyBatisPlus!

确实添加有version这个条件!

5.测试一下多线程的乐观锁

 /*
    线程1 虽然执行了赋值语句,但是还没进行更新操作,线程2就插队了抢先更新了,
    由于并发下,可能导致线程1执行不成功
    如果没有乐观锁就会覆盖线程2的值
 */
 // 测试乐观锁多线程失败
@Test
public void testOptimisticLock2(){
    // 线程1
    User user = userMapper.selectById(1);
    user.setName("cangjian111");
    user.setEmail("32143455@qq.com");

    // 模拟另外一个线程执行了插队操作
    // 线程2
    User user2 = userMapper.selectById(1);
    user2.setName("cagnjian222");
    user2.setEmail("32143455@qq.com");
    userMapper.updateById(user2);

    // 自旋锁来多次尝试提交
    userMapper.updateById(user);
}

一篇搞定MyBatisPlus!

111被吞掉!

5.查询数据

测试查询有如下方法:

一篇搞定MyBatisPlus!

挨个测试一把!

//test select
@Test
public void testSelect(){
    User user = userMapper.selectById(2L);
    System.out.println(user);
}

//测试批量查询
@Test
public void testSelectBatchIds(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3,4));
    users.forEach(System.out::println);
}


//测试按条件查询
@Test
public void testSelectMap(){
    Map<String, Object> map = new HashMap<>();
    map.put("name","cangbaobao");
    map.put("age",6);
    //List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

一篇搞定MyBatisPlus!

★6.逻辑删除

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

逻辑删除:实际上没有真正删除数据,数据还存在!只是用了一个字段来区分,在每次查询的时候都加上这个字段的where判断,就能达到一个逻辑删除的效果! 回收站就是用了逻辑删除实现的。

逻辑删除的步骤:

1,增加数据库字段!

一篇搞定MyBatisPlus!

2.实体类同步!

// 逻辑删除注解
@TableLogic
private Integer deleteId;

3.配置逻辑是删除组件!

// 逻辑删除组件   高版本无序写这个配置   已经自动集成!
@Bean
public ISqlInjector iSqlInjector(){
    return new LogicSqlInjector();
}

4.测试一下删除,发现有如下方法

一篇搞定MyBatisPlus!

这里直接用deleteById测试:

//test delete
@Test
public void testDelete(){
    userMapper.deleteById(6L);
}

发现:它竟然走的是update操作!

一篇搞定MyBatisPlus!

一篇搞定MyBatisPlus!

之后正常的查询将无法查询到它,这就是逻辑查询!!!

7.分页查询

Mybatis-Plus也内置了分页插件!

1.集成分页插件:

// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
    return interceptor;
}

2.可以用了:

    @Test
    public void testPage(){
//        <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        Page<User> page = new Page<>(1,5);
        userMapper.selectPage(page, null);

        page.getRecords().forEach(System.out::println);
        System.out.println("getCurrent()"+page.getCurrent());
        System.out.println("page.getSize()"+page.getSize());
        System.out.println("page.getTotal()"+page.getTotal());

    }

3.一篇搞定MyBatisPlus!

实际上调用的也是limit!

性能分析插件

★★条件构造器★★

一些重要的方法:

eq :

  • 等于 =
  • 例: eq("name", "老王")—>name = '老王'

ne :

  • 不等于 <>
  • 例: ne("name", "老王")—>name <> '老王'

gt :

  • 大于 >
  • 例: gt("age", 18)—>age > 18

ge :

  • 大于等于 >=
  • 例: ge("age", 18)—>age >= 18

lt :

  • 小于 <
  • 例: lt("age", 18)—>age < 18

le :

  • 小于等于 <=
  • 例: le("age", 18)—>age <= 18

between :

  • BETWEEN 值1 AND 值2
  • 例: between("age", 18, 30)—>age between 18 and 30

not between :

  • NOT BETWEEN 值1 AND 值2
  • 例: notBetween("age", 18, 30)—>age not between 18 and 30

like :

  • LIKE ‘%值%’
  • 例: like("name", "王")—>name like '%王%'

notLike :

  • NOT LIKE ‘%值%’
  • 例: notLike("name", "王")—>name not like '%王%'

likeLeft :

  • LIKE ‘%值’
  • 例: likeLeft("name", "王")—>name like '%王'

likeRight :

  • LIKE ‘值%’
  • 例: likeRight("name", "王")—>name like '王%'

isNull :

  • 字段 IS NULL
  • 例: isNull("name")—>name is null

isNotNull :

  • 字段 IS NOT NULL
  • 例: isNotNull("name")—>name is not null

还有好多,可以在官网上找到,写得都很详细!!!

一些测试:

@Test
public void test1(){
    // 查询 name 不为空的用户,并且邮箱不为空的用户,年龄大于等于20
    //控制台查询的语句:SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name IS NOT NULL AND email IS NOT NULL AND age >= ?)
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.isNotNull("name")
                .isNotNull("email")
                .ge("age",20);
    userMapper.selectList(queryWrapper).forEach(System.out::println);
}
    @Test
    public void test2(){
        // 查询名字 CJ
//生成的SQL:SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name = ?)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name","CJ");
        User user = userMapper.selectOne(queryWrapper);
        System.out.println(user);
    }
    @Test
    public void test3(){
        //查询年龄在21到30的
//        sql : SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (age BETWEEN ? AND ?)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("age",21,30);
        userMapper.selectList(queryWrapper).forEach(System.out::println);
    }
    @Test
    public void test4(){
        //模糊查询  查询名字中含有i的和email以3开头的
//        sql :SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name LIKE %i% AND email LIKE 3%)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","i")
                    .likeRight("email","3");
        userMapper.selectMaps(queryWrapper).forEach(System.out::println);
    }
// userMapper.selectList(queryWrapper).forEach(System.out::println);  selectList和selectMap的区别就是返回值形式的不同,一个是按照user表中属性顺序来,一个是安照map形式显示

    @Test
    public void test5(){
        //连接查询(内查询)  查找id<6的
//        sql :SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (id IN (select id from user where id < 6))
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.inSql("id","select id from user where id < 6");
        userMapper.selectList(queryWrapper).forEach(System.out::println);
    }
@Test
public void test6(){
    //根据id倒叙排列
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderByDesc("id");
    userMapper.selectList(queryWrapper).forEach(System.out::println);
}

代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

有两个版本:3.5.1以下和以上

我用的MP是3.4.2的,所以采用历史版本:

1.导入依赖:

        <!--MP代码生成器相关-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>
        <!---->

2.编写一个自动生成类:注意包不要导错了 基本都是generator下的包

// 代码自动生成器
public class AutoGeneratorTest {
    public static void main(String[] args) {
        // 需要构建一个 代码自动生成器 对象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略

        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("CJ");
        gc.setOpen(false);
        gc.setFileOverride(false);  // 是否覆盖
        gc.setServiceName("%Service"); // 服务接口,去Service的I前缀
        gc.setIdType(IdType.ID_WORKER); // 主键生成策略
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);

        // 给代码自动生成器注入配置
        mpg.setGlobalConfig(gc);

        // 2、 设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 3、包的配置

        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.chen");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");

        mpg.setPackageInfo(pc);

        // 4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user"); // 设置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);  // 内置下划线转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);  // 自动Lombok

        strategy.setLogicDeleteFieldName("deleted");  // 逻辑删除字段

        // 自动填充策略
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT);

        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);

        // 乐观锁
        strategy.setVersionFieldName("version");

        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2

        mpg.setStrategy(strategy);

        // 执行
        mpg.execute();
    }
}

3.执行!
一篇搞定MyBatisPlus!

大功告成!!!

MP完结!!!!

版权声明:程序员胖胖胖虎阿 发表于 2022年9月13日 上午10:40。
转载请注明:一篇搞定MyBatisPlus! | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...