微服务项目->在线oj系统(Java-Spring)----2.0
项⽬起步-后端⼯程创建
首先我们大概理清一下我们需要创建什么样的后端项目工程:
这里我们先做oj-modules和oj-common
父级项目创建
⽗⼯程pom.xml⽂件
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><modules><module>oj-modules</module><module>oj-common</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.1</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.bite</groupId><artifactId>bite-oj</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><spring-boot.version>3.0.1</spring-boot.version><spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version><spring-cloud.version>2022.0.0</spring-cloud.version></properties><dependencies><!-- bootstrap 启动器 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><dependencyManagement><dependencies><!-- SpringCloud Alibaba 微服务 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- SpringCloud 微服务 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- SpringBoot 依赖配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>
创建oj-modules
• 删除oj-modules⼯程中的src⽬录,并修改pom.xml⽂件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>bite-oj</artifactId><groupId>com.bite</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>oj-modules</artifactId><packaging>pom</packaging><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies></project>
创建微服务⼯程
以oj-system为例:

@SpringBootApplication
public class OjSystemApplication {public static void main(String[] args) {SpringApplication.run(OjSystemApplication.class, args);}
}
server:port: 9201
# Spring
spring:application:# 应用名称name: oj-system
其他的几个微服务按照oj-system一样完成即可,需要注意的是,我们这里的端口号需要不同
最后:我们的oj-modules中pom.xml文件中的依赖会呈现这样的情况
项目结构如下所示:
这里的oj-common先不做解释,后面用到的时候再给大家展开来讲
依次启动微服务项目:
但是我们发现如果依次一个一个进行启动或者停止的话有点太过于慢,所以我们这里可以换个简单的方法进行:
推送代码到码云:
我们需要把我们的代码放到码云便于我们代码管理、协作开发、项目备份
1.码云上面创建空仓库
2.本地仓库推至远程仓库
这里需要我们之前学习过Git的内容了,我们这里需要在我们的Widows上面安装仓库,创建仓库:
这里推荐大佬的Git博客,她的Widows下的Git写的不错:
【Git】万字详解 Git 的原理与使用(上)-CSDN博客
git initgit add .git commit -m "first commit"git remote add origin https://gitee.com/Ant_o_liu/bite-oj.gitgit push -u origin "master"
这里根据我们gitte上面的提示进行即可
我们直接在IDEA上面的终端执行即可
推送完成之后,我们在Gitte上面的仓库里面查看结果是否正确
MySQL
一个项目怎么可能不涉及数据库呢?这里我们需要使用的数据库是MySQL
我们博客里面使用的是云服务器上面docker的数据库,
1.拉取MySQL镜像
docker pull mysql:5.7
2.启动容器
docker run -d --name oj-mysql -p 3306:3306 -e "TZ=Asia/Shanghai" -e "MYSQL_ROOT_PASSWORD=123456" mysql:5.7
注意:我们这里的建立端口映射做的是docker和宿主机的映射,但是由于我们这里使用的是云服务器,所以我们还需要开通我们本机和云服务器的端口映射,否者后面会报错:
所以我们这里还需要一步(如果你使用的是本机的docker的话则不需要这步)
开通隧道(这个在我们之前学习Redis时候建立的隧道是一个道理)
如果这里有不清楚的,可以去了解一下我之前的Redis文章:
【redis初阶】------redis客户端(1)-CSDN博客
3.进入容器
docker exec -it 容器id bash
4.使用root登录MySQL
mysql -u root -p123456
5.在root里面创建ojtest用户
CREATE USER 'ojtest'@'%' IDENTIFIED BY '123456';
6.创建OJ项目数据库
CREATE database if NOT EXISTS `bitoj_dev` ;
7.赋予用户权力
GRANT CREATE,DROP,SELECT, INSERT, UPDATE, DELETE ON bitoj_dev.* TO
'ojtest'@'%';
8.使用ojtest用户登录
mysql -u ojtest -p123456
9.创建数据表
10.注意事项:
我们在使用Docker的时候总是会遇到各种各样的问题,比如创建的docker名字已经被占用,实例id已经被占用..........,我们这里作为初学者可以停止docker,然后再进行重启!也可以借助AI解决问题
DBServer
官⽹:https://dbeaver.io/


测试连接
MyBatis-plus
为什么使用它而不是mybatis呢?
1.开发效率:从 “手写 SQL” 到 “约定大于配置”
2.复杂查询:链式调用替代 XML 拼接
3.性能优化:内置实用功能
- 分页查询:MyBatis 需手动编写分页 SQL 或集成第三方插件;MP 内置
Page
插件,一行代码实现分页。- 逻辑删除:标记字段(如
deleted
)自动转换为条件,查询时默认过滤已删除数据,避免物理删除风险。4.高级功能:分布式场景支持
- 主键生成:MyBatis 需手动实现主键策略(如 UUID、自增);MP 内置雪花算法(
IdentifierGenerator
),支持分布式唯一主键。
数据库连接池
• 频繁的创建连接和销毁连接:包括TCP层的握⼿和MySQL协议握⼿,这会消耗⼤量时间。• 连接数不受控制:在业务流量⾼峰期,⼤量应⽤服务器可能同时请求数据库连接,⽽数据库能够承载的连接数有限,这可能导致数据库性能降低。• 连接管理困难:开发者需要⾃⾏管理数据库连接的创建、使⽤和关闭,这增加了开发的复杂性和出错的可能性
• 提供统⼀的管理:数据库连接池提供对数据库连接创建等操作的统⼀管理。• 提⾼系统性能 :由于创建和销毁数据库连接需要消耗时间和系统资源,如果每次需要进⾏数据库操作时都进⾏创建和销毁连接,会极⼤地影响系统性能。⽽使⽤连接池,可以复⽤已经创建好的连接,⼤ 提⾼了系统的性能。• 提⾼资源利⽤率 :连接池通过复⽤已有连接,避免了频繁创建和销毁连接带来的资源浪费,提⾼了系统资源的利⽤率。• 提⾼系统稳定性 :数据库连接池可以有效地控制系统中并发访问数据库的连接数量,避免因并发连接数过多⽽导致数据库崩溃。同时,连接池还会定时检查连接的有效性,⾃动替换掉⽆效连接,保证了系统的稳定性。
数据库连接池的⼯作原理:
为什么采⽤HikariCP
最重要原因:与Spring Boot集成良好
Spring Boot对HikariCP提供了良好的⽀持,可以轻松地在项⽬中集成和使⽤。Spring Boot的⾃动配置功能可以⾃动配置HikariCP的连接池参数,简化了配置过程。
集成mybatis-plus与数据库连接池
pom.xml中引入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency>

mapper
public interface TestMapper extends BaseMapper<TestDomain> {
}
server:port: 9201
# Spring
spring:application:# 应用名称name: oj-systemdatasource:url: jdbc:mysql://localhost:3306/bitoj_dev?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8username: ojtestpassword: 123456hikari:minimum-idle: 5 # 最小空闲连接数maximum-pool-size: 20 # 最大连接数idle-timeout: 30000 # 空闲连接存活时间(毫秒)connection-timeout: 30000 # 连接超时时间(毫秒)# connection-test-query: SELECT 1# 说明:如果驱动支持 JDBC4.0,建议不设置此参数,默认会调用 Connection.isValid() 检查连接,更高效# keepalive-time: 0 # 多久检查一次连接的活性,默认不启用
@MapperScan 是 Spring Boot 中 MyBatis 集成的⼀个注解,⽤于指定 MyBatis Mapper 接⼝所在的包路径。当 Spring Boot 应⽤程序启动时,它会扫描这个包路径下的所有接⼝,并将它们注册为MyBatis 的 Mapper。
表结构设计
drop table if exists `tb_sys_user`;
create table `tb_sys_user` (
user_id bigint (20) unsigned NOT NULL COMMENT ' ⽤⼾ id',
user_account varchar (32) DEFAULT NULL COMMENT ' ⽤⼾账号 ',
password varchar (100) DEFAULT NULL COMMENT ' ⽤⼾密码 ',
nick_name varchar (32) DEFAULT NULL COMMENT ' 昵称 ',
create_by bigint (8) NOT NULL COMMENT ' 创建⽤⼾ ',
create_time datetime NOT NULL COMMENT ' 创建时间 ',
update_by bigint (8) DEFAULT NULL COMMENT ' 更新⽤⼾ ',
update_time datetime DEFAULT NULL COMMENT ' 更新时间 ',
PRIMARY KEY (user_id),
UNIQUE KEY user_account (user_account)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理端⽤⼾表';
实体类
@Data
@TableName("tb_sys_user")
public class SysUser extends BaseEntity {@TableId(value = "USER_ID", type = IdType.ASSIGN_ID)//雪花算法private Long userId;private String userAccount;private String password;private Long createBy;private LocalDateTime createTime;private Long updateBy;private LocalDateTime updateTime;}
主键
1.数据迁移和备份
如果你需要将数据从⼀个数据库迁移到另⼀个数据库,或者备份和恢复数据,⾃增主键可能会导致问题。2.删除和插⼊操作如果表中存在⼤量删除和插⼊操作,⾃增主键可能会导致 ID 值的不连续。这可能会浪费存储空间,并可能导致某些应⽤程序或系统逻辑出现问题。3.可预测性⾃增主键的值是可预测的,因为它们总是按照递增的顺序⽣成。这可能会带来安全⻛险,例如,攻击者可能会尝试预测未来的 ID 值来插⼊恶意数据

为什么使用雪花算法
使⽤mybatis-plus⽀持雪花算法
代码优化
因为我们这个实体类里面的更新时间更新人,创建时间,创建人可能会被项目多次使用,所以我打算将他弄出一个公共类来存储
创建一个oj-common-core模块
然后讲那几个属性放入一个单独类BaseEntity
@Data
public class BaseEntity implements Serializable {private Long createBy;private LocalDateTime createTime;private Long updateBy;private LocalDateTime updateTime;
}
引入模块的依赖
为了方便对版本的管理,我们这里可以吧版本号统一放到bite-oj这个父项目的pom.xml里面
修改代码SysUser
@Data
@TableName("tb_sys_user")
public class SysUser extends BaseEntity {@TableId(value = "USER_ID", type = IdType.ASSIGN_ID)//雪花算法private Long userId;private String userAccount;private String password;}
代码初步开发
在开始写代码之前,我们先得有一个接口文档,来规定我们得统一结果返回,已经访问接口
统一返回结果
Data
public class R <T>{private T data;private String msg;private int code;
}
返回结果的枚举类
@AllArgsConstructor
@Getter
public enum ResultCode {//操作成功SUCCESS (1000, "操作成功"),//服务器内部错误,友好提示ERROR (2000, "服务繁忙请稍后重试"),//操作失败,但是服务器不存在异常FAILED (3000, "操作失败"),FAILED_UNAUTHORIZED (3001, "未授权"),FAILED_PARAMS_VALIDATE (3002, "参数校验失败"),FAILED_NOT_EXISTS (3003, "资源不存在"),FAILED_ALREADY_EXISTS (3004, "资源已存在"),FAILED_USER_EXISTS (3101, "用户已存在"),FAILED_USER_NOT_EXISTS (3102, "用户不存在"),FAILED_LOGIN (3103, "用户名或密码错误"),FAILED_USER_BANNED (3104, "您已被列入黑名单,请联系管理员.");/*** 状态码*/private int code;/*** 状态描述*/private String msg;
}
注意:由于这里是枚举类,所以我们无法使用@Data注解,并且因为这个枚举是有参数的构造犯法,所以我们无法直接使用@Data,所以我们这里使用的是@AllArgsConstructor,但是我们从外面需要使用到get方法,所以加了一个@Getter
login接口的接受参数方式(RequestBody)
@Data
public class LoginDTO {private String userAccount;private String password;
}
Controller
@RestController
@RequestMapping("sysUser")
public class SysUserController {@Autowiredprivate ISysUserService sysUserService;@RequestMapping("login")public R<Void> login(@RequestBody LoginDTO loginDTO){return sysUserService.login(loginDTO.getUserAccount(),loginDTO.getPassword());}
}
service
@Service public class SysUserServiceImp implements ISysUserService{@Autowiredprivate SysUserMapper sysUserMapper;@Overridepublic R<Void> login(String userAccount, String password) {LambdaQueryWrapper<SysUser> queryWrapper=new LambdaQueryWrapper<>();SysUser sysUser= sysUserMapper.selectOne(queryWrapper.select(SysUser::getPassword).eq(SysUser::getUserAccount,userAccount));R loginResult=new R();if(sysUser==null){loginResult.setCode(ResultCode.FAILED_USER_NOT_EXISTS.getCode());loginResult.setMsg(ResultCode.FAILED_USER_NOT_EXISTS.getMsg());return loginResult;}if(sysUser.getPassword()==password){loginResult.setCode(ResultCode.SUCCESS.getCode());loginResult.setMsg(ResultCode.SUCCESS.getMsg());return loginResult;}loginResult.setCode(ResultCode.FAILED_LOGIN.getCode());loginResult.setMsg(ResultCode.FAILED_LOGIN.getMsg());return loginResult;} }
package com.bite.system.service.impl;import com.bite.common.core.domain.R;public interface ISysUserService {public R login(String userAccount, String password); }
mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}