目录
7.QueryWrapper与LambdaQueryWrapper
1.MyBatisPlus简介
主要就是简化MyBatis开发。
2.MyBatisPlus入门操作
1.引入依赖:需要引入mp和mysql驱动依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2.编写配置文件,主要是数据库连接信息
spring:
profiles:
active: dev
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/duolaimi?serverTimezone=GMT%2B8
username: root
password: root
注意:有些版本的jdbc连接数据库的地址后面必须要加上时区,如serverTimezone=GMT%2B8,不加就会启动报错
3.mapper层
原来的mapper接口需要继承BaseMapper并传入对应的泛型即可,BaseMapper中包含了基本的增删改查方法。
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
4.service层
原来的service接口继承IService,并传入对应的泛型即可,IService里面也包含了基础的增删改查的业务方法。
public interface UserService extends IService<User> {
}
5.serviceimpl层
serviceimpl类需要实现原来定义好的service接口,但是只是实现了却没有重写必须的方法,所以需要继承ServiceImpl类,其中需要传两个泛型,第一个泛型是mapper接口,第二个泛型是实体类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
6.编写controller
注入service对象,然后直接调用增删改查方法即可。
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/test")
public String test(){
User user = userService.getById(1);
System.out.println(user);
return user.toString();
}
}
3.MP开启日志
直接使用如下配置即可开启日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
开启日志后,每次对数据库进行操作时,会有如下信息输出:
4.MP的自动映射规则
1.表名和实体类名映射,比如user表对应User实体类
2.字段名和实体类属性名映射,如字段名name对应实体类属性名name(getXxx或者setXxx)
3.属性映射默认支持小驼峰命名方式
5.表映射
1.如果表名与实体类名不一致,则可以使用@TableName("xxx")这个注解手动指定映射关系
2.如果每张表都有固定前缀的话,我们可以开启全局配置,使用如下配置指定表名的前缀
mybatis-plus:
global-config:
db-config:
table-prefix: xxx_
6.字段映射
如果数据库字段与类中的属性名不一样,可以直接在属性上使用@TableFiled指定数据库字段名称
@TableField("xxx")
private String name;
如果字段或者属性是数据库中的关键字,则可以在@TableField中使用``(飘号)包起来。
如果希望被查询的字段不展示,则可以在@TableField使用select=false属性
如果数据库中的字段不存在,但是实体对象中有属性,可以使用@TableField中的exist=false属性
7.QueryWrapper与LambdaQueryWrapper
这两个是构建条件查询的对象,QueryWrapper后面的参数通过字符串传入,LambdaQueryWrapper可通过lambda表达式传入,写法上更优雅
如果是多条件查询,可以使用两种方法:
1.LambdaQueryWrapper里的条件方法,一直拼条件
2.QueryWrapper里的allEq()方法,参数传map
方法说明:
- eq:等值查询
- ne:不等查询
- gt:大于查询
- ge:大于等于查询
- lt:小于查询
- le:小于等于查询
- between:区间查询
- notBetween:不在区间查询
- like:模糊查询
- notLike:不包含查询
- likeLeft:左包含查询
- likeRight:右包含查询
- isNull:空值查询
- isNotNull:非空值查询
- in:包含查询
- notIn:不包含查询
- inSql:包含查询,只不过后面的参数会将传入的sql整个放入in后面
- notInSql:不包含查询,后面传sql字符串的方式
8.分组查询
分组查询一般使用QueryWrapper来进行封装数据
@Test
public void test3(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.groupBy("age");
queryWrapper.select("age, count(*) as field_count");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
System.out.println(maps);
}
查看输出语句我们可以知道,groupBy()中的参数是指拼接在sql后面的group by的值,select()参数是指在sql中select后面的值。
9.聚合查询
在原来的分组查询基础上,如果再加上having查询,可使用如下语句:
queryWrapper.having("field_count >= 2");
10.排序查询
最终会在执行的sql后面拼上order by xxx
- orderByAsc:根据指定字段升序
- orderByDesc:根据指定字段降序
- orderBy:其中有三个参数,参数1:如果排序字段的值为null的时候,是否还要作为排序字段参与排序 参数2:是否是升序排序 参数3:排序字段
11.内嵌逻辑查询
queryWrapper.func(e -> {
if(1 == 1){
e.eq("name","zhangsan");
}
});
可以使用queryWrapper的func方法,做一些条件的分支。
12.自定义条件查询
可以使用queryWrapper的apply方法。
13.last方法
last方法会在最后执行,所以一般是做分页查询,如last("limit 0,2");
14.exists方法与notExists方法
exists()表示参数里面如果能返回结果,则外面的sql会执行,否则不执行,notExists则相反。
15.主键策略
可以使用@TableId注解声明这是一个主键字段,并设置type属性,type属性有如下几种类型:
- AUTO:该策略为跟随数据库表的主键按递增策略,前提是数据库表的主键要设置为自增
- INPUT:必须由武松们手动插入主键,否则无法添加数据
- ASSIGN_ID:使用雪花算法生成一个唯一的id,64位二进制,19位长度的十进制
- NONE:不指定主键生成策略,跟随全局策略
- ASSIGN_UUID:UUID的主键,32位数字组成,编码采用16进制
如果我们不设置主键策略,则默认是使用雪花算法
16.分页插件
需要配置拦截器,并指定数据库类型(3.4版本及之后)
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
}
注意,不同版本的MyBatisPlus的分页配置是不一样的。
如果是3.4版本之前的是如下配置:
@Configuration
public class MyBatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
使用分页:
@Test
public void test4(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
Page<User> userPage = new Page<>(1, 10);
userMapper.selectPage(userPage, lambdaQueryWrapper);
List<User> records = userPage.getRecords();
}
使用Page对象,在构造方法上指定页数和每页显示条数,然后使用生成的selectPage方法,不需要接收返回值,直接在原来声明好的page对象中就能接收分页信息。
自定义分页方法:
我们可以在声明方法的时候,传参传IPage对象,返回值也返回IPage对象
17.ActiveRecord模式
ActiveRecord(活动记录,简称AR),是一种领域模型模式,特点是一个模型类对应的关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
如何使用?
原有的实体类继承Model,并指定泛型即可,里面有基本的增删改查方法。
@Data
public class User extends Model<User> {
private Long id;
private String name;
private Long age;
private String email;
}
@Test
public void test5(){
User user = new User();
user.insert();
}
18.SimpleQuery工具类
SimpleQuery可以对selectList查询后的结果用Stream流进行一些封装,使其但会一些指定给结果,简洁了api的调用。
基本方法:
- list:返回一个list集合,存放符合条件的(第一个参数),并且第二个参数属性的集合
- keyMap:返回一个map对象,key为第二个参数类型的值,value为第一个参数类型中泛型类型的值
- group:返回一个map,其中key是第二个参数类型的数据,value是实体类型的集合
19.逻辑删除
逻辑删除的操作是增加一个字段表示这个数据的状态,如果一条数据需要删除,我们通过改变这条数据的状态来实现,这样既可以表示这条数据是删除的状态,又保留了数据以便以后统计。
如何实现?
数据表中加上一个字段,表示逻辑删除,如status(int类型),0删除-1未删除
然后在类的属性上加上@TableLogic注解,其中属性value表示未删除的状态,delvalue表示删除的状态
以后再删除数据的时候,其实是更新状态。
再查询数据的时候,后面会加上status的条件
也可以在配置文件中设定全局逻辑删除的配置
主要设定逻辑删除属性,有效值和被删除值
这里如果配置了全局逻辑删除字段,但实体类中如果没有该字段,其实也不会报错,就不会走逻辑删除
20.通用枚举
如果我们需要插入枚举中具体的值,但又不希望在代码中直接获取到值去插入,我们只希望声明到枚举,这时候我们可以使用@EnumValue注解来声明传递枚举时需要传递这个值。
21.字段类型处理器
在某些场景下,我们在实体类中是使用Map集合作为属性接收前端传递过来的数据的,但是这些数据在数据库时,我们使用的是json格式的数据进行存储,json本质是一个字符串,就是varchar类型。那怎么做到实体类的Map类型和数据库的varchar类型的互相转换,这里就需要使用到字段类型处理器来完成。
如何使用?
1.项目中需要添加fastjson依赖坐标
2.数据库加字段对应和实体类加上属性, 数据库字段是varchar类型,实体类属性是String类型
3.在实体类属性上标注@TableField注解,并指定转换器,说明往数据表中存的数据将要转换成json格式
4.在实体类上加上@TableName注解,并设置json自动转换成map,这会将数据表中读到的json自动转换成map
22.自动填充功能
在项目中有一些属性,如果我们不希望每次都填充的话,我们可以设置为自动填充,比如常见的时间,创建时间和更新时间可以设置为自动填充。
1.我们可以使用@TableField注解,指定fill属性,说明该字段在新增还是更新时做插入
2.编写填充策略处理器
@Component
public class MyMetaHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
setFieldValByName("createTime", new Date(), metaObject);
setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName("updateTime", new Date(), metaObject);
}
}
指定要填充的字段和填充的数据。
23.防止全表更新拦截器
只需在mybatis的配置类中添加一个防止全表更新的拦截器即可,如果出现全表更新操作,则会抛出异常。
24.MyBatisX及逆向工程
我么可以在idea的插件中安装mabatisX的插件,安装完毕后,通过idea连接数据库,然后右击表名,第一个会出现MyBatisX Generator,也就时MyBatisX的代码生成器插件。
在弹出的框中,我们一般做如下配置:
设置模块地址,设置包名,设置表前缀(没有前缀则可以不用设置)
选择mp3版本,使用lombok,设置mapper.xml路径
可以根据名字自动生成更复杂的查询,然后选择生成即可。
25.悲观锁和乐观锁
悲观锁
悲观锁实在查询的时候就锁定数据,在这次请求未完成之前,不会释放锁。等到这次请求完毕之后,再释放锁,释放了锁以后,其他请求才能对这条数据完成读写
这样做的操作能够保证读取到的信息就是当前的信息,保证了信息的正确性,但是并发效率很低,在实际开发中使用悲观锁的场景很少,因为在并发时我们是要保证效率的。
乐观锁
乐观锁是通过表字段完成设计的,他的核心思想是,在读取的时候不加锁,其他请求依然可以读取到这个数据,在修改的时候判断一个数据是否有被修改过,如果有被修改过,那本次请求的修改失效。
MP中的乐观锁使用
1.数据表新增一个字段,比如version,int类型,设置默认值为1
2.在属性上添加@Version注解
3.添加拦截器,看名字就知道是乐观锁拦截器
26.代码生成器
mp的代码生成器区别于mybatisX插件,更加灵活。
如何使用?
1.引入必要依赖
2.查看官网,我们可以在main方法中添加固定代码,根据自己配置稍作配置即可
FastAutoGenerator.create("url", "username", "password")
.globalConfig(builder -> {
builder.author("baomidou") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://"); // 指定输出目录
})
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
}))
.packageConfig(builder -> {
builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
.moduleName("system") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_simple") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
27.SQL分析打印
我们在日常开发中,避免不了查看当前程序锁所执行的SQL语句,以及了解它的执行时间,方便分析是否出现了慢SQL问题。我们可以使用MyBatisPlus提供的SQL分析打印的功能,来获取SQL语句执行的时间。
如何使用?
1.引入坐标,p6spy
2.在配置文件中配置信息
3.在resources下,创建spy.properties配置文件,配置代码基本固定
4.测试
在接下来的sql执行中,会提示输出红色文字,展示sql耗时