前面的案列中我们没有指定id字段
但是它是生成了一个很长的id,并不是我们数据表定义自增
这是Mp内部算法出来的一个值
其实根据不同应用场景,应该使用不同的算法来生成主键的值
那么如何指定对于算法呢
我们需要使用@TableId注解,更改属性type来指定算法类型
如图
比如@TableId(type = IdType.AUTO)也就是自增类型
新增的就会在原来的id自增
@Test
void testInsert(){
User user = new User();
user.setName("小马");
user.setAge((short)10);
user.setGender((short)1);
user.setPhone("110");
userDao.insert(user);
}
类型 | 说明 |
---|---|
AUTO | 数据库ID自增,需保证对应ID为自增 |
NONE | 就是没有策略 |
INPUT | 用户需要自己输入ID的值(必须写) |
ASSIGN-ID | 雪花算法,分配ID,主键类型为number或string,整合过时的ID-WORKER和ID-WORKER_STR |
ASSIGN-UUID | 分配UUID,主键类型为string |
ID_WORKER | 过时,被ASSIGN_ID整合 |
ID_WORKER_STR | 过时,被ASSIGN_ID整合 |
UUID | 过时,被ASSIGN_UUID替代 |
原先ID_WORKER和ID_WORKER_STR就是雪花算法生成的,前者生成整数,后者生成字符串,两个整合成了ASSIGN-ID
同时ASSIGN-ID也是我们现在的默认使用的ID生成类型,且如果你指定了ID的话,会先用指定的
具体来搜一下雪花算法
就是生成64个位数,即8字节,所以数据库用BIGINT存储,Java程序中用Long存储
具体对应的位数有什么含义呢?
第一位占位符固定为0
2-42位是一个时间戳,精确到毫秒
43-53位是机器码,前五位群组标记,后五位机器标记(可以简单理解为mac地址)
最后12位是我们的序列号
为了防止生成的ID重复设置这样的格式,序列号的作用接收,可能一个机器在一毫秒内接收多个ID生成请求,为了防止重复,第一个请求的序列号就为1,这样就减少重复的概率
如标题所言
可以直接配置所有的ID生成id的策略
在对应的spring配置文件中,这样写即可
mybatis-plus:
global-config:
db-config:
id-type: assign_id
然后就可以注释到对应的@TableId效果是相同的
你甚至可以用全局配置来配置对应sql前缀就不用写实体类上的
@TableName(“tbl_user”),因为全局设置前缀就是tbl_
自动匹配类名小写拼接成tbl_user即数据库表名
删除一条数据很简单,之前也演示过-deleteById
我们主要讲解怎么一下删除多行数据
我们删除多行就是用deleteBatchIds方法,传参为集合,集合内容为ID,就会把对应集合中存储的ID数据删除
同样的我们的select也有该功能
效果一样的
一些数据,经过逻辑外键加持
如果你进行对应的员工删除,对应的业务也会消失,统计公司今年总体业务就可能有问题
所以我们要用软删除
可以用一个字段进行标记,这就叫逻辑删除
Mp给我们提供了注解,在下面的具体做法会讲到
逻辑删除操作
1.规定对应值的状态,我这里规定0为正常状态,1为被删除状态
2.在对应的表中添加字段(记得设置默认值,为没有离职)
3.在实体类中加上对应属性
需要加上@TableLogic(value=“没有删除对应状态值”,delval=“被删除对应的状态值”)
value决定了查询时的增添条件,delval影响删除数据时对应值的变化
我们测试一下,看看会不会真的删除
删除ID为1的数据
测试成功,只是deleted属性变为了1
看它底层执行的语句,执行的是update语句的
只有没有被删除且id匹配时会把deleted(逻辑删除属性/字段)改为1(删除状态值)
update 表名 set deleted=1 where id=? AND deleted=0
只弄这个字段修改可能大家认为还是能查询到(没有达到真正删除的效果),接下来我们就测试一下,逻辑删除能不能达到删除的效果
那我们就查一下整个全表呗
返回值
可以看到是没有id为1,即被逻辑删除的数据了
why?
看底层执行的sql语句
select id,name,age,tel,deleted from tbl_user where deleted=0
现在基本就能理解,就是我们设为逻辑的属性,会在sql查询的时候默认作为一个条件,where 逻辑删除属性=“没有被删除的状态值”
所以,我们基本可以了解@TableLogic的两个属性的作用了
也就是value和delval两个值
为了防止写错和提高代码复用性,我们可以进行全局设置
在spring配置文件下进行修改
mybatis-plus:
global-config:#全局设置
banner: false
db-config:#数据库相关设置
id-type: assign_id
#这个就是我们想设置成逻辑删除属性的属性名
logic-delete-field: deleted
#被逻辑删除的状态值
logic-delete-value: "1"
#未被逻辑删除的状态值
logic-not-delete-value: "0"
修改嘛
基础操作没什么好讲的
重点是可能遇到的问题
可能我们上架一个需要抢的产品
如果最后一个同时又好几个用户要抢,我们可能就会update把我们的商品剩余数变为负数
为了避免这种并发问题
我们可以用乐观锁
乐观锁适用于较少的请求量
那么乐观锁怎么实现呢?
同样
1.设置一个sql字段来记录目前是谁在操作这条数据(这条数据是第几版)
就是,比如是我们只有1000个商品,version从1到1000的数据就会被我们收集起来,多出的数据就不会收集,然后统计信息发货
一般名为version,int类型
2.写对应的类属性
并且用@Version注解标注
加锁的实现过程,主要是sql语句的变更进行的加锁
update set abc = 1 version=version+1 where version=1
为什么这样能实现锁的功能?
比如我是用户a,另一个是用户b,我们都在version=1的时候进行修改数据,b先修改了数据,对应version发生改变,变成了2,然后我执行语句的时候有一个version=1的限制条件,我就没法修改了
可以看出,这段其实是先查后改,先查version的值,然后
update set … version=version+1 where version=查到的值
我们肯定是要在对应的sql加上一些语句
就像之前的分页,我们用拦截器实现,这里乐观锁也是用拦截器实现
而version的作用只是告诉拦截器要对哪个字段进行操作
具体还是想分页拦截器那样,把乐观锁拦截器对应的类加入到Mp拦截器群中,就能加sql语句了
然后就是,我们修改数据的时候,一定要设置version才会有效
不要太天真,执行的语句是
update set ... version=version+1 where version=对象中的version值
如果没有 version值的话也起不到锁的作用
还有就是一般是先查,然后返回结果给version嘛
全代码应该是这样的
就是我们是直接把对应的对象给返回,对应version也会自动赋值上去
然后我们再修改我们想要改的(而不是查询对应的version再set)
下图就是加锁的代码+思路
和我们上面分析的一样
看一下对应的日志
可以看到第二句并没有执行
其实这里就和CRUD没关系了
这个是提高开发效率的
就是快速生成Mp的代码
比如Dao层
其实就跟造句一样,就是填入那些不固定的
Dao层不固定是什么,就是我们对应里的实体类的导入啊
就是如图三个空格,添加的就是我们的实体类
就是
模板+参数
然后我们简化一下实体类开发,看看怎么简化
就是我们可以分为两个参数
一个就是从数据库读取的,一个是人为设置的
这样也是一套模板
Mp就把这些模板抽取出来,就做了一个代码生成器
使用代码生成器
步骤
需要导入在maven导入依赖
1.代码生成器依赖 2.模板依赖
里面写个main方法
创建代码生成器,添加一系列配置(因为是有点是从数据库还要读取信息,所以数据库信息肯定是要配置的),然后执行代码生成器
配置的话,主要是数据库的配置
新建一个数据库配置类,然后用set方法定义对应的url和username,password等信息
但是你运行后
他就会弹窗出来对应输出的文件夹位置,默认就直接生成在d盘
但是和我们想的还是不对(我们想直接生成在项目文件中)
不止数据库的信息
首先全局配置信息
新建一个全局配置类
设置对应的输出目录
生成完毕后,不直接打开目录
设置作者(就是添加一个注释,写上作者名)
设置如果前面输出过,是否覆盖
setMapperName设置数据层接口名
setIdType这个就不多说,id生成策略
生成后可以看到
默认的包是com.baomidou
不会在itheima包下直接生成
还有就是setMapperName
默认生成的接口名字是
数据库表名(首字母大写)+Mapper
然后
setMapperName("%sDao")设置数据层接口名,%s就占位符,就对应的数据库表名首字母大写
所以我们输出的就是Tbl_logDao
包名设置
PackageConfig()
注意的是:这个和我们对应的文件路径是不冲突的,这个只是指定包,而不指定目录
先说默认他给我们设置的包名吧
功能 | 默认包名 |
---|---|
默认顶级包 | com.baomidou |
实体类包 | entity |
数据层包 | mapper |
逻辑层 | service |
处理层 | controller |
现在我们想要就是直接生成
在com.itheima下,实体类包名为domain,数据层包名为dao
处理层和逻辑层是不变的
策略设置
StrategyConfig
重点中的重点
就是设置我们那些
@TableName,@Version,@TableField等注解加到什么属性上
首先先要定义我们要生成哪个表
就是setInclude(“表1名”,“表2名”,…)
就会生成多个表对应的三层架构
一般都是一次生成一个奥
看那个前缀
SetTablePrefix就是设置对应的前缀,这样的话tbl_就不会参与实体类名和其他层对应名的生成(这我是没想到的,我还是从自己定义来理解的,但他其实是从数据库里查表名建类的)
包含service类以及dao层接口都不会带tbl_
service接口通用开发习惯就是I开头
setRestControllerStyle(true)
默认是false,生成的 controller,不是@RestController注解
这个就是开启rest风格的
这个是实体类是否用lombok
这里就是指定乐观锁和逻辑删除的字段