当前位置: 代码网 > it编程>编程语言>Java > SpringBoot多模块项目创建和初始化问题

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

2026年05月14日 Java 我要评论
1.使用navicat创建数据库2.创建空项目目录lease├── common(公共模块——工具类、公用配置等)│ ├── pom.xml│ └── src├── model(数据模型——与

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配置

  • 修改父工程lease的pom.xml文件,添加后刷新maven
  • 这里配置的依赖并没有引入到项目里,后面会在子模块里进行引入和下载相关依赖
    <!-- 继承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.配置子模块

  • 在子模块里引入需要的依赖和插件,修改web的pom.xml文件
# 在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文件和创建项目的启动类

  • web-admin和web-app是两个项目,只是共用同一个common和model模块
  • application.yml要在web-admin/src/main/resources目录下
# 配置启动端口号
server:
  port: 8080
  • 每个项目都有单独的启动类,在项目的src/main/java/com.lease下创建adminwebapplication启动类
@springbootapplication
public class adminwebapplication {
    public static void main(string[] args) {
        springapplication.run(adminwebapplication.class, args);
    }
}

4.初始化项目,添加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驱动-->
        <dependency>
            <groupid>com.mysql</groupid>
            <artifactid>mysql-connector-j</artifactid>
        </dependency>
    </dependencies>
  • 因为model模块存放的是实体类,需要mybatis-plus相关注解,所以model的pom.xml也要引入相关的配置
    <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类型的序列化格式

  • 在web-admin项目的application.yml文件的spring下添加jackson序列化工具
  • 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接口的包路径和分页方法配置

  • 在src/main/java目录下创建类com.lease.common.mybatisplus.mybatisplusconfiguration
  • 添加配置类主要是为了指定mapper接口的包扫描路径
  • 配置分页是为了在接口里使用mybatis-puls分页方法,查询数据时就不需要考虑分页逻辑了
@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里添加依赖

  • 因为web模块下有多个子项目都会用到knife4j,所以在web模块下添加依赖
  • 因为model模块是放实体类的,需要配置knife4j相关注解,所以也要添加依赖
<dependency>
    <groupid>com.github.xiaoymin</groupid>
    <artifactid>knife4j-openapi3-jakarta-spring-boot-starter</artifactid>
</dependency>

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

  • 在web-admin项目的src/main/java目录下创建com.lease.web.admin.custom.config.knife4jconfiguration配置类
  • 每个项目都有不同的配置,所以要分别为每个项目创建一个配置类
@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类

  • 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枚举类

  • 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类

  • 当接口请求失败时,会自定识别错误,并抛出result.fail()定义的错误信息
  • 会自动精准识别使用那个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.在对应的方法里使用自定义异常方法,并抛出异常

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

3.编写接口的实体类

1.创建通用实体类

  • 在model模块的src/main/java目录下创建通用实体类com.lease.model.entity.baseentity类
  • baseentity类里定义通用字段,90%的表都会有这些字段
  • 当给50个实体类都要添加一个creator_id字段时,只需要修改baseentity类就可以了,不需要一个个为实体类添加
  • serializable 是为了让对象能够序列化(能存redis、网络传输等),必须继承
@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.封装自动填充的内容

  • 为了防止新增或更新数据时,对应的时间字段不会自动填入值,如创建时间,更新时间等
  • 方法名是metaobjecthandler 接口定义好的,不能随便写,否则不会调用方法
  • mybatis-plus会根据执行的sql操作类型来决定调用哪个方法
@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.创建需要的实体类

  • 在model模块的src/main/java目录下创建com.lease.model.entity.apartmentfacility实体类
  • 实体类与数据库的表是一一对应的,一张表对应一个实体类,具体根据项目需求创建实体类
  • 实体类主要是为了定义接口接收参数类型返回数据类型以及定义接口文档内容描述(个人理解)
@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

  • 主要为了封装通用转换器时converterfactory接收原始类和目标类的父类
  • 原始类是前端传递的值
  • 目标类的父类是需要转换的枚举类共同的父类,因为converterfactory提供的getenumconstants()会获取该父类下的所有子类,所有传教一个通用的父接口供枚举类继承
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转换器

  • 转换器是为了将前端http请求传递的字符串转换为接口对应的枚举类;当前端传来字符串 “1” 时,转换器会自动转换成对应的枚举类
  • spring也有默认的枚举转换,但是转换是按枚举名称匹配,当前端传递"1"时就无法匹配
@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注册类

  • 注册转换器,前端调用接口时,当spring mvc发现传递的参数需要转换为枚举类时会自动去找注册的转换器进行转换类型
@configuration
public class webmvcconfiguration implements webmvcconfigurer {

    @autowired
    private stringtobaseenumconverterfactory stringtobaseenumconverterfactory;

    @override
    public void addformatters(formatterregistry registry) {
        registry.addconverterfactory(this.stringtobaseenumconverterfactory);
    }
}
  • 接口里的@requestparam注解绑定type参数时触发转换器
  • 当前端不传值(type=null)或参数类型不匹配(type=adc)时,转换器不会触发
	@operation(summary = "(根据类型)查询标签列表")
    @getmapping("list")
    public result<list<labelinfo>> labellist(@requestparam(required = false) itemtype type) {}

6.typehandler枚举类型转换

  • 主要是解决接口的枚举类转换为数据库要储存的类型,数据库里存放的是前端传递的数字"1",类型是tinyint
  • 在对应的枚举类里添加@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;
    }

    // 构造方法,给枚举类赋值
    itemtype(integer code, string name) {
        this.code = code;
        this.name = name;
    }
}

7.httpmessageconverter枚举类型转换

  • 主要解决http请求和响应的转换
  • 将数据库的响应值转换成序列化json返回给前端
  • 将前端的请求反序列化后,把对应的数据保存到数据库
  • 在对应的枚举类里添加@jsonvalue注解
【查询响应】
数据库 → 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类

  • vo类就是重写实体类,整合前端需要的数据,将多个包含不同数据的实体类整合为一个,然后传递给result类统一封装进行响应返回
  • apartmentdetailvo类继承了公寓的基本信息apartmentinfo实体类,并整合了公寓图片,标签等实体类,将数据整合后返回给前端,返回的数据包含基本信息,公寓图片,公寓标签等
@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类

  • 在web-admin项目的src/main/java里创建com.lease.web.admin.controller.apartment.attrcontroller类,用于定义接口供前端调用
  • 当进行单表的增删改查基础操作时可以使用mybatis-plus提供的api方法
  • 当进行多表关联查询或者复杂统计查询时,需要手动在mapper.xml里进行手写sql语句查询
@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接口

  • 在web-admin项目的src/main/java里创建com.lease.web.admin.service.attrkeyservice接口
  • 接口里定义controlle类调用的方法
/**
* @author liubo
* @description 针对表【attr_key(房间基本属性表)】的数据库操作service
* @createdate 2023-07-24 15:48:00
*/
public interface attrkeyservice extends iservice<attrkey> {
    list<attrkeyvo> listattrinfo();
}

11.创建service接口的实现类

  • 在web-admin项目的src/main/java里创建com.lease.web.admin.service.impl.attrkeyserviceimpl实现类
  • 实现类用于调用mapper接口方法和编写接口功能的实现逻辑
/**
* @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接口

  • 在web-admin项目的src/main/java里创建com.lease.web.admin.mapper.attrkeymapper接口
  • 定义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

  • 在web-admin项目的src/main/resources/mapper里创建mapper接口对应的xml文件attrkeymapper.xml
  • 编写需要查询的sql语句进行数据查询
<?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.项目缓存优化

  • 缓存优化的核心思想就是将一些原本保存在磁盘(例如mysql)中的、经常访问并且查询开销比较大的数据,临时保存到内存(例如redis)中。后序再访问相同数据时,就可直接从内存中获取结果,而无需再访问磁盘,由于内存的读写速度远高于磁盘,因此就能极大的提高程序的性能

1.自定义redistemplate序列化方法

  • 在common模块的ser/main/java下创建com.lease.common.redis.redisconfiguration类
  • 在类里创建stringobjectredistemplate方法,并自定义redistemplate属性,告诉spring如何把java对象转换成能存入redis的格式(序列化),以及如何从redis读回来的数据转换回java对象`(反序列化)。
  • 要是不自定义redistemplate属性,spring会用默认的java序列化,存入redis的数据是乱码且不可读等问题
@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缓存优化的接口进行数据缓存

  • 在接口方法service的实体类里进行缓存逻辑
	@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缓存的数据

  • 实现思维是当数据发生改变,就删除对应的缓存,当下次查询数据时,redis里没有数据了,会将查询的新数据缓存到redis里
  • 新增,修改,删除操作都需要清除redis对应数据的缓存,查询时会将数据重新缓存到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();
    }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com