SpringBoot多模塊項(xiàng)目創(chuàng)建和初始化問題
1.使用Navicat創(chuàng)建數(shù)據(jù)庫

2.創(chuàng)建空項(xiàng)目
目錄
lease ├── common(公共模塊——工具類、公用配置等) │ ├── pom.xml │ └── src ├── model(數(shù)據(jù)模型——與數(shù)據(jù)庫相對(duì)應(yīng)地實(shí)體類) │ ├── pom.xml │ └── src ├── web(Web模塊) │ ├── pom.xml │ ├── web-admin(后臺(tái)管理系統(tǒng)Web模塊——包含mapper、service、controller) │ │ ├── pom.xml │ │ └── src │ └── web-app(移動(dòng)端Web模塊——包含mapper、service、controller) │ ├── pom.xml │ └── src └── pom.xml
1.打開IDEA,創(chuàng)建項(xiàng)目

2.配置項(xiàng)目,點(diǎn)擊下一步直到完成


3.初始化項(xiàng)目
lease是父工程,不需要編寫代碼,所有把src目錄刪除,然后在父工程里創(chuàng)建子工程
點(diǎn)擊父工程–>新建–>模塊


4.創(chuàng)建web模塊
web模塊主要是放開發(fā)邏輯代碼,在創(chuàng)建模塊時(shí),要注意父項(xiàng)是web模塊

5.在web模塊引入common模塊和model模塊
因?yàn)閣eb模塊的子模塊是依賴與common和model模塊的,在web父模塊里引入后,web的子模塊就可以使用common和model模塊的內(nèi)容了
# 注意:是在web父模塊的pom.xml里添加的,添加后刷新一下Maven
<dependencies>
<dependency>
<groupId>com</groupId># 創(chuàng)建模塊的組ID
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com</groupId>
<artifactId>model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>3.初始化項(xiàng)目,添加SpringBoot配置
1.在父工程里添加相關(guān)的SpringBoot配置
- 修改父工程lease的pom.xml文件,添加后刷新Maven
- 這里配置的依賴并沒有引入到項(xiàng)目里,后面會(huì)在子模塊里進(jìn)行引入和下載相關(guān)依賴
<!-- 繼承Spring Boot父項(xiàng)目 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/>
</parent>
<!-- 注意:直接替換pom文件中原有的properties -->
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 統(tǒng)一聲明依賴的版本,直接通過${}引用 -->
<lombok.version>1.18.34</lombok.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<swagger.version>2.9.2</swagger.version>
<jwt.version>0.11.2</jwt.version>
<easycaptcha.version>1.6.2</easycaptcha.version>
<minio.version>8.2.0</minio.version>
<knife4j.version>4.1.0</knife4j.version>
<aliyun.sms.version>2.0.23</aliyun.sms.version>
</properties>
<!--配置dependencyManagement統(tǒng)一管理依賴版本-->
<dependencyManagement>
<dependencies>
<!--mybatis-plus-->
<!--官方文檔:https://baomidou.com/pages/bab2db/ -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--knife4j文檔-->
<!--官方文檔:https://doc.xiaominfo.com/docs/quick-start -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!--JWT登錄認(rèn)證相關(guān)-->
<!--官方文檔:https://github.com/jwtk/jjwt#install-jdk-maven -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
<version>${jwt.version}</version>
</dependency>
<!--圖形驗(yàn)證碼-->
<!--官方文檔:https://gitee.com/ele-admin/EasyCaptcha -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>${easycaptcha.version}</version>
</dependency>
<!--對(duì)象存儲(chǔ),用于存儲(chǔ)圖像等非結(jié)構(gòu)化數(shù)據(jù)-->
<!--官方文檔:https://min.io/docs/minio/linux/developers/minio-drivers.html?ref=docs#java-sdk -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
</dependency>
<!--阿里云短信客戶端,用于發(fā)送短信驗(yàn)證碼-->
<!--官方文檔:https://help.aliyun.com/document_detail/215759.html?spm=a2c4g.215759.0.0.49f32807f4Yc0y -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${aliyun.sms.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>2.配置子模塊
- 在子模塊里
引入需要的依賴和插件,修改web的pom.xml文件
# 在dependeencies里添加需要的依賴
<dependencies>
<!--包含spring web相關(guān)依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--包含spring test相關(guān)依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Spring Boot Maven插件,用于打包可執(zhí)行的JAR文件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>3.給項(xiàng)目添加application.yml文件和創(chuàng)建項(xiàng)目的啟動(dòng)類
- web-admin和web-app是兩個(gè)項(xiàng)目,只是共用同一個(gè)common和model模塊
- application.yml要在web-admin/src/main/resources目錄下
# 配置啟動(dòng)端口號(hào) server: port: 8080
- 每個(gè)項(xiàng)目都有單獨(dú)的啟動(dòng)類,在項(xiàng)目的src/main/java/com.lease下創(chuàng)建AdminWebApplication啟動(dòng)類
@SpringBootApplication
public class AdminWebApplication {
public static void main(String[] args) {
SpringApplication.run(AdminWebApplication.class, args);
}
}

4.初始化項(xiàng)目,添加Mybatis-Plus配置
1.在pom.xml文件里添加依賴
- Mybatis-Plus為公用工具,故將其配置于common模塊的pom.xml文件里
<dependencies>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mysql驅(qū)動(dòng)-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
</dependencies>- 因?yàn)?strong>model模塊存放的是實(shí)體類,需要Mybatis-Plus相關(guān)注解,所以model的pom.xml也要引入相關(guān)的配置
<dependencies>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
</dependencies>2.修改web-admin模塊的application.yml文件,配置數(shù)據(jù)庫
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource # 指定數(shù)據(jù)庫連接池類型
url: jdbc:mysql://192.168.23.101:3306/lease?useUnicode=true&characte
# url: jdbc:mysql://數(shù)據(jù)庫的ip地址:端口號(hào)/要連接的數(shù)據(jù)庫名?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2b8
username: 數(shù)據(jù)庫賬號(hào)名
password: 數(shù)據(jù)庫賬號(hào)密碼
# 配置連接池參數(shù)
hikari:
connection-test-query: SELECT 1 # 自動(dòng)檢測連接
connection-timeout: 60000 #數(shù)據(jù)庫連接超時(shí)時(shí)間,默認(rèn)30秒
idle-timeout: 500000 #空閑連接存活最大時(shí)間,默認(rèn)600000(10分鐘)
max-lifetime: 540000 #此屬性控制池中連接的最長生命周期,值0表示無限生命周期,默認(rèn)1800000即30分鐘
maximum-pool-size: 12 #連接池最大連接數(shù),默認(rèn)是10
minimum-idle: 10 #最小空閑連接數(shù)量
pool-name: SPHHikariPool # 連接池名稱
#用于打印框架生成的sql語句,便于調(diào)試
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl3.配置Date類型的序列化格式
- 在web-admin項(xiàng)目的application.yml文件的spring下添加jackson序列化工具
- Date格式序列化有兩種方法,一是定義全局格式,二是獨(dú)立定義變量格式
- 推薦
時(shí)區(qū)使用全局配置,日期格式使用獨(dú)立配置
# 方法一:Date類型數(shù)據(jù)Json序列化框架全局配置
jackson:
date-format: yyyy-MM-dd HH:mm:ss #定義全局日期格式
time-zone: GMT+8 # 定義全局日期時(shí)區(qū)方法二:實(shí)體類里獨(dú)立配置Date類型數(shù)據(jù)格式和時(shí)區(qū) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(timezone = "GMT+8") private Date appointmentTime;
4.在common里添加Mapper接口的包路徑和分頁方法配置
- 在src/main/java目錄下創(chuàng)建類com.lease.common.mybatisplus.MybatisPlusConfiguration
- 添加配置類主要是為了指定mapper接口的包掃描路徑
- 配置分頁是為了在接口里使用MyBatis-Puls分頁方法,查詢數(shù)據(jù)時(shí)就不需要考慮分頁邏輯了
@Configuration
@MapperScan("com.lease.web.*.mapper")# 項(xiàng)目里的mapper接口的目錄
public class MybatisPlusConfiguration {
@Bean
// 分頁配置
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
5.初始化項(xiàng)目,添加Knife4j配置
1.分別在web和model模塊的pom.xml里添加依賴
- 因?yàn)?code>web模塊下有多個(gè)子項(xiàng)目都會(huì)用到Knife4j,所以在web模塊下添加依賴
- 因?yàn)?code>model模塊是放實(shí)體類的,需要配置Knife4j相關(guān)注解,所以也要添加依賴
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>2.創(chuàng)建項(xiàng)目的Knife4j的配置類
- 在web-admin項(xiàng)目的src/main/java目錄下創(chuàng)建com.lease.web.admin.custom.config.Knife4jConfiguration配置類
- 每個(gè)項(xiàng)目都有不同的配置,所以要分別為每個(gè)項(xiàng)目創(chuàng)建一個(gè)配置類
@Configuration
public class Knife4jConfiguration {
// 配置接口文檔標(biāo)題
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("hello-knife4j項(xiàng)目API")
.version("1.0")
.description("hello-knife4j項(xiàng)目的接口文檔"));
}
// 配置接口的分類,以/user/開頭的接口都規(guī)劃到‘用戶信息管理'
@Bean
public GroupedOpenApi userAPI() {
return GroupedOpenApi.builder().group("用戶信息管理").
pathsToMatch(
"/user/room",// 精確匹配,只匹配/user/room的路徑
"/user/room/*",// 單層匹配,只匹配/user/room/list這種單層的路徑,多層的就不匹配了,必須有單層路徑
"/user/room/**",// 通配匹配,會(huì)匹配所有以/user/room開頭的路徑,可以是單/多層或者是/user/room這個(gè)路徑,使用通配匹配就不需要精確匹配和單層匹配了
"/user/*/room/**",// 混合使用
"/user/info/**",// 接口地址可以寫多個(gè)
"/admin/**"http:// 可以寫,但不建議開頭不同的寫一起,會(huì)造成文檔分組語義混亂
).
build();
}
// 配置接口的分類,以/product/開頭的接口都規(guī)劃到‘產(chǎn)品信息管理'
@Bean
public GroupedOpenApi systemAPI() {
return GroupedOpenApi.builder().group("產(chǎn)品信息管理").
pathsToMatch("/product/**").
build();
}
}

3 在web-admin項(xiàng)目的application.yml文件里添加接口參數(shù)打平配置
# Knife4j接口參數(shù)打平 springdoc: default-flat-param-object: true


6.編寫接口
1.封裝接口統(tǒng)一返回?cái)?shù)據(jù)結(jié)構(gòu)
1.在common模塊的scr/main/java目錄下創(chuàng)建com/lease/common/result/Result類
- result類用于統(tǒng)一封裝接口返回的數(shù)據(jù)結(jié)構(gòu)
/**
* 全局統(tǒng)一返回結(jié)果類
*/
@Data
public class Result<T> {
//返回碼
private Integer code;
//返回消息
private String message;
//返回?cái)?shù)據(jù)
private T data;
public Result() {
}
private static <T> Result<T> build(T data) {
Result<T> result = new Result<>();
if (data != null)
result.setData(data);
return result;
}
// 自定義錯(cuò)誤碼和錯(cuò)誤消息
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
// 操作成功,需要返回?cái)?shù)據(jù)給前端
public static <T> Result<T> ok(T data) {
return build(data, ResultCodeEnum.SUCCESS);
}
// 操作成功,但不需要返回?cái)?shù)據(jù)(如新增、修改、刪除)
public static <T> Result<T> ok() {
return Result.ok(null);
}
// 操作失敗,不需要返回額外數(shù)據(jù)
public static <T> Result<T> fail() {
return build(null, ResultCodeEnum.FAIL);
}
// 操作失敗,返回自定義異常
public static <T> Result<T> fail(Integer code, String message) {
Result<T> result = build(null);
result.setCode(code);
result.setMessage(message);
return result;
}
}
2.在scr/main/java目錄下創(chuàng)建com/lease/common/result/ResultCodeEnum枚舉類
- ResultCodeEnum枚舉類用于編寫接口的狀態(tài)碼和錯(cuò)誤信息
/**
* 統(tǒng)一返回結(jié)果狀態(tài)信息類
*/
@Getter
public enum ResultCodeEnum {
// 定義枚舉類常量
SUCCESS(200, "成功"),
FAIL(201, "失敗"),
PARAM_ERROR(202, "參數(shù)不正確"),
SERVICE_ERROR(203, "服務(wù)異常"),
DATA_ERROR(204, "數(shù)據(jù)異常"),
ILLEGAL_REQUEST(205, "非法請(qǐng)求"),
REPEAT_SUBMIT(206, "重復(fù)提交"),
DELETE_ERROR(207, "請(qǐng)先刪除子集"),
ADMIN_ACCOUNT_EXIST_ERROR(301, "賬號(hào)已存在"),
ADMIN_CAPTCHA_CODE_ERROR(302, "驗(yàn)證碼錯(cuò)誤"),
ADMIN_CAPTCHA_CODE_EXPIRED(303, "驗(yàn)證碼已過期"),
ADMIN_CAPTCHA_CODE_NOT_FOUND(304, "未輸入驗(yàn)證碼"),
ADMIN_LOGIN_AUTH(305, "未登陸"),
ADMIN_ACCOUNT_NOT_EXIST_ERROR(306, "賬號(hào)不存在"),
ADMIN_ACCOUNT_ERROR(307, "用戶名或密碼錯(cuò)誤"),
ADMIN_ACCOUNT_DISABLED_ERROR(308, "該用戶已被禁用"),
ADMIN_ACCESS_FORBIDDEN(309, "無訪問權(quán)限"),
APP_LOGIN_AUTH(501, "未登陸"),
APP_LOGIN_PHONE_EMPTY(502, "手機(jī)號(hào)碼為空"),
APP_LOGIN_CODE_EMPTY(503, "驗(yàn)證碼為空"),
APP_SEND_SMS_TOO_OFTEN(504, "驗(yàn)證法發(fā)送過于頻繁"),
APP_LOGIN_CODE_EXPIRED(505, "驗(yàn)證碼已過期"),
APP_LOGIN_CODE_ERROR(506, "驗(yàn)證碼錯(cuò)誤"),
APP_ACCOUNT_DISABLED_ERROR(507, "該用戶已被禁用"),
TOKEN_EXPIRED(601, "token過期"),
TOKEN_INVALID(602, "token非法");
// 對(duì)應(yīng)這Result里定義的變量
private final Integer code;
private final String message;
// 構(gòu)造方法,給枚舉類賦值
ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
2.封裝全局錯(cuò)誤處理
1.在common模塊的pom.xml添加依賴
<!--spring-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>2.在common模塊的scr/main/java目錄下創(chuàng)建com.lease.common.exception.LeaseException異常類
@Data
public class LeaseException extends RuntimeException {
//異常狀態(tài)碼
private Integer code;
/**
* 通過狀態(tài)碼和錯(cuò)誤消息創(chuàng)建異常對(duì)象
*
* @param message
* @param code
*/
public LeaseException(String message, Integer code) {
super(message);
this.code = code;
}
/**
* 根據(jù)響應(yīng)結(jié)果枚舉對(duì)象創(chuàng)建異常對(duì)象
*
* @param resultCodeEnum
*/
public LeaseException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
}
@Override
public String toString() {
return "LeaseException{" +
"code=" + code +
", message=" + this.getMessage() +
'}';
}
}
3.在common模塊的scr/main/java目錄下創(chuàng)建com.lease.common.exception.GlobalExceptionHandler類
- 當(dāng)接口請(qǐng)求失敗時(shí),會(huì)自定識(shí)別錯(cuò)誤,并拋出Result.fail()定義的錯(cuò)誤信息
- 會(huì)自動(dòng)精準(zhǔn)識(shí)別使用那個(gè)error方法
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
return Result.fail();
}
@ExceptionHandler(LeaseException.class)
@ResponseBody
public Result error(LeaseException e){
e.printStackTrace();
return Result.fail(e.getCode(), e.getMessage());
}
}
4.在對(duì)應(yīng)的方法里使用自定義異常方法,并拋出異常
@Override
public void removeApartmentById(Long id) {
// 調(diào)用異常類的方法,需要提前在ResultCodeEnum枚舉類里定義好要返回的code和message
throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);
// 或者
// throw new LeaseException(310,"請(qǐng)先刪除房間信息");
}
3.編寫接口的實(shí)體類
1.創(chuàng)建通用實(shí)體類
- 在model模塊的src/main/java目錄下創(chuàng)建
通用實(shí)體類com.lease.model.entity.BaseEntity類 - BaseEntity類里定義通用字段,90%的表都會(huì)有這些字段
- 當(dāng)給50個(gè)實(shí)體類都要添加一個(gè)creator_id字段時(shí),只需要修改BaseEntity類就可以了,不需要一個(gè)個(gè)為實(shí)體類添加
- Serializable 是為了讓對(duì)象能夠序列化(能存Redis、網(wǎng)絡(luò)傳輸?shù)龋?,必須繼承
@Data
public class BaseEntity implements Serializable {
@Schema(description = "主鍵")// 字段描述
@TableId(value = "id", type = IdType.AUTO)// 對(duì)應(yīng)表主鍵ID
private Long id;
@Schema(description = "創(chuàng)建時(shí)間")
@TableField(value = "create_time",fill = FieldFill.INSERT)// 對(duì)應(yīng)數(shù)據(jù)表的字段,第二個(gè)參數(shù)告訴框架插入數(shù)據(jù)時(shí)需要調(diào)用填充方法自動(dòng)填充內(nèi)容
private Date createTime;
@Schema(description = "更新時(shí)間")
@TableField(value = "update_time",fill = FieldFill.UPDATE)// 第二個(gè)參數(shù)告訴框架更新數(shù)據(jù)時(shí)需要調(diào)用填充方法自動(dòng)填充內(nèi)容
@JsonIgnore // 所有的返回值都不會(huì)返回這個(gè)值了
private Date updateTime;
@TableLogic // 過濾邏輯刪除,當(dāng)請(qǐng)求查詢時(shí),會(huì)自動(dòng)過濾掉is_deleted=1的已刪除的數(shù)據(jù),不會(huì)返回給前端
@Schema(description = "邏輯刪除")
@TableField("is_deleted")
private Byte isDeleted;
}
2.封裝自動(dòng)填充的內(nèi)容
- 為了防止新增或更新數(shù)據(jù)時(shí),對(duì)應(yīng)的時(shí)間字段不會(huì)自動(dòng)填入值,如創(chuàng)建時(shí)間,更新時(shí)間等
- 方法名是
MetaObjectHandler接口定義好的,不能隨便寫,否則不會(huì)調(diào)用方法 - MyBatis-Plus會(huì)根據(jù)執(zhí)行的SQL操作類型來決定調(diào)用哪個(gè)方法
@Component
public class MybatisMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
3.創(chuàng)建需要的實(shí)體類
- 在model模塊的src/main/java目錄下創(chuàng)建com.lease.model.entity.ApartmentFacility實(shí)體類
- 實(shí)體類與數(shù)據(jù)庫的表是一一對(duì)應(yīng)的,一張表對(duì)應(yīng)一個(gè)實(shí)體類,具體根據(jù)項(xiàng)目需求創(chuàng)建實(shí)體類
- 實(shí)體類主要是為了定義接口
接收參數(shù)類型和返回?cái)?shù)據(jù)類型以及定義接口文檔內(nèi)容描述(個(gè)人理解)
@Schema(description = "標(biāo)簽信息表") // 接口名稱
@TableName(value = "label_info")// 數(shù)據(jù)庫表的名稱,指定使用該實(shí)體類時(shí)操作的數(shù)據(jù)庫表
@Data
public class LabelInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
@Schema(description = "類型")// 字段描述
@TableField(value = "type")// 對(duì)應(yīng)數(shù)據(jù)表的字段
private ItemType type;// 使用ItemType枚舉類
@Schema(description = "標(biāo)簽名稱")
@TableField(value = "name")
private String name;
// 當(dāng)實(shí)體類不需要通用字段時(shí),告訴 MyBatis-Plus 這個(gè)字段不存在于表
// 方法一:
@TableField(exist = false)
private Date updateTime;
// 方法二:也可以寫在BaseEntity通用實(shí)體類上
@JsonIgnore
private Long id;
}
4.編寫接口的枚舉類
1.定義枚舉類通用父接口BaseEnum
- 主要為了封裝通用轉(zhuǎn)換器時(shí)ConverterFactory接收原始類和目標(biāo)類的父類
- 原始類是前端傳遞的值
- 目標(biāo)類的父類是需要轉(zhuǎn)換的枚舉類共同的父類,因?yàn)镃onverterFactory提供的getEnumConstants()會(huì)獲取該父類下的所有子類,所有傳教一個(gè)通用的父接口供枚舉類繼承
public interface BaseEnum {
Integer getCode();
String getName();
}
2.在model模塊的src/main/java目錄下創(chuàng)建com.lease.model.enums.ItemType枚舉類根據(jù)接口需求創(chuàng)建對(duì)應(yīng)的枚舉類
- 接口返回的字段是
狀態(tài)字段和類型字段時(shí)要定義成枚舉類 - 接口返回給前端數(shù)據(jù)時(shí)使用的枚舉類
public enum ItemType implements BaseEnum {
APARTMENT(1, "公寓"),
ROOM(2, "房間");
@EnumValue
@JsonValue
private Integer code;
private String name;
@Override
public Integer getCode() {
return this.code;// 返回{code:1}
}
@Override
public String getName() {
return name; // 返回{name:"公寓"}
}
// 構(gòu)造方法,給枚舉類賦值
ItemType(Integer code, String name) {
this.code = code;
this.name = name;
}
}
5.封裝類型轉(zhuǎn)換器
1.在model模塊的src/main/java目錄下創(chuàng)建com.lease.web.admin.custom.converter.StringToBaseEnumConverterFactory轉(zhuǎn)換器
- 轉(zhuǎn)換器是為了將
前端HTTP請(qǐng)求傳遞的字符串轉(zhuǎn)換為接口對(duì)應(yīng)的枚舉類;當(dāng)前端傳來字符串 “1” 時(shí),轉(zhuǎn)換器會(huì)自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的枚舉類 - Spring也有默認(rèn)的枚舉轉(zhuǎn)換,但是轉(zhuǎn)換是按枚舉名稱匹配,當(dāng)前端傳遞"1"時(shí)就無法匹配
@Component
// 接收兩個(gè)參數(shù),一個(gè)是前端傳遞String類型值,一個(gè)是枚舉類的父接口
public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
@Override
public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
return new Converter<String, T>() {// 返回一個(gè)轉(zhuǎn)換器
@Override
public T convert(String source) {// source = 前端傳來的字符串,比如 "1"
/**
* 1. 遍歷所有值繼承BaseEnum接口的枚舉類
* targetType.getEnumConstants():獲取所有枚舉類的值[APARTMENT, ROOM, DELETED...]
*/
for (T enumConstant : targetType.getEnumConstants()) {
// 2. 判斷1如果前端傳的code等于枚舉的code不
if (enumConstant.getCode().equals(Integer.valueOf(source))) {
return enumConstant; // 返回對(duì)應(yīng)的枚舉對(duì)象
}
}
// 3. 找不到匹配的,拋異常
throw new IllegalArgumentException("非法的枚舉值:" + source);
}
};
}
}
2.在model模塊的src/main/java目錄下創(chuàng)建com.lease.web.admin.custom.config.WebMvcConfiguration注冊(cè)類
- 注冊(cè)轉(zhuǎn)換器,前端調(diào)用接口時(shí),當(dāng)Spring MVC發(fā)現(xiàn)傳遞的參數(shù)需要轉(zhuǎn)換為枚舉類時(shí)會(huì)自動(dòng)去找注冊(cè)的轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換類型
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(this.stringToBaseEnumConverterFactory);
}
}
- 接口里的@RequestParam注解綁定type參數(shù)時(shí)觸發(fā)轉(zhuǎn)換器
- 當(dāng)前端不傳值(type=null)或參數(shù)類型不匹配(type=adc)時(shí),轉(zhuǎn)換器不會(huì)觸發(fā)
@Operation(summary = "(根據(jù)類型)查詢標(biāo)簽列表")
@GetMapping("list")
public Result<List<LabelInfo>> labelList(@RequestParam(required = false) ItemType type) {}
6.TypeHandler枚舉類型轉(zhuǎn)換
- 主要是解決
接口的枚舉類轉(zhuǎn)換為數(shù)據(jù)庫要儲(chǔ)存的類型,數(shù)據(jù)庫里存放的是前端傳遞的數(shù)字"1",類型是tinyint - 在對(duì)應(yīng)的枚舉類里添加
@EnumValue注解
public enum ItemType implements BaseEnum {
APARTMENT(1, "公寓"),
ROOM(2, "房間");
@EnumValue
private Integer code;
private String name;
@Override
public Integer getCode() {
return this.code;
}
@Override
public String getName() {
return name;
}
// 構(gòu)造方法,給枚舉類賦值
ItemType(Integer code, String name) {
this.code = code;
this.name = name;
}
}
7.HTTPMessageConverter枚舉類型轉(zhuǎn)換
- 主要解決HTTP請(qǐng)求和響應(yīng)的轉(zhuǎn)換
- 將數(shù)據(jù)庫的響應(yīng)值轉(zhuǎn)換成序列化JSON返回給前端
- 將前端的請(qǐng)求反序列化后,把對(duì)應(yīng)的數(shù)據(jù)保存到數(shù)據(jù)庫
- 在對(duì)應(yīng)的枚舉類里添加
@JsonValue注解
【查詢響應(yīng)】
數(shù)據(jù)庫 → TypeHandler → 實(shí)體類(含枚舉) → Controller → HttpMessageConverter → JSON → 前端
【保存請(qǐng)求】
前端 → JSON → HttpMessageConverter → Controller → 實(shí)體類(含枚舉) → TypeHandler → 數(shù)據(jù)庫
public enum ItemType implements BaseEnum {
APARTMENT(1, "公寓"),
ROOM(2, "房間");
@JsonValue
private Integer code;
private String name;
@Override
public Integer getCode() {
return this.code;
}
@Override
public String getName() {
return name;
}
// 構(gòu)造方法,給枚舉類賦值
ItemType(Integer code, String name) {
this.code = code;
this.name = name;
}
}
8.創(chuàng)建VO類(需要時(shí)才創(chuàng)建相應(yīng)的vo類)
1.在web-admin項(xiàng)目的src/main/java目錄下創(chuàng)建com.lease.web.admin.vo.apartment.ApartmentDetailVo類
- vo類就是重寫實(shí)體類,整合前端需要的數(shù)據(jù),將多個(gè)包含不同數(shù)據(jù)的實(shí)體類整合為一個(gè),然后傳遞給
Result類統(tǒng)一封裝進(jìn)行響應(yīng)返回 - ApartmentDetailVo類繼承了公寓的基本信息ApartmentInfo實(shí)體類,并整合了公寓圖片,標(biāo)簽等實(shí)體類,將數(shù)據(jù)整合后返回給前端,返回的數(shù)據(jù)包含基本信息,公寓圖片,公寓標(biāo)簽等
@Data
@Schema(description = "公寓信息")// 類描述
public class ApartmentDetailVo extends ApartmentInfo {
@Schema(description = "圖片列表")// 字段描述
private List<GraphVo> graphVoList;// 拿取之前寫的VO類圖片列表,新起字段為graphVoList
@Schema(description = "標(biāo)簽列表")
private List<LabelInfo> labelInfoList;// 拿取之前寫的實(shí)體類標(biāo)簽列表,新起字段為labelInfoList
}
9.創(chuàng)建controlle類
- 在web-admin項(xiàng)目的src/main/java里創(chuàng)建com.lease.web.admin.controller.apartment.AttrController類,用于
定義接口供前端調(diào)用 - 當(dāng)進(jìn)行
單表的增刪改查基礎(chǔ)操作時(shí)可以使用MyBatis-Plus提供的api方法 - 當(dāng)進(jìn)行
多表關(guān)聯(lián)查詢或者復(fù)雜統(tǒng)計(jì)查詢時(shí),需要手動(dòng)在mapper.xml里進(jìn)行手寫SQL語句查詢
@RestController
@Tag(name = "房間屬性管理") // 定義接口所屬模塊名
@RequestMapping("/admin/attr")// 接口根路徑
public class AttrController {
@Autowired// 自動(dòng)注入,可以理解為將AttrKeyService引入并賦值給attrKeyService,不需要new對(duì)象了
private AttrKeyService attrKeyService;
// 單表的簡單增加和更新操作
@Operation(summary = "新增或更新屬性名稱")// 接口描述
@PostMapping("key/saveOrUpdate") // 接口路徑名
public Result saveOrUpdateAttrKey(@RequestBody AttrKey attrKey) {
attrKeyService.saveOrUpdate(attrKey);// 直接調(diào)用MyBatis-Plus內(nèi)部的api進(jìn)行操作
return Result.ok();
}
// 進(jìn)行復(fù)雜查詢
@Operation(summary = "查詢?nèi)繉傩悦Q和屬性值列表")// 接口描述
@GetMapping("list")// 接口路徑名
public Result<List<AttrKeyVo>> listAttrInfo() {
// 調(diào)用AttrKeyService服務(wù)類里的方法listAttrInfo進(jìn)行操作
List<AttrKeyVo> list = attrKeyService.listAttrInfo();
return Result.ok(list);
}
}
10.創(chuàng)建Service接口
- 在web-admin項(xiàng)目的src/main/java里創(chuàng)建com.lease.web.admin.service.AttrKeyService接口
- 在
接口里定義Controlle類調(diào)用的方法
/**
* @author liubo
* @description 針對(duì)表【attr_key(房間基本屬性表)】的數(shù)據(jù)庫操作Service
* @createDate 2023-07-24 15:48:00
*/
public interface AttrKeyService extends IService<AttrKey> {
List<AttrKeyVo> listAttrInfo();
}
11.創(chuàng)建Service接口的實(shí)現(xiàn)類
- 在web-admin項(xiàng)目的src/main/java里創(chuàng)建com.lease.web.admin.service.impl.AttrKeyServiceImpl實(shí)現(xiàn)類
實(shí)現(xiàn)類用于調(diào)用Mapper接口方法和編寫接口功能的實(shí)現(xiàn)邏輯
/**
* @author liubo
* @description 針對(duì)表【attr_key(房間基本屬性表)】的數(shù)據(jù)庫操作Service實(shí)現(xiàn)
* @createDate 2023-07-24 15:48:00
*/
@Service
public class AttrKeyServiceImpl extends ServiceImpl<AttrKeyMapper, AttrKey> implements AttrKeyService{
@Autowired // 引入會(huì)在mapper下顯示紅線錯(cuò)誤,這只是IDEA的錯(cuò)誤,不影響運(yùn)行
private AttrKeyMapper mapper;
@Override
public List<AttrKeyVo> listAttrInfo() {
// 一些復(fù)雜的功能邏輯也會(huì)在這里完成,比如登錄接口驗(yàn)證,獲取多表內(nèi)容進(jìn)行統(tǒng)計(jì)等
List<AttrKeyVo> attrKeyVos = mapper.listAttrInfo(); // 調(diào)取相應(yīng)的Mapper接口方法
return attrKeyVos;
}
}
12.創(chuàng)建Mapper接口
- 在web-admin項(xiàng)目的src/main/java里創(chuàng)建com.lease.web.admin.mapper.AttrKeyMapper接口
- 定義Mapper接口方法
/**
* @author liubo
* @description 針對(duì)表【attr_key(房間基本屬性表)】的數(shù)據(jù)庫操作Mapper
* @createDate 2023-07-24 15:48:00
* @Entity com.lease.model.AttrKey
*/
public interface AttrKeyMapper extends BaseMapper<AttrKey> {
List<AttrKeyVo> listAttrInfo();
}
13.創(chuàng)建Mapper.xml
- 在web-admin項(xiàng)目的src/main/resources/mapper里創(chuàng)建Mapper接口對(duì)應(yīng)的Xml文件AttrKeyMapper.xml
- 編寫需要查詢的SQL語句進(jìn)行數(shù)據(jù)查詢
<?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.lease.web.admin.mapper.AttrKeyMapper">
<select id="listAttrInfo" resultMap="BaseResultMap">
// 進(jìn)行SQL語句的編寫
</select>
</mapper>7.項(xiàng)目緩存優(yōu)化
緩存優(yōu)化的核心思想就是將一些原本保存在磁盤(例如MySQL)中的、經(jīng)常訪問并且查詢開銷比較大的數(shù)據(jù),臨時(shí)保存到內(nèi)存(例如Redis)中。后序再訪問相同數(shù)據(jù)時(shí),就可直接從內(nèi)存中獲取結(jié)果,而無需再訪問磁盤,由于內(nèi)存的讀寫速度遠(yuǎn)高于磁盤,因此就能極大的提高程序的性能
1.自定義RedisTemplate序列化方法
- 在common模塊的ser/main/java下創(chuàng)建com.lease.common.redis.RedisConfiguration類
- 在類里創(chuàng)建stringObjectRedisTemplate方法,并自定義RedisTemplate屬性,告訴Spring如何把
Java對(duì)象轉(zhuǎn)換成能存入Redis的格式(序列化),以及如何從Redis讀回來的數(shù)據(jù)轉(zhuǎn)換回Java對(duì)象`(反序列化)。 - 要是不自定義RedisTemplate屬性,Spring會(huì)用默認(rèn)的Java序列化,存入Redis的數(shù)據(jù)是亂碼且不可讀等問題
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate<String, Object> stringObjectRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.java());
return template;
}
}
2.在需要Redis緩存優(yōu)化的接口進(jìn)行數(shù)據(jù)緩存
- 在接口方法Service的
實(shí)體類里進(jìn)行緩存邏輯
@Override
public List<AttrKeyVo> listAttrInfo() {
// 拼接Redis的key
String key = RedisConstant.ADMIN_LOGIN_PREFIX;
// 查詢Redis里是否有該數(shù)據(jù)
List<AttrKeyVo> attrKeyVos = (List<AttrKeyVo>) redisTemplate.opsForValue().get(key);
// 判斷,當(dāng)Redi沒有對(duì)應(yīng)數(shù)據(jù)時(shí)才到數(shù)據(jù)庫里查詢
if(attrKeyVos == null){
// 查詢數(shù)據(jù)
attrKeyVos = mapper.listAttrInfo();
// 將查詢的數(shù)據(jù)存入Redis,并設(shè)置過期時(shí)間為12小時(shí)
redisTemplate.opsForValue().set(key, attrKeyVos, 12, TimeUnit.HOURS);
}
return attrKeyVos;
}
3.為保證數(shù)據(jù)庫與緩存數(shù)據(jù)的一致性,當(dāng)對(duì)應(yīng)接口的數(shù)據(jù)發(fā)生改變時(shí)也要修改Redis緩存的數(shù)據(jù)
- 實(shí)現(xiàn)思維是當(dāng)數(shù)據(jù)發(fā)生改變,就刪除對(duì)應(yīng)的緩存,當(dāng)下次查詢數(shù)據(jù)時(shí),Redis里沒有數(shù)據(jù)了,會(huì)將查詢的新數(shù)據(jù)緩存到Redis里
- 新增,修改,刪除操作都需要清除Redis對(duì)應(yīng)數(shù)據(jù)的緩存,查詢時(shí)會(huì)將數(shù)據(jù)重新緩存到Redis里
@Operation(summary = "新增或更新屬性名稱")// 接口描述
@PostMapping("key/saveOrUpdate") // 接口路徑名
public Result saveOrUpdateAttrKey(@RequestBody AttrKey attrKey) {
Boolean result = attrKeyService.saveOrUpdate(attrKey);
if (result) {
// 清除屬性列表緩存(因?yàn)?listAttrInfo 包含屬性值)
String key = RedisConstant.ADMIN_LOGIN_PREFIX;
redisTemplate.delete(key);
}
return Result.ok();
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中ArrayList與順序表的定義與實(shí)現(xiàn)方法
ArrayList是一個(gè)實(shí)現(xiàn)List接口的類,底層是動(dòng)態(tài)類型順序表,本質(zhì)也就是數(shù)組,動(dòng)態(tài)主要體現(xiàn)在它的擴(kuò)容機(jī)制,下面這篇文章主要給大家介紹了關(guān)于Java中ArrayList與順序表的定義與實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2022-07-07
多數(shù)據(jù)源@DS和@Transactional實(shí)戰(zhàn)
這篇文章主要介紹了多數(shù)據(jù)源@DS和@Transactional實(shí)戰(zhàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot實(shí)現(xiàn)反向代理的示例代碼
本文主要介紹了SpringBoot實(shí)現(xiàn)反向代理的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java操作XML轉(zhuǎn)JSON數(shù)據(jù)格式詳細(xì)代碼實(shí)例
在Java中我們可以使用一些現(xiàn)成的庫來實(shí)現(xiàn)XML到JSON的轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于Java操作XML轉(zhuǎn)JSON數(shù)據(jù)格式的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04
java代碼獲取jenkins數(shù)據(jù),構(gòu)建歷史等信息方式
這篇文章主要介紹了java代碼獲取jenkins數(shù)據(jù),構(gòu)建歷史等信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-05-05

