首先看这样一段简单的插入代码:
@Test
void insertTest() {
User user = new User();
user.setName("zhangsan");
user.setAge(5);
user.setEmail("[email protected]");
userMapper.insert(user);
}
执行代码后我们查看数据,会看到一条新数据产生,但是请重点关注以下id字段
这里的id数据库类型是long,id并未自增,mybatisPlus会帮我们自动生产,而这个生产策略是使用了『雪花算法 - SnowFlake https://blog.csdn.net/lq18050010830/article/details/89845790』
等同于pojo中给id加了如下注解
@TableId(type = IdType.ID_WORKER)
private Long id;
接下来我们解读TableId的type类型 - IdType
public enum IdType {
/*
主键自增
1、实体类字段注解:@TableId(type = IdType.AUTO)
2、数据库字段一定是自增的,如果数据库未设置自增会报错
*/
AUTO(0),
// 不维护主键
NONE(1),
// 手动输入
INPUT(2),
// 全球唯一id
ID_WORKER(3),
// 全球唯一id
UUID(4),
// WORKER的字符串表示法
ID_WORKER_STR(5);
private int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
代码如下:
@Test
void updateTest() {
User user = new User();
user.setId(1407693933195702274L);
user.setName("zhangsan1");
user.setAge(18);
userMapper.updateById(user);
}
这里的 updateById 接受的并非id,而是一个对象,这个方法可以实现动态sql,可以根据id更新其他已经填充的字段,我们看下下面的这张图,注意sql位置
创建时间、修改时间,这些操作我们不希望人工维护,一般都是自动完成的,阿里巴巴java开发手册明确规定,所有的数据库表,gmt_create/gmt_modified(格林尼治时间) 这两个字段必须存在,并且必须自动填充
在数据库创建两个字段:gmt_create/gmt_modified,时间字段默认值设置为:CURRENT_TIMESTAMP,更新时间字段根据数据库更新操作自动更新
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这里注意:数据库中时间字段要求数据类型为:datetime,对应java中的util包Date
private Date gmtCreate;
private Date gmtModified;
到此方式一已经完成,测试观察时间变化
1、删除数据库对应时间字段的默认值,自动更新操作,保证时间字段在数据库层面什么操作都没有!
2、在对应java实体类的时间属性上增加注解『@TableField』
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
3、编写一个处理器(这个处理器官方已经给出了)
@Slf4j
@Component // 处理器已经要加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
// default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
到此方式二已经完成,测试观察时间变化
乐观锁(version):顾名思义总是非常乐观,它总是认为数据不会出现问题,无论干什么都不加锁!每次更新,版本号都会随之变化,如果出现问题,则再次更新值测试 悲观锁:顾名思义总是非常悲观,它总是认为数据会出现问题,无论干什么都加锁!
乐观锁基本操作见下图
举个例子,sql见下
-- 线程A:查询,获得当前数据的version=1,再更新
update user set name = "xxx", version = version + 1
where id = 1 and version = 1
-- 线程B:查询,获得当前数据的version=1,再更新,这时候线程B抢先完成了更新操作,这时version的值等于2,就会导致线程A更新失败
update user set name = "xxx", version = version + 1
where id = 1 and version = 1
mybatis-plus对乐观锁的支持,我们需要完成以下几步
保证数据库中存在 version 字段,且默认值为1
对应java实体类存在 verison 属性,并且为version字段添加注解『@Version』,这个version注解是mybatis-plus包下的
@Version
private Integer version;
注册插件
@MapperScan("com.sun.mapper")
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
// 注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
更新数据,查看乐观锁支持情况,注意看下sql执行情况
这里在执行update的时候,增加了对version的操作
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
Map<String, Object> map = new HashMap<>();
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
传统分页 limit、pageHelper , mybatis-plus 只需要在MybatisPlusConfig中注册分页插件即可实现分页
// 注册分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
执行分页查询代码见下:
// 查询第一页,每页5条
Page<User> page = new Page<>(1, 5);
IPage<User> iPage = userMapper.selectPage(page, null);
得到数据见图:
『Ipage』对象中的可调用方法也显而易见,分页常用的数据都包含其中
现在再分析一下sql
执行2次sql,第一次查询总数,第二次使用limit查询所需数据
物理删除:从数据库中直接删除 逻辑删除:在数据库中没有直接删除数据,而是通过一个变量让数据失效:deleted=0 标记未删除 deleted=1 标记删除
@TableLogic
@TableLogic
private Integer deleted;
mybatis-plus.global-config.db-config.logic-delete-value=1 # 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-not-delete-value=0 # 逻辑未删除值(默认为 0)
// 注册逻辑删除插件
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
我们可以看到,逻辑删除生效了,在执行delete操作的时候,mybatis-plus并没有直接对数据库的数据进行抹除操作,而是进行了更新操作,将对应数据的deleted字段更为了1