官方地址
<!-- mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//DbType 数据库类型
PaginationInnerInterceptor page = new PaginationInnerInterceptor(DbType.MYSQL);
//单次查询最大的数量 如果我查10条,返回还是5条。
page.setMaxLimit(5L);
//溢出总页数后是否做处理(默认不做,true表示做处理,回到首页) false 继续请求
page.setOverflow(false);
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//设置数据库类型为mysql
interceptor.addInnerInterceptor(page);
return interceptor;
}
属性:
常用的数据库类型DbType.mysql等,详细请见官方文档。
分页查询的条件
current:当前页
size:当前页显示数据条数
records:返回数据
total:数据库数据总量
pages:总页数
当然,也缺少不了数据的排序方式。
Page
IPage接口
Page实现了IPage接口
通过这两个默认方法我们可知,查询时,如果当前页数<=1,那么默认从第0页开始查,如果>=1,则从传入页数-1开始查。
虽然MP给我们提供了丰富的分页查询接口,但是由于功能过于繁多,正常开发中我们很少用到,所以适合我们的才是最好的。我这里提供了一个自定义的查询条件接口,满足自己作品的使用。
在这里,我们默认从第一页开始查询数据。
并且排序方式[{‘column’: ‘create_time’,‘asc’: false},{‘column’: ‘name’,‘asc’: "true}]
默认是按照,创建时间倒序的方式[{‘column’: ‘create_time’,‘asc’: false}]
@Data
@ApiModel(value = "分页", description = "orders传参实例:[{'column': 'create_time','asc': false},{'column': 'name','asc': " +
"true}]")
public class PageVO {
/**
* 当前页
*/
@ApiModelProperty(value = "当前页")
private Integer current;
/**
* 当前页显示数据的条数
*/
@ApiModelProperty(value = "当前页显示数据的条数")
private Integer size;
/**
* 获取排序信息,排序的字段和正反序
*/
@ApiModelProperty(value = "排序方式。(默认【创建时间倒序】:[{'column': 'create_time','asc': false}])。",
notes = "例子:[{'column': 'create_time','asc': false},{'column':'name','asc': true}]"
)
private String orders;
/**
* 当前页默认值为1
*/
public Integer getCurrent() {
return current = (current == null || current <= 0) ? 1 : current;
}
/**
* 每页大小默认为10
*/
public Integer getSize() {
return size = (size == null || size == 0) ? 10 : size;
}
public Integer obtainRealCurrent() {
return current;
}
public Integer obtainRealSize() {
return size;
}
/**
* description:将orders(json数组字符串)转为List
* Date: 2020/11/22 16:43
*/
public List<OrderItem> generateOrderList() {
List<OrderItem> orderItemList = new ArrayList<>();
if (StrUtil.isBlank(getOrders())) {
orderItemList.add(OrderItem.desc("create_time"));
} else {
try {
orderItemList = JSONArray.parseArray(orders, OrderItem.class);
} catch (Exception e) {
throw new BadRequestException("分页排序参数orders不合法,请传正确的参数格式——['column':'','asc':'true/false']");
}
}
return orderItemList;
}
/**
* description:根据pageVO构建分页查询IPage
* @return IPage查询条件
* Date: 2020/11/22 17:19
*/
public <K> IPage<K> buildPage() {
Page<K> page = new Page<>(getCurrent(), getSize());
page.addOrder(generateOrderList());
return page;
}
}
分页查询所有学生信息
controller
public Result<Object> getAllStudent(Page studentPage) {
IPage<StudentDTO> allStudents = studentService.selectPageStudent(studentPage);
return Result.success(allStudents);
}
service
@Override
public IPage<StudentDTO> selectPageStudent(Page studentIPage) {
//设置返回的分页查询结果。由于我们要返回DTO类型的数据,分页查询时,我们只能查Student类型数据,所有我们需要重新封装查询结果
IPage<StudentDTO> returnPage = studentIPage;
IPage<Student> studentPage = studentMapper.selectPage(studentIPage, null);
List<Student> students = studentPage.getRecords();
List<StudentDTO> studentsDTO = studentMapStruct.toDto(students);
returnPage.setRecords(studentsDTO);
return returnPage;
}
returnPage结果:
"data": {
"records": [
{
"id": 1,
"name": "张浩琦"
},
{
"id": 2,
"name": "张武"
},
{
"id": 3,
"name": "花花"
},
{
"id": 4,
"name": "花花"
},
{
"id": 5,
"name": "花花"
}
],
"total": 21,
"size": 5,
"current": 1,
"orders": [],
"optimizeCountSql": true,
"hitCount": false,
"countId": null,
"maxLimit": null,
"searchCount": true,
"pages": 5
分页查询所有的项目信息
controller
@ApiOperation("分页查询所有项目基本信息")
@GetMapping
public Result<Object> query(AppQueryCriteria criteria, PageVO pageVO) {
log.info(StrUtil.format("【查询所有项目基本信息 /api/app】操作人id:{},项目查询条件 criteria:{},分页pageVo:{}", SecurityUtils.getCurrentUserId(), criteria, pageVO));
IPage<AppDTO> app = appService.queryAll(criteria, pageVO, true);
return Result.success(app);
}
AppQueryCriteria 为查询条件
service
@Override
@Transactional(rollbackFor = Exception.class)
public IPage<AppDTO> queryAll(AppQueryCriteria criteria, PageVO pageVO) {
//创建结果存储
IPage<AppDTO> returnPage = pageVO.buildPage();
//判断查询条件是否为全空,全空的话就进行默认查询。这个true可以忽略,业务需求。
if (judgeObjectIsAllNull(criteria, true)) {
IPage<App> appPage = appMapper.selectPage(pageVO.buildPage(), new LambdaQueryWrapper<App>().eq(App::getEnabled, true));
List<AppDTO> appDtos = appMapStruct.toDto(appPage.getRecords());
returnPage.setRecords(appDtos).setTotal(appPage.getTotal());
log.info(StrUtil.format("【查询所有项目基本信息】默认查询。操作人id:{},项目信息:{}", SecurityUtils.getCurrentUserId(),
appDtos));
return returnPage;
}
//条件查询 produceWrapper(criteria)为条件构造器的生成方法
IPage<App> page = appMapper.selectPage(pageVO.buildPage(), produceWrapper(criteria));
List<AppDTO> appDtoList = appMapStruct.toDto(page.getRecords());
BeanUtil.copyProperties(page, returnPage);
returnPage.setRecords(appDtoList);
log.info(StrUtil.format("【查询所有项目基本信息】条件查询。操作人id:{},项目信息:{}", SecurityUtils.getCurrentUserId(),
appDtoList));
return returnPage;
}
我们创建了returnPage,用来存储返回的结果。
returnPage结果:其中包含分页的5要素
"data": {
"records": [
{
"appId": 63,
"name": "123"
},
{
"appId": 62,
"name": "123"
}
],
"total": 50,
"size": 2,
"current": 1,
"orders": [
{
"column": "create_time",
"asc": false
}
],
"optimizeCountSql": true,
"hitCount": false,
"countId": null,
"maxLimit": null,
"searchCount": true,
"pages": 25
附代码:
1.判断对象属性是否为全空
enabled属性表示的是否可用,可忽略。
public boolean judgeObjectIsAllNull(Object obj, Boolean enabled) {
Field[] declaredFields = ReflectUtil.getFields(obj.getClass());
boolean isAllNull = true;
String en = "enabled";
//判断对象是否为全空
for (Field field : declaredFields) {
if (ObjectUtil.isNotNull(ReflectUtil.getFieldValue(obj, field))) {
if (enabled) {
if (!field.getName().equals(en)) {
isAllNull = false;
break;
}
} else {
isAllNull = false;
break;
}
}
}
return isAllNull;
}
2.条件构造器生成方法
public LambdaQueryWrapper<App> produceWrapper(AppQueryCriteria criteria) {
LambdaQueryWrapper<App> wrapper = Wrappers.lambdaQuery();
//查询是否可用的项目
wrapper.eq(App::getEnabled, ObjectUtil.isNull(criteria.getEnabled()) ? true : criteria.getEnabled());
if (StrUtil.isNotBlank(criteria.getName())) {
wrapper.like(App::getName, criteria.getName());
}
if (ObjectUtil.isNotNull(criteria.getBeginTime())) {
//如果有开始时间,默认从开始到现在
wrapper.between(App::getBeginTime, criteria.getBeginTime(), ObjectUtil.isNotNull(criteria.getEndTime()) ? criteria.getEndTime() : LocalDateTime.now());
}
return wrapper;
}
controller
@ApiOperation("大盘查询所有项目信息,项目信息,人员信息")
@GetMapping("detail/all")
public Result<Object> selectAppByName(AppNameQueryCriteria criteria, PageVO pageVO) {
log.info(StrUtil.format("【大盘查询所有项目信息 /api/app/detail/all】操作人id:{},项目查询条件 criteria:{},分页pageVo:{}", SecurityUtils.getCurrentUserId(), criteria, pageVO));
IPage<AppBigViewDTO> appBigView = appService.selectAppByName(criteria, pageVO, true);
return Result.success(appBigView);
}
查询条件:AppNameQueryCriteria
@Data
public class AppNameQueryCriteria {
@ApiModelProperty(value = "项目名称")
private String name;
@ApiModelProperty(value = "状态:开发中(0)、开发完成(1)、暂停开发(2)、持续维护中(3)、未开始(4)")
private Integer status;
}
service
@Override
@Transactional(rollbackFor = Exception.class)
public IPage<AppBigViewDTO> selectAppByName(AppNameQueryCriteria criteria, PageVO pageVO, boolean b) {
//查询条件全空
IPage<AppBigViewDTO> returnPage = pageVO.buildPage();
//默认查询
if (judgeObjectIsAllNull(criteria, false)) {
List<AppBigViewDTO> apps = appMapper.selectAppByName(pageVO.buildPage(), new LambdaQueryWrapper<>());
returnPage.setRecords(apps).setTotal(apps.size());
log.info(StrUtil.format("【查询成功】。操作人id:{},项目信息:{}", SecurityUtils.getCurrentUserId(), apps));
return returnPage;
}
//条件查询
LambdaQueryWrapper<App> wrapper = Wrappers.lambdaQuery();
if (ObjectUtil.isNotNull(criteria.getName())) {
wrapper.like(App::getName, criteria.getName());
}
if (ObjectUtil.isNotNull(criteria.getStatus())) {
wrapper.eq(App::getStatus, criteria.getStatus());
}
List<AppBigViewDTO> apps = appMapper.selectAppByName(pageVO.buildPage(), wrapper);
returnPage.setRecords(apps).setTotal(apps.size());
log.info(StrUtil.format("【查询成功】。操作人id:{},项目信息:{}", SecurityUtils.getCurrentUserId(), apps));
return returnPage;
}
mapper
@Select("")
@Results(id = "appMap", value = {
@Result(column = "app_id", property = "id"),
@Result(column = "name", property = "appName"),
@Result(column = "begin_time", property = "beginTime"),
@Result(column = "end_time", property = "endTime"),
@Result(column = "finish_time", property = "finishTime"),
@Result(property = "appUsers", column = "app_id", many = @Many(select = "marchsoft.modules.system.mapper.SystemMapper.selectUserByAppId"))
})
List<AppBigViewDTO> selectAppByName(IPage<AppUserBaseDTO> appUser, @Param(Constants.WRAPPER) LambdaQueryWrapper<App> wrapper);
注意:当自定义mapper进行模糊分页查询时,需要在mapper层的接口形参条件构造器前添加 @Param(Constants.WRAPPER) 不然的话我们的分页条件是传递不过来的,并且还要在sql添加分页条件的位置添加 ${ew.SqlSegment},他会将分页条件生成在添加的地方。
执行的SQL
SELECT a.app_id, a.name, a.begin_time, a.end_time, a.finish_time, a.status FROM mnt_app a WHERE
(name LIKE '%测试%') AND is_deleted = false ORDER BY create_time DESC LIMIT 1
注意:
除了 ${ew.SqlSegment} 外,还有一个 ${ew.customSqlSegment},他们的区别是前者直接 like “%测试%”,后者是where like “%测试%”,会自动帮我们生成一个where关键字,在写动态sql时,一定要多加注意。
如果我们写一个简单的sql的模糊分页,我们使用,${ew.customSqlSegment},可以不写where。
@Select("")
返回结果
{
"data": {
"records": [
{
"id": 60,
"appName": "测试now6",
"status": 1,
"beginTime": 1612800000000,
"endTime": 1614268800000,
"finishTime": null,
"appUsers": [
{
"userId": 7,
"name": "啊啊",
"avatarPath": ""
}
]
}
],
"total": 1,
"size": 1,
"current": 1,
"orders": [
{
"column": "create_time",
"asc": false
}
],
"optimizeCountSql": true,
"hitCount": false,
"countId": null,
"maxLimit": null,
"searchCount": true,
"pages": 1
}
}
mybatis为我们提供了一对一,一对多,以及多对多的查询方式,来方便我们的查询。
优点:
查询员工所在部门
service
UserBaseDTO userBaseDTO = systemMapper.selectUserJoinAppByUserId(userId);
mapper
1. 主句
@Select({"SELECT u.id ,u.nick_name,u.`avatar_path`,u.`phone`\n" +
"FROM sys_user u \n" +
"WHERE u.`id`=#{userId}"})
//字段名和属性名不相等时,要映射。avatar_path其实会自动转化的,写不写都可以。
@Results({
@Result(column = "id", property = "id",
@Result(column = "nick_name", property = "name"),
@Result(column = "avatar_path", property = "avatarPath"),
@Result(column = "id",property = "dept",one=@One(select = "marchsoft.modules.system.mapper.SystemMapper.selectDeptByUserId"))
})
UserBaseDTO selectUserJoinAppByUserId(Long userId);
注意:
2. 子句
@Select("SELECT d.`name`" +
"FROM sys_dept d,sys_user u\n" +
"WHERE u.id=#{userId} AND u.`dept_id`=d.`id`\n")
String selectDeptByUserId(Long userId);
查询某个项目的参与人员(一个项目有多个参与人员)
service
List<AppBigViewDTO> apps = appMapper.selectAppByName(pageVO.buildPage(), new LambdaQueryWrapper<>());
mapper
主句:
@Select("")
@Results(id = "appMap", value = {
@Result(column = "app_id", property = "id"),
@Result(column = "name", property = "appName"),
@Result(column = "begin_time", property = "beginTime"),
@Result(column = "end_time", property = "endTime"),
@Result(column = "finish_time", property = "finishTime"),
@Result(property = "appUsers", column = "app_id", many = @Many(select = "marchsoft.modules.system.mapper.SystemMapper.selectUserByAppId"))
})
List<AppBigViewDTO> selectAppByName(IPage<AppUserBaseDTO> appUser, @Param(Constants.WRAPPER) LambdaQueryWrapper<App> wrapper);
子句:
@Select({"SELECT u.id ,u.nick_name,u.`avatar_path`",
"FROM mnt_app a,sys_user u, sys_users_roles_apps sa",
"WHERE a.`app_id`=#{appId}",
"AND a.`app_id`=sa.`app_id`",
"AND sa.`user_id`=u.`id`",
"GROUP BY a.`app_id`,u.id,u.`nick_name`"})
@Results({
@Result(column = "id", property = "userId"),
@Result(column = "nick_name", property = "name"),
@Result(column = "avatar_path", property = "avatarPath")
})
Set<AppUserBaseDTO> selectUserByAppId(Long appId);
查询某个人的基本信息(部门信息在另外一张表)
service
UserBaseDTO userBaseDTO = systemMapper.selectUserJoinAppByUserId(userId);
mapper
1. 主句
@Select({"SELECT u.id ,u.nick_name,u.`avatar_path`,u.`phone`\n" +
"FROM sys_user u \n" +
"WHERE u.`id`=#{userId}"})
@Results({
@Result(column = "id", property = "id"),
@Result(column = "nick_name", property = "name"),
@Result(column = "avatar_path", property = "avatarPath"),
})
@Queries({
@Query(column = "id", property = "dept", select = "marchsoft.modules.system.mapper.SystemMapper.selectDeptByUserId"),
})
UserBaseDTO selectUserJoinAppByUserId(Long userId);
注意:
2. 子句
@Select("SELECT d.`name`" +
"FROM sys_dept d,sys_user u\n" +
"WHERE u.id=#{userId} AND u.`dept_id`=d.`id`\n")
String selectDeptByUserId(Long userId);
查询某个项目的参与人员
service
List<AppBigViewDTO> apps = appMapper.selectAppByName(pageVO.buildPage(), new LambdaQueryWrapper<>());
mapper
主句:
@Select("")
@Results(id = "appMap", value = {
@Result(column = "app_id", property = "id"),
@Result(column = "name", property = "appName"),
@Result(column = "begin_time", property = "beginTime"),
@Result(column = "end_time", property = "endTime"),
@Result(column = "finish_time", property = "finishTime"),
})
@Queries({
@Query(column = "id", property = "appUsers",
select = "marchsoft.modules.system.mapper.SystemMapper.selectUserByAppId")
})
List<AppBigViewDTO> selectAppByName(IPage<AppUserBaseDTO> appUser, @Param(Constants.WRAPPER) LambdaQueryWrapper<App> wrapper);
子句:
@Select({"SELECT u.id ,u.nick_name,u.`avatar_path`",
"FROM mnt_app a,sys_user u, sys_users_roles_apps sa",
"WHERE a.`app_id`=#{appId}",
"AND a.`app_id`=sa.`app_id`",
"AND sa.`user_id`=u.`id`",
"GROUP BY a.`app_id`,u.id,u.`nick_name`"})
@Results({
@Result(column = "id", property = "userId"),
@Result(column = "nick_name", property = "name"),
@Result(column = "avatar_path", property = "avatarPath")
})
Set<AppUserBaseDTO> selectUserByAppId(Long appId);
我们正常进行一对一或一对多查询时,一般只需要传递一个子表的主键id,就能够查出来相应的数据。上面我们举得例子都是这样进行的。如: @Result(property = "appUsers", column = "app_id", many = @Many(select = "marchsoft.modules.system.mapper.SystemMapper.selectUserByAppId")) })
,我们就只传递了一个app_id,但是我们在复杂(变态)的业务中,很可能会出现需要传递多个参数才能查出子表的内容。
方式很简单,column用一个大括号包裹即可。
@Result(column = "{userId=userId,appId=app_id}", property = "roles", many = @Many(select = "marchsoft.modules.system.mapper.SystemMapper.selectRoleByUserAndAppId"))
同样,子表接收时,接收的参数也应该是两个,一个userId,一个appId。
一张考卷对应多条配置信息,通过试卷id获取试卷详情及配置。
实体类:
考卷
@Data
@ApiModel(value = "试卷")
public class ExaminationPapersDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键id")
private Long id;
@ApiModelProperty(value = "试卷名称")
private String paperName;
@ApiModelProperty(value = "试卷类型(0固定试卷,1随机试卷)")
private Integer paperType;
@ApiModelProperty(value = "试卷总题量")
private Integer questionNumber;
@ApiModelProperty(value = "试卷总分值")
private Integer paperScore;
@ApiModelProperty(value = "判断题数量")
private Integer checkQuestionNumber;
@ApiModelProperty(value = "判断题单题分数")
private Integer checkQuestionScore;
@ApiModelProperty(value = "单选题数量")
private Integer schoiceQuestionNumber;
@ApiModelProperty(value = "单选题单题分数")
private Integer schoiceQuestionScore;
@ApiModelProperty(value = "多选题数量")
private Integer dchoiceQuestionNumber;
@ApiModelProperty(value = "多选题单题分数")
private Integer dchoiceQuestionScore;
@ApiModelProperty(value = "考试开始时间")
private String examinationBeaginDate;
@ApiModelProperty(value = "考试开始时间段")
private String examinationBeaginScope;
@ApiModelProperty(value = "考试结束时间")
private String examinationEndDate;
@ApiModelProperty(value = "考试结束时间段")
private String examinationEndScope;
@ApiModelProperty(value = "应考人数")
private Integer numberOfCandidates;
@ApiModelProperty(value = "已考人数")
private Integer numberOfExaminees;
@ApiModelProperty(value = "试卷状态(0待考试,1考试中,2已结束)")
private Integer paperState;
@ApiModelProperty(value = "考试配置信息")
List<ExaminationConfDTO> examinationConfs;
}
配置
@Data
@ApiModel("考试配置信息")
public class ExaminationConfDTO {
private Long examinationPapersId;
private Integer questionType;
private Integer questionNumber;
private Integer knowledgeId;
private Integer singleScore;
}
查询:
//试卷
<select id="selectPageAndConf" resultMap="paperAllMes">
SELECT t1.*
FROM tb_examination_papers t1
WHERE t1.id = #{pageId}
AND t1.del_flag = 0;
</select>
//试卷配置
<select id="selectPageConfs" resultType="io.renren.modules.question.dto.save.ExaminationConfDTO">
SELECT t1.*
FROM tb_exam_conf t1
WHERE t1.examination_papers_id = #{id}
</select>
//字段映射
<resultMap id="paperAllMes" type="io.renren.modules.question.dto.save.ExaminationPapersDTO">
<id property="id" column="id" /> // 必须加,否则id为null
<collection property="examinationConfs" ofType="io.renren.modules.question.dto.save.ExaminationConfDTO"
column="id" select="io.renren.modules.question.dao.ExaminationPapersDao.selectPageConfs">
</collection>
</resultMap>
参考文章:MyBatis一对多关联查询
xml文件使用: