02 CRUD扩展
CRUD扩展
插入操作
@Test
public void testInsert(){
User user = new User();
user.setName("芜湖");
user.setAge(18);
user.setEmail("151548@qq.com");
usermapper.insert(user);//帮我们自动生成id
}
数据库插入的id的默认值为:全局的唯—id
主键生成策略
分布式系统唯一Id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html
源码解释
public enum IdType {
AUTO(0),//数据库id自增
NONE(1),//未设置主键
INPUT(2),// 手动输入
ASSIGN_ID(3),
ASSIGN_UUID(4),
}
主键自增:AUTO 我们需要配置主键自增
- 在实体类字段上配置
@TableId(type = IdType.AUTO)
- 数据库字段一定是自增
手动输入:INPUT 就需要自己写id
-
在实体类字段上配置
@TableId(type = IdType.INPUT)
-
Twitter的snowflake算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心(北京、香港···),5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
- 具体实现的代码可以参看https://github.com/twitter/snowflake。
更新操作
@Test
public void testUpdate(){
User user = new User();
//通过条件自动拼接动态sql
user.setId(5l);
user.setName("啊哈");
//注意方法名:updateById 但是参数是一个 对象!
usermapper.updateById(user);
}
所有的sql都是帮你自动配置的
自动填充
创建时间、更改时间! 这些操作一般都是自动化完成,我们不希望手动更新
阿里巴巴开发手册︰几乎所有的表都要配置 gmt_create、gmt_modified !而且需要自动化
方式一:数据库级别(工作中不允许修改数据库级别)
1、在表中增加字段:create_time,update_time
2、再次测试插入或更新方法,我们需要在实体类中同步!
方式二:代码级别
1、数据库字段的没有默认值
2、User类 实体类字段属性上需要增加注解
//字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
3、编写处理器来处理这个注解即可!
@Slf4j
@Component //一定要把处理器加到 IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
// default <T, E extends T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {
this.strictInsertFill(metaObject,"createTime", LocalDateTime.class,LocalDateTime.now());
this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class,LocalDateTime.now());
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class,LocalDateTime.now());
}
}
4、测试插入/更新,观察时间
乐观锁&悲观锁
在面试过程中经常被问到乐观锁/悲观锁,这个其实很简单
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不上锁!如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,他总是认为出现问题,无论干什么都会上锁!再去操作!
我们这里主要讲解 乐观锁机制!
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时,set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
乐观锁:先查询,获得版本号
-- A
update user set name = "wsk",version = version+1
where id = 1 and version = 1
-- B (B线程抢先完成,此时version=2,会导致A线程修改失败!)
update user set name = "wsk",version = version+1
where id = 1 and version = 1
测试一下Mybatis-Plus乐观锁插件
1、给数据库中增加version字段
2、实体类加对应的字段
@Version//乐观锁version注解
private Integer version;
3、注册组件
//扫描我们的mapper 文件夹
@MapperScan("com.kuang.mapper")
@EnableTransactionManagement
public class MyBatisPlusConfig {
//注册乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
4、测试一下
- 成功
@Test//测试乐观锁成功
public void testOptimisticLocker1(){
//1、查询用户信息
User user = userMapper.selectById(1L);
//2、修改用户信息
user.setAge(18);
user.setEmail("2803708553@qq.com");
//3、执行更新操作
userMapper.updateById(user);
}
- 失败
@Test//测试乐观锁失败 多线程下
public void testOptimisticLocker2(){
//线程1
User user1 = userMapper.selectById(1L);
user1.setAge(1);
user1.setEmail("2803708553@qq.com");
//模拟另外一个线程执行了插队操作
User user2 = userMapper.selectById(1L);
user2.setAge(2);
user2.setEmail("2803708553@qq.com");
userMapper.updateById(user2);
//自旋锁来多次尝试提交!
userMapper.updateById(user1);//如果没有乐观锁就会覆盖插队线程的值
}
查询操作
- 通过id查询单个用户
@Test//通过id查询单个用户
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
- 通过id查询多个用户
@Test//通过id查询多个用户
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
users.forEach(System.out::println);
//System.out.println(users);
}
- 条件查询 通过map封装
@Test//通过条件查询之一 map
public void testMap(){
HashMap<String, Object> map = new HashMap<>();
//自定义要查询的
map.put("name","www");
map.put("age",18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
分页在网站的使用十分之多!
1、原始的limit分页
2、pageHelper第三方插件
3、MybatisPlus其实也内置了分页插件!
如何使用:
1、配置拦截器组件
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
2、直接使用page对象即可
@Test//测试分页查询
public void testPage(){
//参数一current:当前页 参数二size:页面大小
//使用了分页插件之后,所有的分页操作都变得简单了
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println("总页数==>"+page.getTotal());
}
删除操作
基本的删除操作:
@Test//通过id删除
public void testDeleteById(){
userMapper.deleteById(1447818839571083267L);
}
@Test//通过id批量删除
public void testDeleteBatchIds(){
userMapper.deleteBatchIds(Arrays.asList(1359507762519068675L,1359507762519068676L));
}
//通过map删除
@Test
public void testD(){
HashMap<String, Object> map = new HashMap<>();
map.put("age","18");
map.put("name","哈哈哈");
userMapper.deleteByMap(map);
}
逻辑删除
步骤1: 配置com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
步骤2: 实体类字段上加上@TableLogic
注解
@TableLogic
private Integer deleted;
4、测试一下删除
发现: 记录还在,deleted变为1
再次测试查询被删除的用户,发现查询为空
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)