Mybatis-Plus:持久层数据的高效处理(三)--条件查询与分页查询
🤚我的博客
- 欢迎光临我的博客:
https://blog.csdn.net/qq_52434217?type=blog
🥛前言
上一章介绍了常用的API并对其进行了测试。这一章主要的任务是介绍一下前面忘记介绍的mybatis生成的实体类,以级两种高级查询方式:条件查询以及分页查询。这两个高级查询语句都不需要单独编写SQL语句,而是框架自动调用生成SQL语句,从而提高生产效率。
📄MybatisX生成代码实体类的介绍
🔷MybatisX Generator配置
这个地方只在第一章涉及到,但没有细讲。后面仔细考虑一下还是决定讲一下这个插件的使用。
在下面的界面中,可以指定数据表的信息与实体类的一些关系,如ignore table prefix
和ignore table suffix
可以去掉表中的前缀和后缀,比如该处的表名为user_info
就可以忽略掉后面的_info
从而生成User
实体类。
点击next
进入下一页的配置选项页面,这个页面选择实体类的注释版本以及模板版本。options
是注释对实体类的操作,而template
则是生成代码的模板。所谓代码模板,是指生成代码包含的东西,这个template
与IDEA
的自定义template
没有区别。而不同的配置则有不同的文件以及模板。
点击FINISH
生成代码,可以在上一页的base path
下的base package
中的relative package
中找到实体类。通过插件生成的实体类,自动实现了equals()
方法、toString()
方法和hashCode()
方法,并且实现了序列化接口,这里只贴出数据库中的字段以及序列化ID。
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
** @TableName test1
*/@TableName(value ="test1")
@Data
public class Test1 implements Serializable {
/**
* 唯一id
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 姓名
*/
private String name;
/**
* */
private Integer age;
/**
* 1为男,0为女 2保密
*/
private Integer gender;
/**
* 电话
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 头像地址
*/
private String avatar;
/**
* */
private String department;
/**
* */
private Double salary;
/**
* */
private String address;
/**
* 1为正常 0为停用
*/
private Integer status;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
下面对常用注解进行介绍
@TableName()
@TableName()
是表名注释,通过该注释指定实体类与数据表的映射关系。只要指定了表明,实体类的类名可以不遵循表的名字。但是数据字段需要一一对应,不能随意改写。下表是该注释的常用属性。
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
value | String | "" | 表名 |
resultMap | String | "" | xml中resultMapde id |
autoReusltMap | boolean | false | 是否自动构建 resultMap 并使用(如果设置 resultMap ,则不会进行 resultMap 的自动构建并注入) |
@TableId()
@TableId
注解是专门用在主键上的注解,如果数据库中的主键字段名和实体中的属性名,不一样且不是驼峰之类的对应关系,可以在实体中表示主键的属性上加@TableId
注解,并指定@TableId
注解的value
属性值为表中主键的字段名既可以对应上。
/** 主键 */
@TableId("test1_id")
@TableId(value = "test1_id")
private Long id;
该注释除了value
属性还包括一个type
属性,用来设置主键类型、主键的生成策略。id类型如下表所示。
类型名 | 说明 |
---|---|
IdType.AUTO | 数据库自增 |
IdType.NONE | mybatis plus 通过雪花算法设置主键 |
IdType.INPUT | 手动赋值 |
IdType.ASSIGN_ID | MP分配ID ,类型为Long 、Integer 、String |
IdType.ASSIGN_UUID | 分配UUID,类型为String |
📄两种高级查询方式
🔷条件查询
MP的条件查询是采用条件构造器实现的,条件构造器在上一章的API中出现过,但是没有细讲。
所谓条件构造器就是一个实例对象,传入它可以实现条件查询。在MP中有四个条件构造器,分别是QueryWrapper
,UpdateQueryWrapper
,LambdaQueryWrapper
,LambdaUpdateWrapper
。
接下来具体看看条件构造器的使用。该段代码在单元测试方法或者在serviceImpl
的方法中编写,以单元测试为例。
QueryWrapper
@Test
void conditionalTest(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("salary", 15000.0);
System.out.println(test1Mapper.selectList(wrapper));
}
//[User [Hash = 667046708, id=1, username=zhangsan111, password=zhangsan111, name=张三, age=35, gender=1, phone=13512345678, email=13512345678@qq.com, avatar=, department=销售部, salary=15000.0, address=北京市海淀区中关村, status=1, serialVersionUID=1], User [Hash = -1715870505, id=3, username=wangwu111, password=wangwu111, name=王五, age=42, gender=1, phone=13811112222, email=13811112222@qq.com, avatar=, department=研发部, salary=20000.0, address=广东省深圳市南山区科技园, status=1, serialVersionUID=1], User [Hash = 1975952528, id=5, username=liuqi111, password=liuqi111, name=刘七, age=22, gender=0, phone=13655556666, email=13655556666@qq.com, avatar=, department=财务部, salary=18000.0, address=四川省成都市高新区, status=1, serialVersionUID=1]]
这段代码相当于实现的sql语句
SELECT id,username,password,name,age,gender,phone,email,avatar,department,salary,address,status FROM test1 WHERE (salary >= 15000.0)
wrapper的每一个函数都可以接受一个条件判断语句,条件判断放在第一个参数。
@Test
void conditionalTest(){
Double salary = 15000.0;
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge(salary!=null,"salary", 15000.0);
System.out.println(test1Mapper.selectList(wrapper));
}
这是在mapper中的实现,当然也可以在service中实现。在service中除了wrapper条件,还有一个lambda条件。以listObjs
为例。
@Test
void serviceTest1() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("salary", 15000.0);
List<User> users = test1Service.listObjs(wrapper, id -> test1Service.getById((Serializable) id));
users.forEach(System.out::println);
}
/*
User [Hash = 667046708, id=1, username=zhangsan111, password=zhangsan111, name=张三, age=35, gender=1, phone=13512345678, email=13512345678@qq.com, avatar=, department=销售部, salary=15000.0, address=北京市海淀区中关村, status=1, serialVersionUID=1]
User [Hash = -1715870505, id=3, username=wangwu111, password=wangwu111, name=王五, age=42, gender=1, phone=13811112222, email=13811112222@qq.com, avatar=, department=研发部, salary=20000.0, address=广东省深圳市南山区科技园, status=1, serialVersionUID=1]
User [Hash = 1975952528, id=5, username=liuqi111, password=liuqi111, name=刘七, age=22, gender=0, phone=13655556666, email=13655556666@qq.com, avatar=, department=财务部, salary=18000.0, address=四川省成都市高新区, status=1, serialVersionUID=1]
*/
这段代码相当于执行了如下的代码
SELECT id,username,password,name,age,gender,phone,email,avatar,department,salary,address,status FROM test1 WHERE (salary >= 15000.0)
这里的lambd函数的参数是一个id值,根据id进行后续的操作。
条件构造器的函数返回实例本身,所以可以采用链式调用。比如下面的代码
@Test
void serviceTest1() {
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.ge("salary", 15000.0)
.gt("id",3);
List<User> users = test1Service.listObjs(wrapper, id -> test1Service.getById((Serializable) id));
users.forEach(System.out::println);
}
QueryWrapper
与UpdateQueryWrapper
的区别在于使用queryWrapper
+ 实体类形式可以实现修改,但是无法将列值修改为null值,而使用updateWrapper可以随意设置列的值。
使用queryWrapper
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE username LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("username", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
使用updateWrapper
@Test
public void testQuick2(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//将id = 3 的email设置为null, age = 18
updateWrapper.eq("id",3)
.set("email",null) // set 指定列和结果
.set("age",18);
//如果使用updateWrapper 实体对象写null即可!
int result = userMapper.update(null, updateWrapper);
System.out.println("result = " + result);
}
queryWrapper常用的API接口在下面的表中展示。
函数名 | 说明 | 说明/示例 |
---|---|---|
eq | equals ,,等于,= | eq("name","老王") –>name=='老王' |
ne | not equals ,,不等于,<> | ne("name","老王") -->name<>'老王' |
gt | great than ,大于,> | gt("age",18) –>age>18 |
ge | great and equal,大于等于,>= | ge("age",18) –>age>=18 |
lt | less than,小于, < | lt("age",18) –>age<18 |
le | less and equal,小于等于,<= | le("age",18) –>age<=18 |
between | between("age",18,20) –>age between 18 and 20 | |
notBetween | notBetween("age",18,20) –>age not between 18 and 20 | |
like | like("name","王") –>name like '%王%' | |
notLike | notLike("name","王") –>name not like '%王%' | |
likeLeft | likeLeft("name","王") –>name like '%王' | |
likeRight | likeRight("name","王") –>name like '王%' | |
isNull | isNull("name") -->name is null | |
isNotNull | isNotNull("name") –> name is not null | |
in | in("age",{18,19,20}) –>age in (18,19,20) | |
notIn | notIn("age",{18,19,20}) –>age not in (18,19,20) | |
inSql | inSql("id","select id from table where id < 3") –>id in (select id from table where id < 3) | |
notInSql | notInSql("id","select id from table where id < 3") –>id not in (select id from table where id < 3) | |
groupBy | groupBy("id","name") –>group by id,name | |
orderByAsc | orderByAsc("id","name") -->order by id ASC, name ASC | |
orderByDesc | orderByDesc("id","name") -->order by id DESC, name DESC | |
orderBy | orderBy(boolean condition,boolean isAsc,String colunm,...colunm) | orderBy(true,true,"id","name") –>order by id ASC, name ASC |
having | having("sum(age) >{0}",11) -->having sum(age)>11 | |
or | 拼接 | 主动调用or表示紧接着的下一个方法不用and连接,如果需要一直使用,则一直需要主动调用 |
and | and嵌套 |
LambdaQueryWrapper
相比于 QueryWrapper,LambdaQueryWrapper 使用了实体类的属性引用(例如 User::getName
、User::getAge
),而不是字符串来表示字段名,这提高了代码的可读性和可维护性。
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "John")
.ge(User::getAge, 18)
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
🔷分页查询
分页拦截器配置
使用分页查询,需要在Application入口处配置分页拦截器,当然也可以新建一个配置类将拦截器配置注入。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 创建MP拦截器实例
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 确定数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
分页查询的使用
@Test
void pageSelect(){
IPage<User> page = new Page<>(1,5);
IPage<User> test1IPage = test1Mapper.selectPage(page, null);
// 获取当前页的数据
List<User> records = test1IPage.getRecords();
System.out.println("总页数:"+test1IPage.getPages());
System.out.println("当前页:"+test1IPage.getCurrent());
System.out.println("当前页数量大小"+test1IPage.getSize());
System.out.println("获取查询总数"+test1IPage.getTotal());
records.forEach(System.out::println);
}
END
至此,本章内容补充介绍了Mybatis Plus插件的配置以及生成的实例和常用注解。同时在前面的常用API基础之上,实现了条件查询与分页查询。条件查询采用的条件构造器实例进行查询,而分页查询则需要添加MP拦截器,并且需要配置数据库类型才能实现分页操作。下一章将介绍手写mapper中的sql语句,这一章的内容回到了mybatis中。学完手写sql语句后就可以总结mybtis plus相较于mybatis的优势。
公众号
更多内容请关注小夜的公众号,将持续更新java spring全家桶,vue全家桶,python数据分析与爬虫以及js爬虫逆向。
目前正在做小蓝书项目(没错,仿的小红书),后续也会更新项目笔记。
欢迎关注小夜的公众号。