MyBatisPlus_CRUD扩展

CRUD扩展

1. 数据插入

1. 测试插入

//测试插入
@Test
public void testInsert() {
    User user = new User();
    user.setName("Wang")
            .setAge(3)
            .setEmail("wsk4715@sina.com");

    //帮我们自动生成id
    int result = userMapper.insert(user);

    //受影响的行数
    System.out.println(result);
    //发现, id会自动回填
    System.out.println(user);
}

测试结果

image-20201015135550239

2. 主键生成策略

数据库插入的id默认值为: 全局的唯一id

使用雪花算法, 可以保证几乎全球唯一

package com.wang.mybatis_plus.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {

    @TableId(type = IdType.ID_WORKER)
    private Long id;
    private String name;
    private Integer age;
    private String email;

}

注意

  • @TableId是主键的注解

  • 默认为ID_WORKER 雪花算法

  • image-20201015140532853

  • AUTO

    • 实体类字段上@TableId(type = IdType.AUTO)
    • 数据库字段一定要是自增的, 否则报错

2. 更新

//测试更新
@Test
public void testUpdate() {
    User user = new User();
    user.setId(5L)
            .setName("这是修改后的数据")
            .setAge(3)
            .setEmail("wsk4715@sina.com");
    userMapper.updateById(user);
}

注意

  • 虽然叫做updateById, 但是参数是一个对象! (根据id更新)

  • 所有的sql都是自动帮你动态配置的

3. 自动填充

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

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

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

  1. 在表中新增字段 creat_time, update_time

image-20201015142758993

注意要在update_time字段上勾选更新, 默认值写CURRENT_TIMESTAMP

  1. 先将实体类同步
package com.wang.mybatis_plus.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {

    @TableId(type = IdType.ID_WORKER)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private Date createTime;
    private Date updateTime;

}
  1. 再次更新查看结果

image-20201015143221407

2. 代码级别

  1. 删除默认值和更新

image-20201015143536357

  1. 实体类的字段属性上需要增加注解
package com.wang.mybatis_plus.pojo;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {

    @TableId(type = IdType.ID_WORKER)
    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;

}

updateTime用 .INSERT_UPDATE , 防止插入创建时间后update为空

  1. 编写处理器来处理这个注解即可
package com.wang.mybatis_plus.handler;

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;

//一定不要忘记把处理器加到IOC容器中
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    //插入时的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill....");
        //插入时顺带初始化updateTime, 否则为空
        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);
    }
}

注意

  • 实现 MetaObjectHandler 接口
  • 一定不要忘记把处理器加到IOC容器中 @Component

4. 乐观锁

意图:

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
乐观锁:
1. 先查询, 获得版本号 version = 1

-- A
update user set name = "wang", version = version + 1
where id = 2 and version = 1

-- B 线程抢先完成, 这个时候 version = 2, 会导致 A 修改失败
update user set name = "wang", version = version + 1
where id = 2 and version = 1

1. 给数据库增加version字段

image-20201016101708757

image-20201016101855619

2. 实体类添加对应字段

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

3. 注册组件

package com.wang.mybatis_plus.config;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

//开启管理事务
@EnableTransactionManagement
@Configuration
//扫描我们的Mapper文件夹
@MapperScan("com.wang.mybatis_plus.mapper")
public class MybatisPlusConfig {

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

一定要注册Bean!

4. 测试乐观锁

1. 成功

//测试乐观锁成功
@Test
public void testOptimisticLocker() {
    // 1. 查询用户信息
    User user = userMapper.selectById(1L);
    // 2. 修改用户信息
    user.setName("小明")
            .setEmail("715180879@qq.com");
    // 3. 执行更新操作
    userMapper.updateById(user);

}

2. 失败

//测试乐观锁失败! 多线程下
@Test
public void testOptimisticLocker2() {
    // 线程 1
    User user = userMapper.selectById(1L);
    user.setName("小明")
            .setEmail("715180879@qq.com");
    //模拟另外一个线程执行了插队操作
    User user2 = userMapper.selectById(1L);
    user2.setName("小明2")
            .setEmail("715180879@qq.com");
    userMapper.updateById(user2);

    // 虽然user设置了值但是没有更新, 被user2插队更新了同一条数据!
    //如果没有乐观锁, 就会覆盖插队线程的值!
    userMapper.updateById(user);
}

image-20201016103356209

可以看出, 线程1并没有覆盖另一个线程的更新提交, 如果想要多次提交, 使用自旋锁

5. 查询操作

1. 按照ID查询

// 测试查询
@Test
public void testSelectById() {
    // 查询单个用户
    User user = userMapper.selectById(1L);
    System.out.println(user);

    // 查询多个用户, 要传入一个Collection
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
    for (User user1 : users) {
        System.out.println(user1);
    }
}
  • selectById 查询一个用户
  • selectBatchIds 查询多个用户, 参数为 Collection

2. 条件查询(使用Map)

// 按条件查询之一 使用map操作
@Test
public void testSelectByMap() {
    HashMap<String, Object> map = new HashMap<>();
    // 自定义要查询的条件
    map.put("name", "小明2");
    List<User> users = userMapper.selectByMap(map);
    for (User user : users) {
        System.out.println(user);
    }
}
  • 在 map 中存放要查询的条件, K-V键值对等效于 where K = V 这句SQL

  • 可以查询多个条件, 使用多个K-V键值对即可, MP会帮我们自动拼接SQL

  • K-V键值对中的value一般放一个Object!

3. 分页查询

1. 配置拦截器组件即可

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

2. 直接使用Page对象即可

// 测试分页查询
@Test
public void testPage() {
    //参数 1 : 当前页
    //参数 2 : 页面的大小
    //使用了分页插件后, 所有的分页操作也变的简单
    Page<User> userPage = new Page<>(2, 5);
    userMapper.selectPage(userPage, null);
    // getRecords 获得当前页所有的记录
    userPage.getRecords().forEach(System.out::println);
}
  • 使用 selectPage 方法只是按照分页查询的结果
  • 查询结果还是用的Page对象
  • Page泛型要传入一个对象

6. 删除操作

1. 基本的删除操作

可以看出, 与select中的操作类似, 删除单个, 删除多个, 按照条件删除

// 测试删除
@Test
public void testDeleteById() {
    userMapper.deleteById(1L);
}

2. 逻辑删除

  • 物理删除: 在数据库中直接移除
  • 逻辑删除: 在数据库中没有被移除, 而是通过一个变量, 来让他失效! deleted = 0 => deleted = 1
    • 管理员可以查看被删除的记录! 防止数据的丢失, 类似回收站!

1. 在数据库中加一个deleted字段

image-20201016135753730

默认值为0, 没有被删除

2. 实体类中增加一个属性

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

3. 配置逻辑删除组件

//  逻辑删除组件
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }
#配置逻辑删除(删除为0, 没有删除为1)
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

4. 测试删除

// 测试删除
@Test
public void testDeleteById() {
    userMapper.deleteById(2L);
}

image-20201016140619736

记录依旧在数据库, 但是值已经变化了

查询的时候会自动过滤被逻辑删除的数据(where deleted = 0)

image-20201016141715832

原文地址:https://www.cnblogs.com/wang-sky/p/13838629.html