官方API https://mp.baomidou.com/
狂神说 https://gitee.com/kuangstudy/openclass/raw/master/%E7%8B%82%E7%A5%9E%E8%AF%B4MyBatisPlus%E8%AE%B2%E8%A7%A3/MyBatisPlus.pdf

快速入门

依赖

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.3.2.RELEASE</version>
  5. <relativePath/>
  6. </parent>
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-test</artifactId>
  9. <scope>test</scope>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.projectlombok</groupId>
  13. <artifactId>lombok</artifactId>
  14. <optional>true</optional>
  15. </dependency>
  16. <dependency>
  17. <groupId>com.baomidou</groupId>
  18. <artifactId>mybatis-plus-boot-starter</artifactId>
  19. <version>3.3.2</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>com.h2database</groupId>
  23. <artifactId>h2</artifactId>
  24. <scope>runtime</scope>
  25. </dependency>
  26. </dependencies>

建表

  1. DROP TABLE IF EXISTS user;
  2. CREATE TABLE user
  3. (
  4. id BIGINT(20) NOT NULL COMMENT '主键ID',
  5. name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  6. age INT(11) NULL DEFAULT NULL COMMENT '年龄',
  7. email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
  8. PRIMARY KEY (id)
  9. );
  10. --真实开发中,version(乐观锁),deleted(逻辑删除),gmt_create,gmt_modified--
  11. DELETE FROM user;
  12. INSERT INTO user (id, name, age, email) VALUES
  13. (1, 'Jone', 18, 'test1@baomidou.com'),
  14. (2, 'Jack', 20, 'test2@baomidou.com'),
  15. (3, 'Tom', 28, 'test3@baomidou.com'),
  16. (4, 'Sandy', 21, 'test4@baomidou.com'),
  17. (5, 'Billie', 24, 'test5@baomidou.com');

继承接口(核心)

  1. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  2. public interface UserMapper extends BaseMapper<User> {
  3. }

启动类

@MapperScan("com.sms.dao")

  1. @SpringBootApplication
  2. @MapperScan("com.sms.dao")
  3. public class SpringBootQuickStartApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringBootQuickStartApplication.class, args);
  6. }
  7. }

数据库连接配置

  1. #数据库相关配置
  2. spring.datasource.username = root
  3. spring.datasource.password = 123456
  4. spring.datasource.url = jdbc:mysql://localhost:3306/mybatisplus
  5. spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver

CRUD

配置日志

sql全部被基本Mapper接口底层操作完了,我们看不见,所以我们需要用日志来细看,这里用的是自带的一个日志。当然要用slf4j,log4j都可以

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

新增

@Test
public void testInsert(){
User user = new User();
user.setName("狂神说Java");
user.setAge(3);
user.setEmail("24736743@qq.com");
int result = userMapper.insert(user); // 帮我们自动生成id
System.out.println(result); // 受影响的行数
System.out.println(user); // 发现,id会自动回填
}

image.png
发现主键自动回填,”雪花算法”。
另外相关的有,自增id,uuid,redis,zookeeper

修改

// 测试更新
@Test
public void testUpdate(){
    User user = new User();
    // 通过条件自动拼接动态sql
    user.setId(6L);
    user.setName("关注公众号:狂神说");
    user.setAge(18);
    // 注意:updateById 但是参数是一个 对象!
    int i = userMapper.updateById(user);
    System.out.println(i);
}

查询

// 测试查询
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
// 测试批量查询!
@Test
public void testSelectByBatchId(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
// 按条件查询之一使用map操作
@Test
public void testSelectByBatchIds(){
HashMap<String, Object> map = new HashMap<>();
// 自定义要查询
map.put("name","狂神说Java");
map.put("age",3);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}

分页查询

mybatisPlus已经内置了一个分页插件

逻辑删除

image.png
image.png

CRUD(额外补充)

自动填充

阿里巴巴开发手册:所有的数据库都需要配置 gmt_create,gmt_modified,因为我们要追踪每一条数据是什么时候创建,什么时候修改的。
ps:gmt - greenwich mean time 格林威治时间

1.数据库级别(工作中不允许修改)

直接在字段上加上时间函数 current_timestamp
image.png

2.代码级别(推荐)

//在插入和插入更新的时候,自动填充内容
@TableField(fill = FieldFill.INSERT)
@TableField(fill = FieldFill.INSERT_UPDATE)
package com.sms.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;

@Slf4j
@Component // 一定不要忘记把处理器加到IOC容器中!
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill.....");
        // 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("start update fill.....");
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

主键生成策略

https://www.cnblogs.com/haoxinyue/p/5208136.html

1.主键自增

· @TableId(type = IdType.AUTO)
· 数据库字段要自增

public enum IdType {
    AUTO(0), // 数据库id自增  !!
    NONE(1), // 未设置主键
    INPUT(2), // 手动输入
    ID_WORKER(3), // 默认的全局唯一id  !!
    UUID(4), // 全局唯一id uuid
    ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}

上锁(并发问题)

乐观锁很乐观,它不会去上锁,出现问题了,就再次更新值
悲观锁很悲观,它什么都上锁才操作

1.乐观锁

概念

  • 它会先看version是多少
  • 然后再一个update语句中,同时判定version是否被别的线程动过,也就是version是不是它之前看到的那个

set version = newVersion where version = oldVersion

  • 如果不是,说明有线程抢先插队了,它就更新失败 ```sql 乐观锁:1、先查询,获得版本号 version = 1 — A update user set name = “kuangshen”, version = version + 1 where id = 2 and version = 1 — B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败! update user set name = “kuangshen”, version = version + 1 where id = 2 and version = 1
<a name="qRNRJ"></a>
#### 配置
实体类
```sql
    // 扫描我们的 mapper 文件夹
    @MapperScan("com.kuang.mapper")
    @EnableTransactionManagement
    @Configuration // 配置类
    public class MyBatisPlusConfig {
        // 注册乐观锁插件
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }

对应字段

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

测试一下

// 测试乐观锁失败!多线程下
@Test
public void testOptimisticLocker2(){
// 线程 1
User user = userMapper.selectById(1L);
user.setName("kuangshen111");
user.setEmail("24736743@qq.com");
// 模拟另外一个线程执行了插队操作
User user2 = userMapper.selectById(1L);
user2.setName("kuangshen222");
user2.setEmail("24736743@qq.com");
userMapper.updateById(user2);
// 自旋锁来多次尝试提交!
userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值!
}

2.悲观锁