聲明:本文系原創,轉載請注明出處。
注意:本程序使用SpringBoot+Mybatis Plus
一、現有表
student學生表:
id | stuName | stuAge | graduateDate | facultyId |
---|---|---|---|---|
1 | 盧1 | 21 | 2019-11-20 20:29:20 | 1 |
2 | 盧2 | 20 | 2019-11-27 20:29:40 | 2 |
3 | 盧3 | 22 | 2019-11-28 20:29:53 | 3 |
4 | 盧4 | 17 | 2019-11-28 20:30:20 | 2 |
5 | 盧5 | 17 | 2019-11-21 20:29:20 | 1 |
6 | 盧6 | 17 | 2025-12-11 20:29:20 | 3 |
7 | 盧7 | 20 | 2019-11-20 20:29:20 | 2 |
8 | 盧8 | 22 | 2019-11-27 20:29:40 | 3 |
9 | 盧9 | 17 | 2025-12-11 20:29:20 | 2 |
10 | 盧10 | 21 | 2019-11-28 20:30:20 | 1 |
11 | 盧11 | 17 | 2019-11-21 20:29:20 | 1 |
12 | 盧12 | 17 | 2019-11-11 20:29:20 | 3 |
13 | 盧13 | 17 | 2019-11-20 20:29:20 | 2 |
14 | 盧14 | 18 | 2025-12-11 20:29:20 | 3 |
15 | 盧15 | 22 | 2019-11-28 20:29:53 | 3 |
16 | 盧16 | 22 | 2019-11-28 20:30:20 | 1 |
17 | 盧17 | 18 | 2019-11-21 20:29:20 | 1 |
18 | 盧18 | 20 | 2025-12-11 20:29:20 | 2 |
19 | 盧19 | 21 | 2019-11-21 20:29:20 | 3 |
20 | 盧20 | 19 | 2025-12-11 20:29:20 | 3 |
21 | 盧21 | 18 | 2019-11-28 22:16:17 | 1 |
facultylist學院表:
id | facultyName |
---|---|
1 | 計算機與通信工程學院 |
2 | 數學與統計學院 |
3 | 文法學院 |
二、同時滿足以下需求:
1.多表聯合查詢出學院名字
需求展示:學生表聯合學院表隱去學院id直接展示學院名給用戶:
id | stuName | stuAge | graduateDate | facultyName |
---|---|---|---|---|
1 | 盧1 | 21 | 2019-11-20 20:29:20 | 計算機與通信工程學院 |
2 | 盧2 | 20 | 2019-11-27 20:29:40 | 數學與統計學院 |
3 | 盧3 | 22 | 2019-11-28 20:29:53 | 文法學院 |
4 | 盧4 | 17 | 2019-11-28 20:30:20 | 數學與統計學院 |
5 | 盧5 | 17 | 2019-11-21 20:29:20 | 計算機與通信工程學院 |
… | … | … | … | … |
2.可以帶多條件查詢
學生名字模糊搜索
年齡范圍搜索
搜索是否畢業
指定字段排序
3.指定頁碼,頁數據大小進行物理分頁查詢
三、解決步驟
今天找了一下午資料,終於可以同時滿足以上所有需求了!開干!
Spring Boot配置
重要配置已經做注釋!
server.port=9999
#設置jackson的時區和輸出形式
spring.jackson.time-zone=GMT+8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
#URL要設置數據庫編碼、時區、允許多語句查詢(等會會講原因)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2b8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=6666
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#關閉字段名的映射(關閉如userName映射成user_name)
mybatis-plus.configuration.map-underscore-to-camel-case=false
#配置xml Mapper路徑
mybatis-plus.mapper-locations=classpath:mapping/*Mapper.xml
MP配置
創建一個MP的配置類寫入如下內容,主要是配置mapper路徑
package com.looyeagee.web.util;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
@MapperScan("com.looyeagee.web.mapper")
public class MybatisPlusConfig {
}
實體類編寫
Student.java
package com.looyeagee.web.bean;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Student implements Serializable {
private Long id;
private String stuName;
private Integer stuAge;
private Date graduateDate;
private Long facultyId;
}
Select.java(把查詢條件封裝成類)
package com.looyeagee.web.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
public class Select implements Serializable {
String stuName;//模糊搜索學生名
Integer minAge;//最小年齡 用Integer不用int是因為用戶可以不選擇此條件(null)即沒有最小年齡限制 用int默認值是0
Integer maxAge;//最大年齡
Boolean isGraduate;//是否畢業 為null就是不管畢業還是沒畢業都要
Integer pageNumber;//第幾頁 從1開始
Integer pageSize;//每頁幾個數據
String orderBy;//排序字段
Boolean highToLow;//是否降序 為false和null就是升序 為true就是降序
}
Result.java(把結果也封裝)
package com.looyeagee.web.bean;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)//為null的字段 不輸出到前端 看以下的stuAge字段
public class Result implements Serializable {
private Long id;
private String stuName;
private Integer stuAge;//為什么要用Integer而不是int 因為int會有默認值0,而Integer默認是null,如果查到年齡為null就會顯示為0歲了,這樣輸出到前端就是0歲,而我們不期望前端展示為null的數據
private Date graduateDate;
private String facultyName;
}
StudentMapper.xml編寫
前提知識:
mysql如何通過當前頁碼和頁數據條數分頁?
mysql limit m,n是mysql分頁用的語句,不過此處的m不是當前頁碼而是從第幾條數據開始,n是取幾條數據。所以假設PageNumber為第幾頁,PageSize為一頁數據條數,則寫法為limit (PageNumber-1)*PageSize,PageSize
如何用mysql limit分頁查詢的同時返回符合條件數據總數以確定總頁數?
用上面的方法雖然能分頁,但是沒有查出符合條件總個數,所以就不知道數據總共有多少頁數。MP內部分頁的實現是先查詢符合條件的個數再用limit查詢,不過這樣會導致兩次查詢浪費資源,而且要寫2個條件一模一樣的select查詢,很不方便,這里有個解決方案可以一次性返回數據和總條數:通過SQL_CALC_FOUND_ROWS和SELECT FOUND_ROWS();來獲取。
為了說的更清楚,我直接貼出測試sql:我的21個數據中名字含有字符"2"
的有4個同學,我想每頁展示2條數據,並且訪問第1頁:根據我貼出的數據,滿足條件的有4個,id分別為2,12,20,21。因為1頁只展示2個數據,所以第1頁應該是返回2,12這兩個數據。語句
SELECT SQL_CALC_FOUND_ROWS * FROM `student` WHERE stuName LIKE '%2%' limit 0,2;SELECT FOUND_ROWS();
執行后一次性返回2個結果集(這就是前面要配置一次性可執行多個語句的原因,默認不可以一次性執行多個語句),如圖:
第二個結果集就是總條數。下面介紹Mapper的配置。
2個結果集的配置:
<resultMap id="ResultMap" type="com.looyeagee.web.bean.Result"/>
<resultMap id="RecordsCount" type="integer"/>
resultMap標簽是mapper的子標簽用來指定結果集的id和類型。由於返回的第一個結果集的結果為我們定義的實體類Result,所以類型填寫完整實體類路徑;由於返回的第二個結果集的結果為一個整數,所以類型是integer。
<select id="findResultByInfo" resultMap="ResultMap,RecordsCount"
parameterType="com.looyeagee.web.bean.Select" resultType="java.util.List">
在select標簽中添加resultMap
屬性來指定結果集的id,由於這個select會返回2個結果集,所以resultMap屬性填寫2個剛剛定義的結果集id,用英文逗號隔開。parameterType
屬性就是我們封裝的查詢實體類。返回的結果類型是List。
下面貼出完整多條件查詢代碼:
注意:
1.大於號小於號要用xml實體字符轉義。
2.orderby字段的使用要用${},不讓程序自動預編譯。
3.排序多加一個通過id升序,因為mysql排序是不穩定的,可能會出現不同頁數出現相同數據的情況。
4.此處傳進去的pageNumber已經經過了PageNumber=(PageNumber-1)*PageSize
的處理。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.looyeagee.web.mapper.StudentMapper">
<resultMap id="ResultMap" type="com.looyeagee.web.bean.Result"/>
<resultMap id="RecordsCount" type="integer"/>
<select id="findResultByInfo" resultMap="ResultMap,RecordsCount"
parameterType="com.looyeagee.web.bean.Select" resultType="java.util.List">
SELECT SQL_CALC_FOUND_ROWS
`student`.`id` AS `id`,
`student`.`stuName` AS `stuName`,
`student`.`stuAge` AS `stuAge`,
`student`.`graduateDate` AS `graduateDate`,
`facultylist`.`facultyName` AS `facultyName`
FROM
( `facultylist` JOIN `student` )
WHERE
(
`facultylist`.`id` = `student`.`facultyId`)
-- 標題模糊搜索
<if test="stuName != null">
AND `student`.`stuName` LIKE CONCAT('%',#{stuName},'%')
</if>
-- >=是大於等於
<if test="minAge!=null">
AND `student`.`stuAge`>= #{minAge}
</if>
-- <=是小於等於
<if test="maxAge!=null">
AND `student`.`stuAge` <= #{maxAge}
</if>
-- 沒畢業 畢業時間大於現在
<if test="isGraduate != null and isGraduate ==false">
AND `student`.`graduateDate`>=NOW()
</if>
-- 畢業了 畢業時間小於現在
<if test="isGraduate != null and isGraduate ==true">
AND `student`.`graduateDate`<=NOW()
</if>
<if test="orderBy!=null and orderBy!=''">
<if test="highToLow ==null or highToLow ==false">
ORDER BY ${orderBy} ASC,`student`.`id` ASC -- 加id ASC是為了保證分頁結果的唯一性 mysql排序是不穩定的 https://www.jianshu.com/p/1e8a19738ae4
</if>
<if test="highToLow !=null and highToLow ==true">
ORDER BY ${orderBy} DESC,`student`.`id` ASC
</if>
</if>
-- 分頁查詢
LIMIT
#{pageNumber},#{pageSize};
-- 接着查詢符合條件個數
SELECT FOUND_ROWS();
</select>
</mapper>
StudentMapper類編寫
注意返回類型即可。
package com.looyeagee.web.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.looyeagee.web.bean.Select;
import com.looyeagee.web.bean.Student;
import java.util.List;
public interface StudentMapper extends BaseMapper<Student> {
List<List<?>> findResultByInfo(Select select);
}
測試類編寫
關於總頁數計算的說明:總數據條數除以每頁數據條數,如果沒有余數,結果就是總頁數;如果有余數,則要將計算結果+1(進1法);用一句話就是(totalCount + pageSize - 1) / pageSize
package com.looyeagee.web;
import com.looyeagee.web.bean.Result;
import com.looyeagee.web.bean.Select;
import com.looyeagee.web.mapper.StudentMapper;
import com.looyeagee.web.service.impl.StudentServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class WebApplicationTests {
@Autowired
StudentMapper studentMapper;
@Test
void selectTest() {
Select selectInfo = new Select();
int nowPageIndex = 1;
int pageSize = 5;
selectInfo.setPageNumber((nowPageIndex - 1) * pageSize);
selectInfo.setPageSize(pageSize);
selectInfo.setOrderBy("stuAge");
selectInfo.setHighToLow(true);
selectInfo.setIsGraduate(true);
selectInfo.setMinAge(17);
selectInfo.setMaxAge(20);
List<List<?>> findtest = studentMapper.findResultByInfo(selectInfo);
List<Result> orderinfos = (List<Result>) findtest.get(0);
int totalCount = (Integer) findtest.get(1).get(0);
System.out.println("當前頁面記錄數:" + orderinfos.size());
System.out.println("符合條件記錄數:" + totalCount);
System.out.println("當前頁數:" +nowPageIndex);
System.out.println("總頁數:" + ((totalCount + pageSize - 1) / pageSize));
orderinfos.forEach(System.out::println);
}
}
以上的測試是篩選在我們的21個數據中,最小年齡17,最大年齡20,已經畢業(畢業時間小於現在時間),總共結果是有9個同學:通過年齡降序分別為2,7,17,21,4,5,11,12,13號,
選擇第1頁,1頁5個數據,輸出結果
當前頁面記錄數:5
符合條件記錄數:9
當前頁數:1
總頁數:2
Result(id=2, stuName=盧2, stuAge=20, graduateDate=Wed Nov 27 20:29:40 CST 2019, facultyName=數學與統計學院)
Result(id=7, stuName=盧7, stuAge=20, graduateDate=Wed Nov 20 20:29:20 CST 2019, facultyName=數學與統計學院)
Result(id=17, stuName=盧17, stuAge=18, graduateDate=Thu Nov 21 20:29:20 CST 2019, facultyName=計算機與通信工程學院)
Result(id=21, stuName=盧21, stuAge=18, graduateDate=Thu Nov 28 22:16:17 CST 2019, facultyName=計算機與通信工程學院)
Result(id=4, stuName=盧4, stuAge=17, graduateDate=Thu Nov 28 20:30:20 CST 2019, facultyName=數學與統計學院)
將nowPageIndex
改為2,輸出第二頁結果(剩余4個數據):
當前頁面記錄數:4
符合條件記錄數:9
當前頁數:2
總頁數:2
Result(id=5, stuName=盧5, stuAge=17, graduateDate=Thu Nov 21 20:29:20 CST 2019, facultyName=計算機與通信工程學院)
Result(id=11, stuName=盧11, stuAge=17, graduateDate=Thu Nov 21 20:29:20 CST 2019, facultyName=計算機與通信工程學院)
Result(id=12, stuName=盧12, stuAge=17, graduateDate=Mon Nov 11 20:29:20 CST 2019, facultyName=文法學院)
Result(id=13, stuName=盧13, stuAge=17, graduateDate=Wed Nov 20 20:29:20 CST 2019, facultyName=數學與統計學院)