SpringBoot开发基础流程

SpringBoot后端项目快速搭建基础代码。

1.项目环境搭建

项目地址:https://github.com/Wahoyu/SpringBootBasicCode

创建后端项目

可以创建空的项目,然后在下面pom.xml文件中选择依赖:

导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!--Web供Controller使用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>

<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>

<!-- 代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.0</version>
</dependency>

<!--模板引擎 依赖:mybatis-plus代码生成的时候报异常-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!--配置ApiModel在实体类中不生效-->
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>spring-boot-starter-swagger</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>
<!--freemarker(模板引擎)-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!-- 时间格式化需要-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.17</version>
</dependency>
<!--beet(Java模板引擎))-->
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.3.2.RELEASE</version>
</dependency>

<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.5.2</version>
</dependency>

连接数据库测试

配置文件配置数据源和MybatisPlus日志

1
2
3
4
5
6
7
8
9
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

测试成功!

2.代码生成器

MybatisPlus代码生成器可以根据配置文件生成数据库表对应的从Controller→Entity的文件,相当于搭建了项目的框架,方便我们进行后续开发。

搭建数据库表环境

MyBatis Plus的代码生成器不对数据库表中的字段有任何必须要求。生成器会根据数据库表的结构自动生成相应的实体类、Mapper接口、Service类等代码。

然而,建议数据库表至少包含以下字段,以便使用MyBatis Plus代码生成器更好地生成代码:

  1. 主键字段:建议表中具有一个主键字段,用于唯一标识每一条记录。最常见的选择是使用递增的整数类型作为主键。
  2. 创建时间和更新时间字段:为了方便记录信息的创建和更新时间,建议在表中包含创建时间和更新时间字段。常见的选择是使用时间戳字段(如datetimetimestamp类型)。
  • update_time 修改时间字段
  • create_time 创建时间字段

以上字段是一些常见的最佳实践,但并非强制要求。你可以根据具体的业务需求和设计选择其他字段类型和结构。

另外,我们还可以设置乐观锁和逻辑删除字段。

  • version 乐观锁更新版本字段
  • deleted 逻辑删除字段

如果我们在配置文件中对这些字段进行了配置,但是数据库表中没有这些字段,问题也不大。

1
2
3
4
5
6
7
CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT,
created_time DATETIME,
updated_time DATETIME
);

编写可执行文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class CodeGenerator {

/*??????????*/
private static final String[] TABLE_NAMES = new String[]{"sys_org"};
/*项目文件路径(到com文件夹的上一层也就是java文件夹截止,java后面没有斜杠)*/
public static final String PROJECT_PATH = "/Users/wanghongyu/IdeaProjects/GeneratorTest/src/main/java";
/*项目名*/
public static final String PROJECT_NAME = "meeting";
/*模块名称*/
public static final String MODULE_NAME ="";

//此时已经锁定到了模块内部
public static void main(String[] args) {
//创建代码生成器
AutoGenerator autoGenerator = new AutoGenerator();
//设置数据源
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
autoGenerator.setDataSource(dataSource);
//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(PROJECT_PATH);
globalConfig.setOpen(true);//生成完毕后是否打开目录
globalConfig.setAuthor("wahoyu");//作者
globalConfig.setFileOverride(false);//是否覆盖
globalConfig.setMapperName("%sMapper");//设置数据层接口名,%s表示占位符,代指模块名称
// globalConfig.setIdType(IdType.ASSIGN_ID);//设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
//设置包名相关配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example");//想要将mapper等文件夹生成在哪个文件夹中
packageConfig.setEntity("entity");//设置实体类的包名
packageConfig.setMapper("mapper");//设置数据层的包名
autoGenerator.setPackageInfo(packageConfig);
//策略配置
StrategyConfig strategyConfig = new StrategyConfig();
//strategyConfig.setInclude()//设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("");//去掉生成的前缀
strategyConfig.setRestControllerStyle(true);//设置是否启动Rest风格
strategyConfig.setVersionFieldName("version");//设置乐观锁字段名
strategyConfig.setEntityLombokModel(true);//设置是否启用lombok
strategyConfig.setCapitalMode(true);
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
//下划线转驼峰命名
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
//需要生成的的表名,多个表名传数组
String[] tableList = new String[] {"student"};
strategyConfig.setInclude(tableList);
autoGenerator.setStrategy(strategyConfig);
//执行代码生成器
autoGenerator.execute();
}


/**
* @MethodName: getStrategyConfigInfo
* @Description: 策略配置
* @Return: StrategyConfig
**/
public static StrategyConfig getStrategyConfigInfo(String... tableNames) {
StrategyConfig strategyConfigInfo = new StrategyConfig();
strategyConfigInfo.setCapitalMode(true);
strategyConfigInfo.setNaming(NamingStrategy.underline_to_camel);
//下划线转驼峰命名
strategyConfigInfo.setColumnNaming(NamingStrategy.underline_to_camel);
//需要生成的的表名,多个表名传数组
strategyConfigInfo.setInclude(tableNames);
//设置逻辑删除字段
strategyConfigInfo.setLogicDeleteFieldName("deleted");
//使用lombok
strategyConfigInfo.setEntityLombokModel(true);
//设置表格前缀
strategyConfigInfo.setTablePrefix("");
//rest风格
strategyConfigInfo.setRestControllerStyle(true);
return strategyConfigInfo;
}
}

运行代码生成器

文件生成成功!

移动mapper.xml

配置文件application.yml,SpringBoot扫描xml文件的路径。

1
2
mybatis-plus: 
mapper-locations: classpath*:mapper/**/*.xml

classpath*:mapper/**/*.xml表示在classpath(也就是resources文件夹)下的mapper目录以及其子目录中查找所有的xml文件。

3.项目开发通用流程

在使用代码生成器生成文件后,编写通用的增删改查接口。

MybatisPlus分页插件

配置MybatisPlus分页插件和扫描mapper文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.example.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan({"com.example.mapper"}) //可将主类中的注解移到此处
public class MybatisPlusConfig {

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}

}

实体类配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Getter
@Setter
@TableName("student")
public class Student implements Serializable {

private static final long serialVersionUID=1L;

//如果这里配置的是AUTO,那么数据库的主键应该设置为自增
@TableId(value = "id", type = IdType.AUTO)
private Integer id;

private String name;

private Integer age;

//如果没设置时间自动填充,那么应该在增加的时候手动填充new Date()
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdTime;

//如果没设置时间自动填充,那么应该在更新的时候手动填充new Date()
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updatedTime;


}

时间填充配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", LocalDateTime.now(),metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now(),metaObject);
}

@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", LocalDateTime.now(),metaObject);
}
}

Ext.增加时间、更新时间

如果我们不想像上面一样,在代码级别添加时间注释,我们也可以在数据库表创建的时候,在数据库级别,设置自动更新创建时间和更新时间。但是这种相对困难,我们不一定有权限修改数据库。

1
2
3
4
5
6
CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

4.代码编写

五个方法:1、分页查询(可模糊查询) 2、查询一个 3、更新一个 4、删除一个 5、添加一个。

Result与Response

  • Result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.lhcc.common.result;

import lombok.Data;

import java.io.Serializable;

/**
* 统一API响应结果格式封装
*/
@Data
public class Result<T> implements Serializable {

private static final long serialVersionUID = 6308315887056661996L;
private Integer code;
private String message;
private T data;


public Result setResult(ResultCode resultCode) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
return this;
}

public Result setResult(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.setData(data);
return this;
}



}


  • ResultCode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.lhcc.common.result;

import lombok.Getter;

/**
* 响应码枚举,对应HTTP状态码
*/
@Getter
public enum ResultCode {

SUCCESS(200, "成功"),//成功
BAD_REQUEST(400, "失败"),
UNAUTHORIZED(401, "认证失败"),//未认证
NOT_FOUND(404, "接口不存在"),//接口不存在
INTERNAL_SERVER_ERROR(500, "服务器内部错误"),//服务器内部错误
METHOD_NOT_ALLOWED(405,"方法不被允许"),
ILLEGAL_HEADER(406,"请求头无效"),
REPLAY_ERROR(410,"请求重复"),
/*参数错误:1001-1999*/
PARAMS_IS_INVALID(1001, "参数无效"),
PARAMS_IS_BLANK(1002, "参数为空");
/*用户错误2001-2999*/

private Integer code;
private String message;

ResultCode(int code, String message) {
this.code = code;
this.message = message;
}
}

  • ResultResponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.lhcc.common.result;

/* *
* 响应结果返回封装
* */
public class ResultResponse {


// 只返回状态
public static Result success() {
return new Result()
.setResult(ResultCode.SUCCESS);
}

// 成功返回数据
public static Result success(Object data) {
return new Result()
.setResult(ResultCode.SUCCESS, data);


}

// 失败
public static Result failure(ResultCode resultCode) {
return new Result()
.setResult(resultCode);
}

// 失败
public static Result failure(ResultCode resultCode, Object data) {
return new Result()
.setResult(resultCode, data);
}

//参数无效
public static Result paramInvalid(Object data) {
return new Result()
.setResult(ResultCode.PARAMS_IS_INVALID, data);
}


}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@RestController
@RequestMapping("/student")
public class StudentController {

@Resource
IStudentService iStudentService;

/**
* 分页查询
*/
@GetMapping("/list")
public Result list(Student student, Page<Student> page) {
return ResultResponse.success(iStudentService.selectByPage(student, page));
}

/**
* 根据Id查询
*/
@GetMapping("/{id}")
public Result getInfo(@PathVariable BigInteger id) {
return ResultResponse.success(iStudentService.getById(id));
}

/**
* 修改
*/
@PostMapping("/update")
public Result update(@RequestBody Student student) {
//这里设置更新者...
//student.setUpdateBy(...);
return ResultResponse.success(iStudentService.updateById(student));
}

/**
* 删除
*/
@DeleteMapping("/{id}")
public Result remove(@PathVariable BigInteger id) {
return ResultResponse.success(iStudentService.removeById(id));
}

/**
* 添加
* @param student
* @return
*/
@PostMapping("/add")
public Result add(@RequestBody Student student) {
//这里设置创建者...
//student.setCreateBy(...);
iStudentService.save(student);
return ResultResponse.success();
}


}


Service

1
2
3
public interface IStudentService extends IService<Student> {
Page<Student> selectByPage(Student student, Page<Student> page);
}

ServiceImpl

在分页的同时,对必要的空值做判断,同时支持模糊查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {

@Resource
StudentMapper studentMapper;

@Override
public Page<Student> selectByPage(Student student, Page<Student> page) {

//进行必要的判空操作和模糊查询,主要是name等字段,自行判断,可后续添加
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();

if (StringUtils.isNotBlank(student.getName())) {
queryWrapper.like("name", student.getName());
}

if (student.getAge() != null) {
queryWrapper.eq("age", student.getAge());
}
Page<Student> studentPage = studentMapper.selectPage(page, queryWrapper);
return studentPage;
}
}

5.接口测试

项目文件目录

测试一:添加一条数据

测试二:全部查询

测试三:分页查询

Student属性与Page属性可传入的参数直接拼接URL就行

测试四:查询一条数据

测试五:更新一条数据

注意属性必须要完整,如果想要简单一点的方法,可以使用updateById方法

更新前:

更新后:

测试六:删除一条数据


SpringBoot开发基础流程
http://wahoyu.xyz/2023/07/29/SpringBootBasicCode/
作者
Wahoyu
发布于
2023年7月29日
许可协议