天天看点

mybatisplus快速使用MP入门主键更新自动填充乐观锁查询删除性能分析条件查询

快速使用

  • MP入门
  • 主键
  • 更新
  • 自动填充
  • 乐观锁
  • 查询
  • 删除
  • 性能分析
  • 条件查询

MP入门

mybatis

增求,简化开发

  1. 创建数据库和表,添加数据
    create database mybatis_plus;
    user 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)
    );
    
    DELETE FROM user;
    INSERT INTO user (id, name, age, email) VALUES
    (1, 'Jone', 18, '[email protected]'),
    (2, 'Jack', 20, '[email protected]'),
    (3, 'Tom', 28, '[email protected]'),
    (4, 'Sandy', 21, '[email protected]'),
    (5, 'Billie', 24, '[email protected]');
               
  2. 创建

    SpringBoot

    工程
  3. 引入

    mybatis-plus

    相关依赖
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
               
  4. 配置数据库对应的属性
    # mysql8+ 用com.mysql.cj.jdbc.Driver 和 serverTimezone=GMT%2B8时区
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=
               
  5. 编写实体类
    package com.jerry.mpdemo.pojo;
    
    import lombok.Data;
    
    @Data
    public class User {
        private Long id;
        private String name;
        private Integer age;
        protected String email;
    }
               
  6. 编写

    Mapper

    文件
    package com.jerry.mpdemo.mapper;
    
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jerry.mpdemo.pojo.User;
    
    public interface UserMapper extends BaseMapper<User> {
    }
               
  7. 扫描

    mapper

    @SpringBootApplication
    @MapperScan("com.jerry.mpdemo.mapper")
    public class MpdemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MpdemoApplication.class, args);
        }
    
    }
               
  8. 测试
    @SpringBootTest
    class MpdemoApplicationTests {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void contextLoads() {
            List<User> users = userMapper.selectList(null);
            users.forEach((User user) -> {
                System.out.println(user);
            });
        }
    
    }
               

主键

MP自动生成19位的ID

自动增长

@Data
public class User {
    @TableId(type = IdType.AUTO) // 加注解
    private Long id;
    private String name;
    private Integer age;
    protected String email;
}
           

更新

@Test
public void updateUser() {
    User user = new User();
    user.setId(2L);
    user.setAge(21);
    int row = userMapper.updateById(user);
    System.out.println(row);
}
           

自动填充

  1. 表添加两个字段
    • create_time

    • update_time

  2. 实体类添加属性
    private Date createTime;
    private Date updateTime;
               
  3. 添加注解

    @TableField(fill = FieldFill.INSERT)

  4. 实现接口

    MetaObjectHandler

    @Component // 放在spring容器中,很重要!!
    public class MyMetaObjectHandler implements MetaObjectHandler {
        // 使用mp实现更新操作,这个方法执行
        @Override
        public void updateFill(MetaObject metaObject) {
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    
        // 使用mp实现添加操作,这个方法执行
        @Override
        public void insertFill(MetaObject metaObject) {
            this.setFieldValByName("createTime", new Date(), metaObject);
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    }
               
  5. 正常添加更新即可,不用再写这两个字段

乐观锁

主要解决丢失更新,如果不考虑事务隔离性,产生读问题:

  1. 脏读
  2. 不可重复读
  3. 幻读s

写问题:丢失更新问题:

比如

id = 1

salary = 500

,两个人都想修改这个工资

两人都需要开启事务,

A

500

改为了

800

B

500

改成

200

事务最终需要提交,如果

A

先提交了事务,表中工资就会变为

800

B

随后提交事务,工资变为了

200

,此时

A

看到了就会发现不对劲,

自己提交的数据被覆盖了。正常应该是

B

800

改成

200

解决方法:

  1. 悲观锁:串行,

    A

    用的时候别人不能用,隔离级别max
  2. 乐观锁:获取当前version,

    A

    提交时比较版本号和数据库中的是否一样,然后将版本号+1,

    B

    在看到版本号不一样就无法提交
  3. 添加字段,作为版本号
  4. 实体类添加版本号

    private Integer version;

  5. 添加

    @Version

    注解
    @Version
    private Integer version;
               
  6. 配置乐观锁的插件
    @Configuration
    @MapperScan("com.jerry.mpdemo.mapper")
    public class MpConfig {
    
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }
               
  7. 测试
    @Test
    public void addUser() {
        User user = new User();
        user.setAge(18);
        user.setName("xxx");
        user.setEmail("[email protected]");
    
        int insert = userMapper.insert(user);
        System.out.println("insert: ---------- " + insert);
    }
    
    // 乐观锁必须先查再改
    @Test
    public void testLock() {
        User user = userMapper.selectById(1355352835969359878L);
        user.setAge(666);
        int row = userMapper.updateById(user);
        System.out.println(row);
    }
               

查询

多个

id

的批量查询

@Test
public void testSelect() {
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); // 将1,2,3查询出来
    for (User user : users) {
        System.out.println(user);
    }
}
           

分页查询:

  1. 配置分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
               
  2. 使用
    @Test
    public void testPage() {
        Page<User> page = new Page<>(1, 3); // 当前页, 每页记录数
        IPage<User> userIPage = userMapper.selectPage(page, null);
        System.out.println(userIPage.getCurrent());
        System.out.println(userIPage.getSize());
        System.out.println(userIPage.getTotal());
        System.out.println(userIPage.getPages());
        List<User> records = userIPage.getRecords();
        for (User record : records) {
            System.out.println(record);
        }
    }
               

删除

物理删除

@Test
public void testDelete() {
    int count = userMapper.deleteById(1L);
    System.out.println("成功删除了" + count + "行");
}
           

批量删除

@Test
public void testBatchDelete() {
    userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
}
           

逻辑删除,数据真实存在,但是查询不出来

  1. 添加deleted字段
  2. 添加字段并且添加注解

    @TableLogic

  3. 配置逻辑删除插件
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }
               

性能分析

@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(200); // 超过100ms的sql不执行
    performanceInterceptor.setFormat(true);
    return performanceInterceptor;
}
           

条件查询

@Test
public void testSelectQuery() {
    QueryWrapper queryWrapper = new QueryWrapper();
    // ge大于等于 gt大于 le小于等于 lt小于
    queryWrapper.ge("age", 20);
    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach((user) -> System.out.println(user));

    // eq等于 ne不等于

    // between在范围内 like模糊查询会->自动帮忙传递%

    // orderBy排序

    // last拼接到sql的最后,有sql注入风险

    // 查询指定列
    queryWrapper.select("id", "name", "age");


}
           

继续阅读