MyBatis-Plus(概述、快速入门、日志配置、主键生成策略、自动填充、CRUD、性能分析插件、条件构造器、代码生成器)

安稳与你 提交于 2020-05-07 11:57:51

MyBatis-Plus

在这里插入图片描述

(一)概述

  1. MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生;是 MyBatis 最好的搭档,就像魂斗罗中的 1P、2P,基友搭配,效率翻倍!
    在这里插入图片描述
  2. 官网地址:https://mp.baomidou.com/
  3. 特性:
    无侵入、损耗小、强大的 CRUD 操作、支持 Lambda 形式调用、支持主键自动生成、支持 ActiveRecord 模式、支持自定义全局通用操作、内置代码生成器、内置分页插件、分页插件支持多种数据库、内置性能分析插件、内置全局拦截插件。

(二)快速入门

1. 创建数据库和表

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
	age INT(11) NULL DEFAULT NULL COMMENT '年龄',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY (id)
);
-- 插入数据
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

2. 创建SpringBoot项目导入项目依赖

<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>
    <!-- mysql -->
    <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
   <!-- mybatis-plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
</dependencies>

3. 配置 application.properties

注意 mysql 5 和 mysql 8 的配置区别:mysql 8 需要时区配置 serverTimezone

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/数据库名?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

4. 主体测试代码

  1. pojo层
@Data
public class User {

    private Long id;

    private String name;

    private Integer age;

    private String email;

}
  1. mapper层
@Repository
public interface UserMapper extends BaseMapper<User> {

}

到这里基本的CRUD代码已经编写完成!!

  1. 启动类
@SpringBootApplication
@MapperScan("com.chen.mapper")
public class MybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }

}
  1. 测试
@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
}
  1. 结果
    在这里插入图片描述

MyBatis-Plus帮我们写了sql、还有一些方法!!

(三)日志输出

1. 配置

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2. 结果

在这里插入图片描述

(四)基本操作测试

1. 数据插入测试&雪花算法

  • 插入数据:
@Test
public void testInsert(){
   User user = new User();
   user.setName("zhangsan");
   user.setAge(10);
   user.setEmail("123456@qq.com");
   int result = userMapper.insert(user);
   System.out.println(result);
   System.out.println(user);
}
  • 结果:我们并没有定义id,却生成了一长串数字!
    在这里插入图片描述
  • 为什么会自动生成id?
    原因:MyBatis-Plus 会采用主键生成策略,这里采用了 Twitter 的 snowflake 算法(雪花算法

雪花算法:结果是一个 long 型的ID,其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
具体实现的代码可以参看:https://github.com/twitter/snowflake

2. 主键生成策略

2.1 主键自增
  • 在主键上增加注解
@Data
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    
}
  • 保证数据库的主键为自增
    在这里插入图片描述
    我么可以发现自动增量为: 1256210312843653122
    在这里插入图片描述


  • 测试
@Test
public void testInsert(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(10);
    user.setEmail("123456@qq.com");
    int result = userMapper.insert(user);
    System.out.println(result);
    System.out.println(user);
}
  • 结果
    在这里插入图片描述
2.2 注解TableId

我们进入 TableId 源码查看:发现IdType

public @interface TableId {
    String value() default "";

    IdType type() default IdType.NONE;
}

进入 IdType源码:

public enum IdType {
    AUTO(0),              // 主键自增
    NONE(1),			  // 未设置主键
    INPUT(2),			  // 手动输入:必须自己手动设置id,不然id=null
    ID_WORKER(3),         // 默认的全局唯一id
    UUID(4),              // uuid 全局唯一
    ID_WORKER_STR(5);     // ID_WORKER 字符串表示
}

3. 更新数据操作

将 id 属性的注解设置为 @TableId(type = IdType.INPUT),在插入一条数据用来更改。

// 插入数据
@Test
public void testInsert2(){
    User user = new User();
    user.setId(6L);
    user.setName("zhangsan");
    user.setAge(20);
    user.setEmail("123456@qq.com");
    int result = userMapper.insert(user);
}

在这里插入图片描述
更新数据:

@Test
public void updateTest(){
    User user = new User();
    user.setId(6L);
    user.setName("lisi");
    int result = userMapper.updateById(user);
}

在这里插入图片描述
发现:updateById方法需要的参数类型是一个对象;并且自动拼接了动态sql:
在这里插入图片描述

4. 自动填充

我们新增两个属性:create_timeupdate-time,但不设置默认值和更新属性!
在这里插入图片描述

在pojo层:标记为填充字段

@TableField(fill = FieldFill.INSERT)
private Date createTime;

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

FieldFill 属性源码:

public enum FieldFill {
    DEFAULT,        // 默认不处理
    INSERT,         // 插入填充字段
    UPDATE,         // 更新填充字段
    INSERT_UPDATE;  // 插入和更新填充字段
}

新建一个 Handler 包,创建实现类:填充处理器MyHandler在 Spring Boot 中需要声明@Component

@Slf4j
@Component
public class MyHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        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);
    }
}

测试:

// 插入操作
@Test
public void testInsert(){
    User user = new User();
    user.setName("test1");
    user.setAge(10);
    user.setEmail("123456@qq.com");
    int result = userMapper.insert(user);
    System.out.println(result);
    System.out.println(user);
}
// 更新操作
@Test
public void updateTest(){
    User user = new User();
    user.setId(6L);
    user.setName("lisi");
    int result = userMapper.updateById(user);
}

结果:mybatis-plus 会自动帮我们填充!
在这里插入图片描述

5. 查询操作

  1. 查询测试:查询 id=1 的user
@Test
public void selectTest1(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}

结果:
在这里插入图片描述

  1. 批量查询:插叙 id = 1、2、3 的user
@Test
public void selectTest2(){
    List<User> users= userMapper.selectBatchIds(Arrays.asList(1,2,3));
    users.forEach(System.out::println);
}

结果:
在这里插入图片描述

  1. 条件查询:查询 name=zhangsan AND age=10 的user
@Test
public void selectTest3(){
    HashMap<String, Object> map = new HashMap<>();
    // 通过map自定义条件
    map.put("name","zhangsan");
    map.put("age",10);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

结果:
在这里插入图片描述

6. 分页查询

  1. 配置分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}
  1. 测试:每页三条数据查询
@Test
public void testPage(){
    // 参数:当前页,页数大小
    Page<User> page = new Page<>(1,3);
    userMapper.selectPage(page,null);
    page.getRecords().forEach(System.out::println);
}
  1. 结果:sql语句还是使用了 Limit
    在这里插入图片描述

7. 删除操作

@Test
public void deleteTest(){
    // 通过id删除:删除id=1的user
    userMapper.deleteById(1L);
    // 批量删除:删除1、2、3号user
    userMapper.deleteBatchIds(Arrays.asList(1,2,3));
    // 条件删除:删除name=zhangsan的对象
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","zhangsan");
    userMapper.deleteByMap(map);
}

8. 逻辑删除

  1. 物理删除:从数据库中直接移除数据;
    逻辑删除:数据库中并没有移除数据,而是通过一个变量使数据失效(deleted=0、deleted=1);管理员可以查看删除记录,类似于回收站!
  2. 测试
  • 在表新增 deleted 字段,默认值为0;
    在这里插入图片描述

  • 在实体类中新增属性:

@TableLogic 
private Integer deleted;
  • 配置类
// 逻辑删除
@Bean
public ISqlInjector iSqlInjector(){
    return new LogicSqlInjector();
}
  • 配置
# 逻辑已删除值(默认为1)
mybatis-plus.global-config.db-config.logic-delete-value=1 
mybatis-plus.global-config.db-config.logic-not-delete-value=0
  • 测试:删除1号user
@Test
public void logicalDeleteTest() {
    userMapper.deleteById(1L);
}

结果:数据库1号user还存在,sql语句是update,将deleted字段更新为1
在这里插入图片描述
在这里插入图片描述
我们再来查询一下1号user:


@Test
public void selectTest1(){
   User user = userMapper.selectById(1L);
   System.out.println(user);
}

结果:已经查询不到1号user!
在这里插入图片描述

9. 乐观锁操作

  1. 适用场景:更新的数据是最新的,即这条数据没有被别人更新。
  2. 实现方式:
  • 取出记录时,获取当前 version;
  • 更新时,带上这个 version;
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果 version 不对,就更新失败。
  1. 测试:
  • 在表中添加 version 字段,默认值为1;
  • 实体类:
@Version
private Integer version;
  • 新增一个配置类:
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
	// 注册乐观锁插件配置
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
    
}
  • 测试乐观锁:

测试前的表:
在这里插入图片描述
乐观锁成功:执行一次updateById

@Test
public void OptimisticLockerTest1(){
    User user = userMapper.selectById(1L);
    user.setName("aaa");
    userMapper.updateById(user);
}

结果:数据更新成功,version 由 1 更新为 2
在这里插入图片描述
乐观锁失败:模拟两条线程去更新同一条数据

@Test
public void OptimisticLockerTest2(){
    // 线程 1
    User user1 = userMapper.selectById(1L);
    user1.setName("bbb");
    // 线程 2
    User user2 = userMapper.selectById(1L);
    user2.setName("ccc");
    userMapper.updateById(user2);
    userMapper.updateById(user1);
}

结果:由于乐观锁的存在,数据更新为user2,user1更新失败,version更新为 3 ,如果没有乐观锁,会覆盖user2的值!

在这里插入图片描述

(五)性能分析插件

用于输出每条 SQL 语句及其执行时间,比如超过某个时间就会停止操作!使用此插件可以帮助我们提升效率!

1. 配置

@Bean
// 设置 dev test 环境开启
@Profile({"dev","test"})
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100);   // 设置sql语句执行的最大时间,超过这个时间就不执行
    performanceInterceptor.setFormat(true);   // 设置格式化
    return performanceInterceptor;
}

在application.properties中,设置开发环境:

spring.profiles.active=dev

2. 测试

查询user结果:

@Test
void contextLoads() {
    List<User> list = userMapper.selectList(null);
    list.forEach(System.out::println);
}

结果:
在这里插入图片描述

(六)条件构造器

用来操作一些复杂查询的构造器!

1. isNotNull、ge、gt、lt、le

isNotNull:不为空
ge:大于等于
gt:大于
lt:小于
le:小于等于



如查询user,条件:name不为空 AND email不为空 AND age大于等于20

@Test
void test1() {
 QueryWrapper<User> wrapper = new QueryWrapper<>();
 wrapper
         .isNotNull("name")
         .isNotNull("email")
         .ge("age",20);
 List<User> list = userMapper.selectList(wrapper);
 list.forEach(System.out::println);
}

结果:
在这里插入图片描述

2. eq

eq:等于
如:查询user姓名为 Jack 的用户

@Test
void test2(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name","Jack");
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}

结果:
在这里插入图片描述

3. between

between:值1 AND 值2
如:查询20~30岁之间的用户数量

@Test
void test3(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",20,30);
    Integer count = userMapper.selectCount(wrapper);
    System.out.println(count);
}

结果:
在这里插入图片描述

4. 模糊查询

  • like
    查询name中有王字的name:like("name", "王")—>name like '%王%'
  • notLike
    查询name中没有王字的name:notLike("name", "王")—>name not like '%王%'
  • likeLeft
    从name的左边开始匹配含有王字的name,likeLeft("name", "王")—>name like '%王'
  • likeRight
    从name的右边开始匹配含有王字的name,likeRight("name", "王")—>name like '王%'

5. inSql

子查询,字段 IN ( sql语句 )

@Test
void test4(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id","select id from user where id<3");
    List<Object> list = userMapper.selectObjs(wrapper);
    list.forEach(System.out::println);
}

结果:
在这里插入图片描述

6. orderByAsc、orderByDesc

orderByAsc:降序查询
orderByDesc:升序查询

@Test
void test5(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByAsc("id");
    List<Object> list = userMapper.selectObjs(wrapper);
    list.forEach(System.out::println);
}

结果:
在这里插入图片描述

(七)代码生成器

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

1. 新建一个SpringBoot项目

我们并没有创建entity、mapper、service…包。
在这里插入图片描述

2. 编写代码生成器

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;

public class CodeGenerator {
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        /**
         * 全局配置
         */
        GlobalConfig gc = new GlobalConfig();
        // 获得实体类
        String projectPath = System.getProperty("user.dir");
        // 设置输出路径
        gc.setOutputDir(projectPath + "/src/main/java");
        // 设置作者
        gc.setAuthor("Mr.Chen");
        // 是否覆盖
        gc.setOpen(false);
        // 去除Service的I前缀
        gc.setServiceName("%sService");
        // 设置id的类型
        gc.setIdType(IdType.ID_WORKER);
        // 设置时间类型
        gc.setDateType(DateType.ONLY_DATE);
        // 设置Swagger文档生成工具:当然需要导入Swagger的依赖
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        /**
         * 数据源配置
         */
        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("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        /**
         * 包配置
         * */
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("study");
        pc.setParent("com.chen");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setController("controller");
        pc.setService("service");
        mpg.setPackageInfo(pc);

        /**
         *  策略配置
         * */
        StrategyConfig strategy = new StrategyConfig();
        // 设置映射的表
        strategy.setInclude("user");
        // 下划线转驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 开启lombok
        strategy.setEntityLombokModel(true);
        strategy.setLogicDeleteFieldName("deleted");
        // 自动填充配置
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT);
        ArrayList<TableFill> list = new ArrayList<>();
        list.add(createTime);
        list.add(updateTime);
        strategy.setTableFillList(list);
        // 乐观锁配置
        strategy.setVersionFieldName("version");
        // controller层配置
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);// 跳转地址变为下划线连接格式
        mpg.setStrategy(strategy);
        mpg.execute();
    }
}

3. 测试结果

包和代码全部自动生成!!后面要生成其他对应的代码,只需要改数据库名即可!!
在这里插入图片描述
我们查看实体类:这全是mybatis-plus的代码生成器帮我们实现的!!!

import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *  用户类
 * </p>
 *
 * @author Mr.Chen
 * @since 2020-05-05
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="User对象", description="")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

        @ApiModelProperty(value = "主键ID")
        @TableId(value = "id", type = IdType.AUTO)
    private Long id;

        @ApiModelProperty(value = "姓名")
        private String name;

        @ApiModelProperty(value = "年龄")
        private Integer age;

        @ApiModelProperty(value = "邮箱")
        private String email;

        @ApiModelProperty(value = "创建时间")
        @TableField(fill = FieldFill.INSERT)
    private Date createTime;

        @ApiModelProperty(value = "更新时间")
        @TableField(fill = FieldFill.INSERT)
    private Date updateTime;

        @ApiModelProperty(value = "乐观锁")
        @Version
    private Integer version;

    @TableLogic
    private Integer deleted;
    
}

//下篇再见…谢谢
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!