MybatisPlus快速入门

MybatisPlus快速入门


一、MybatisPlus简介

MybatisPlus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生

二、快速开始

1、创建数据库 mybatis_plus

2、创建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)
);

3、添加数据

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

4、打开IDEA在pom.xml添加相关依赖

    <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1</version>
    </dependency>

    <!--mysql运行时依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!--lombok用来简化实体类-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

5、编写连接数据库的properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

# MybatisPlus日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

6、创建包entity编写User实体类

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

7、创建mapper编写UserMapper文件(MybatisPlus核心持久层接口)

@Repository
public interface UserMapper extends BaseMapper<User> {
    
}

8、在主启动类上添加扫面mapper文件夹(稍后我们会统一放入MybatisPlusConfig文件内)

@SpringBootApplication
@MapperScan("xxx.xxxx.mapper")
public class MybatisPlusApplication {
    ......
}

9、在测试类中完成测试

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

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

}

10、在控制台查看输出效果

MybatisPlus快速入门-

三、插入数据和主键策略

插入数据

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testInsert(){
        User user = new User();
        user.setName("张三");
        user.setAge(18);
        user.setEmail("1746344046@qq.com");

        int result = userMapper.insert(user);
        System.out.println("成功添加的行数:" + result);
        System.out.println("添加用户的id为:" + user.getId());  //id:1272668550292856834 MybatisPlus默认主键策略为雪花算法
    }

主键策略

1、ASSIGN_ID

MyBatis-Plus默认的主键策略是:ASSIGN_ID(使用了雪花算法)

@TableId(type = IdType.ASSIGN_ID)
private Long id;

2、AUTO自增策略

需要在数据库的id字段设置主键自增

@TableId(type = IdType.AUTO)
private Long id;

3、要想影响所有实体的配置,可以设置全局主键配置(properties或yml文件)

# 全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto

四、更新数据、自动填充和乐观锁

更新数据

    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(1L);
        user.setName("李四");

        int result = userMapper.updateById(user);
        System.out.println("成功更新的行数:" + result);
    }

自动填充

1、需求:项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。

2、数据库修改

在User表中添加datetime类型的新的字段 create_time、update_time

3、实体类修改

@Data
public class User {
    ......
        
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

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

4、实现元对象处理器接口

创建handler包编写MyMetaObjectHandler文件

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("开始填充insertFill.......");
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("开始填充updateFill.......");
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

5、测试

    @Test
    public void testAutoInsertFill(){
        User user = new User();
        user.setName("王五");
        user.setAge(20);
        user.setEmail("1746344046@qq.com");

        int result = userMapper.insert(user);
        System.out.println("成功添加的行数:" + result);  //2020-06-16 07:32:57.821(Timestamp), 2020-06-16 07:32:57.821(Timestamp)
    }

乐观锁

1、场景:

一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元

此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了

现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1万多

2、乐观锁与悲观锁

  • 上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库
  • 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证最终的价格是120元

3、模拟上述场景

  • 数据库中增加商品表

    CREATE TABLE product
    (
        id BIGINT(20) NOT NULL COMMENT '主键ID',
        name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
        price INT(11) DEFAULT 0 COMMENT '价格',
        version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
        PRIMARY KEY (id)
    );
  • 添加数据

    INSERT INTO product (id, NAME, price) VALUES (1, 'RTX3080', 100);
  • 编写实体类

    @Data
    public class Product {
        private Long id;
        private String name;
        private Integer price;
        private Integer version;
    }
  • 编写ProductMapper

    @Repository
    public interface ProductMapper extends BaseMapper<Product> {
        
    }
  • 测试

        @Autowired
        private ProductMapper productMapper;
    
        @Test
        public void testNoLockCurrentUpdate(){
            //1、小李
            Product p1 = productMapper.selectById(1L);
            System.out.println("小李取出的价格:" + p1.getPrice());
    
            //2、小王
            Product p2 = productMapper.selectById(1L);
            System.out.println("小王取出的价格:" + p2.getPrice());
    
            //3、小李将价格加了50元,存入了数据库
            p1.setPrice(p1.getPrice() + 50);
            productMapper.updateById(p1);
    
            //4、小王将商品减了30元,存入了数据库
            p2.setPrice(p2.getPrice() - 30);
            int result = productMapper.updateById(p2);
            if(result == 0){//更新失败,重试
                //重新获取数据
                p2 = productMapper.selectById(1L);
                //更新
                p2.setPrice(p2.getPrice() - 30);
                productMapper.updateById(p2);
            }
    
            //5、最后的结果
            Product p3 = productMapper.selectById(1L);
            System.out.println("最后的结果:" + p3.getPrice());  //最后的结果:70
        }

4、使用MybatisPlus的乐观苏解决问题

  • 数据库中添加version字段
  • 修改实体类

    @Version
    private Integer version;
  • 创建配置文件

    • 创建config包,创建文件MybatisPlusConfig.java
    • 此时可以删除主启动类中的@MapperScan扫描注解
    @EnableTransactionManagement
    @Configuration
    @MapperScan("xxxx.xxxx.mapper")
    public class MybatisPlusConfig {
        
    }
  • 编写乐观锁插件

        /**
         * 乐观锁插件
         */
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
  • 测试

        @Autowired
        private ProductMapper productMapper;
    
        @Test
        public void testNoLockCurrentUpdate(){
            //1、小李
            Product p1 = productMapper.selectById(1L);
            System.out.println("小李取出的价格:" + p1.getPrice());
    
            //2、小王
            Product p2 = productMapper.selectById(1L);
            System.out.println("小王取出的价格:" + p2.getPrice());
    
            //3、小李将价格加了50元,存入了数据库
            p1.setPrice(p1.getPrice() + 50);
            productMapper.updateById(p1);
    
            //4、小王将商品减了30元,存入了数据库
            p2.setPrice(p2.getPrice() - 30);
            int result = productMapper.updateById(p2);
            if(result == 0){//更新失败,重试
                //重新获取数据
                p2 = productMapper.selectById(1L);
                //更新
                p2.setPrice(p2.getPrice() - 30);
                productMapper.updateById(p2);
            }
    
            //5、最后的结果
            Product p3 = productMapper.selectById(1L);
            System.out.println("最后的结果:" + p3.getPrice());  //最后的结果:120
        }
  • 总结:当我们使用了乐观锁后,如果出现了多个人同时修改同一数据的情况,那么所有人在拿到数据的同时也获取了乐观锁的版本号,当第一个人修改完毕后,乐观锁就会版本号+1,那么当其他人提交数据时由于版本号不对应的关系,所以数据就会提交失败

五、查询数据和分页插件

查询数据

1、通过多个id批量查询

    @Test
    public void testSelectByBatchIds(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }

2、通过map封装完成简单条件查询

    @Test
    public void testSelectByMap(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "张三");
        map.put("age", 18);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

分页插件

1、在MybatisPlusConfig添加组件

/**
 * 分页插件
 */
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

2、测试

    @Test
    public void testSelectPage() {

        Page<User> page = new Page<>(1,5);
        Page<User> pageParam = userMapper.selectPage(page, null);

        pageParam.getRecords().forEach(System.out::println);
        System.out.println(pageParam.getCurrent());  // 获取当前是第几页
        System.out.println(pageParam.getPages());    // 获取一共的页数
        System.out.println(pageParam.getSize());     // 获取每一页的个数
        System.out.println(pageParam.getTotal());    // 获取总记录条数
        System.out.println(pageParam.hasNext());     // 判断是否还有下一页
        System.out.println(pageParam.hasPrevious()); // 判断是否还有前一页
    }

3、通过QueryWrapper完成条件查询

    @Test
    public void testSelectMapsPage() {
        Page<Map<String, Object>> page = new Page<>(1, 5);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name", "age");   // 表示查询结果只显示name和age列
        Page<Map<String, Object>> pageParam = userMapper.selectMapsPage(page, queryWrapper);

        List<Map<String, Object>> records = pageParam.getRecords();
        records.forEach(System.out::println);
    }

六、删除数据与逻辑删除

删除数据

1、根据id删除数据

@Test
public void testDeleteById(){

    int result = userMapper.deleteById(5L);
    System.out.println(result);
}

2、批量删除数据

@Test
public void testDeleteBatchIds() {

    int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
    System.out.println(result);
}

3、根据map封装完成删除

@Test
public void testDeleteByMap() {

    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "张三");
    map.put("age", 18);

    int result = userMapper.deleteByMap(map);
    System.out.println(result);
}

逻辑删除

1、修改数据库字段

ALTER TABLE `user` ADD COLUMN `deleted` tinyint DEFAULT false

2、修改实体类

@TableLogic
private Integer deleted;

3、测试

    @Test
    public void testLogicDelete(){
        int result = userMapper.deleteById(1L);  //此时的删除只是把deleted字段变成1
        System.out.println(result);
    }

七、条件构造器和常用接口

MybatisPlus快速入门

案例一:

    @Test
    public void testDelete() { //删除名字为空年龄大于12且邮箱不为空的用户(这里SQL为UPODATE因为逻辑删除原因)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("name").ge("age", 12).isNotNull("email");
        int result = userMapper.delete(queryWrapper); // UPDATE user SET deleted=1 WHERE deleted=0 AND (name IS NULL AND age >= ? AND email IS NOT NULL)
        System.out.println("delete return count = " + result);
    }

案例二:

    @Test
    public void testSelectObjs() { // 查询id in (select id from user where id <= 3)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.inSql("id", "select id from user where id <= 3");

        List<Object> objects = userMapper.selectObjs(queryWrapper);  //返回值是Object列表
        objects.forEach(System.out::println);
    }

案例三:

@Test
public void testSelectCount() { // 查询age在20-30之间的数量(包含20与30)

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.between("age", 20, 30);

    Integer count = userMapper.selectCount(queryWrapper);  //返回数据数量
    System.out.println(count);
}

案例四:

@Test
public void testSelectListOrderBy() { // 先按照age降序排列,如果age相同按照id降序排列

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.orderByDesc("age", "id");

    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

Wrapper的常见查询关键字:

MybatisPlus快速入门-

上一篇:Java虚拟机(一):JVM体系结构


下一篇:Java虚拟机(二):类加载子系统