# 1. 插入测试及雪花算法
**CRUD扩展**
> Insert插入
```java
@Test
void testInsert(){
User user = new User();
user.setName("levnli").setAge(23).setEmail("levnliservice@126.com");
// 自动生成id
int result = userMapper.insert(user);
// 受影响的行数
System.out.println(result);
// 发现 id 会自动回填
System.out.println(user);
}
```
> 数据库插入的id的默认值为:全局唯一的
**主键生成策略:雪花算法**
* id自增策略是:ID_WORKER
* snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID,其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一
# 2. 不同主键策略测试
```java
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 全局唯一ID (idWorker)
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
```
* 如果想选用策略为:AUTO
* 实体类字段上添加`@TableId(type = IdType.AUTO)`
* 数据库字段一定要是自增!
* 再次测试插入
# 3. 更新操作
```java
@Test
void testUpdate(){
User user = new User().setId(1336837066142076930L).setName("test");
int i = userMapper.updateById(user);
System.out.println(i);
}
```
* 所有的sql都是自动拼接,动态配置的
**自动装填**
* 创建时间、修改时间!这些操作都是自动化完成的,不希望手动更新
* 所有的数据库表:gmt_create、gmt_modified,几乎所以的表都要配置上,而且需要自动填充
# 4. 自动填充处理
> 方式一:数据库级别(工作中不允许修改数据库)
* 在表中新增字段create_time,update_time
```mysql
ALTER TABLE `mybatis_plus`.`user`
ADD COLUMN `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' AFTER `email`,
ADD COLUMN `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' AFTER `create_time`;
```
* 再次测试插入方法,先把实体类同步
* ```java
private Date createTime;
private Date updateTime;
```
* 再次更新查看即可
> 方式二:代码级别
* 删除数据库的默认值、更新操作!
* 去掉时间配置
* ALTER TABLE `mybatis_plus`.`user`
MODIFY COLUMN `create_time` datetime(0) NULL COMMENT '创建时间' AFTER `email`,
MODIFY COLUMN `update_time` datetime(0) NOT NULL COMMENT '更新时间' AFTER `create_time`;
* 实体类的字段属性上需要增加注解
* ```
// 字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
```
* 编写处理器,来处理这个注解即可
```java
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时的填充策略
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill....");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
/**
* 更新时的填充策略
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("start updateTime fill....");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
```
* 测试插入、更新
* 观察时间即可
# 5. 乐观锁处理详解
* 在面试过程中,经常被问到乐观锁、悲观锁
* 乐观锁
* 十分乐观,不会认为出现问题
* 无论干什么都不会上锁
* 如果出现了问题,再次更新值测试
* version、new version
* 悲观锁
* 十分悲观,总是认为都会出现问题
* 无论干什么都会上锁!再去操作
* 主要讲解乐观锁机制
* 乐观锁实现方式:
* 取出记录时,获取当前version
* 更新时,带上这个version
* 执行更新时, set version = newVersion where version = oldVersion
* 如果version不对,就更新失败
```mysql
乐观锁:先查询,获得版本号version = 1
-- A线程
update user set name = "test" , version = version + 1
where id = 2 and version = 1
-- B线程
update user set name = "test" , version = version + 1
where id = 2 and version = 1
结果B线程抢先完成,这时候version = 2,导致A线程修改失败
```
> 测试一下MP的乐观锁插件
* 给数据库中添加version字段
* ALTER TABLE `mybatis_plus`.`user`
MODIFY COLUMN `version` int(10) NULL DEFAULT 1 COMMENT '乐观锁' AFTER `update_time`
ADD COLUMN `version` int(10) NULL COMMENT '乐观锁' AFTER `update_time`;
* 实体类添加对应的字段
* ```
/**
* 乐观锁version注解
*/
@Version
private Integer version;
```
* 注册组件
```java
@EnableTransactionManagement
@MapperScan("com.example.mapper")
@Configuration
public class MyBatisPlusConfig {
// 注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
```
* 测试一下
```java
@Test
void testOptimisticLocker(){
// 查询用户信息
User user = userMapper.selectById(6L);
// 修改用户信息
user.setEmail("123@qq.com");
// 执行更新
userMapper.updateById(user);
}
@Test
void testOptimisticLockerThread(){
// 模拟乐观锁,多线程并发
// 线程1
User user = userMapper.selectById(6L);
user.setEmail("111@qq.com");
// 线程2,抢先提交
User user2 = userMapper.selectById(6L);
user2.setEmail("222@qq.com");
userMapper.updateById(user2);
userMapper.updateById(user);
}
```
# 6. 查询操作
* 批量查询和条件查询
```java
/**
* 测试批量查询
*/
@Test
void testSelectByIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
users.forEach(System.out::println);
}
/**
* 条件查询之一使用 map
*/
@Test
void selectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","levnli");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
```
# 7. 分页查询实现
* 分页查询在网站使用十分之多!
* 原始的limit进行分页
* pageHelper第三方插件
* MP其实也内置了分页插件
> 如何使用
* 配置拦截器组件即可
* ```
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
```
* 直接使用Page对象即可
* ```java
@Test
void testPage(){
// 参数:当前页、页面大小
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page,null);
// 打印查询结果
page.getRecords().forEach(System.out::println);
// 当前表总共条数
System.out.println(page.getTotal());
}
```
# 8. 删除操作
* 根据ID删除记录
```java
/**
* 通过ID删除
*/
@Test
void testDeleteById(){
userMapper.deleteById(1336837066142076931L);
}
```
* 根据ID批量删除
```java
/**
* 通过ID批量删除
*/
@Test
void testDeleteBatchIds(){
userMapper.deleteBatchIds(Arrays.asList(1336837066142076931L));
}
```
* 根据条件删除
```java
/**
* 通过条件删除
*/
@Test
void testDeleteMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","test2");
userMapper.deleteByMap(map);
}
```
# 9. 逻辑删除
* 物理删除:从数据库中直接移除
* 逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!
* deleted = 0 -> deleted = 1
* 管理员可以查看被删除的记录!防止数据的丢失,类似于回收站
测试一下:
* 在数据报中增加一个`deleted`字段
```mysql
ALTER TABLE `mybatis_plus`.`user`
ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '删除标志位' AFTER `version`;
```
* 在实体类上添加字段
```java
/**
* 逻辑删除
*/
@TableLogic
private Integer deleted;
```
* 添加插件
```java
// 逻辑删除插件
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
```
* 配置逻辑删除
```yml
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
```
# 10. 性能分析插件
开发中,会遇到慢sql
MP也提供性能分析插件,如果超过这个时间就是停止运行
* 导入插件
```java
// SQL执行效率插件
@Bean
@Profile("dev")
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
// sql最大执行时间,超过不执行
performanceInterceptor.setMaxTime(100);
// 输出sql格式化
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
```
* 修改application.yml
```yml
spring:
profiles:
active: dev
```
* 测试使用
* 执行任何一个方法
Mybatis-Plus使用(二)操作语法