SpringBoot整合(二)MyBatisPlus技术详解
MyBatisPlus详解
一、标准数据层开发
MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
MyBatisPlus的官网为:https://mp.baomidou.com/
1.1 标准CRUD
1.2 新增
int insert (T t)
-
T:泛型,新增用来保存新增数据
-
int:返回值,新增成功后返回1,没有新增成功返回的是0
在测试类中进行新增操作:
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testSave() {User user = new User();user.setName("yyds");user.setPassword("yyds");user.setAge(12);user.setTel("10086");userDao.insert(user);}
}
执行测试后,数据库表中就会添加一条数据。但是数据中的主键ID,有点长,这是主键ID生成策略决定的。
1.3 删除
int deleteById (Serializable id)
-
Serializable:参数类型
-
思考:参数类型为什么是一个序列化类?
- String和Number是Serializable的子类,
- Number又是Float,Double,Integer等类的父类,
- 能作为主键的数据类型都已经是Serializable的子类,
- MP使用Serializable作为参数类型,就好比我们可以用Object接收任何数据类型一样。
-
-
int:返回值类型,数据删除成功返回1,未删除数据返回0。
在测试类中进行操作:
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testDelete() {userDao.deleteById(1401856123725713409L);}
}
1.4 修改
int updateById(T t);
-
T:泛型,需要修改的数据内容,注意因为是根据ID进行修改,所以传入的对象中需要有ID属性值
-
int:返回值,修改成功后返回1,未修改数据返回0
在测试类中进行新增操作:
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testUpdate() {User user = new User();user.setId(1L);user.setName("Tom888");user.setPassword("tom888");userDao.updateById(user);}
}
说明: 修改的时候,只修改实体对象中有值的字段。
1.5 根据ID查询
T selectById (Serializable id)
- Serializable:参数类型,主键ID的值
- T:根据ID查询只会返回一条数据
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetById() {User user = userDao.selectById(2L);System.out.println(user);}
}
1.6 查询所有
List<T> selectList(Wrapper<T> queryWrapper)
- Wrapper:用来构建条件查询的条件,目前我们没有可直接传为Null
- List:因为查询的是所有,所以返回的数据是一个集合
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll() {List<User> userList = userDao.selectList(null);System.out.println(userList);}
}
我们所调用的方法都是来自于DAO接口继承的BaseMapper类中。里面的方法有很多。
1.7 分页查询
分页查询使用的方法是:
IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)
- IPage:用来构建分页查询条件
- Wrapper:用来构建条件查询的条件,目前我们没有可直接传为Null
- IPage:返回值,你会发现构建分页条件和方法的返回值都是IPage
IPage是一个接口,我们需要找到它的实现类来构建它,有一个实现类为Page
。
步骤1:调用方法传入参数获取返回值
@SpringBootTest
class Mybatisplus01QuickstartApplicationTests {@Autowiredprivate UserDao userDao;//分页查询@Testvoid testSelectPage(){//1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数IPage<User> page=new Page<>(1,3);//2 执行分页查询userDao.selectPage(page,null);//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());}
}
步骤2:设置分页拦截器
这个拦截器MP已经为我们提供好了,我们只需要将其配置成Spring管理的bean对象即可。
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){//1 创建MybatisPlusInterceptor拦截器对象MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();//2 添加分页拦截器mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mpInterceptor;}
}
步骤3:运行测试程序
如果想查看MP执行的SQL语句,可以修改application.yml配置文件,
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印SQL日志到控制台
二、DQL编程控制
2.1 条件查询
2.1.1 条件查询的类
- MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。
这个我们在前面都有见过,比如查询所有和分页查询的时候,都有看到过一个Wrapper
类,这个类就是用来构建查询条件的,如下图所示:
2.1.2 环境构建
-
创建一个SpringBoot项目
-
pom.xml中添加对应的依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.0</version></parent><groupId>com.yyds</groupId><artifactId>mybatisplus_02_dql</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
-
编写UserDao接口
@Mapper public interface UserDao extends BaseMapper<User> { }
-
编写模型类
@Data public class User {private Long id;private String name;private String password;private Integer age;private String tel; }
-
编写引导类
@SpringBootApplication public class Mybatisplus02DqlApplication {public static void main(String[] args) {SpringApplication.run(Mybatisplus02DqlApplication.class, args);}}
-
编写配置文件
# dataSource 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
-
编写测试类
@SpringBootTest class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){List<User> userList = userDao.selectList(null);System.out.println(userList);} }
-
application.yml添加如下内容:
# mybatis-plus日志控制台输出 mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:banner: off # 关闭mybatisplus启动图标
-
取消SpringBoot的log打印
application.yml添加如下内容:
spring:main:banner-mode: off # 关闭SpringBoot启动图标(banner)
-
2.1.3 构建条件查询
在进行查询的时候,我们的入口是在Wrapper这个类上,因为它是一个接口,所以我们需要去找它对应的实现类,关于实现类也有很多,说明我们有多种构建查询条件对象的方式,
- 先来看第一种:QueryWrapper
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){QueryWrapper qw = new QueryWrapper();qw.lt("age",18);List<User> userList = userDao.selectList(qw);System.out.println(userList);}
}
-
lt: 小于(<) ,最终的sql语句为
SELECT id,name,password,age,tel FROM user WHERE (age < ?)
第一种方式介绍完后,有个小问题就是在写条件的时候,容易出错,比如age写错,就会导致查询不成功
- 接着来看第二种:QueryWrapper的基础上使用lambda
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){QueryWrapper<User> qw = new QueryWrapper<User>();qw.lambda().lt(User::getAge, 10);//添加条件List<User> userList = userDao.selectList(qw);System.out.println(userList);}
}
- User::getAget,为lambda表达式中的,类名::方法名,最终的sql语句为:
SELECT id,name,password,age,tel FROM user WHERE (age < ?)
注意: 构建LambdaQueryWrapper的时候泛型不能省。
此时我们再次编写条件的时候,就不会存在写错名称的情况,但是qw后面多了一层lambda()调用
- 接着来看第三种:LambdaQueryWrapper
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.lt(User::getAge, 10);List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
这种方式就解决了上一种方式所存在的问题。
2.1.4 多条件构建
学完了三种构建查询对象的方式,每一种都有自己的特点,所以用哪一种都行,刚才都是一个条件,那如果有多个条件该如何构建呢?
需求:查询数据库表中,年龄在10岁到30岁之间的用户信息
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.lt(User::getAge, 30);lqw.gt(User::getAge, 10);List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
-
gt:大于(>),最终的SQL语句为
SELECT id,name,password,age,tel FROM user WHERE (age < ? AND age > ?)
-
构建多条件的时候,可以支持链式编程
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); lqw.lt(User::getAge, 30).gt(User::getAge, 10); List<User> userList = userDao.selectList(lqw); System.out.println(userList);
需求:查询数据库表中,年龄小于10或年龄大于30的数据
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
-
or()就相当于我们sql语句中的
or
关键字,不加默认是and
,最终的sql语句为:SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
3.1.5 null判定
需求:查询数据库表中,根据输入年龄范围来查询符合条件的记录
用户在输入值的时候,
如果只输入第一个框,说明要查询大于该年龄的用户
如果只输入第二个框,说明要查询小于该年龄的用户
如果两个框都输入了,说明要查询年龄在两个范围之间的用户
思考第一个问题:后台如果想接收前端的两个数据,该如何接收?
我们可以使用两个简单数据类型,也可以使用一个模型类,但是User类中目前只有一个age属性,如:
@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;
}
方案一:添加属性age2,这种做法可以但是会影响到原模型类的属性内容
@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;private Integer age2;
}
方案二:新建一个模型类,让其继承User类,并在其中添加age2属性,UserQuery在拥有User属性后同时添加了age2属性。
@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;
}@Data
public class UserQuery extends User {private Integer age2;
}
环境准备好后,我们来实现下刚才的需求:
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){//模拟页面传递过来的查询数据UserQuery uq = new UserQuery();uq.setAge(10);uq.setAge2(30);LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();if(null != uq.getAge2()){lqw.lt(User::getAge, uq.getAge2());}if( null != uq.getAge()) {lqw.gt(User::getAge, uq.getAge());}List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
上面的写法可以完成条件为非空的判断,但是问题很明显,如果条件多的话,每个条件都需要判断,代码量就比较大,来看MP给我们提供的简化方式:
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){//模拟页面传递过来的查询数据UserQuery uq = new UserQuery();uq.setAge(10);uq.setAge2(30);LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();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);}
}
2.2 查询投影
2.2.1 查询指定字段
目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容,我们所说的查询投影即不查询所有字段,只查询出指定内容的数据。
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.select(User::getId,User::getName,User::getAge);List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
-
select(…)方法用来设置查询的字段列,可以设置多个,最终的sql语句为:
SELECT id,name,age FROM user
-
如果使用的不是lambda,就需要手动指定字段
@SpringBootTest class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){QueryWrapper<User> lqw = new QueryWrapper<User>();lqw.select("id","name","age","tel");List<User> userList = userDao.selectList(lqw);System.out.println(userList);} }
2.2.2 聚合查询
需求:聚合函数查询,完成count、max、min、avg、sum的使用
count:总记录数
max:最大值
min:最小值
avg:平均值
sum:求和
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){QueryWrapper<User> lqw = new QueryWrapper<User>();//lqw.select("count(*) as count");//SELECT count(*) as count FROM user//lqw.select("max(age) as maxAge");//SELECT max(age) as maxAge FROM user//lqw.select("min(age) as minAge");//SELECT min(age) as minAge FROM user//lqw.select("sum(age) as sumAge");//SELECT sum(age) as sumAge FROM userlqw.select("avg(age) as avgAge");//SELECT avg(age) as avgAge FROM userList<Map<String, Object>> userList = userDao.selectMaps(lqw);System.out.println(userList);}
}
为了在做结果封装的时候能够更简单,我们将上面的聚合函数都起了个名称,方面后期来获取这些数据
2.2.3 分组查询
需求:分组查询,完成 group by的查询使用
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){QueryWrapper<User> lqw = new QueryWrapper<User>();lqw.select("count(*) as count,tel");lqw.groupBy("tel");List<Map<String, Object>> list = userDao.selectMaps(lqw);System.out.println(list);}
}
-
groupBy为分组,最终的sql语句为
SELECT count(*) as count,tel FROM user GROUP BY tel
注意:
- 聚合与分组查询,无法使用lambda表达式来完成
- MP只是对MyBatis的增强,如果MP实现不了,我们可以直接在DAO接口中使用MyBatis的方式实现
2.3 查询条件
MP的查询条件有很多:
- 范围匹配(> 、 = 、between)
- 模糊匹配(like)
- 空判定(null)
- 包含性匹配(in)
- 分组(group)
- 排序(order)
- ……
2.3.1 等值查询
需求:根据用户名和密码查询用户信息
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");User loginUser = userDao.selectOne(lqw);System.out.println(loginUser);}
}
-
eq(): 相当于
=
,对应的sql语句为SELECT id,name,password,age,tel FROM user WHERE (name = ? AND password = ?)
-
selectList:查询结果为多个或者单个
-
selectOne:查询结果为单个
2.3.2 范围查询
需求:对年龄进行范围查询,使用lt()、le()、gt()、ge()、between()进行范围查询
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.between(User::getAge, 10, 30);//SELECT id,name,password,age,tel FROM user WHERE (age BETWEEN ? AND ?)List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
- gt():大于(>)
- ge():大于等于(>=)
- lt():小于(<)
- lte():小于等于(<=)
- between():between ? and ?
2.3.3 模糊查询
需求:查询表中name属性的值以
J
开头的用户信息,使用like进行模糊查询
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();lqw.likeLeft(User::getName, "J");//SELECT id,name,password,age,tel FROM user WHERE (name LIKE ?J)List<User> userList = userDao.selectList(lqw);System.out.println(userList);}
}
- like():前后加百分号,如 %J%
- likeLeft():前面加百分号,如 %J
- likeRight():后面加百分号,如 J%
2.3.4 排序查询
需求:查询所有数据,然后按照id降序
@SpringBootTest
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetAll(){LambdaQueryWrapper<User> lwq = new LambdaQueryWrapper<>();/*** condition :条件,返回boolean,当condition为true,进行排序,如果为false,则不排序* isAsc:是否为升序,true为升序,false为降序* columns:需要操作的列*/lwq.orderBy(true,false, User::getId);userDao.selectList(lw}
}
除了上面介绍的这几种查询条件构建方法以外还会有很多其他的方法,比如isNull,isNotNull,in,notIn等等方法可供选择,具体参考官方文档的条件构造器来学习使用,具体的网址为:
https://mp.baomidou.com/guide/wrapper.html#abstractwrapper
2.4 映射匹配兼容性
前面我们已经能从表中查询出数据,并将数据封装到模型类中,这整个过程涉及到一张表和一个模型类:
问题1:表字段与编码属性设计不同步
当表的列名和模型类的属性名发生不一致,就会导致数据封装不到模型对象,这个时候就需要其中一方做出修改,那如果前提是两边都不能改又该如何解决?
MP给我们提供了一个注解@TableField
,使用该注解可以实现模型类属性名和表的列名之间的映射关系
问题2:编码中添加了数据库中未定义的属性
当模型类中多了一个数据库表不存在的字段,就会导致生成的sql语句中在select的时候查询了数据库不存在的字段,程序运行就会报错,错误信息为:
Unknown column ‘多出来的字段名称’ in ‘field list’
具体的解决方案用到的还是@TableField
注解,它有一个属性叫exist
,设置该字段是否在数据库表中存在,如果设置为false则不存在,生成sql语句查询的时候,就不会再查询该字段了。
问题3:采用默认查询开放了更多的字段查看权限
查询表中所有的列的数据,就可能把一些敏感数据查询到返回给前端,这个时候我们就需要限制哪些字段默认不要进行查询。解决方案是@TableField
注解的一个属性叫select
,该属性设置默认是否需要查询该字段的值,true(默认值)表示默认查询该字段,false表示默认不查询该字段。
知识点1:@TableField
名称 | @TableField |
---|---|
类型 | 属性注解 |
位置 | 模型类属性定义上方 |
作用 | 设置当前属性对应的数据库表中的字段关系 |
相关属性 | value(默认):设置数据库表字段名称 exist:设置属性在数据库表字段中是否存在,默认为true,此属性不能与value合并使用 select:设置属性是否参与查询,此属性与select()映射配置不冲突 |
问题4:表名与编码开发设计不同步
该问题主要是表的名称和模型类的名称不一致,导致查询失败,这个时候通常会报如下错误信息:
Table ‘databaseName.tableNaem’ doesn’t exist,翻译过来就是数据库中的表不存在。
解决方案是使用MP提供的另外一个注解@TableName
来设置表与模型类之间的对应关系。
知识点2:@TableName
名称 | @TableName |
---|---|
类型 | 类注解 |
位置 | 模型类定义上方 |
作用 | 设置当前类对应于数据库表关系 |
相关属性 | value(默认):设置数据库表名称 |
三、DML编程控制
查询相关的操作我们已经介绍完了,紧接着我们需要对另外三个,增删改进行内容的讲解。挨个来说明下,首先是新增(insert)中的内容。
3.1 id生成策略控制
- 不同的表应用不同的id生成策略
- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
- ……
不同的业务采用的ID生成方式应该是不一样的,那么在MP中都提供了哪些主键生成策略,以及我们该如何进行选择?
在这里我们又需要用到MP的一个注解叫@TableId
(@TableId(type = IdType.INPUT)
)
知识点1:@TableId
名称 | @TableId |
---|---|
类型 | 属性注解 |
位置 | 模型类中用于表示主键的属性定义上方 |
作用 | 设置当前类中主键属性的生成策略 |
相关属性 | value(默认):设置数据库表主键名称 type:设置主键属性的生成策略,值查照IdType的枚举值 |
从源码中可以看到,除了AUTO这个策略以外,还有如下几种生成策略:
- NONE: 不设置id生成策略
- INPUT:用户手工输入id
- ASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型)
- ASSIGN_UUID:以UUID生成算法作为id生成策略
- 其他的几个策略均已过时,都将被ASSIGN_ID和ASSIGN_UUID代替掉。
分布式ID是什么?
- 当数据量足够大的时候,一台数据库服务器存储不下,这个时候就需要多台数据库服务器进行存储
- 比如订单表就有可能被存储在不同的服务器上
- 如果用数据库表的自增主键,因为在两台服务器上所以会出现冲突
- 这个时候就需要一个全局唯一ID,这个ID就是分布式ID。
介绍了这些主键ID的生成策略,我们以后该用哪个呢?
- NONE: 不设置id生成策略,MP不自动生成,约等于INPUT,所以这两种方式都需要用户手动设置,但是手动设置第一个问题是容易出现相同的ID造成主键冲突,为了保证主键不冲突就需要做很多判定,实现起来比较复杂
- AUTO:数据库ID自增,这种策略适合在数据库服务器只有1台的情况下使用,不可作为分布式ID使用
- ASSIGN_UUID:可以在分布式的情况下使用,而且能够保证唯一,但是生成的主键是32位的字符串,长度过长占用空间而且还不能排序,查询性能也慢
- ASSIGN_ID:可以在分布式的情况下使用,生成的是Long类型的数字,可以排序性能也高,但是生成的策略和服务器时间有关,如果修改了系统时间就有可能导致出现重复主键
- 综上所述,每一种主键策略都有自己的优缺点,根据自己项目业务的实际情况来选择使用才是最明智的选择。
简化配置
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_
加上模型类的首字母小写,就刚好组装成数据库的表名。
3.2 多记录操作
如何实现多条删除,我们找找对应的API方法
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
需求:根据传入的id集合将数据库表中的数据删除掉。
@SpringBootTest
class Mybatisplus03DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testDelete(){//删除指定多条数据List<Long> list = new ArrayList<>();list.add(1402551342481838081L);list.add(1402553134049501186L);list.add(1402553619611430913L);userDao.deleteBatchIds(list);}
}
执行成功后,数据库表中的数据就会按照指定的id进行删除。
除了按照id集合进行批量删除,也可以按照id集合进行批量查询,还是先来看下API
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
方法名称翻译为:查询(根据ID 批量查询),参数是一个集合,可以存放多个id值。
需求:根据传入的ID集合查询用户信息
@SpringBootTest
class Mybatisplus03DqlApplicationTests {@Autowiredprivate UserDao userDao;@Testvoid testGetByIds(){//查询指定多条数据List<Long> list = new ArrayList<>();list.add(1L);list.add(3L);list.add(4L);userDao.selectBatchIds(list);}
}
查询结果就会按照指定传入的id值进行查询
3.3 逻辑删除
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(value="pwd",select=false)private String password;private Integer age;private String tel;@TableField(exist=false)private Integer online;@TableLogic(value="0",delval="1")//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));} }
可想而知,MP的逻辑删除会将所有的查询都添加一个未被删除的条件,也就是已经被删除的数据是不应该被查询出来的。
-
如果还是想把已经删除的数据都查询出来该如何实现呢?
@Mapper public interface UserDao extends BaseMapper<User> {//查询所有数据包含已经被删除的数据@Select("select * from tbl_user")public List<User> selectAll(); }
-
如果每个表都要有逻辑删除,那么就需要在每个模型类的属性上添加
@TableLogic
注解,如何优化?在配置文件中添加全局配置,如下:
mybatis-plus:global-config:db-config:# 逻辑删除字段名logic-delete-field: deleted# 逻辑删除字面值:未删除为0logic-not-delete-value: 0# 逻辑删除字面值:删除为1logic-delete-value: 1
知识点1:@TableLogic
名称 | @TableLogic |
---|---|
类型 | 属性注解 |
位置 | 模型类中用于表示删除字段的属性定义上方 |
作用 | 标识该字段为进行逻辑删除的字段 |
相关属性 | value:逻辑未删除值 delval:逻辑删除值 |
3.4 乐观锁
3.4.1 概念
业务并发现象带来的问题:秒杀
- 假如有100个商品或者票在出售,为了能保证每个商品或者票只能被一个人购买,如何保证不会出现超买或者重复卖
- 对于这一类问题,其实有很多的解决方案可以使用
- 第一个最先想到的就是锁,锁在一台服务器中是可以解决的,但是如果在多台服务器下锁就没有办法控制,比如12306有两台服务器在进行卖票,在两台服务器上都添加锁的话,那也有可能会导致在同一时刻有两个线程在进行卖票,还是会出现并发问题
- 我们接下来介绍的这种方式是针对于小型企业的解决方案,因为数据库本身的性能就是个瓶颈,如果对其并发量超过2000以上的就需要考虑其他的解决方案了。
简单来说,乐观锁主要解决的问题是当要更新一条记录的时候,希望这条记录没有被别人更新。
3.4.2 实现思路
乐观锁的实现方式:
- 数据库表中添加version列,比如默认值给1
- 第一个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
- 第二个线程要修改数据之前,取出记录时,获取当前数据库中的version=1
- 第一个线程执行更新时,set version = newVersion where version = oldVersion
- newVersion = version+1 [2]
- oldVersion = version [1]
- 第二个线程执行更新时,set version = newVersion where version = oldVersion
- newVersion = version+1 [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提供的乐观锁的实现原理分析。
3.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(value="pwd",select=false)private String password;private Integer age;private String tel;@TableField(exist=false)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);}
}
你会发现,我们传递的是1,MP会将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); //version=3User user2 = userDao.selectById(3L); //version=3user2.setName("Jock aaa");userDao.updateById(user2); //version=>4user.setName("Jock bbb");userDao.updateById(user); //verion=3?条件还成立吗?}
}
官方文档:
https://mp.baomidou.com/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
四、快速开发
4.1 代码生成器
步骤1:创建一个Maven项目
步骤2:导入对应的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.1</version></parent><groupId>com.yyds</groupId><artifactId>mybatisplus_04_generator</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>1.8</java.version></properties><dependencies><!--spring webmvc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mybatisplus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><!--代码生成器--><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><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
步骤3:编写引导类
@SpringBootApplication
public class Mybatisplus04GeneratorApplication {public static void main(String[] args) {SpringApplication.run(Mybatisplus04GeneratorApplication.class, args);}}
步骤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?serverTimezone=UTC");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("yyds"); //设置作者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
,service
,mapper
和entity
4.2 MP中Service的CRUD
MP提供了一个Service接口和实现类,分别是:IService
和ServiceImpl
,后者是对前者的一个具体实现。
以后我们自己写的Service就可以进行如下修改:
public interface UserService extends IService<User>{}@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService{}
修改以后的好处是,MP已经帮我们把业务层的一些基础的增删改查都已经实现了,可以直接进行使用。
编写测试类进行测试:
@SpringBootTest
class Mybatisplus04GeneratorApplicationTests {private IUserService userService;@Testvoid testFindAll() {List<User> list = userService.list();System.out.println(list);}}
注意: mybatisplus_04_generator项目中对于MyBatis的环境是没有进行配置,如果想要运行,需要提取将配置文件中的内容进行完善后在运行。
思考:在MP封装的Service层都有哪些方法可以用?
查看官方文档:https://mp.baomidou.com/guide/crud-interface.html
相关文章:
SpringBoot整合(二)MyBatisPlus技术详解
MyBatisPlus详解 一、标准数据层开发 MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率 MyBatisPlus的官网为:https://mp.baomidou.com/ 1.1 标准CRUD 1.2 新增 int insert (T t)T:泛型,…...
导入importk8s集群,添加node节点,rancher agent,Rancher Agent设置选项
curl方式: Rancher在每个节点上部署代理以与节点通信。 此页面描述了可以传递给代理的选项,要使用这些选项,您需要采用创建自定义集群 ,并在docker run添加节点时将选项添加到生成的命令中。 常规选项 参数环境变量描述—serve…...
C++11--右值引用与移动语义
目录 基本概念 左值与右值 左值引用与右值引用 右值引用的使用场景和意义 左值引用的使用场景 右值引用和移动语义 移动构造和拷贝构造的区别 编译器的优化 移动赋值和赋值运算符重载的区别 右值引用的其他应用场景 完美转发 万能引用 完美转发保持值属性 完美转…...
Python SQLAlchemy入门教程
本文将以Mysql举例,介绍sqlalchemy的基本用法。其中,Python版本为2.7,sqlalchemy版本为1.1.6。 一. 介绍 SQLAlchemy是Python中最有名的ORM工具。 关于ORM: 全称Object Relational Mapping(对象关系映射࿰…...
你是真的“C”——操作符详解【下篇】+整形提升+算术转换
你是真的“C”——操作符详解下篇😎前言🙌操作符详解【上篇】内容:操作符详解【下篇】内容:1、 条件操作符2、逗号表达式:3、下标引用、函数调用和结构成员3、访问一个结构的成员表达式求值1、隐式类型转换:…...
文本匹配SimCSE模型代码详解以及训练自己的中文数据集
前言 在上一篇博客文本匹配中的示例代码中使用到了一个SimCSE模型,用来提取短文本的特征,然后计算特征相似度,最终达到文本匹配的目的。但是该示例代码中的短文本是用的英文短句,其实SimCSE模型也可以用于中文短文本的特征提取&a…...
Biotin-PEG-FITC 生物素聚乙二醇荧光素;FITC-PEG-Biotin 科研用生物试剂
结构式: Biotin-PEG-FITC 生物素聚乙二醇荧光素 英文名称:Biotin-PEG-Fluorescein 中文名称:生物素聚乙二醇荧光素 外观:黄色液体、半固体或固体,取决于分子量。 溶剂:溶于大部分有机溶剂,…...
FISCO BCOS 搭建区块链,在SpringBoot中调用合约
一、搭建区块链 使用的是FISCO BCOS 和 WeBASE-Front来搭建区块链,详细教程: https://blog.csdn.net/yueyue763184/article/details/128924144?spm1001.2014.3001.5501 搭建好能达到下图效果即可: 二、部署智能合约与导出java文件、SDK证…...
面试官:int和Integer有什么区别?
回答思路: 原始数据类型和包装类介绍 主要区别(数据使用内存) 自动装箱、自动拆箱机制和实践原则 回答总结: int 是8种基本数据类型(byte、boolean、char、short、int、long、float、double)之一ÿ…...
MFC常用技巧
MFC常用技巧1、句柄MFC中如何获取窗口的句柄2、字符串CString转char*Unicode下char *转换为CString3、Visual C 64 位迁移的常见问题(数据类型、指针类型的长度问题)4、c - 将_beginthread返回的uintptr_t转换为HANDLE是否安全1、句柄 MFC中如何获取窗口…...
C++ —— 多态
目录 1.多态的概念 2.多态的定义及实现 2.1构成多态的两个硬性条件 2.2虚函数的重写 2.3override和final 3.抽象类 3.1接口继承和实现继承 4.多态原理 4.1虚函数表 4.2原理 4.3静态绑定和动态绑定 5.单继承和多继承体系的虚函数表 5.1单继承体系的虚函数表 5.2多继…...
java agent设计开发概要
agent开发设计 agent 开发的一些心得,适合熟悉agent或者有agent开发需求的同学 1 有个基础的agent,是java 标准的agent。这是agent代码入口 2 设计包结构, 基础agent agent下有plugin,加载plugin可以自己定义一个类加载器 plugin࿱…...
node.js笔记-模块化(commonJS规范),包与npm(Node Package Manager)
目录 模块化 node.js中模块的分类 模块的加载方式 模块作用域 向外共享模块作用域中的成员 向外共享成员 包与npm(Node package Manager) 什么是包? 包的来源 为什么需要包? 查找和下载包 npm下载和卸载包命令 配置np…...
Linux 磁盘坏块修复处理(错误:read error: Input/output error)
当磁盘出现坏块时,你对所关联的文件进行读取时,一般会出现 read error: Input/output error 这样的错误。 反过来讲,当你看到 read error: Input/output error 这种错误时,很大可能就是磁盘出现了坏块问题。 解决步骤:…...
API 面试四连杀:接口如何设计?安全如何保证?签名如何实现?防重如何实现?
下面我们就来讨论下常用的一些API设计的安全方法,可能不一定是最好的,有更牛逼的实现方式,但是这篇是我自己的经验分享. 一、token 简介 Token:访问令牌access token, 用于接口中, 用于标识接口调用者的身份、凭证,减…...
操作系统题目收录(六)
1、某系统采用基于优先权的非抢占式进程调度策略,完成一次进程调度和进程切换的系统时间开销为1us。在T时刻就绪队列中有3个进程P1P_1P1、P2P_2P2和P3P_3P3,其在就绪队列中的等待时间、需要的CPU时间和优先权如下表所示。若优先权值大的进程优先获…...
2023年十款开源测试开发工具推荐!
今天为大家奉献一篇测试开发工具集锦干货。在本篇文章中,将给大家推荐10款日常工作中经常用到的测试开发工具神器,涵盖了自动化测试、性能压测、流量复制、混沌测试、造数据等。 1、AutoMeter-API 自动化测试平台 AutoMeter 是一款针对分布式服务&…...
MySQL慢查询分析和性能优化
1 背景我们的业务服务随着功能规模扩大,用户量扩增,流量的不断的增长,经常会遇到一个问题,就是数据存储服务响应变慢。导致数据库服务变慢的诱因很多,而RD最重要的工作之一就是找到问题并解决问题。下面以MySQL为例子&…...
C++学习笔记(四)
组合、继承。委托(类与类之间的关系) 复合 queue类里有一个deque,那么他们的关系叫做复合。右上角的图表明复合的概念。上图的特例表明,queue中的功能都是通过调用c进行实现(adapter)。 复合关系下的构造和…...
【4】深度学习之Pytorch——如何使用张量处理时间序列数据集(共享自行车数据集)
表格数据 表格中的每一行都独立于其他行,他们的顺序页没有任何关系。并且,没有提供有关行之前和行之后的列编码信息。 表格类型的数据是指通过表格的形式表示的数据,它以行和列的方式组织数据。表格中的每一行代表一个数据项,每…...
mulesoft MCIA 破釜沉舟备考 2023.02.10.01
mulesoft MCIA 破釜沉舟备考 2023.02.10.01 1. What is a defining charcateristic of an integration-Platform-as-a-Service(iPaaS)?2. An application deployed to a runtime fabric environment with two cluster replicas is designed to periodically trigger of flow f…...
干货 | PCB拼板,那几条很讲究的规则!
拼板指的是将一张张小的PCB板让厂家直接给拼做成一整块。一、为什么要拼板呢,也就是说拼板的好处是什么?1.为了满足生产的需求。有些PCB板太小,不满足做夹具的要求,所以需要拼在一起进行生产。2.提高SMT贴片的焊接效率。只需要过一…...
笔试题-2023-思远半导体-数字IC设计【纯净题目版】
回到首页:2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 推荐内容:数字IC设计学习比较实用的资料推荐 题目背景 笔试时间:2022.08.20应聘岗位:数字IC设计工程师笔试时长:90min笔试平台:牛客网题目类型:填空题(2道),不定项选择题(3道),单选题(2道),问…...
canvas根据坐标点位画图形-canvas拖拽编辑单个图形形状
首先在选中图形的时候需要用鼠标右击来弹出选择框,实现第一个编辑节点功能 在components文件夹下新建右键菜单 RightMenu文件: <template><div v-show"show" class"right-menu" :style"top:this.ypx;left:this.xpx…...
JavaEE 初阶 — 确认应答机制
文章目录确认应答机制(安全机制)1 什么是后发先至问题1 如何解决后发先至问题确认应答机制(安全机制) 确认应答 是实现可靠传输的最核心机制。 这里指的 可靠传输 不是说 100% 可以把消息发给接收方,而是尽力而为&…...
0207 事件
事件监听事件监听版本事件类型事件概念事件在编程时系统内发生的动作或者发生的事情例子点击按钮鼠标经过拖拽鼠标事件监听(注册事件,绑定事件)让程序员检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应…...
SpringBoot整合Swagger
目录 一、swagger介绍 二、springboot集成swagger 1、创建一个springboot-web项目 2、导入相关依赖 3、编写一个Hellow工程 4、配置swagger --->config 5、启动springboot工程 6、配置swagger信息 7、配置swagger扫描接口 8、如何设置Swagger在生产环境中使用&…...
20230210英语学习
Why Do So Many Cats Have White ‘Socks’ on Their Paws? 为什么好多猫咪脚上都“穿着白袜子”? If you see a house cat, the odds are high that it will have white paws, a look that many owners affectionately call "socks."But socks are rar…...
【图像处理OpenCV(C++版)】——4.5 全局直方图均衡化
前言: 😊😊😊欢迎来到本博客😊😊😊 🌟🌟🌟 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快…...
2022年API安全研究报告
目录 导读 2022年API安全风险概况 2022年平均每月遭受攻击的API数量超21万...
淄博企业网站建设/英文站友情链接去哪里查
背景:目前在做项目的时候,需要使用hbase进行数据的查询,一些评论记录,消费数据,交易日志等数据都是存储在hbase中的,因为都是海量的数据。了解hbase的原理是必要的,因此必须要学习好 rowkey的设…...
wordpress 充值记录/电子商务营销策划方案
这两天一直在搞重定向,问题是一直看不明白,自己弄来弄去,都不成,我的想法是把 http:\\webmail 转到https:\\server\exchange,就是用户输入 http:\\webmail ,就可以直接访问 https:\\server\exchange, 但是…...
30日长春疫情/seo外链优化培训
仅作为记录,大佬请跳过。 修改endnote的endnote import,将%B改为%J即可。 文章目录步骤endnote中:Edit——Import Filters——Open Filter Manager——找到Endnote Import——Edit——templates——journal article——修改成%J参考步骤 en…...
查看网站建设时间/怎么制作网站教程手机
原因 今天下午在抓取一个网页时,发现的网页字体反爬,这种情况一句话总结:即网页文本里的数字与网页上显示的字体不一致。为什么会出现这样的情况呢?原因是开发者在网页文本里引入了改变字体的文件。 然后可以看到,这…...
本人想求做网站/深圳seo优化seo优化
1WebLogic连接池配置 WebLogic Server已经包含有Oracle9i的数据库驱动程序,而不含Informix的数据库驱动,因此本文专门针对Informix数据库进行数据库缓冲池进行,需要在WebLogic Server中添加Informix的驱动。1.1文件准备需要准备Informix的…...
门户网站的建设/合肥最新消息今天
电视猫的新版本是越来越难用了,不知道有没有老的版本可以使用,这里小编就给大家提供一款去升级版本,不提示升级的哦,而且还是比较老的版本,非常爽,需要的朋友可以下载用用。电视猫去升级版版功能海量视频 免…...