java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot多模块项目创建和初始化

SpringBoot多模块项目创建和初始化问题

作者:等....

本文详细介绍了使用SpringBoot框架创建一个包含数据库操作、MyBatis-Plus、Knife4j、HTTP请求响应等的Java Web项目的步骤和配置方法,内容涵盖创建项目结构、配置数据库连接、添加依赖、编写接口、处理异常、序列化和反序列化、配置接口文档等等内容

1.使用Navicat创建数据库

2.创建空项目

目录

lease
├── common(公共模块——工具类、公用配置等)
│   ├── pom.xml
│   └── src
├── model(数据模型——与数据库相对应地实体类)
│   ├── pom.xml
│   └── src
├── web(Web模块)
│   ├── pom.xml
│   ├── web-admin(后台管理系统Web模块——包含mapper、service、controller)
│   │   ├── pom.xml
│   │   └── src
│   └── web-app(移动端Web模块——包含mapper、service、controller)
│       ├── pom.xml
│       └── src
└── pom.xml

1.打开IDEA,创建项目

2.配置项目,点击下一步直到完成

3.初始化项目

lease是父工程,不需要编写代码,所有把src目录删除,然后在父工程里创建子工程

点击父工程–>新建–>模块

4.创建web模块

web模块主要是放开发逻辑代码,在创建模块时,要注意父项是web模块

5.在web模块引入common模块和model模块

因为web模块的子模块是依赖与common和model模块的,在web父模块里引入后,web的子模块就可以使用common和model模块的内容了

# 注意:是在web父模块的pom.xml里添加的,添加后刷新一下Maven
    <dependencies>
        <dependency>
            <groupId>com</groupId># 创建模块的组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.初始化项目,添加SpringBoot配置

1.在父工程里添加相关的SpringBoot配置

    <!-- 继承Spring Boot父项目 -->
    <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>
        <!-- 统一声明依赖的版本,直接通过${}引用 -->
        <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统一管理依赖版本-->
    <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登录认证相关-->
            <!--官方文档: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>
            <!--图形验证码-->
            <!--官方文档:https://gitee.com/ele-admin/EasyCaptcha -->
            <dependency>
                <groupId>com.github.whvcse</groupId>
                <artifactId>easy-captcha</artifactId>
                <version>${easycaptcha.version}</version>
            </dependency>
            <!--对象存储,用于存储图像等非结构化数据-->
            <!--官方文档: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>
            <!--阿里云短信客户端,用于发送短信验证码-->
            <!--官方文档: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.配置子模块

# 在dependeencies里添加需要的依赖
<dependencies>
	<!--包含spring web相关依赖-->
	<dependency>
    	<groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!--包含spring test相关依赖-->
	<dependency>
    	<groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-test</artifactId>
    	<scope>test</scope>
	</dependency>
</dependencies>
<!-- Spring Boot Maven插件,用于打包可执行的JAR文件 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

3.给项目添加application.yml文件和创建项目的启动类

# 配置启动端口号
server:
  port: 8080
@SpringBootApplication
public class AdminWebApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminWebApplication.class, args);
    }
}

4.初始化项目,添加Mybatis-Plus配置

1.在pom.xml文件里添加依赖

    <dependencies>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
    </dependencies>
    <dependencies>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
    </dependencies>

2.修改web-admin模块的application.yml文件,配置数据库

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource # 指定数据库连接池类型
    url: jdbc:mysql://192.168.23.101:3306/lease?useUnicode=true&characte
    # url: jdbc:mysql://数据库的ip地址:端口号/要连接的数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2b8
    username: 数据库账号名
    password: 数据库账号密码
    # 配置连接池参数
    hikari:
      connection-test-query: SELECT 1 # 自动检测连接
      connection-timeout: 60000 #数据库连接超时时间,默认30秒
      idle-timeout: 500000 #空闲连接存活最大时间,默认600000(10分钟)
      max-lifetime: 540000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
      maximum-pool-size: 12 #连接池最大连接数,默认是10
      minimum-idle: 10 #最小空闲连接数量
      pool-name: SPHHikariPool # 连接池名称
#用于打印框架生成的sql语句,便于调试
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.配置Date类型的序列化格式

  # 方法一:Date类型数据Json序列化框架全局配置
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss #定义全局日期格式
    time-zone: GMT+8 # 定义全局日期时区
方法二:实体类里独立配置Date类型数据格式和时区
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8")
private Date appointmentTime;

4.在common里添加Mapper接口的包路径和分页方法配置

@Configuration
@MapperScan("com.lease.web.*.mapper")# 项目里的mapper接口的目录
public class MybatisPlusConfiguration {
    @Bean
    // 分页配置
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

5.初始化项目,添加Knife4j配置

1.分别在web和model模块的pom.xml里添加依赖

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>

2.创建项目的Knife4j的配置类

@Configuration
public class Knife4jConfiguration {

    // 配置接口文档标题
    @Bean
    public OpenAPI openAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("hello-knife4j项目API")
                        .version("1.0")
                        .description("hello-knife4j项目的接口文档"));
    }

    // 配置接口的分类,以/user/开头的接口都规划到‘用户信息管理'
    @Bean
    public GroupedOpenApi userAPI() {
        return GroupedOpenApi.builder().group("用户信息管理").
                pathsToMatch(
                        "/user/room",// 精确匹配,只匹配/user/room的路径
                        "/user/room/*",// 单层匹配,只匹配/user/room/list这种单层的路径,多层的就不匹配了,必须有单层路径
                        "/user/room/**",// 通配匹配,会匹配所有以/user/room开头的路径,可以是单/多层或者是/user/room这个路径,使用通配匹配就不需要精确匹配和单层匹配了
                        "/user/*/room/**",// 混合使用
                        "/user/info/**",// 接口地址可以写多个
                        "/admin/**"// 可以写,但不建议开头不同的写一起,会造成文档分组语义混乱
                ).
                build();
    }

    // 配置接口的分类,以/product/开头的接口都规划到‘产品信息管理'
    @Bean
    public GroupedOpenApi systemAPI() {
        return GroupedOpenApi.builder().group("产品信息管理").
                pathsToMatch("/product/**").
                build();
    }
}

3 在web-admin项目的application.yml文件里添加接口参数打平配置

# Knife4j接口参数打平
springdoc:
  default-flat-param-object: true

6.编写接口

1.封装接口统一返回数据结构

1.在common模块的scr/main/java目录下创建com/lease/common/result/Result类

/**
 * 全局统一返回结果类
 */
@Data
public class Result<T> {

    //返回码
    private Integer code;

    //返回消息
    private String message;

    //返回数据
    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;
    }

    // 自定义错误码和错误消息
    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;
    }

    // 操作成功,需要返回数据给前端
    public static <T> Result<T> ok(T data) {
        return build(data, ResultCodeEnum.SUCCESS);
    }

    // 操作成功,但不需要返回数据(如新增、修改、删除)
    public static <T> Result<T> ok() {
        return Result.ok(null);
    }

    // 操作失败,不需要返回额外数据
    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目录下创建com/lease/common/result/ResultCodeEnum枚举类

/**
 * 统一返回结果状态信息类
 */
@Getter
public enum ResultCodeEnum {
    // 定义枚举类常量
    SUCCESS(200, "成功"),
    FAIL(201, "失败"),
    PARAM_ERROR(202, "参数不正确"),
    SERVICE_ERROR(203, "服务异常"),
    DATA_ERROR(204, "数据异常"),
    ILLEGAL_REQUEST(205, "非法请求"),
    REPEAT_SUBMIT(206, "重复提交"),
    DELETE_ERROR(207, "请先删除子集"),

    ADMIN_ACCOUNT_EXIST_ERROR(301, "账号已存在"),
    ADMIN_CAPTCHA_CODE_ERROR(302, "验证码错误"),
    ADMIN_CAPTCHA_CODE_EXPIRED(303, "验证码已过期"),
    ADMIN_CAPTCHA_CODE_NOT_FOUND(304, "未输入验证码"),


    ADMIN_LOGIN_AUTH(305, "未登陆"),
    ADMIN_ACCOUNT_NOT_EXIST_ERROR(306, "账号不存在"),
    ADMIN_ACCOUNT_ERROR(307, "用户名或密码错误"),
    ADMIN_ACCOUNT_DISABLED_ERROR(308, "该用户已被禁用"),
    ADMIN_ACCESS_FORBIDDEN(309, "无访问权限"),

    APP_LOGIN_AUTH(501, "未登陆"),
    APP_LOGIN_PHONE_EMPTY(502, "手机号码为空"),
    APP_LOGIN_CODE_EMPTY(503, "验证码为空"),
    APP_SEND_SMS_TOO_OFTEN(504, "验证法发送过于频繁"),
    APP_LOGIN_CODE_EXPIRED(505, "验证码已过期"),
    APP_LOGIN_CODE_ERROR(506, "验证码错误"),
    APP_ACCOUNT_DISABLED_ERROR(507, "该用户已被禁用"),


    TOKEN_EXPIRED(601, "token过期"),
    TOKEN_INVALID(602, "token非法");

    // 对应这Result里定义的变量
    private final Integer code;

    private final String message;

    // 构造方法,给枚举类赋值
    ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

2.封装全局错误处理

1.在common模块的pom.xml添加依赖

<!--spring-web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.在common模块的scr/main/java目录下创建com.lease.common.exception.LeaseException异常类

@Data
public class LeaseException extends RuntimeException {
    //异常状态码
    private Integer code;

    /**
     * 通过状态码和错误消息创建异常对象
     *
     * @param message
     * @param code
     */
    public LeaseException(String message, Integer code) {
        super(message);
        this.code = code;
    }

    /**
     * 根据响应结果枚举对象创建异常对象
     *
     * @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目录下创建com.lease.common.exception.GlobalExceptionHandler类

@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.在对应的方法里使用自定义异常方法,并抛出异常

@Override
public void removeApartmentById(Long id) {
	// 调用异常类的方法,需要提前在ResultCodeEnum枚举类里定义好要返回的code和message
	throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);
	// 或者
	// throw new LeaseException(310,"请先删除房间信息");
}

3.编写接口的实体类

1.创建通用实体类

@Data
public class BaseEntity implements Serializable {
    @Schema(description = "主键")// 字段描述
    @TableId(value = "id", type = IdType.AUTO)// 对应表主键ID
    private Long id;

    @Schema(description = "创建时间")
    @TableField(value = "create_time",fill = FieldFill.INSERT)// 对应数据表的字段,第二个参数告诉框架插入数据时需要调用填充方法自动填充内容
    private Date createTime;

    @Schema(description = "更新时间")
    @TableField(value = "update_time",fill = FieldFill.UPDATE)// 第二个参数告诉框架更新数据时需要调用填充方法自动填充内容
    @JsonIgnore // 所有的返回值都不会返回这个值了
    private Date updateTime;

	@TableLogic // 过滤逻辑删除,当请求查询时,会自动过滤掉is_deleted=1的已删除的数据,不会返回给前端
    @Schema(description = "逻辑删除")
    @TableField("is_deleted")
    private Byte isDeleted;
}

2.封装自动填充的内容

@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.创建需要的实体类

@Schema(description = "标签信息表") // 接口名称
@TableName(value = "label_info")// 数据库表的名称,指定使用该实体类时操作的数据库表
@Data
public class LabelInfo extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @Schema(description = "类型")// 字段描述
    @TableField(value = "type")// 对应数据表的字段
    private ItemType type;// 使用ItemType枚举类

    @Schema(description = "标签名称")
    @TableField(value = "name")
    private String name;

    // 当实体类不需要通用字段时,告诉 MyBatis-Plus 这个字段不存在于表
	// 方法一:
    @TableField(exist = false)
    private Date updateTime;
    // 方法二:也可以写在BaseEntity通用实体类上
    @JsonIgnore
    private Long id;
}

4.编写接口的枚举类

1.定义枚举类通用父接口BaseEnum

public interface BaseEnum {
    Integer getCode();
    String getName();
}

2.在model模块的src/main/java目录下创建com.lease.model.enums.ItemType枚举类根据接口需求创建对应的枚举类

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:"公寓"}
    }
    
	// 构造方法,给枚举类赋值
    ItemType(Integer code, String name) {
        this.code = code;
        this.name = name;
    }
}

5.封装类型转换器

1.在model模块的src/main/java目录下创建com.lease.web.admin.custom.converter.StringToBaseEnumConverterFactory转换器

@Component
// 接收两个参数,一个是前端传递String类型值,一个是枚举类的父接口
public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
        return new Converter<String, T>() {// 返回一个转换器
            @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; // 返回对应的枚举对象
                    }
                }
                // 3. 找不到匹配的,抛异常
                throw new IllegalArgumentException("非法的枚举值:" + source);
            }
        };
    }
}

2.在model模块的src/main/java目录下创建com.lease.web.admin.custom.config.WebMvcConfiguration注册类

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Autowired
    private StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(this.stringToBaseEnumConverterFactory);
    }
}
	@Operation(summary = "(根据类型)查询标签列表")
    @GetMapping("list")
    public Result<List<LabelInfo>> labelList(@RequestParam(required = false) ItemType type) {}

6.TypeHandler枚举类型转换

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;
    }

    // 构造方法,给枚举类赋值
    ItemType(Integer code, String name) {
        this.code = code;
        this.name = name;
    }
}

7.HTTPMessageConverter枚举类型转换

【查询响应】
数据库 → TypeHandler → 实体类(含枚举) → Controller → HttpMessageConverter → JSON → 前端

【保存请求】  
前端 → JSON → HttpMessageConverter → Controller → 实体类(含枚举) → TypeHandler → 数据库

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;
    }

    // 构造方法,给枚举类赋值
    ItemType(Integer code, String name) {
        this.code = code;
        this.name = name;
    }
}

8.创建VO类(需要时才创建相应的vo类)

1.在web-admin项目的src/main/java目录下创建com.lease.web.admin.vo.apartment.ApartmentDetailVo类

@Data
@Schema(description = "公寓信息")// 类描述
public class ApartmentDetailVo extends ApartmentInfo {

    @Schema(description = "图片列表")// 字段描述
    private List<GraphVo> graphVoList;// 拿取之前写的VO类图片列表,新起字段为graphVoList

    @Schema(description = "标签列表")
    private List<LabelInfo> labelInfoList;// 拿取之前写的实体类标签列表,新起字段为labelInfoList
}

9.创建controlle类

@RestController
@Tag(name = "房间属性管理") // 定义接口所属模块名
@RequestMapping("/admin/attr")// 接口根路径
public class AttrController {
    @Autowired// 自动注入,可以理解为将AttrKeyService引入并赋值给attrKeyService,不需要new对象了
    private AttrKeyService attrKeyService;

	// 单表的简单增加和更新操作
    @Operation(summary = "新增或更新属性名称")// 接口描述
    @PostMapping("key/saveOrUpdate") // 接口路径名
    public Result saveOrUpdateAttrKey(@RequestBody AttrKey attrKey) {
        attrKeyService.saveOrUpdate(attrKey);// 直接调用MyBatis-Plus内部的api进行操作
        return Result.ok();
    }
    
    // 进行复杂查询
    @Operation(summary = "查询全部属性名称和属性值列表")// 接口描述
    @GetMapping("list")// 接口路径名
    public Result<List<AttrKeyVo>> listAttrInfo() {
        // 调用AttrKeyService服务类里的方法listAttrInfo进行操作
        List<AttrKeyVo> list = attrKeyService.listAttrInfo();
        return Result.ok(list);
    }
}

10.创建Service接口

/**
* @author liubo
* @description 针对表【attr_key(房间基本属性表)】的数据库操作Service
* @createDate 2023-07-24 15:48:00
*/
public interface AttrKeyService extends IService<AttrKey> {
    List<AttrKeyVo> listAttrInfo();
}

11.创建Service接口的实现类

/**
* @author liubo
* @description 针对表【attr_key(房间基本属性表)】的数据库操作Service实现
* @createDate 2023-07-24 15:48:00
*/
@Service
public class AttrKeyServiceImpl extends ServiceImpl<AttrKeyMapper, AttrKey> implements AttrKeyService{
    @Autowired // 引入会在mapper下显示红线错误,这只是IDEA的错误,不影响运行
    private AttrKeyMapper mapper;

    @Override
    public List<AttrKeyVo> listAttrInfo() {
    	// 一些复杂的功能逻辑也会在这里完成,比如登录接口验证,获取多表内容进行统计等
        List<AttrKeyVo> attrKeyVos = mapper.listAttrInfo(); // 调取相应的Mapper接口方法
        return attrKeyVos;
    }
}

12.创建Mapper接口

/**
* @author liubo
* @description 针对表【attr_key(房间基本属性表)】的数据库操作Mapper
* @createDate 2023-07-24 15:48:00
* @Entity com.lease.model.AttrKey
*/
public interface AttrKeyMapper extends BaseMapper<AttrKey> {
    List<AttrKeyVo> listAttrInfo();
}

13.创建Mapper.xml

<?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">
       // 进行SQL语句的编写
    </select>
</mapper>

7.项目缓存优化

1.自定义RedisTemplate序列化方法

@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缓存优化的接口进行数据缓存

	@Override
    public List<AttrKeyVo> listAttrInfo() {
        // 拼接Redis的key
        String key = RedisConstant.ADMIN_LOGIN_PREFIX;
        // 查询Redis里是否有该数据
        List<AttrKeyVo> attrKeyVos = (List<AttrKeyVo>) redisTemplate.opsForValue().get(key);
        // 判断,当Redi没有对应数据时才到数据库里查询
        if(attrKeyVos == null){
        	// 查询数据
            attrKeyVos = mapper.listAttrInfo();
            // 将查询的数据存入Redis,并设置过期时间为12小时
            redisTemplate.opsForValue().set(key, attrKeyVos, 12, TimeUnit.HOURS);
        }
        return attrKeyVos;
    }

3.为保证数据库与缓存数据的一致性,当对应接口的数据发生改变时也要修改Redis缓存的数据

	@Operation(summary = "新增或更新属性名称")// 接口描述
    @PostMapping("key/saveOrUpdate") // 接口路径名
    public Result saveOrUpdateAttrKey(@RequestBody AttrKey attrKey) {
        Boolean result = attrKeyService.saveOrUpdate(attrKey);
        if (result) {
            // 清除属性列表缓存(因为 listAttrInfo 包含属性值)
            String key = RedisConstant.ADMIN_LOGIN_PREFIX;
            redisTemplate.delete(key);
        }
        return Result.ok();
    }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文