2.乐观锁(@version)
- 应用场景举例
添加和删除的操作简单的介绍完了,接下来介绍一下修改相关的问题及其解决方在案:乐观锁。比如我们在网上购物进行秒杀活动的时候,大家都去抢一个商品,每抢一次商品数量减一,最后一个被抢完的时候需要一个限制条件防止异常,这个处理并发问题的解决方案叫做乐观锁。它通过一个字段来标识,每抢一次该标识+1,这样每个人拿到的标识都不一样,当标识到达设定值后程序开始拦截请求。
- 添加乐观锁字段version
在user表上右键设计表格,点击添加字段,编辑version字段,设置数据类型为int,默认值为1
效果如下
- 在实体类user中添加乐观锁字段version,并添加@version注解
- 在Mpconfig类中添加乐观锁拦截器
要实现每次访问version的效果需要在访问的sql语句里添加功能可以实现 version = version + 1 的字段,mp里乐观锁拦截器可以做这个事情
- 在测试类的 testUpdate()方法中编辑修改记录的代码看看效果
- 代码
void testUpdate(){ User user = new User(); user.setId(3L); user.setName("Jock666"); //要传version,如果没有传version就没有锁机制 user.setVersion(1); userDao.updateById(user); }
- 运行结果
右边的version是传入的version,左边的version是更新后的version,可以看到实现了version = version + 1的效果
6. 开启锁机制的第二种方法,先查询,再进行更新操作
- 代码
@Test void testUpdate(){ //1.先通过要修改的数据id将当前数据查询出来 User user = userDao.selectById(3L); //2.将要修改的属性逐一设置进去 user.setName("Jock777"); userDao.updateById(user);
- 运行结果
- 模拟多用户秒杀场景
假设此时秒杀活动已经过去了2秒,数据库里id为3的商品乐观锁字段version已经累加到了3
假设用户1 user, 用户2 user2 再进行秒杀活动,用户2先访问秒杀界点击下单,然后数库更新代表该商品的 id 为 3 的乐观锁字段version, 当用户1再进行访问下单的时候,version == 3的条件不成立,该更新请求操作失效,即秒杀失败
- 代码
@Test void testUpdate(){ //用户1查询时version=3 User user = userDao.selectById(3L); //用户2查询时version=3 User user2 = userDao.selectById(3L); user2.setName("Jock aaa"); //用户2更新后version==4 userDao.updateById(user2); user.setName("Jock bbb"); //此时用户1更新时verion=3的条件不成立,该更新语句失效 userDao.updateById(user); }
- 运行结果
3. mp快速开发-代码生成器
1.场景介绍
在我们开发mp项目的时候,创建编写实体类功能的时候,模板都差不多,区别就是模块名不一样,这就类似于造句,只要提供模板和参数,mp就能自动帮我们生成代码
- 模板 :MyBatisPlus提供
- 数据库相关配置 :读取数据库获取信息
- 开发者自定义配置 :手工配置
- 创建新的springboot空项目DL_mp_generator(不勾选任何依赖,一会手动加),创建好新项目后导入 代码生成器 以及 velocity模板引擎 等坐标(完整代码见资源mybatis-plus源码)
//pom.xml文件 <dependencies> <!--代码生成器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!--velocity模板引擎--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency> </dependencies>
- 核心代码
创建Generator类,编写核心代码
此时运行没有什么意义,因为什么信息都没有配置
public class Generator { public static void main(String[] args) { //创建生成器对象 AutoGenerator autoGenerator = new AutoGenerator(); //执行生成器 autoGenerator.execute(); } }
4.配置数据源
public class Generator { public static void main(String[] args) { //创建生成器对象 AutoGenerator autoGenerator = new AutoGenerator(); //配置数据源信息 DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("root"); autoGenerator.setDataSource(dataSource); //执行生成器 autoGenerator.execute(); } }
(默认生成文件路径在D盘根目录下)
2.配置文件生成路径
- 代码
public class Generator { public static void main(String[] args) { //创建生成器对象 AutoGenerator autoGenerator = new AutoGenerator(); //配置数据源 DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("root"); autoGenerator.setDataSource(dataSource); //设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); //设置代码生成位置 globalConfig.setOpen(true); //设置生成完毕后是否打开生成代码所在的目录 globalConfig.setAuthor("东离与糖宝"); //设置作者 globalConfig.setFileOverride(true); //设置是否覆盖原始生成的文件 globalConfig.setMapperName("%sDao"); //设置数据层接口名,%s为占位符,指代模块名称 globalConfig.setIdType(IdType.ASSIGN_ID); //设置Id生成策略 autoGenerator.setGlobalConfig(globalConfig); //执行生成器 autoGenerator.execute(); } }
- 运行结果
(默认生成的包名叫baomidou)
- 配置包名相关信息
先把baomidou包删除,并将临时表名改为tbl_user并执行如下代码
- 代码
public class Generator { public static void main(String[] args) { //创建生成器对象 AutoGenerator autoGenerator = new AutoGenerator(); //配置数据源 DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("root"); autoGenerator.setDataSource(dataSource); //设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); //设置代码生成位置 globalConfig.setOpen(true); //设置生成完毕后是否打开生成代码所在的目录 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); //执行生成器 autoGenerator.execute(); } }
- 运行结果
解析举例; 可以看到根据表名称生成了tbl前缀,并且实体类中有getter和setter方法
- 策略配置
- 代码
public class Generator { public static void main(String[] args) { //创建生成器对象 AutoGenerator autoGenerator = new AutoGenerator(); //配置数据源 DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC"); dataSource.setUsername("root"); dataSource.setPassword("root"); autoGenerator.setDataSource(dataSource); //设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); //设置代码生成位置 globalConfig.setOpen(true); //设置生成完毕后是否打开生成代码所在的目录 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); //设置是否启用lombok autoGenerator.setStrategy(strategyConfig); //执行生成器 autoGenerator.execute(); } }
- 运行结果
解析举例: 可以看到数据库表前缀名称 tbl_ 在生成实体类等文件的时候被成功剪切,并且生成了乐观锁和逻辑删除字段,并且启用lombok(自带getter 和 setter方法)
`博客内容借鉴了bilibili黑马程序员SSM课程资料,如有侵权,请联系作者删除`
总结
欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。