目录

MybatisPlus之CRUD

插入操作

插入主键回填

1
2
3
4
5
6
7
8
9
@Test
void contextLoads() {
    User user = new User();
    user.setName("张三");
    user.setAge(13);
    user.setEmail("zhangsan@gmail.com");
    userMapper.insert(user);
    System.out.println(user);
}

控制台输出:

1
User(id=1323944814852505602, name=张三, age=13, email=zhangsan@gmail.com)

我们发现插入之后id会自动回填

主键生成策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {

    /**
     * 字段名(该值可无)
     */
    String value() default "";

    /**
     * 主键类型
     * {@link IdType}
     */
    IdType type() default IdType.NONE;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Getter
public enum IdType {
    /**
     * 数据库ID自增
     */
    AUTO(0), // 如果设置主键自增,数据库主键必须先得设置成自增
    /**
     * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
     */
    NONE(1),
    /**
     * 用户输入ID
     * <p>该类型可以通过自己注册自动填充插件进行填充</p>
     */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 分配ID (主键类型为number或string),
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
     *
     * @since 3.3.0
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主键类型为 string)
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
     */
    @Deprecated
    ID_WORKER(3),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
     */
    @Deprecated
    ID_WORKER_STR(3),
    /**
     * @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
     */
    @Deprecated
    UUID(4);

    private final int key;

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

雪花算法

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为 毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味 着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯 一!

更新操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/**
 * 更新
 */
@Test
public void testUpdate() {
    User user = new User();
    user.setId(1L);
    user.setName("徐达");
    user.setEmail("xuda@gmail.com");
    userMapper.updateById(user);
    System.out.println(user);
}

自动填充

创建时间、修改时间!这些个操作一般都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有的表都要配置上!而且需要自动化!

数据库级别

1
2
3
4
5
CREATE TABLE `mytest` (
    `text` varchar(255) DEFAULT '' COMMENT '内容',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

代码级别

1
2
3
ALTER TABLE `eden`.`user` 
ADD COLUMN `create_time` datetime NULL COMMENT '创建时间' AFTER `email`,
ADD COLUMN `update_time` datetime NULL COMMENT '更新时间' AFTER `create_time`;

实体类字段属性上需要增加注解

1
2
3
4
5
 // 字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

往容器中添加一个填充时间处理器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.eh.mp.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Date;

@Configuration
public class MPConfig {

    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            // 插入时的填充策略
            @Override
            public void insertFill(MetaObject metaObject) {
                this.setFieldValByName("createTime", new Date(), metaObject);
                this.setFieldValByName("updateTime", new Date(), metaObject);
            }

            // 更新时的填充策略
            @Override
            public void updateFill(MetaObject metaObject) {
                this.setFieldValByName("updateTime", new Date(), metaObject);
            }
        };
    }

}

乐观锁

乐观锁机制:

  1. 取出记录时,获取当前 version
  2. 更新时,带上这个version ,执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败

使用乐观锁插件

1
2
ALTER TABLE `eden`.`user` 
ADD COLUMN `version` int(11) not NULL DEFAULT 1 COMMENT '版本号' AFTER `update_time`;

给实体类增加对应字段

1
2
@Version //乐观锁Version注解
private Integer version;

注册插件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@EnableTransactionManagement // 如果更新失败就回滚
@MapperScan("com.eh.mp.dao")
@Configuration
public class MPConfig {


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

}

查询操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 测试查询
 */
@Test
public void testSelect() {
    userMapper.selectList(null).stream().forEach(System.out::println);
}

/**
 * 测试批量查询
 */
@Test
public void testSelectByBatchIds() {
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

// 按条件查询之一使用map操作
@Test
public void testSelectByMap() {
    HashMap<String, Object> map = new HashMap<>(); // 自定义要查询
    map.put("name", "张三");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

分页查询

实现分页查询常见的方式有以下几种:

  • 原始的 limit 进行分页
  • pageHelper 第三方插件
  • MP 其实也内置了分页插件

使用方式还是一样,往容器中注册分页插件

1
2
3
4
5
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

直接使用Page对象即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 测试分页查询
@Test
public void testPage() {

    // 参数一:当前页
    // 参数二:页面大小
    // 使用了分页插件之后,所有的分页操作也变得简单
    Page<User> page = new Page<>(2, 2);
    userMapper.selectPage(page, null);

    page.getRecords().forEach(System.out::println);
    System.out.println(page.getTotal());

}

删除操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 测试删除
@Test
public void testDeleteById() {
    userMapper.deleteById(1240620674645544965L);
}

// 通过id批量删除
@Test
public void testDeleteBatchId() {

    userMapper.deleteBatchIds(Arrays.asList(1240620674645544961L, 1240620674645544962L));
}

// 通过map删除
@Test
public void testDeleteMap() {

    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "狂神说Java");
    userMapper.deleteByMap(map);

}

逻辑删除

往实体类中增加属性

1
2
@TableLogic //逻辑删除 
private Integer deleted;

注册逻辑删除组件

1
2
// 逻辑删除组件!
@Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }

配置逻辑删除值

1
2
3
# 配置逻辑删除 
mybatis-plus.global-config.db-config.logic-delete-value=1 
mybatis-plus.global-config.db-config.logic-not-delete-value=0