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);
}
测试结果
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 雪花算法
-
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. 数据库级别(工作中不允许修改数据库)
- 在表中新增字段 creat_time, update_time
注意要在update_time字段上勾选更新, 默认值写CURRENT_TIMESTAMP
- 先将实体类同步
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;
}
- 再次更新查看结果
2. 代码级别
- 删除默认值和更新
- 实体类的字段属性上需要增加注解
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为空
- 编写处理器来处理这个注解即可
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字段
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);
}
可以看出, 线程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字段
默认值为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);
}
记录依旧在数据库, 但是值已经变化了
查询的时候会自动过滤被逻辑删除的数据(where deleted = 0)