欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MybatisPlus入门学习

程序员文章站 2022-04-07 18:22:26
基于狂神说Java-MybatisPlusMybatisPlus快速入门学习MybatisPlus,前提需要熟悉以下内容Spring、SpringMVC、Mybatis为什么要学习它?它可以实现简化开发,CRUD代码实现全自动,解放双手辣~~~MybatisPlus,只做增强,不做改变,只需要简单的配置就可以实现功能,如丝般顺滑MybatisPlus是什么我们知道Mybatis是一个封装了JDBC的半自动CRUD框架,而MybatisPlus在Mybatis的基础上又做了一个加强,实现半...

基于狂神说Java-MybatisPlus

MybatisPlus快速入门

学习MybatisPlus,前提需要熟悉以下内容

Spring、SpringMVC、Mybatis

为什么要学习它?它可以实现简化开发,CRUD代码实现全自动,解放双手辣~~~

MybatisPlus,只做增强,不做改变,只需要简单的配置就可以实现功能,如丝般顺滑

MybatisPlus入门学习

MybatisPlus是什么

我们知道Mybatis是一个封装了JDBC的半自动CRUD框架,而MybatisPlus在Mybatis的基础上又做了一个加强,实现半自动到全自动的转变

能干嘛?

MybatisPlus入门学习

在哪下?

MybatisPlus官网:https://baomidou.com

项目地址(源码)

GitHub:https://github.com/baomidou/mybatis-plus

Gitee:https://gitee.com/baomidou/mybatis-plus

怎么玩?

MybatisPlus的快速开始:https://baomidou.com/guide/quick-start.html#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A5%E7%A8%8B

使用第三方组件:

1、导入依赖

2、依赖如何配置

3、代码怎么写

4、提高技术扩展能力

首先创建数据库,名称为mybatis_plus

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)
);

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');

项目构建,使用SpringBoot的初始化向导进行快速构建

在pom文件中引入依赖

<!--数据库驱动-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.47</version>
</dependency>
<!--lombok-->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>
<!--MybatisPlus-->
<!--MybatisPlus启动器是属于baomidou公司本身自己开发,而不是SpringBoot家族-->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.0.5</version>
</dependency>

数据库配置

# MySQL 8 数据库需要在URL下增加一些规则
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?useSSL=false&useUnicode=true\
  &CharacterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

原生MVC架构:pojo-dao(连接Mybatis、mapper.xml配置文件)-Service-Controller

使用了MybatisPlus之后

pojo

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

mapper

// 在对应的mapper上面实现基本的接口
@Repository // 声明当前接口是一个持久层接口
public interface UserMapper extends BaseMapper<User> {
    // 和JPA一样,只要实现或者继承父类(接口),那么基本的单表的CRUD已经编写完成
}

SpringBoot主配置类

// 扫描mapper包下的持久层接口
@MapperScan("com.hrc.mapper")

测试

// 继承BaseMapper,所有的方法都来自父类,也可以编写属于自己的API
@Autowired
private UserMapper userMapper;

@Test
void contextLoads() {
  // API:selectList(@Param Wrapper wrapper);括号传递一个参数构造器
  // 如果传递为null,那么就表示没有条件,查询全部数据
  List<User> list = userMapper.selectList(null);
  list.forEach(System.out::println);
}

MybatisPlus配置日志

对于现在的sql来说,是不可见的,我们希望知道它是怎么执行的,而它执行的过程都会在日志中进行详细的记录,所以MybatisPlus的日志配置也成为了必需

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

MybatisPlus入门学习

CRUD扩展

数据添加

@Test
public void testIns() {
  User user = new User(null, "YouTuBe", 22, "test6@baomidou.com");
  int res = userMapper.insert(user);
  System.out.println(res);
  System.out.println(user);
}

请注意,上面的代码,id传值为空,但是在控制台输出时,MybatisPlus自动将id进行回填了

MybatisPlus入门学习

这边就需要多聊一嘴东西,这边数据库插入id的默认值是,全局的唯一id

这边就需要聊一聊主键生成策略

id主键生成策略

MybatisPlus的默认主键生成策略

@TableId(type = IdType.ID_WORKER)

给大家推荐一篇博客

这边主要要聊的是一个东西叫做雪花算法

雪花算法是Twitter使用zookeeper实现的一种全局唯一id的服务,英文名叫snowflake,它生成的id的结构如下

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

  • 第一位,0,表示未使用
  • 接下来的41位,它表示毫秒级时间戳,它的使用时间可以长达69年
  • 在接下来的5位,数据中心id
  • 还有5位,机器id
  • 最后12位,毫秒内的计数,可以产生4096个id

这些数位加起来刚好64位,正好对应一个64位的Long类型

既然它使用的是MybatisPlus默认的,现在需要使用MySQL数据库支持的主键自增,那么,就需要将数据库中的表的主键id设置为主键自增

MybatisPlus入门学习
接着将User类中的id字段设置为主键自增策略

@TableId(type = IdType.AUTO)

MybatisPlus入门学习

当然,不止这两个主键生成策略,其余的在源码中也解释到了,而且还是中文注释(题外话,MybatisPlus的作者是中国人[doge])

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

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

    private int key;

    IdType(int key) {
        this.key = key;
    }
}

数据更新

@Test
public void testUpd() {
    User user = new User();
    // 根据条件实现动态拼接
    user.setId(1330100039132925955L);
    user.setAge(19);
    // updateById,虽然是根据id进行修改,但是参数是一个对象
    int res = userMapper.updateById(user);
    System.out.println(res);
}

并且MybatisPlus会根据你设置了多少属性来实现动态SQL

MybatisPlus入门学习

自动填充

在数据库中,数据的创建时间,数据的修改时间,一般在语句执行之后显示,但是我们不能手动添加这些东西,这需要时间

在阿里巴巴开发手册中表示,所有的数据库表创建时间和修改时间一定要有,也就是gmt_create和gmt_modified,这两个字段,需要实现自动化

数据库增加字段

MybatisPlus入门学习
按照上面的进行同步设置就可以辣~~

字段增加完毕就可以在代码当中进行同步

private Date createTime;
private Date updateTime;

Java代码自动填充

在对应的创建时间和修改时间添加注解

// 字段的自动填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

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

并且还需要编写对应的处理器

// 在进行数据插入时的填充策略
@Component // 需要将组件加载到IOC容器中
@Slf4j
public class MyMetaObjectMapperHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("正在读取当前插入时间...");
        // 这边的API,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("正在读取当前修改时间...");
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

乐观锁

先聊一聊乐观锁悲观锁的概念吧

乐观锁,很乐观,觉得任何操作都不会出现问题,所以不会加锁,不过在数据库设计中要使用到乐观锁的话,一般都会添加一个字段叫做version,版本号,在进行数据更新的时候,需要去返回比对一下版本号,版本号不相同,它就不让你修改数据,需要重新设置值进行测试

悲观锁,很悲观,觉得不管是什么操作它都会出现问题,只要有一个线程进来,立刻加锁,不允许其他线程访问,在高并发使用悲观锁将是一个非常致命的选择

这边主要是聊聊乐观锁在MybatisPlus中的使用

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
-- 对于乐观锁来说,首先需要查询获得版本号
-- 在更新时,比对版本号
update user set name = "xxx" , version = version + 1
where id = 3 and version = 1

-- 如果这个时候有一个线程在上一个线程抢先完成,那么,这个时候,version = 2,就无法使用
update user set name = "abc", version = version + 1
where id = 1 and version = 1

在数据库中添加一个version字段

MybatisPlus入门学习

在实体类中添加一个属性

@Version // 添加一个版本号,实现乐观锁
private Integer version;

添加一个配置类

@MapperScan("com.hrc.mybatisplus.mapper") // 本来这个东西是可以放在SpringBoot的主启动类上来使用,不过现在有了配置类,可以交给MybatisPlus自己监管
@Configuration // 声明此类为配置类,加载到容器中使用
@EnableTransactionManagement // 此注解声明交给MybatisPlus自动管理事务
public class MybatisPlusConfig {

    // 注册乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

测试

// 测试一个成功的乐观锁
    @Test
    public void testOpt() {
        User user = userMapper.selectById(1L);
        user.setName("Jackie");
        user.setEmail("Mylove@baomidou.com");
        System.out.println(userMapper.updateById(user));
    }

    // 测试乐观锁失败
    @Test
    public void testOptFail() {
        // 在这边模拟两个线程进行插队操作
        // 线程1正常执行
        User user = userMapper.selectById(2L);
        user.setAge(22);
        user.setName("Jose");

        // 线程2进行插队操作,在线程1之前抢先更新
        User user2 = userMapper.selectById(2L);
        user2.setAge(21);
        user2.setName("Marting");
        userMapper.updateById(user2);
        
        userMapper.updateById(user); // 在它的上方,第二个线程最先执行完毕,所以这一段代码不会执行
      	// 如果这个地方执行失败,就是使用自旋锁来进行重复提交,直到成功为止
    }

数据查询

@Test
public void testSel() {
    // User user = userMapper.selectById(5L);
    List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4, 5));// 批量查询
    userList.forEach(System.out::println);
}

@Test // Map条件查询
public void testSel2() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "Tom");
    List<User> userList = userMapper.selectByMap(map);
    userList.forEach(System.out::println);
}

分页查询

分页对于一些网站来说,使用的还是比较多,比较常见的有几种

  • limit分页
  • pageHelper第三方插件
  • MybatisPlus内置分页插件

如何配置MybatisPlus分页插件?

配置类

    // MybatisPlus分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        // PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        // paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return new PaginationInterceptor();
    }

测试

@Test
public void testSelPage() {
  Page<User> page = new Page<>(1, 5);
  IPage<User> userIPage = userMapper.selectPage(page, null);
  userIPage.getRecords().forEach(System.out::println);
}

数据删除

@Test // 删除单个
public void testDel() {
    System.out.println(userMapper.deleteById(1330817100494761987L));
}

@Test // 删除多个
public void testBenchDel() {
    System.out.println(userMapper.deleteBatchIds(Arrays.asList(1330817100494761986L, 1330817100494761985L)));
}

@Test // Map删除
public void testDelMap() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", "957");
    System.out.println(userMapper.deleteByMap(map));
}

逻辑删除

在讲物理删除的时候,需要了解两个东西

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

逻辑删除:在数据库中没有被移除,而是使用一个字段来让他失效(举一个不是特别恰当的例子:可以理解为在未删除和删除之间,中间加了一个“回收站”)

可以使用一个字段叫做deleted,int类型,指定一个数值,表示这个数据会被删除

使用逻辑删除的好处,如果删除了,就可以使用管理员的身份直接查看被删除的记录

首先在数据库中添加一个字段

MybatisPlus入门学习

实体类增加字段

    @TableLogic // 实现逻辑删除
    private Integer deleted;

在配置类中增加组件

// 逻辑删除
    @Bean
    public ISqlInjector iSqlInjector() {
        return new LogicSqlInjector();
    }

配置文件中添加配置

# 配置逻辑删除
# 当字段值为1时,默认删除当前数据
mybatis-plus.global-config.db-config.logic-delete-value=1
# 当字段值为0时,不删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0

用来删除的API进行测试,但是结果可以看到

MybatisPlus入门学习

逻辑删除本质上就是一个Update语句

它指定要删除的记录在数据库中还是存在的

MybatisPlus入门学习

但是在查询这一条数据时,它依旧不会被查出来

MybatisPlus入门学习

在进行查询的时候,会自动过滤掉被逻辑删除的字段;

性能分析插件

在开发中,会遇到一种情况叫做慢查询(也叫慢SQL),慢查询会导致CPU负载太高(主要是查询语句中会有比较复杂的处理算法,又或者是在进行IO时负载过高),MybatisPlus针对此种情况推出了一个东西叫做性能分析插件,一旦查询的时间超过插件指定时间,程序就会自动停止

首先需要定义一个开发环境,因为在下面导入插件的时候会使用到

spring.profiles.active=dev

1、导入插件

@Bean
// 一定要设置当前项目的环境,切记!切记!
@Profile({"dev"}) // 指定环境为dev生效
public PerformanceInterceptor performanceInterceptor() {
  PerformanceInterceptor interceptor = new PerformanceInterceptor();
  // 是否格式化代码
  interceptor.setFormat(true);
  // 设置SQL超时时间
  interceptor.setMaxTime(5000L);
  return interceptor;
}

测试,在测试时,控制台输出的东西会有一些不同

MybatisPlus入门学习

控制台打印输出的东西,会出现一个SQL的执行时间,如果当前SQL的执行时间超过了你所设定的这个最大超时时间,那么程序会抛出异常

条件构造器

其实之前就已经在查询全部数据的时候就已经埋下了一个伏笔,测试SQL是否连接成功的时候,当时的selectList方法的传递的值为null(不知道的可以看博客开头的快速入门),这个null表示的就是Wrapper,这才是本小节的重点

复杂的条件查询,MybatisPlus中提供了一个类,AbstractWrapper,这是个抽象类,源码中也明确的表示了,这是一个条件查询的封装,也就是说,所有的类都可以通过继承它来实现复杂查询的API调用

MybatisPlus入门学习

MybatisPlus入门学习

并且在官网中也罗列出了相关的API,这是一一对应的

MybatisPlus入门学习

来吧,测试一下叭~~~

多条件查询

@Test
void contextLoads() {
  // 查询name不为空,email不为空的用户,年龄>=21岁
  // QueryWrapper是AbstractWrapper的子类,内部属性是为了生成查询条件
  QueryWrapper<User> wrapper = new QueryWrapper<>();
  wrapper.isNotNull("name").isNotNull("email").ge("age", 21);
  List<User> users = userMapper.selectList(wrapper);
  users.forEach(System.out::println);
}

查看一下测试的结果

MybatisPlus入门学习

多条件查询这边控制台打印的信息和代码当中的Wrapper条件是一一对饮的

单表单个数据

@Test // 查询单个
void test01() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "957");
    System.out.println(userMapper.selectOne(wrapper));
}

查询 范围

@Test // 查询区间
void test0203() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age", 20, 24);
    // 查询在此区间之内的数据
    // userMapper.selectMaps(wrapper).forEach(System.out::println);
    // 查询在此区间的数据有几条
    System.out.println(userMapper.selectCount(wrapper));
}

左右两边定义查询范围

@Test // 左右模糊匹配
void test04() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    /*
        这边的API,likeLeft和likeRight
            likeLeft:从指定字段的属性值的从右往左进行模糊查询,即 column like "%xx"
            likeRight:从指定字段的属性值的从左往右进行模糊查询,即 column like "xx%"
     */
    wrapper.likeRight("email", "t").likeLeft("name", "e");
    userMapper.selectMaps(wrapper).forEach(System.out::println);
}

套娃

@Test // 子查询
void test05() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id", "select id from user where id < 3");
    userMapper.selectObjs(wrapper).forEach(System.out::println);
}

MybatisPlus入门学习

排序

@Test // 查询结果排序
void test06() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByDesc("id");
    userMapper.selectObjs(wrapper).forEach(System.out::println);
}

代码自动生成器

让MybatisPlus自己写代码,dao,service,pojo,controller全部都是自动生成

public class HrcCode {
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 配置策略
        // 1. 全局配置
        GlobalConfig gc = new GlobalConfig();
        String strProPath = System.getProperty("user.dir");
        gc.setOutputDir(strProPath + "/src/main/java");
        gc.setAuthor("小黄要成为一线程序员吖"); // 作者
        gc.setOpen(false); // 是否打开资源管理器
        gc.setFileOverride(false); // 是否覆盖
        mpg.setGlobalConfig(gc);

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

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

        // 4. 策略配置
        StrategyConfig sc = new StrategyConfig();
        sc.setNaming(NamingStrategy.underline_to_camel);
        sc.setColumnNaming(NamingStrategy.underline_to_camel);
        sc.setEntityLombokModel(true); // 是否使用lombok
        sc.setLogicDeleteFieldName("deleted"); // 逻辑删除

        // 字段自动填充
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtUpdate = new TableFill("gmt_update", FieldFill.INSERT_UPDATE);
        List<TableFill> list = new ArrayList<>();
        list.add(gmtCreate);
        list.add(gmtUpdate);
        sc.setTableFillList(list);

        sc.setVersionFieldName("version"); // 乐观锁
        sc.setRestControllerStyle(true);
        sc.setControllerMappingHyphenStyle(true); // 是否为驼峰转连字符

        mpg.setStrategy(sc);
        mpg.execute(); // 执行
    }
}
underline_to_camel);
        sc.setColumnNaming(NamingStrategy.underline_to_camel);
        sc.setEntityLombokModel(true); // 是否使用lombok
        sc.setLogicDeleteFieldName("deleted"); // 逻辑删除

        // 字段自动填充
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtUpdate = new TableFill("gmt_update", FieldFill.INSERT_UPDATE);
        List<TableFill> list = new ArrayList<>();
        list.add(gmtCreate);
        list.add(gmtUpdate);
        sc.setTableFillList(list);

        sc.setVersionFieldName("version"); // 乐观锁
        sc.setRestControllerStyle(true);
        sc.setControllerMappingHyphenStyle(true); // 是否为驼峰转连字符

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

本文地址:https://blog.csdn.net/weixin_46468474/article/details/110210608