当前位置: 首页 > news >正文

MybatisPlus简单到入门

一、MybatisPlus简介

1、入门案例(重点):

1.SpringBoot整合MP1).创建新模块选择,Spring项初始化。2).选择当前模块使用的技术,只保留MySQL Driver就行,不要选择mybatis避免与后面导入mybatisPlus的依赖发生冲突。`使用联网创建工程,有两个弊端,所以在以后工作中基本不使用:1、必须联网2、只能选最近几个比较新的SpringBoot版本,企业开发追求稳定,一般不会用太新的版本`企业工作中,直接创建一个Maven工程就行,自行指定继承的SpringBoot父工程3).企业中手动创建项目,指定版本group: com.itheima项目名:mp_demojdk:使用8
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.0</version><relativePath/> <!-- lookup parent from repository -->
</parent><dependencies><!--注意事项1:由于mybatisPlus并未被收录到idea的系统内置配置,无法直接选择加入--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><!--数据源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
   4).制作实体类与表结构(类名与表名对应,属性名与字段名对应)
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (id bigint(20) primary key auto_increment,name varchar(32) not null,password  varchar(32) not null,age int(3) not null ,tel varchar(32) not null
);
insert into user values(null,'tom','123456',12,'12345678910');
insert into user values(null,'jack','123456',8,'12345678910');
insert into user values(null,'jerry','123456',15,'12345678910');
insert into user values(null,'tom','123456',9,'12345678910');
insert into user values(null,'snake','123456',28,'12345678910');
insert into user values(null,'张益达','123456',22,'12345678910');
insert into user values(null,'张大炮','123456',16,'12345678910');
package com.itheima.domain;
//pojo, entity, domain@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;//可以使用lombok自动生成//自行添加getter、setter、toString()等方法
}
  5).设置Jdbc参数(application.yml)
spring:datasource:type: com.alibaba.druid.pool.DruidDataSource #可选,如果使用必须引入Druid坐标driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTCusername: rootpassword: root
  6).定义数据接口,继承BaseMapper@Mapper
public interface UserDao extends BaseMapper<User> {//注意:BaseMapper后边必须指定泛型User,以此来确定操作的表
}
  7).测试类中注入dao接口,测试功能SpringBoot引导类(必须放到某一个包下)@SpringBootApplication
public class Mybatisplus01QuickstartApplication {public static void main(String[] args) {SpringApplication.run(Mybatisplus01QuickstartApplication.class, args);}
}
/************************************************************************************/打开自动生成的测试类,编写单元测试:@SpringBootTest
public class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll() {List<User> userList = userDao.selectList(null);System.out.println(userList);}@Testvoid testFindById() {//业务层: get(list), remove, modify(update), save//数据层:select, delete, update, insertUser user = userDao.selectById(1);System.out.println(user);}
}

2、MyBatisPlus概述

MyBatisPlus介绍- MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。
- 官网:https://mybatis.plus/  https://mp.baomidou.comMyBatisPlus特性:- 无侵入:只做增强不做改变,不会对现有工程产生影响
- 强大的 CRUD 操作:内置通用 BaseMapper,少量配置即可实现单表CRUD 操作
- 支持 Lambda:编写查询条件无需担心字段写错
- 支持主键自动生成(雪花算法)
- 内置分页插件(MyBatis分页需要用到Pagehelper通过拦截器给SQL追加limit 0,10)
- ……

二、标准数据层开发【重点】

1、CRUD操作

功能自定义接口MP接口
新增boolean save(T t)int insert(T t)
删除boolean delete(int id)int deleteById(Serializable id)
修改boolean update(T t)int updateById(T t)
根据id查询T getById(int id)T selectById(Serializable id)
查询全部List<T> getAllList<T> selectList()
分页查询PageInfo<T> getAll(int page,int size)IPage<T> selectPage(IPage<T> page)
按条件查询List<T> getAll(Condition condition)IPage<T> selectPage(Wrapper<T> queryWrapper)
编写单元测试,完成对user表的增删查改:
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testSave() {User user = new User();user.setName("程序员");user.setPassword("it");user.setAge(12);user.setTel("400000");userDao.insert(user);}@Testvoid testDelete() {userDao.deleteById(2L);}@Testvoid testUpdate() {User user = new User();user.setId(1L);user.setName("Tom888");user.setPassword("tom888");userDao.updateById(user);}@Testvoid testGetById() {User user = userDao.selectById(2L);System.out.println(user);}@Testvoid testGetAll() {List<User> userList = userDao.selectList(null);System.out.println(userList);}
} 

2、Lombok插件【常用】

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。
<!--自动生成get,set,toString等方法-->
<!--实体类上添加@Data即可-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>常用注解:@Data,为当前实体类在编译期设置对应的get/set方法,无参/无参构造方法,toString方法,hashCode方法,equals方法等
package com.itheima.domain;import lombok.*;
/*1 生成getter和setter方法:@Getter、@Setter生成toString方法:@ToString生成equals和hashcode方法:@EqualsAndHashCode2 统一成以上所有:@Data3 生成空参构造: @NoArgsConstructor生成全参构造: @AllArgsConstructor4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】*/
@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;
}

3、分页功能

1、引入Pagehelper坐标(通过拦截器给sql语句加limit 0,10)
2、Pagehelper.startPage(size, page)
1).设置分页拦截器作为Spring管理的bean@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){//1 创建MybatisPlusInterceptor拦截器对象MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();//2 添加分页拦截器:当调用selectPage方法时自动在最后追加分页实现:limit 0,10mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mpInterceptor;}
}2).执行分页查询//分页查询
@Test
void testSelectPage(){//1 创建IPage分页对象,设置分页参数IPage<User> page=new Page<>(1,3);//2 执行分页查询userDao.selectPage(page,null);//框架底层,会把查询到的数据放到page对象中//page.setRecords(new ArrayList<>());//page.setTotal(100);//3 获取分页结果System.out.println("当前页码值:"+page.getCurrent());System.out.println("每页显示数:"+page.getSize());System.out.println("总页数:"+page.getPages());System.out.println("总条数:"+page.getTotal());System.out.println("当前页数据:"+page.getRecords());
}
3).输出SQL到控制台spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTCusername: rootpassword: root
# 开启mp的日志(输出到控制台)
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

三、DQL编程控制【重点】

DQL:数据查询Query语言(select)
DML:数据操作语言(insert,update,delete)

1、简化日志【了解】

SpringBoot底层是logback框架记录日志的
做法:在resources下新建一个logback.xml文件,名称固定,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration></configuration>
# 取消SpringBoot启动banner图标#spring:main:banner-mode: off # 关闭SpringBoot启动图标(banner)## 取消MybatisPlus启动banner图标# mybatis-plus日志控制台输出
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:banner: off # 关闭mybatisplus启动图标

2、条件查询方式

1、条件查询方式MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。//方式一:按条件查询
QueryWrapper<User> qw = new QueryWrapper<>();
//lt小于:less than; gt大于:greater than
//le小于等于:less than or equal
//ge大于等于:greater than or equal
qw.lt("age", 18); //where age < 18
List<User> userList = userDao.selectList(qw); //select * from user
System.out.println(userList);
2、lambda格式按条件查询//方式二:lambda格式按条件查询qw.lambda()
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
3、lambda格式按条件查询(推荐)//方式三:lambda格式按条件查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.lt(User::getAge, 18);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);//方式三变种1:通过Wrappers的静态方法获取
//LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
//lqw.lt(User::getAge, 18);
//List<User> userList = userDao.selectList(lqw);
//System.out.println(userList);//方式三变种2:链式编程
//List<User> users = userDao.selectList(
//        Wrappers.<User>lambdaQuery() //产生查询条件对象
//                .lt(User::getAge, 18));
//System.out.println(users);

3、组合条件查询

1、并且关系(and)//并且关系:and
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//并且关系:10到30岁之间,默认多个条件使用and连接
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
2、或者关系(or)//或者关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者关系:小于18岁或者大于10岁
//where age < 18 or age > 10
lqw.lt(User::getAge, 18).or().gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
3NULL值处理在多条件查询中,有条件的值为空1).模拟用户查询条件类:UserQuery 继承需要限制条件的类,定义相同类型的属性限制需要被限制的字段。@Data
public class UserQuery extends User {//此时对年龄进行限制private Integer age2; //最大年龄
}
/********************************************************************************************************/2).if语句控制条件追加//模拟页面传递过来的查询数据
UserQuery uq = new UserQuery();
uq.setAge(10);
uq.setAge2(30);LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, uq.getAge2());//null判定
if (null != uq.getAge()) {lqw.gt(User::getAge, uq.getAge());
}
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
/*************************************************************************************************************/3).条件参数控制【常用】//null判断:第二种,条件参数控制
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//先判定第一个参数是否为true,如果为true连接当前条件
lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2());
lqw.gt(null != uq.getAge(), User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);/**************************************************************************************************************/4).条件参数控制(链式编程)//先判定第一个参数是否为true,如果为true连接当前条件
//lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2());
//lqw.gt(null != uq.getAge(), User::getAge, uq.getAge());
lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2()).gt(null != uq.getAge(), User::getAge, uq.getAge());

4、查询投影

1.查询结果包含实体类中部分属性【常用】LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId, User::getName, User::getAge);/*QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id", "name", "age", "tel");*/List<User> userList = userDao.selectList(lqw);
System.out.println(userList);    
2 查询结果包含实体类中未定义的属性【了解】这个统计SQL在企业开发中,一般会使用MyBatis在xml中编写sql
SELECT COUNT(*) count,tel FROM user GROUP BY tel
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel"); //统计lqw.groupBy("tel"); //分组
//qw.lambda().groupBy(User::getTel);List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);//System.out.println(userDao.selectCount(null)); //查询总记录数
# 3、查询条件设置多条件查询有哪些组合?- 范围匹配(> 、 = 、between)
- 模糊匹配(like)
- 空判定(null)
- 包含性匹配(in)
- 分组(group)
- 排序(order by)
- ……
- 用户登录(eq匹配)LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//eq()等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
//如果查询返回的不是一条数据或者null,如果有多条直接抛异常
User loginUser = userDao.selectOne(lqw); 
System.out.println(loginUser);
- 购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 查信息,搜索新闻(非全文检索版:like匹配)//模糊匹配
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//%,_必须是一个
//likeLeft() == where name like '%J'
//likeRight() == where name like 'J%'
lqw.like(User::getName, "J"); //where name like '%J%'
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
/*****************************************************************/
- 排序//排序
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//lqw.orderByAsc(User::getAge); //升序
lqw.orderByDesc(User::getAge); //降序
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
# 查询API- 更多查询条件设置参看https://baomidou.com/pages/10c804/

5、映射匹配兼容【了解】

# 创建新的表tbl_user做测试:USE mybatisplus_db;
CREATE TABLE tbl_user (id BIGINT ( 20 ) PRIMARY KEY auto_increment,`name` VARCHAR ( 32 ) NOT NULL,pwd VARCHAR ( 32 ) NOT NULL,age INT ( 3 ) NOT NULL,tel VARCHAR ( 32 ) NOT NULL,deleted INT(1) DEFAULT '0',version INT(11) DEFAULT '1'
);insert into tbl_user(name,pwd,age,tel) values('snake','123456',28,'12345678910');
insert into tbl_user(name,pwd,age,tel) values('张益达','123456',22,'12345678910');
insert into tbl_user(name,pwd,age,tel) VALUES('张大炮','123456',16,'12345678910');
// 1.表字段与编码属性设计不同步在模型类属性上方,使用@TableField属性注解,通过value属性,设置当前属性对应的数据库表中的字段关系。@TableField(value = "pwd")//给password定义了别名对应数据库中的字段
private String password;// 2.编码中添加了数据库中未定义的属性在模型类属性上方,使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。//select online from user;
@TableField(exist = false) //设置的属性在数据库表字段中是否存在(默认为true存在)
private Integer online;// 3.采用默认查询开放了更多的字段查看权限在模型类属性上方,使用@TableField注解,通过select属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。
@TableField(value="pwd",select=false) //将密码阶段设置不参与查询
private String password;//4.表名与编码开发设计不同步在模型类上方,使用@TableName注解,通过value属性,设置当前类对应的数据库表名称。@Data
@TableName("tbl_user")
public class User {/*id为Long类型,因为数据库中id为bigint类型,并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型*/private Long id;private String name;@TableField(value = "pwd",select = false)private String password;private Integer age;private String tel;@TableField(exist = false) //表示online字段不参与CRUD操作private Boolean online;
}

四、DML编程控制【重点】

DQL:数据查询语言(select)DML:数据操作语言(insert, update, delete)

1.ID生成策略控制(Insert)

不同的表应用不同的id生成策略- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
- ……- id生成策略控制
名称:@Tableld
类型:属性注解
位置:模型类中用于表示主键的属性定义上方
作用:设置当前类中主键属性的生成策略
相关属性type:设置主键属性的生成策略,值参照IdType枚举值
public class User{@Tableld(type = IdType.AUTO)/***   AUTO(0):使用数据库id自增策略控制id生成*   NONE(1):不设置id生成策略*  INPUT(2):用户手工输入id*  ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型)*  ASSIGN_UUID(4):以UUID生成算法作为id生成策略*/private Long id;
} 
** 雪花(snowflake)算法:Twitter开源的一个生成不重复ID的算法scala **- 其核心思想是:第一个是符号位,0代表是正数;使用41bit作为毫秒数;10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号。
0001001101110110101011000011010100110001101000010001000000000010
占位符时间戳(41)数据中心(5)机器ID(5)流水号
    计算机使用一个叫做实时时钟(Real-Time Clock,简称 RTC)的硬件设备来跟踪时间。RTC一般以晶振的形式存在,每秒钟产生一次脉冲。操作系统通过读取RTC的脉冲来确定时间的流逝,从而计算出当前的日期和时间。当我们电脑中硬件设备受到外界影响就会导致时间的不准确过快或者过慢,从而会使得时间校正,而在这段或快或慢的时间段中可能出现由雪花算法生成的id,但是矫正完之后,又有可能生成新的雪花id和上一个id相同。- 时钟回拨:可能导致生成重复的ID,可以使用第三方成熟方案:百度IdGenerator,美团Leaf
全局策略配置mybatis-plus:global-config:db-config:id-type: assign_id #雪花算法table-prefix: tbl_   # tbl_ + User.java -> select * from tbl_user# 设置前缀为tbl_的表的id 设置成雪花算法生成的id

2.批量操作

1、按照主键删除多条记录//删除指定多条数据
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);//where id in (1,2,3)
userDao.deleteBatchIds(list);2、 根据主键查询多条记录//查询指定多条数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
List<User> users = userDao.selectBatchIds(list);

3.逻辑删除

  
- 删除操作业务问题:业务数据从数据库中丢弃
- 物理删除:将数据从硬盘(数据库)当中删除(delete from user where id = 1)
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,
- 数据保留在数据库中(update user set deleted=1 where id = 1)1、数据库表中添加逻辑删除标记字段,deleted默认值为0- 注意:不要使用SQL的关键字做为表名或者字段名,容易出错所以:订单order - orders是否删除字段delete -> deleted
2、实体类中添加对应字段,并设定当前字段为逻辑删除标记字段@Data
public class User {private Long id;//逻辑删除字段,标记当前记录是否被删除//@TableLogic(value = "0", delval = "1")private Integer deleted;}
3、配置全局逻辑删除字面值2和3,任选一种即可
mybatis-plus:global-config:db-config:table-prefix: tbl_# 逻辑删除字段名logic-delete-field: deleted# 逻辑删除字面值:未删除为0logic-not-delete-value: 0# 逻辑删除字面值:删除为1logic-delete-value: 1
- 逻辑删除本质:其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。/删除一条数据,观察字段deleted变化//deleted会由0 -> 1userDao.deleteById(1450393635949465602L);`说明:当加入了逻辑删除之后,后续查询或者更新时,自动多一个条件(where deleted = 0)`

4.乐观锁与悲观锁

乐观锁和悲观锁都是用于解决并发场景下的数据竞争问题,但是却是两种完全不同的思想。- 它们的使用非常广泛,也不局限于某种编程语言或数据库。数据库自身解决并发两种策略:- 悲观锁(Pessimistic Lock):很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,降低了性能,适用于写多读少。
三种常见的悲观锁解决方案的区别:1. synchronized关键字:- 线程访问资源时自动获取锁,代码块执行完毕后自动释放锁。- 无法手动中断获取锁的线程,只能等待锁的释放。- 只支持独占锁,不支持公平性设置。- 隐式地进行锁的获取和释放,使用相对较简单。2. ReentrantLock类:- 可以手动地获取锁和释放锁,更灵活地控制锁的粒度。- 可以设置是否公平获取锁,即等待时间较长的线程优先获取锁。- 支持可中断获取锁,即等待获取锁的线程可被中断。- 支持多个条件变量(Condition),可以精确地进行线程通信和等待。3. ReadWriteLock接口:- 提供读写锁的支持,可以允许多个线程同时读取共享数据,但只允许一个线程写入数据。- 读锁是共享的,可同时被多个线程持有,但写锁是独占的,只能被一个线程持有。- 读锁的并发性能好于独占锁,适用于读操作频繁、写操作较少的场景。- 可以通过读写锁的方式提高并发性能,减少对共享资源的争用。总的来说,synchronized是最基本且简便的悲观锁实现方式,ReentrantLock类提供了更高级的功能和灵活性,而ReadWriteLock接口则适用于读多写少的场景。根据具体需求和场景,可以选择合适的锁机制来保障线程安全性和性能。
-- 关闭数据库自动提交-- 在查询语句后添加for update,加行锁
select * from tb_user where id = 1 for update;-- 更新数据
-- update xxx-- 事务提交或回滚后,锁会释放
commit;
rollback;
- 乐观锁(Optimistic Lock)很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,它并非是真的锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制;使用读多写少,性能比悲观锁高。
1.数据库表中添加锁标记字段, 默认值为1
2.实体类中添加对应字段,并设定当前字段为逻辑删除标记字段@Data
public class User {private Long id;//省略其他属性@Version //用这个字段实现乐观锁private Integer version;
}
3、配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装- 如果是常用的功能,直接就能用(逻辑删除)
- 乐观锁并不是所有的更新都要用,如果需要用,得自己来添加拦截器
//mybatis plus的配置类@Configuration
public class MpConfig {@Beanpublic MybatisPlusInterceptor mpInterceptor() {//1.定义Mp拦截器MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();//2.添加分页拦截器mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());//3.添加乐观锁拦截器: set version = version+1 where version =?mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mpInterceptor;}
}
4、使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行@Test
void testUpdate() throws InterruptedException {//1.先通过要修改的数据id将当前数据查询出来//目的:在更新之前必须先知道当前的versionUser user = userDao.selectById(222L);System.out.println(user);//2.将要修改的属性逐一设置进去user.setName("Jock888");Thread.sleep(15 * 1000); //在休眠期间使用MySQL客户端去修改version值//UPDATE tbl_user SET name=?, age=?, tel=?, version=?// WHERE id=? AND version=? AND deleted=0int num = userDao.updateById(user);if (num == 0 ) { //在我查询 和 修改 之间,有其他人(线程)修改了此数据,导致版本号发生了变化//数据库受影响行数==0,说明修改失败,有两种处理方案://1. 提示用户修改失败,让用户自行决定是否再次修改System.err.println("修改失败,需要再次修改");//2. TODO 重新调用查询+修改,自动重试(危险)} else {System.out.println("修改成功");}
}
- 1、乐观锁实现,必须先查询,再更新- 2、乐观锁拦截器负责添加2个sql片段:update user set version = 原来的版本号+1 where version=原来查询出来的版本号 //UPDATE tbl_user SET name=?, age=?, tel=?, version=? WHERE id=? AND version=?

五、代码生成器【了解】

1.MyBatisPlus提供模板

根据数据库中的表字段,自动生成实体类、数据层、业务层、表现层代码。
第一步:添加代码生成器相关依赖
<!--代码生成器-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version>
</dependency><!--模板引擎:velocity, jsp, freemarker-->
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version>
</dependency>
第二步:编写代码生成器类public class Generator {public static void main(String[] args) {//1. 创建代码生成器对象,执行生成代码操作AutoGenerator autoGenerator = new AutoGenerator();//设置生成目录autoGenerator.setGlobalConfig(new GlobalConfig().setOutputDir("c:/work"));//2. 数据源相关配置:读取数据库中的信息,根据数据库表结构生成代码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);//3. 执行生成操作autoGenerator.execute();}
}

2.开发者自定义配置

//设置全局配置GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");   //设置代码生成位置globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("Wen阿杜");    //设置作者
globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
//设置包名相关配置PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.abc");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("pojo");    //设置实体类包名:entity, domain
packageInfo.setMapper("dao");   //设置数据层包名:mapper
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);

3、其他实现

- 使用MyBatisX插件
- 开源:https://gitee.com/renrenio/renren-security/tree/master/renren-generator
- 公司自己实现的……

MyBatis和MyBatisPlus的使用场景:

  • 单表操作:使用MP,不用写SQL语句
  • 多表关联查询:使用MyBatis在xml中写SQL实现

相关文章:

MybatisPlus简单到入门

一、MybatisPlus简介 1、入门案例&#xff08;重点&#xff09;&#xff1a; 1.SpringBoot整合MP1).创建新模块选择&#xff0c;Spring项初始化。2).选择当前模块使用的技术&#xff0c;只保留MySQL Driver就行&#xff0c;不要选择mybatis避免与后面导入mybatisPlus的依赖发…...

9. 优化器

9.1 优化器 ① 损失函数调用backward方法&#xff0c;就可以调用损失函数的反向传播方法&#xff0c;就可以求出我们需要调节的梯度&#xff0c;我们就可以利用我们的优化器就可以根据梯度对参数进行调整&#xff0c;达到整体误差降低的目的。 ② 梯度要清零&#xff0c;如果梯…...

go学习之流程控制语句

文章目录 流程控制语句1.顺序控制2.分支控制2.1单分支2.2双分支单分支和双分支的四个题目switch分支结构 3.循环控制for循环控制while 和do...while的实现 4.跳转控制语句breakcontinuegotoreturngotoreturn 流程控制语句 介绍&#xff1a;在程序中&#xff0c;程序运行的流程…...

docker基于已有容器和通过Dockerfile进行制作镜像配置介绍

目录 一.制作镜像的两种方式 1.在已有容器中更新并提交这个镜像 2.使用Dockerfile来制作 二.基于容器制作镜像 1.格式 &#xff08;1&#xff09;主要格式 &#xff08;2&#xff09;可选参数 2.案例 基于容器创建镜像设置标签并进行验证是否可用 &#xff08;1&…...

2022年09月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;最长上升子序列 一个数的序列bi&#xff0c;当b1 < b2 < … < bS的时候&#xff0c;我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN)&#xff0c;我们可以得到一些上升的子序列(ai1, ai2, …, aiK)&#xff0c;这里1 < i1 < i2 &…...

二级MySQL(九)——表格数据处理练习

在Mysql中&#xff0c;可以用INSERT或【REPLACE】语句&#xff0c;向数据库中已一个已有的表中插入一行或多行记录。 在Mysql中&#xff0c;可以用【DELETE】或【TRUNCATE】语句删除表中的所有记录。 在Mysql中&#xff0c;可以用【UPDATE】语句来修改数据表中的记录。 为了完…...

QT ListQvector at赋值出错以及解决办法 QT基础入门【QT存储结构】

1、问题 error: passing const QString as this argument discards qualifiers error: assignment of read-only location vec.QVector<int>::at(0) 在Qt中QList,Qvector一般获取元素都是通过at(index)来获取,但是at()的返回是一个const & 常引用,也就是元素不支…...

STM32 CubeMX (H750)RGB屏幕 LTDC

STM32 CubeMX STM32 RGB888 LTDC STM32 CubeMX一、STM32 CubeMX 设置时钟树LTDC使能设置屏幕参数修改RGB888的GPIO 二、代码部分效果 RGB屏幕线束定义&#xff1a; 一、STM32 CubeMX 设置 时钟树 这里设置的时钟&#xff0c;关于刷新速度 举例子&#xff1a;LCD_CLK24MHz 时…...

Redis问题集合(三)在Redis容器里设置键值对

前言 前提是已经拉取了Redis镜像并创建了对应的容器做个记录&#xff0c;方便后续查看 步骤 查看Redis容器的ID&#xff1a;docker ps -a 进入容器&#xff1a;docker exec -it 容器ID /bin/bash进入redis命令行&#xff1a;redis-cli输入密码&#xff1a;auth 配置密码 查看…...

spark中排查Premature EOF: no length prefix available

报错信息 /07/22 10:20:28 WARN DFSClient: Error Recovery for block BP-888461729-172.16.34.148-1397820377004:blk_15089246483_16183344527 in pipeline 172.16.34.64:50010, 172.16.34.223:50010: bad datanode 172.16.34.64:50010 [DataStreamer for file /bdp/data/u9…...

numpy高级函数之where和extract函数

1 numpy.where() 函数返回输入数组中满足给定条件的元素的索引 ---------------------------------------------------- 代码&#xff1a; n1np.random.randint(10,20,10) n2np.where(n1>15) 结果&#xff1a; [17 15 19 15 12 10 16 11 15 13] #原始数组 (array([…...

用Python写一个武侠游戏

前言 在本教程中&#xff0c;我们将使用Python写一个武侠类的游戏&#xff0c;大的框架全部搭好了&#xff0c;很多元素都可以自己添加&#xff0c;让游戏更丰富 &#x1f4dd;个人主页→数据挖掘博主ZTLJQ的主页 个人推荐python学习系列&#xff1a; ☄️爬虫JS逆向系列专栏 -…...

Java --- 异常处理

目录 一、什么是异常 二、异常抛出机制 三、如何对待异常 四、 Java异常体系 4.1、Throwable 4.2、Error 4.2、Exception 4.2.1、编译时异常 4.2.2、运行时期异常 五、异常处理 5.1、捕获异常&#xff08;try-catch&#xff09; 5.1.2、catch中异常处理方式 …...

CDN/DCDN(全站加速)排查过程中如何获取Eagle ID/UUID

目录 前言1.通过浏览器直接访问文件时获取Request ID 前言 阿里云CDN/DCDN(全站加速)为接收到的每个请求分配唯一的服务器请求ID&#xff0c;作为关联各类日志信息的标识符。当您在使用CDN/DCDN(全站加速)过程中遇到错误且希望阿里云技术支持提供协助时&#xff0c;需要提交失…...

网络安全应急响应预案培训与演练目的

1、增强网络安全意识 网络安全事故隐患往往“生成”于无形。例如&#xff0c;漏洞或黑客攻 击发生之时&#xff0c;受害方企事业单位可能处于非常危险的境地而无所察 觉&#xff0c;一些内部部门人员的网络安全意识也容易懈怠。但不论是内部 员工的疏忽还是管理上的大意&am…...

2023年高教社杯 国赛数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…...

7.Oracle视图创建与使用

1、视图的创建与使用 在所有进行的SQL语句之中&#xff0c;查询是最复杂的操作&#xff0c;而且查询还和具体的开发要求有关&#xff0c;那么在开发过程之中&#xff0c;程序员完成的并不是是和数据库的所有内容&#xff0c;而更多的是应该考虑到程序的设计结构。可以没有一个项…...

rust学习-不安全操作

在 Rust 中,不安全代码块用于避开编译器的保护策略 四种不安全操作 解引用裸指针通过 FFI (Foreign Function Interface,外部语言函数接口)调用函数调用不安全的函数内联汇编(inline assembly)解引用裸指针 原始指针(raw pointer,裸指针)* 和引用 &T 有类似的功…...

RHCE——八、DNS域名解析服务器

RHCE 一、概述1、产生原因2、作用3、连接方式4、因特网的域名结构4.1 拓扑4.2 分类4.3 域名服务器类型划分 二、DNS域名解析过程1、分类2、解析图&#xff1a;2.1 图&#xff1a;2.2 过程分析 三、搭建DNS域名解析服务器1、概述2、安装软件3、/bind服务中三个关键文件4、配置文…...

flink cdc初始全量速度很慢原因和优化点

link cdc初始全量速度很慢的原因之一是&#xff0c;它需要先读取所有的数据&#xff0c;然后再写入到目标端&#xff0c;这样可以保证数据的一致性和顺序。但是这样也会导致数据的延迟和资源的浪费。flink cdc初始全量速度很慢的原因之二是&#xff0c;它使用了Debezium作为捕获…...

论文笔记: MOGRIFIER LSTM

2020 ICLR 修改传统LSTM 当前输入和隐藏状态充分交互&#xff0c;从而获得更佳的上下文相关表达 1 Mogrifier LSTM LSTM的输入X和隐藏状态H是完全独立的 机器学习笔记&#xff1a;GRU_gruc_UQI-LIUWJ的博客-CSDN博客这篇论文想探索&#xff0c;如果在输入LSTM之前&#xf…...

Angular中使用drag and drop实现文件拖拽上传,及flask后端接收

效果&#xff1a;拖拽文件到组件上面时 边框变大变红 松手后发送到服务器(或者点击蓝字手动选择文件)并且把文件名显示在框内&#xff0c;美化还没做 html <div class"drapBox"><div id"drop" (dragenter)"dragenter($event)" (dragov…...

Spring Authorization Server入门 (十六) Spring Cloud Gateway对接认证服务

前言 之前虽然单独讲过Security Client和Resource Server的对接&#xff0c;但是都是基于Spring webmvc的&#xff0c;Gateway这种非阻塞式的网关是基于webflux的&#xff0c;对于集成Security相关内容略有不同&#xff0c;且涉及到代理其它微服务&#xff0c;所以会稍微比较麻…...

配置Flink

配置flink_1.17.0 1.Flink集群搭建1.1解压安装包1.2修改集群配置1.3分发安装目录1.4启动集群、访问Web UI 2.Standalone运行模式3.YARN运行模式4.K8S运行模式 1.Flink集群搭建 1.1解压安装包 链接: 下载Flink安装包 解压文件 [gpbhadoop102 software]$ tar -zxvf flink-1.1…...

39、springboot的前端静态资源的WebJar支持(bootstrap、jquery等)及自定义图标和首页

★ WebJar支持 Spring Boot支持加载WebJar包中的静态资源&#xff08;图片、JS、CSS&#xff09;&#xff0c; WebJar包中的静态资源都会映射到/webjars/**路径。——这种方式下&#xff0c;完全不需要将静态资源复制到应用的静态资源目录下。只要添加webjar即可。假如在应用的…...

【图论】缩点的综合应用(一)

一.缩点的概念 缩点&#xff0c;也称为点缩法&#xff08;Vertex Contraction&#xff09;&#xff0c;是图论中的一种操作&#xff0c;通常用于缩小图的规模&#xff0c;同时保持了图的某些性质。这个操作的目标是将图中的一些节点合并为一个超级节点&#xff0c;同时调整相关…...

C++—纯虚函数

一、前言 定义一个函数为虚函数&#xff0c;不代表函数为不被实现的函数。 定义函数为虚函数是为了允许用基类的指针来调用子类的这个函数。 定义一个函数为纯虚函数&#xff0c;才代表函数没有被实现。 定义纯虚函数是为了实现一个接口&#xff0c;起到一个规范的作用&…...

经过卷积神经网络之后的图片的尺寸如何计算

经过卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;处理后&#xff0c;图片的尺寸会发生变化&#xff0c;这是由于卷积层、池化层等操作引起的。计算图片经过卷积神经网络后的尺寸变化通常需要考虑卷积核大小、步幅&#xff08;stride&…...

Java升级JDK17(更高版本同理),修改maven

记住三个网址就行&#xff1a;下面这个是oracle的 Java Platform, Standard Edition 17 ReferenceImplementations https://www.oracle.com/java/technologies/downloads/#jdk17-windows 另外一个 redhat旗下的&#xff1a;这个是开源的&#xff08;推荐这个&#xff01;&am…...

Go测试之.golden 文件

Go测试中的.golden 文件是干什么用的&#xff1f;请举例说明 在Go语言中&#xff0c;.golden文件通常用于测试中的黄金文件&#xff08;golden files&#xff09;。黄金文件是在测试期间记录预期输出结果的文件。测试用例运行时&#xff0c;黄金文件用于比较实际输出与预期输出…...

哈尔滨做网站多少钱/品牌运营管理公司

慕村9548890注释空间太小&#xff0c;因此这里有一些有关的更多信息static final。正如我在对Andrzej的回答的评论中所说的那样&#xff0c;只有原始和String直接作为文字直接编译到代码中。为了证明这一点&#xff0c;请尝试以下操作&#xff1a;通过创建三个类(在单独的文件中…...

如何在360做网站SEO/网络营销岗位

转自&#xff1a;https://blog.csdn.net/z69183787/article/details/48933481 自从开始使用Maven管理项目&#xff0c;最近在配置MyBatis的Mapper&#xff0c;在Eclipse上调试时都是正常的&#xff0c;但是最近把项目迁移到 IntelliJ IDEA 上后发现不管是直接用Jetty调试&#…...

wordpress邮箱订阅/企业seo

1、使用UltraISO 导入ISO文件。2、然后在硬盘中点击setup安装。3、http://jingyan.baidu.com/article/fea4511a72fa38f7ba912574.html...

网站域名后缀那个好/输入关键词自动生成文章

智和网管平台以提供全方位网络监控功能&#xff0c;全面管理联网设备为目标&#xff0c;深入用户需求&#xff0c;实现个性化网络监控解决方案。监控设备类型覆盖网络设备、服务器、交换机、中间件、数据库、安全设备、应用服务等&#xff1b;监测指标涵盖连通性、可用性、负载…...

平台网站开发/52种新颖的促销方式

重要国策《文化产业振兴规划》于9月26日正式对外公布。巧合的是&#xff0c;就在前一天&#xff08;9月25日 &#xff09;&#xff0c;盛大游戏&#xff08;SDG&#xff09;成功在NASDAQ上市&#xff0c;并且创下了美股IPO规模之最&#xff08;10亿美元&#xff09;。1&#xf…...

国外网站制作/百度平台推广的营销收费模式

你能够轻松地配置 Plasma 桌面并且使用它大量方便且节省时间的特性来加速你的工作&#xff0c;拥有一个能够帮助你而非阻碍你的桌面环境。-- Nick Congleton众所周知&#xff0c;KDE 的 Plasma 是 Linux 下最强大的桌面环境之一。它是高度可定制的&#xff0c;并且看起来也很棒…...