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

【轻松学】Springboot整合Mybatis-PLus进行快速开发

程序员文章站 2022-03-26 17:06:13
...

在正式开始介绍Mybatis-Plus之前,先介绍一款相当方便的实体类自动getter/setter的插件Lombok,注意Lombok插件现在针对IDEA2020.1.1版本的话还是不兼容的,并且Mybatis-Plus搭配Lombok开发进行CRUD操作的话效率会更高。

首先我们导入坐标依赖
 <dependencies>

        <!-- springboot启动类 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- lombok插件 实体类插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- MybatisPuls的包,这个是自己开发的 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>
整体项目结果如图所示

【轻松学】Springboot整合Mybatis-PLus进行快速开发

首先我们要配置一个实体类User
package com.ysw.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    /*
        自增策略
     */

    /*
        这个id对应我们数据库的主键,是随机生成的唯一id(主键策略 - 雪花算法[分布式系统唯一id生成])
        自增id
    */

    /*
        默认全局唯一id
        @TableId(type = IdType.ID_WORKER)
     */

    /*
        * 需要配置主键自增
             在实体类上配置 @TableId(type = IdType.ID_WORKER)
             数据库字段一定是自增的

             type = IdType.AUTO 数据库id自增
             type = IdType.NONE 未设置主键,意思就是这个表没有主键
             type = IdType.INPUT 手动输入,意思就是要我们自己输入一个属性值(自己配置)
             type = IdType.WORKER 默认全局id
             type = IdType.UUID 全局唯一id
             type = IdType.ID_WORKER_STR id的字符串表示法
     */

    //使用这个注解可以直接主键自增(我们的用户id自增),所以我们都不需要设置我们下一个ID的注解了
    // * 推荐使用
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    /*
        这里通过代码级别让它自动更新
        字段添加填充内容
     */

    //这里是直接在插入的时候自动执行,可以理解为插入的时候就设置好创建时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    //在插入/更新的时候都自动更新,可以理解为更新的时候就更新一次时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    //乐观锁注解,版本号。然后去注册主键
    @Version
    private Integer version;

    //逻辑删除:未删除是0,已删除是1
    @TableLogic
    private Integer deleted;

}
如果使用了逻辑删除的话我们需要在yml文件中配置逻辑删除的对应配置,其实逻辑删除就是当我们删除这个字段的时候,我们数据库有一个字段被自动修改了,当删除之后该条信息在数据库中依旧是存在的。但是由于这个数据库中deleted字段值改为了1,所以我们要使用这条数据的话会发现被MP屏蔽掉了,但是这条数据依旧存在。这就是逻辑删除,yml配置如下:
# 这里我们用的是 MySQL 5.X 版本的
spring:
  # 设置当前为开发环境
  profiles:
    active: dev
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

# 如果用的MySQL驱动是8版本的话,就要增加时区的配置
# serverTimeZone=GMT%2B8   东八区

# 配置我们的日志在控制台打印输出
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 配置逻辑删除
  global-config:
    db-config:
      # 已经删除的值是1
      logic-delete-value: 1
      # 没有删除的值是0
      logic-not-delete-value: 0
当我们定义好我们的实力类之后,我们就可以在Mapper中定义一个接口了,这个接口是继承自BaseMapper<User>的。其实我们MP中所有的CRUD都是来自于这个BaseMapper的接口的。
/**
 * @Repository 表示是持久层的
 * 用户接口,所有的CRUD都已经编写完成了,不需要任何配置
 */

public interface UserMapper extends BaseMapper<User> {
    //在对应的mapper上面继承基本的类 BaseMapper
}
如果我们想进行分页操作的话,还需要一个配置类进行配置,以及乐观锁的配置。注意,我在配置类中配置了一个MapperScan的标签,用于扫描我们的Mapper接口。我们也可以在Springboot启动类里面加上这个注解来扫描包。(小编发现当我们Mapper类有@Repository注解的时候我们可以直接通过Autowired来进行识别,如果Mapper类没有@Repository的话可以通过@Resource注解来进行识别)
/**
 * 配置类
 */

//使用注解扫描mapper文件,一般我们都在配置类中扫描,这样不用在启动类中进行扫描
@MapperScan("com.ysw.mapper")
//开启自动管理事务
@EnableTransactionManagement
//设置当前类为配置类
@Configuration
public class MybatisPlusConfig {

    //注册乐观锁插件,配置乐观锁插件完毕(使用的是拦截器)
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }

    //分页配置插件,然后就可以直接使用Page对象了
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
如果我们想实现自动填充功能(比如我们插入一条数据和更改一条数据的时候都会记录时间),就可以配置一个handler处理器
/**
 * 自定义处理器:自动填充
 */

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 创建数据的时候就执行一次更新(主要是设置更新的时间) - 插入时的填充策略
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill");
        //创建的时候设置create_time字段的值,第一个值指定实体类的属性,第二个值指定数据,第三个值指定metaObject
        this.setFieldValByName("createTime", new Date(), metaObject);
        //创建的时候设置update_time字段的值,三个值同上
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    /**
     * 更新数据的时候就执行一次更新(主要是更新最新的操作时间) - 更新时的更新策略
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill");
        //更新的时候只更新update_time字段的值
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}
链接好数据库之后,我们就可以尽情的CRUD啦!
package com.ysw;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ysw.entity.User;
import com.ysw.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootTest
class MybatisPlusApplicationTests {

    /**
     * UserMapper继承了BaseMapper,所有的方法都来自父类,我们也可以编写自己的扩展方法
     *
     * Resource - 直接不需要在Mapper文件中使用 @Repository 注解
     * Autowired - 需要使用 @Repository 注解,否则无法直接扫描到
     */
    @Resource
    private UserMapper userMapper;

    /**
     * 查询所有
     */
    @Test
    void contextLoads() {

        //参数是一个Wrapper,条件构造器
        //查询所有用户
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);

    }

    /**
     * 测试插入
     */
    @Test
    void insert(){

        User user = new User();

        //听说可以自动生成id,但是自测的时候id并不会自动生成
        //数据库插入的id默认值为:全局的唯一id(也就是随机生成的一个id)
        //user.setId(111L);
        //这里注意,我们是无法直接提取出我们的ID的,这个是会在我们的数据库中直接自增的

        user.setName("tuofengTest");
        user.setAge(21);
        user.setEmail("aaa@qq.com");

        int count = userMapper.insert(user);
        System.out.println(count);

    }

    /**
     * 测试更新
     */
    @Test
    void update(){

//        User user = new User();
//        //通过条件自动拼接动态sql
//        user.setId(115L);
//        user.setName("世文AAAAAAi");
//
//        userMapper.updateById(user);

        //更新同时使用乐观锁
        User user = userMapper.selectById(115L);
        user.setName("test2222222");
        //测试乐观锁版本
        int count = userMapper.updateById(user);
        System.out.println(count);

    }

    /*
       所有的时间操作都是自动完成的
            我们不希望手动进行更新
                   因此数据库表中一定有两个参数:gmt_create(创建的时间)、gmt_modify(修改的时间)
                   这两个字段是几乎所有的表都要配置上的,自动化的进行
                        1、数据库级别
                            1.1、在表中新增字段:create_time、update_time
                            1.2、创建时间指定,但是更新时间随时更新

                        2、代码级别 handler.MyMetaObjectHandler
                            2.1、删除数据库的默认值
                            2.2、实体类字段上增加注解
     */

    /*
        乐观锁:
               顾名思义十分乐观,它总是认为不会出现问题,无论干什么都不去上锁。
               如果出现了问题就在测试加锁处理,再次更新测试
               version、new version(版本号机制 -> ABA问题)

        悲观锁:
               它认为总是会出问题,无论干什么都会上锁,再去操作
     */

    //测试乐观锁成功,单线程情况下进行修改(并且将版本号 + 1)
    @Test
    public void testHappyLock(){

        //查询用户信息
        User user = userMapper.selectById(1L);
        //修改用户信息
        user.setName("世文");
        user.setEmail("aaa@qq.com");
        //3、执行更新操作
        userMapper.updateById(user);

    }

    //测试乐观锁失败
    @Test
    public void testHappyLock2(){

        //线程1
        User user1 = userMapper.selectById(1L);
        user1.setName("世文1111");
        user1.setEmail("aaa@qq.com");

        //模拟另外一个线程执行了插队操作
        User user2 = userMapper.selectById(1L);
        user2.setName("世文2222");
        user2.setEmail("aaa@qq.com");

        //多线程情况下一定要记得加锁

        //user2先更新了
        userMapper.updateById(user2);

        //user1被插队了,如果没有乐观锁就会覆盖插队线程的值(先执行的操作,反而被插队的操作替代了)
        //这里可以使用自旋锁来多次提交
        userMapper.updateById(user1);
    }

    /**
     * 测试查询一个用户
     */
    @Test
    public void testSelectById(){
        User user = userMapper.selectById(1L);
    }

    /**
     * 查询一群数据(批量查询)
     */
    @Test
    public void testSelectByIds(){
        //自动将数字转成集合
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }

    /**
     * 条件查询之一,使用map操作
     */
    @Test
    public void testSelectByBatchIds(){
        //使用hashmap自定义查询条件
        HashMap<String, Object> hashMap = new HashMap<>();
        //自定义查询,用map来进行多条件查询,相当于 name = jane
        //hashMap.put("name","Jane");
        hashMap.put("age","21");
        //进行查询
        List<User> users = userMapper.selectByMap(hashMap);
        users.forEach(System.out::println);
    }

    /**
     * 测试分页查询 - MP其实是内置了分页插件的
     */
    @Test
    public void testPage(){

        //参数一: 当前页
        //参数二: 页面大小

        //这里导入mp的page对象,然后找到第一页的5数据,每页5个数据
        Page<User> page = new Page<>(1,5);
        userMapper.selectPage(page, null);

        page.getRecords().forEach(System.out::println);
        //获取当前页数
        long current = page.getCurrent();
        //获取每一页的页数
        long size = page.getSize();
        //获取总页数
        long total = page.getTotal();
        //是否有下一页
        boolean hasNext = page.hasNext();
        //是否有上一页
        boolean hasPrevious = page.hasPrevious();

        System.out.println(current + " - " + size + " - " + total);

    }

    /**
     * 删除操作
     */
    public void testDeleteById(){

        //根据id进行删除
        //userMapper.deleteById();
        //根据ids进行批量删除
        //userMapper.deleteBatchIds();
        //根据map条件进行批量删除(map指定之后都是批量删除的)
        //userMapper.deleteByMap();

    }

    /**
     * 逻辑删除 - 走的是更新操作,而不是删除操作
     */
     @Test
    public void deleteById(){

         //更改了数据库的deleted字段
         //但是在查询的时候会自动过滤被删除的字段(deleted = 1的被过滤字段)
         userMapper.deleteById(1L);

     }



}
以下案例都是使用Wrapper进行条件构造的
package com.ysw;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ysw.entity.User;
import com.ysw.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * Wrapper构造器
 */

@SpringBootTest
public class WrapperTest {

    @Resource
    private UserMapper userMapper;

    /**
     * 查询所有,测试wrapper(isNotNull、gt - greater than、ge - greater equals)
     */
    @Test
        void contextLoads() {

        //定义查询器,这个和map其实是差不多的
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //查询名字这个数据库字段不为空的(这里写数据库字段)
        wrapper
                .isNotNull("create_time")
                .isNotNull("email")
                .gt("age",10)    //年龄大于10
                .ge("age",21);   //年龄大于或等于21
        //查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于21岁的
        List<User> users = userMapper.selectList(wrapper);
        System.out.println(users);

    }


    /**
     * 查询名字
     */
    @Test
    void test2(){
        //创建条件构造器
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("name",  "世文");

        userMapper.selectOne(queryWrapper);

    }

    /**
     * 查询年龄在20 - 23之间用户(数量即可)
     */
    @Test
    void test3(){
        //创建条件构造器
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //between的边界值是包含进去的
        queryWrapper
                .between("age", 20, 23);
        //查询人数
        Integer count = userMapper.selectCount(queryWrapper);
        System.out.println(count);
    }

    /**
     * 模糊查询(不包含)
     */
    @Test
    void test5(){
        //创建条件构造器
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //左和右 意思是%是在左边还是右边,如果是左边的话就是 %世文 左边通配,右边就是 世文% 右边通配
        //名字里面不包含世文的
        queryWrapper
                .notLike("name", "世文")
                .likeLeft("age", "1"); //这里意思是年龄是1结尾的 %1
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }

    /**
     * id在子查询中查出来,这个是一个in查询
     */
    @Test
    void test6(){
        QueryWrapper<User> wrapper = new QueryWrapper();
        //IN (select id from user where id < 3))
        wrapper
                .inSql("id", "select id from user where id < 3");
        List<Object> objects = userMapper.selectObjs(wrapper);
        objects.forEach(System.out::println);
    }

    /**
     * 排序
     */
    @Test
    void test7(){
        QueryWrapper<User> wrapper = new QueryWrapper();
        //通过id进行降序排序
        wrapper.orderByDesc("id");
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

}
相关标签: MybatisPlus