找深圳做网站的公司,wordpress插件实现响应式,tiktok跨境电商怎么做,众展seo推广MyBatisPlus 1#xff0c;DML编程控制1.1 id生成策略控制知识点1#xff1a;TableId1.1.1 环境构建1.1.2 代码演示AUTO策略步骤1:设置生成策略为AUTO步骤3:运行新增方法 INPUT策略步骤1:设置生成策略为INPUT步骤2:添加数据手动设置ID步骤3:运行新增方法 ASSIGN_ID策略步骤1:设… MyBatisPlus 1DML编程控制1.1 id生成策略控制知识点1TableId1.1.1 环境构建1.1.2 代码演示AUTO策略步骤1:设置生成策略为AUTO步骤3:运行新增方法 INPUT策略步骤1:设置生成策略为INPUT步骤2:添加数据手动设置ID步骤3:运行新增方法 ASSIGN_ID策略步骤1:设置生成策略为ASSIGN_ID步骤2:添加数据不设置ID步骤3:运行新增方法 ASSIGN_UUID策略步骤1:设置生成策略为ASSIGN_UUID步骤2:修改表的主键类型步骤3:运行新增方法 1.1.3 ID生成策略对比1.1.4 简化配置模型类主键策略设置数据库表与模型类的映射关系 1.2 多记录操作1.3 逻辑删除步骤1:修改数据库表添加deleted列步骤2:实体类添加属性步骤3:运行删除方法知识点1TableLogic 1.4 乐观锁1.4.1 概念1.4.2 实现思路1.4.3 实现步骤步骤1:数据库表添加列步骤2:在模型类中添加对应的属性步骤3:添加乐观锁的拦截器步骤4:执行更新操作 2快速开发2.1 代码生成器原理分析2.2 代码生成器实现步骤1:创建一个Maven项目代码2:导入对应的jar包步骤4:创建代码生成类步骤5:运行程序 1DML编程控制
1.1 id生成策略控制
在使用MP新增成功后主键ID是一个很长串的内容我们更想要的是按照数据库表字段进行自增长所以我们先来分析下ID该如何选择:
不同的表应用不同的id生成策略 日志自增1,2,3,4……购物订单特殊规则FQ23948AK3843外卖单关联地区日期等信息10 04 20200314 34 91关系表可省略id……
不同的业务采用的ID生成方式应该是不一样的那么在MP中都提供了哪些主键生成策略以及我们该如何进行选择?
在这里我们又需要用到MP的一个注解叫TableId
知识点1TableId
名称TableId类型属性注解位置模型类中用于表示主键的属性定义上方作用设置当前类中主键属性的生成策略相关属性value(默认)设置数据库表主键名称type:设置主键属性的生成策略值查照IdType的枚举值
1.1.1 环境构建
创建一个SpringBoot项目
1.1.2 代码演示
AUTO策略
步骤1:设置生成策略为AUTO
Data
TableName(tbl_user)
public class User {TableId(type IdType.AUTO)private Long id;private String name;TableField(valuepwd,selectfalse)private String password;private Integer age;private String tel;TableField(existfalse)private Integer online;
}步骤3:运行新增方法
会发现新增成功并且主键id也是从5开始。
经过演示会发现AUTO的作用是使用数据库ID自增在使用该策略的时候一定要确保对应的数据库表设置了ID主键自增否则无效。
接下来我们可以进入源码查看下ID的生成策略有哪些?
打开源码后你会发现并没有看到中文注释这就需要我们点击右上角的Download Sources,会自动帮你把这个类的java文件下载下来我们就能看到具体的注释内容。因为这个技术是国人制作的所以他代码中的注释还是比较容易看懂的。
当把源码下载完后就可以看到如下内容:
从源码中可以看到除了AUTO这个策略以外还有如下几种生成策略:
NONE: 不设置id生成策略INPUT:用户手工输入idASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型)ASSIGN_UUID:以UUID生成算法作为id生成策略其他的几个策略均已过时都将被ASSIGN_ID和ASSIGN_UUID代替掉。
拓展:
分布式ID是什么?
当数据量足够大的时候一台数据库服务器存储不下这个时候就需要多台数据库服务器进行存储比如订单表就有可能被存储在不同的服务器上如果用数据库表的自增主键因为在两台服务器上所以会出现冲突这个时候就需要一个全局唯一ID,这个ID就是分布式ID。
INPUT策略
步骤1:设置生成策略为INPUT
Data
TableName(tbl_user)
public class User {TableId(type IdType.INPUT)private Long id;private String name;TableField(valuepwd,selectfalse)private String password;private Integer age;private String tel;TableField(existfalse)private Integer online;
}注意:这种ID生成策略需要将表的自增策略删除掉
步骤2:添加数据手动设置ID
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testSave(){User user new User();//设置主键ID的值user.setId(666L);user.set....;userDao.insert(user);}
}步骤3:运行新增方法
如果设置主键ID的值则运行成功。
如果没有设置主键ID的值则会报错错误提示就是主键ID没有给值:
ASSIGN_ID策略
步骤1:设置生成策略为ASSIGN_ID
Data
TableName(tbl_user)
public class User {TableId(type IdType.ASSIGN_ID)private Long id;private String name;TableField(valuepwd,selectfalse)private String password;private Integer age;private String tel;TableField(existfalse)private Integer online;
}步骤2:添加数据不设置ID
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testSave(){User user new User();user.setName(黑马程序员);user.setPassword(itheima);user.setAge(12);user.setTel(4006184000);userDao.insert(user);}
}注意:这种生成策略不需要手动设置ID如果手动设置ID则会使用自己设置的值。
步骤3:运行新增方法
生成的ID就是一个Long类型的数据。
ASSIGN_UUID策略
步骤1:设置生成策略为ASSIGN_UUID
使用uuid需要注意的是主键的类型不能是Long而应该改成String类型
Data
TableName(tbl_user)
public class User {TableId(type IdType.ASSIGN_UUID)private String id;private String name;TableField(valuepwd,selectfalse)private String password;private Integer age;private String tel;TableField(existfalse)private Integer online;
}步骤2:修改表的主键类型 主键类型设置为varchar长度要大于32因为UUID生成的主键为32位如果长度小的话就会导致插入失败。
步骤3:运行新增方法
生成的ID就是一个String类型的数据。
接下来我们来聊一聊雪花算法:
雪花算法(SnowFlake),是Twitter官方给出的算法实现 是用Scala写的。其生成的结果是一个64bit大小整数它的结构如下图:
1bit,不用,因为二进制中最高位是符号位1表示负数0表示正数。生成的id一般都是用整数所以最高位固定为0。41bit-时间戳用来记录时间戳毫秒级10bit-工作机器id用来记录工作机器id,其中高位5bit是数据中心ID其取值范围0-31低位5bit是工作节点ID其取值范围0-31两个组合起来最多可以容纳1024个节点序列号占用12bit每个节点每毫秒0开始不断累加最多可以累加到4095一共可以产生4096个ID
1.1.3 ID生成策略对比
介绍了这些主键ID的生成策略我们以后该用哪个呢?
NONE: 不设置id生成策略MP不自动生成约等于INPUT,所以这两种方式都需要用户手动设置但是手动设置第一个问题是容易出现相同的ID造成主键冲突为了保证主键不冲突就需要做很多判定实现起来比较复杂AUTO:数据库ID自增,这种策略适合在数据库服务器只有1台的情况下使用,不可作为分布式ID使用ASSIGN_UUID:可以在分布式的情况下使用而且能够保证唯一但是生成的主键是32位的字符串长度过长占用空间而且还不能排序查询性能也慢ASSIGN_ID:可以在分布式的情况下使用生成的是Long类型的数字可以排序性能也高但是生成的策略和服务器时间有关如果修改了系统时间就有可能导致出现重复主键综上所述每一种主键策略都有自己的优缺点根据自己项目业务的实际情况来选择使用才是最明智的选择。
1.1.4 简化配置
前面我们已经完成了表关系映射、数据库主键策略的设置接下来对于这两个内容的使用我们再讲下他们的简化配置:
模型类主键策略设置
对于主键ID的策略已经介绍完但是如果要在项目中的每一个模型类上都需要使用相同的生成策略如:
确实是稍微有点繁琐我们能不能在某一处进行配置就能让所有的模型类都可以使用该主键ID策略呢?
答案是肯定有我们只需要在配置文件中添加如下内容:
mybatis-plus:global-config:db-config:id-type: assign_id配置完成后每个模型类的主键ID策略都将成为assign_id.
数据库表与模型类的映射关系
MP会默认将模型类的类名名首字母小写作为表名使用假如数据库表的名称都以tbl_开头那么我们就需要将所有的模型类上添加TableName如: 配置起来还是比较繁琐简化方式为在配置文件中配置如下内容:
mybatis-plus:global-config:db-config:table-prefix: tbl_设置表的前缀内容这样MP就会拿 tbl_加上模型类的首字母小写就刚好组装成数据库的表名。
1.2 多记录操作
实现多条删除我们先找找对应的API方法。
int deleteBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList);翻译方法的字面意思为:删除根据ID 批量删除,参数是一个集合可以存放多个id值。 需求:根据传入的id集合将数据库表中的数据删除掉。 SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testDelete(){//删除指定多条数据ListLong list new ArrayList();list.add(1402551342481838081L);list.add(1402553134049501186L);list.add(1402553619611430913L);userDao.deleteBatchIds(list);}
}执行成功后数据库表中的数据就会按照指定的id进行删除。
除了按照id集合进行批量删除也可以按照id集合进行批量查询还是先来看下API
ListT selectBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList);方法名称翻译为:查询根据ID 批量查询参数是一个集合可以存放多个id值。 需求根据传入的ID集合查询用户信息 SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testGetByIds(){//查询指定多条数据ListLong list new ArrayList();list.add(1L);list.add(3L);list.add(4L);userDao.selectBatchIds(list);}
}查询结果就会按照指定传入的id值进行查询。
1.3 逻辑删除
接下来要讲解是删除中比较重要的一个操作逻辑删除先来分析下问题: 这是一个员工和其所签的合同表关系是一个员工可以签多个合同是一个一(员工)对多(合同)的表。 员工ID为1的张业绩总共签了三个合同如果此时他离职了我们需要将员工表中的数据进行删除会执行delete操作。 如果表在设计的时候有主外键关系那么同时也得将合同表中的前三条数据也删除掉 后期要统计所签合同的总金额就会发现对不上原因是已经将员工1签的合同信息删除掉了 如果只删除员工不删除合同表数据那么合同的员工编号对应的员工信息不存在那么就会出现垃圾数据就会出现无主合同根本不知道有张业绩这个人的存在 所以经过分析我们不应该将表中的数据删除掉而是需要进行保留但是又得把离职的人和在职的人进行区分这样就解决了上述问题如: 区分的方式就是在员工表中添加一列数据deleted如果为0说明在职员工如果离职则将其改完10和1所代表的含义是可以自定义的。
所以对于删除操作业务问题来说有:
物理删除:业务数据从数据库中丢弃执行的是delete操作逻辑删除:为数据设置是否可用状态字段删除时设置状态字段为不可用状态数据保留在数据库中执行的是update操作
MP中逻辑删除具体该如何实现?
步骤1:修改数据库表添加deleted列
字段名可以任意内容也可以自定义比如0代表正常1代表删除可以在添加列的同时设置其默认值为0正常。
步骤2:实体类添加属性
(1)添加与数据库表的列对应的一个属性名名称可以任意如果和数据表列名对不上可以使用TableField进行关系映射如果一致则会自动对应。
(2)标识新增的字段为逻辑删除字段使用TableLogic
Data
//TableName(tbl_user) 可以不写是因为配置了全局配置
public class User {TableId(type IdType.ASSIGN_UUID)private String id;private String name;TableField(valuepwd,selectfalse)private String password;private Integer age;private String tel;TableField(existfalse)private Integer online;TableLogic(value0,delval1)//value为正常数据的值delval为删除数据的值private Integer deleted;
}步骤3:运行删除方法
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testDelete(){userDao.deleteById(1L);}
}从测试结果来看逻辑删除最后走的是update操作会将指定的字段修改成删除状态对应的值。
思考
逻辑删除对查询有没有影响呢? 执行查询操作 SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testFind(){System.out.println(userDao.selectList(null));}
}运行测试会发现打印出来的sql语句中会多一个查询条件如: 可想而知MP的逻辑删除会将所有的查询都添加一个未被删除的条件也就是已经被删除的数据是不应该被查询出来的。 如果还是想把已经删除的数据都查询出来该如何实现呢? Mapper
public interface UserDao extends BaseMapperUser {//查询所有数据包含已经被删除的数据Select(select * from tbl_user)public ListUser selectAll();
}如果每个表都要有逻辑删除那么就需要在每个模型类的属性上添加TableLogic注解如何优化? 在配置文件中添加全局配置如下: mybatis-plus:global-config:db-config:# 逻辑删除字段名logic-delete-field: deleted# 逻辑删除字面值未删除为0logic-not-delete-value: 0# 逻辑删除字面值删除为1logic-delete-value: 1介绍完逻辑删除逻辑删除的本质为:
逻辑删除的本质其实是修改操作。如果加了逻辑删除字段查询数据时也会自动带上逻辑删除字段。
执行的SQL语句为:
UPDATE tbl_user SET deleted1 where id ? AND deleted0
知识点1TableLogic
名称TableLogic类型属性注解位置模型类中用于表示删除字段的属性定义上方作用标识该字段为进行逻辑删除的字段相关属性value逻辑未删除值delval:逻辑删除值
1.4 乐观锁
1.4.1 概念
在讲解乐观锁之前我们还是先来分析下问题:
业务并发现象带来的问题:秒杀
假如有100个商品或者票在出售为了能保证每个商品或者票只能被一个人购买如何保证不会出现超买或者重复卖对于这一类问题其实有很多的解决方案可以使用第一个最先想到的就是锁锁在一台服务器中是可以解决的但是如果在多台服务器下锁就没有办法控制比如12306有两台服务器在进行卖票在两台服务器上都添加锁的话那也有可能会导致在同一时刻有两个线程在进行卖票还是会出现并发问题。
简单来说乐观锁主要解决的问题是当要更新一条记录的时候希望这条记录没有被别人更新。
1.4.2 实现思路
乐观锁的实现方式: 数据库表中添加version列比如默认值给1第一个线程要修改数据之前取出记录时获取当前数据库中的version1第二个线程要修改数据之前取出记录时获取当前数据库中的version1第一个线程执行更新时set version newVersion where version oldVersion newVersion version1 [2]oldVersion version [1] 第二个线程执行更新时set version newVersion where version oldVersion newVersion version1 [2]oldVersion version [1] 假如这两个线程都来更新数据第一个和第二个线程都可能先执行 假如第一个线程先执行更新会把version改为2第二个线程再更新的时候set version 2 where version 1,此时数据库表的数据version已经为2所以第二个线程会修改失败假如第二个线程先执行更新会把version改为2第一个线程再更新的时候set version 2 where version 1,此时数据库表的数据version已经为2所以第一个线程会修改失败不管谁先执行都会确保只能有一个线程更新数据这就是MP提供的乐观锁的实现原理分析。 上面所说的步骤具体该如何实现呢?
1.4.3 实现步骤
分析完步骤后具体的实现步骤如下:
步骤1:数据库表添加列
列名可以任意比如使用version,给列设置默认值为1
步骤2:在模型类中添加对应的属性
根据添加的字段列名在模型类中添加对应的属性值
Data
//TableName(tbl_user) 可以不写是因为配置了全局配置
public class User {TableId(type IdType.ASSIGN_UUID)private String id;private String name;TableField(valuepwd,selectfalse)private String password;private Integer age;private String tel;TableField(existfalse)private Integer online;private Integer deleted;Versionprivate Integer version;
}步骤3:添加乐观锁的拦截器
Configuration
public class MpConfig {Beanpublic MybatisPlusInterceptor mpInterceptor() {//1.定义Mp拦截器MybatisPlusInterceptor mpInterceptor new MybatisPlusInterceptor();//2.添加乐观锁拦截器mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mpInterceptor;}
}步骤4:执行更新操作
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testUpdate(){User user new User();user.setId(3L);user.setName(Jock666);userDao.updateById(user);}
}你会发现这次修改并没有更新version字段原因是没有携带version数据。
添加version数据
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testUpdate(){User user new User();user.setId(3L);user.setName(Jock666);user.setVersion(1);userDao.updateById(user);}
}你会发现我们传递的是1MP会将1进行加1然后更新回到数据库表中。
所以要想实现乐观锁首先第一步应该是拿到表中的version然后拿version当条件在将version加1更新回到数据库表中所以我们在查询的时候需要对其进行查询
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testUpdate(){//1.先通过要修改的数据id将当前数据查询出来User user userDao.selectById(3L);//2.将要修改的属性逐一设置进去user.setName(Jock888);userDao.updateById(user);}
}大概分析完乐观锁的实现步骤以后我们来模拟一种加锁的情况看看能不能实现多个人修改同一个数据的时候只能有一个人修改成功。
SpringBootTest
class Mybatisplus03DqlApplicationTests {Autowiredprivate UserDao userDao;Testvoid testUpdate(){//1.先通过要修改的数据id将当前数据查询出来User user userDao.selectById(3L); //version3User user2 userDao.selectById(3L); //version3user2.setName(Jock aaa);userDao.updateById(user2); //version4user.setName(Jock bbb);userDao.updateById(user); //verion3?条件还成立吗}
}运行程序分析结果
乐观锁就已经实现完成了如果对于上面的这些步骤记不住咋办呢?
参考官方文档来实现:
https://mp.baomidou.com/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
2快速开发
2.1 代码生成器原理分析
观察我们之前写的代码会发现其中也会有很多重复内容比如: 那我们就想如果我想做一个Book模块的开发是不是只需要将红色部分的内容全部更换成Book即可如 所以我们会发现做任何模块的开发对于这段代码基本上都是对红色部分的调整所以我们把去掉红色内容的东西称之为模板红色部分称之为参数以后只需要传入不同的参数就可以根据模板创建出不同模块的dao代码。
除了Dao可以抽取模块其实我们常见的类都可以进行抽取只要他们有公共部分即可。再来看下模型类的模板
① 可以根据数据库表的表名来填充② 可以根据用户的配置来生成ID生成策略③到⑨可以根据数据库表字段名称来填充
所以只要我们知道是对哪张表进行代码生成这些内容我们都可以进行填充。
分析完后我们会发现要想完成代码自动生成我们需要有以下内容:
模板: MyBatisPlus提供可以自己提供但是麻烦不建议。数据库相关配置:读取数据库获取表和字段信息。开发者自定义配置:手工配置比如ID生成策略。
2.2 代码生成器实现
步骤1:创建一个Maven项目
代码2:导入对应的jar包 !--代码生成器--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-generator/artifactIdversion3.4.1/version/dependency!--velocity模板引擎--dependencygroupIdorg.apache.velocity/groupIdartifactIdvelocity-engine-core/artifactIdversion2.3/version/dependency/dependencies
步骤4:创建代码生成类
public class CodeGenerator {public static void main(String[] args) {//1.获取代码生成器的对象AutoGenerator autoGenerator new AutoGenerator();//设置数据库相关配置DataSourceConfig dataSource new DataSourceConfig();dataSource.setDriverName(com.mysql.cj.jdbc.Driver);dataSource.setUrl(jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezoneUTC);dataSource.setUsername(root);dataSource.setPassword(root);autoGenerator.setDataSource(dataSource);//设置全局配置GlobalConfig globalConfig new GlobalConfig();globalConfig.setOutputDir(System.getProperty(user.dir)/mybatisplus_04_generator/src/main/java); //设置代码生成位置globalConfig.setOpen(false); //设置生成完毕后是否打开生成代码所在的目录globalConfig.setAuthor(黑马程序员); //设置作者globalConfig.setFileOverride(true); //设置是否覆盖原始生成的文件globalConfig.setMapperName(%sDao); //设置数据层接口名%s为占位符指代模块名称globalConfig.setIdType(IdType.ASSIGN_ID); //设置Id生成策略autoGenerator.setGlobalConfig(globalConfig);//设置包名相关配置PackageConfig packageInfo new PackageConfig();packageInfo.setParent(com.aaa); //设置生成的包名与代码所在位置不冲突二者叠加组成完整路径packageInfo.setEntity(domain); //设置实体类包名packageInfo.setMapper(dao); //设置数据层包名autoGenerator.setPackageInfo(packageInfo);//策略设置StrategyConfig strategyConfig new StrategyConfig();strategyConfig.setInclude(tbl_user); //设置当前参与生成的表名参数为可变参数strategyConfig.setTablePrefix(tbl_); //设置数据库表的前缀名称模块名 数据库表名 - 前缀名 例如 User tbl_user - tbl_strategyConfig.setRestControllerStyle(true); //设置是否启用Rest风格strategyConfig.setVersionFieldName(version); //设置乐观锁字段名strategyConfig.setLogicDeleteFieldName(deleted); //设置逻辑删除字段名strategyConfig.setEntityLombokModel(true); //设置是否启用lombokautoGenerator.setStrategy(strategyConfig);//2.执行生成操作autoGenerator.execute();}
}对于代码生成器中的代码内容我们可以直接从官方文档中获取代码进行修改
https://mp.baomidou.com/guide/generator.html
步骤5:运行程序
运行成功后会在当前项目中生成很多代码代码包含controller,servicemapper和entity
至此代码生成器就已经完成工作我们能快速根据数据库表来创建对应的类简化我们的代码开发。
后记 美好的一天到此结束下次继续努力欲知后续请看下回分解写作不易感谢大家的支持