您现在的位置是:亿华云 > 知识

我在前端写Java SpringBoot项目

亿华云2025-10-04 03:06:09【知识】2人已围观

简介来源:京东云开发者前言玩归玩,闹归闹,别拿 C端 开玩笑!这里不推荐大家把Node服务作为C端服务,毕竟它是单线程多任务 机制。这一特性是 Javascript 语言设计之初,就决定了它的使命 - J

来源:京东云开发者

前言玩归玩,前端闹归闹,写Jt项别拿 C端 开玩笑!前端这里不推荐大家把Node服务作为C端服务,写Jt项毕竟它是前端单线程多任务 机制。这一特性是写Jt项 Javascript 语言设计之初,就决定了它的前端使命 - Java >>>【Script】,这里就不多解释了,写Jt项大家去看看 JavaScript 的前端历史就知道啦~这也就决定了,它不能像后端语言那样 多线程多任务,写Jt项用户访问量小还能承受,前端一旦承受访问量大高并发,写Jt项就得凉凉~那为什么我们还要去写 Node 服务?前端主要是方便快捷,对于小项目可以迅速完成建设,写Jt项开发成本小。前端其次,主要通过写 Nest 完成下面收获:学习装饰器语法,感受其简洁优美;自己学习一门新的开发框架,感受不同框架的优缺点,为以后开发选型打基础;感受服务端排查问题的复杂性,找找前端设计的灵感。本篇文章主要是使用 NestJs + Sequelize + MySQL  完成基础运行, 带大家了解 Node 服务端的基础搭建,云南idc服务商也可以顺便看看 Java SpringBoot 项目的基础结构,它俩真的非常相似,不信你去问服务端开发同学。第一步:项目跑起来在选择服务端的时候,我之前使用过 Egg.js ,所以这次就不选它了。其次,Egg 也是继承了 Koa 的开发基础,加上 Express 也是基于 Koa 上创新的,两者应该差不多,就不选择 Koa 和 Express 。所以,我想尝试下 Nest.js 看语法跟 Java 是一样的,加上之前也自己开发过 Java + SpringBoot 的项目,当然更古老的 SSH 2.0 也从无到有搭建过,即:Spring2.0 + Struts2+ Hibernate3.2,想想应该会很容易上手,顺便怀旧下写写。参考文档:https://www.geeksforgeeks.org/best-nodejs-frameworks-for-app-development/https://anywhere.epam.com/business/best-node-js-frameworks说下我的想法,首先我们刚入门,估计会有一堆不清楚的服务器托管坑,我们先简单点,后续我们再继续加深。既然要搞服务端,要搞就多搞点,我们都去尝鲜玩玩。我们打算使用 Nest 作为前端框架,Graphql 作为中间处理层。底层数据库我们用传统的 MySQL,比较稳定可靠,而且相对比较熟悉,这个就不玩新的了,毕竟数据库是一切的基石 。说下我们具体实现步骤:【必须】没有任何数据库,完成接口请求运行,能够跑起来;【必须】创建基础数据库 MySQL ,接入 @nestjs/sequelize 库 完成 增删改查 功能即:CRUD【可选】打算采取 Graphql 处理 API 查询,做到精确数据查询,这个已经火很久了,打算后续可以直接用到业务上。【可选】接入 Swagger 自动生成 API 文档,快捷进行前端与后端服务联调测试。Swagger是一个开源工具,用于设计、构建、记录和使用RESTful web服务。【可选】接口请求,数据库优化处理请求分流,源码库数据库写入加锁,处理并发流程增加 middleware 中间件统一处理请求及响应,进行鉴权处理,请求拦截等操作数据库分割备份,数据库容灾处理,分为:主、备、灾数据库读写分离,数据双写,建立数据库缓存机制,使用 redis 处理也欢迎大家补充更多的优化点,我们一起探讨~有兴趣可以帮忙补充代码哈~确定了大概方向,我们就开始整。先不追求一步到位,否则越多越乱,锦上添花的东西,我们可以后续增加,基础功能我们要优先保障完成。Nest.js 官网:https://docs.nestjs.com/ ,话不多说,我们直接开整。# 进入文件夹目录cd full-stack-demo/packages# 安装脚手架npm i -g @nestjs/cli# 创建基础项目nest new node-server-demo # 进入项目 cd new node-server-demo # 运行项目测试npm run start:dev我们移除一些不需要的东西,先简单再复杂,别把自己搞晕了。接下来写一个简单示例感受下这个框架,之后完整的代码,我会公布在后面。废话不多说,开整!调整后目录结构:common - 公用方法类config - 配置类文件controller - 控制器,用于处理前端发起的各类请求service - 服务类,用于处理与数据库交互逻辑dto - DTO(Data Transfer Object)可以用于验证输入数据、限制传输的字段或格式。entities - 实体类,用于描述对象相关的属性信息module - 模块,用于注册所有的服务类、控制器类,类似 Spring 里面的 bean

这里不能完全等同哈,两个实现机制上就不同,只是帮助大家理解。

main.ts - nest 启动入口

types - typescript 相关声明类型

只是写 demo, 搞快点就没有怎么写注释了,我感觉是一看就懂了,跟 Java SpringBoot 的写法非常一致,部分代码展示:控制器 controller// packages/node-server-demo/src/controller/user/index.tsimport { Controller, Get, Query } from @nestjs/common;import UserServices from @/service/user;import { GetUserDto, GetUserInfoDto } from @/dto/user;@Controller(user)export class UserController {  constructor(private readonly userService: UserServices) { }  // Get 请求 user/name?name=bricechou  @Get(name)  async findByName(@Query() getUserDto: GetUserDto) {    return this.userService.read.findByName(getUserDto.name);  }  // Get 请求 user/info?id=123  @Get(info)  async findById(@Query() getUserInfoDto: GetUserInfoDto) {    const user = await this.userService.read.findById(getUserInfoDto.id);    return { gender: user.gender, job: user.job };  }}// packages/node-server-demo/src/controller/log/add.tsimport { Controller, Post, Body } from @nestjs/common;import { AddLogDto } from @/dto/log;import LogServices from @/service/log;@Controller(log)export class CreateLogController {  constructor(private readonly logServices: LogServices) { }  // post(/log/add)  @Post(add)  create(@Body() createLogDto: AddLogDto) {    return this.logServices.create.create(createLogDto);  }}数据转换 Data Transfer Object// packages/node-server-demo/src/dto/user.tsexport class CreateUserDto {  name: string;  age: number;  gender: string;  job: string;}// 可以分开写,也可以合并export class GetUserDto {  id?: number;  name: string;}// 可以分开写,也可以合并export class GetUserInfoDto {  id: number;}service 数据库交互处理类// packages/node-server-demo/src/service/user/read.tsimport { Injectable } from @nestjs/common;import { User } from @/entities/User;@Injectable()export class ReadUserService {  constructor() { }  async findByName(name: string): Promise<User> {    // 可以处理判空,从数据库读取/写入数据,可能会被多个 controller 进行调用    console.info(ReadUserService findByName > , name);    return Promise.resolve({ id: 1, name, job: 程序员, gender: 1, age: 18 });  }  async findById(id: number): Promise<User> {    console.info(ReadUserService findById > , id);    return Promise.resolve({      id: 1,      name: BriceChou,      job: 程序员,      gender: 1,      age: 18,    });  }}module 模块注册,服务类/控制类// packages/node-server-demo/src/module/user.tsimport { Module } from @nestjs/common;import UserService, { ReadUserService } from @/service/user;import { UserController } from @/controller/user;@Module({  providers: [UserService, ReadUserService],  controllers: [UserController],})export class UserModule { }// packages/node-server-demo/src/module/index.ts 根模块注入import { Module } from @nestjs/common;import { UserModule } from ./user;import { LogModule } from ./log;@Module({  imports: [    UserModule,    LogModule,  ],})export class AppModule { }main.js 启动注册的所有类// packages/node-server-demo/src/main.tsimport { AppModule } from @/module;import { NestFactory } from @nestjs/core;import { NestExpressApplication } from @nestjs/platform-express;async function bootstrap() {  const app = await NestFactory.create<NestExpressApplication>(AppModule);  // 监听端口 3000  await app.listen(3000);}bootstrap();这样一个单机的服务器就启动起来了,我们可以使用 Postwoman [https://hoppscotch.io/] 进行请求,瞅瞅看返回效果。控制台也收到日志了,后面可以把这些日志请求保留成 .log 文件,这样请求日志也有了,完美!下一步,我们开始连接数据库,这样就不用单机玩泥巴了~第二步:配置MySQLMySQL 安装其实很简单,我电脑是 Mac 的,所以下面的截图都是以 mac 为例,先下载对应的数据库。下载地址:https://dev.mysql.com/downloads/mysql/ 至于其他系统的,可以网上找教程,这个应该烂大街了,我就不重复搬运教程了。注意:安装的数据库,一定要设置密码,连接数据库必须要有密码,否则会导致连接数据库失败。MySQL 我们只安装数据库就行,熟悉指令的童鞋,就直接命令行操作就行。不熟悉的话,那就下载图形化管理工具。Mysql 官方控制台 https://dev.mysql.com/downloads/workbench/Windows 也可以使用 https://www.heidisql.com/download.php?download=installerPS:安装 workbench 时发现要求 MacOS 13以上,我的电脑是 MacOS 12。白白下载,所以只能 https://downloads.mysql.com/archives/workbench/ 从归档里面找低版本 8.0.31。对于数据库服务也有版本要求,大家按照自己电脑版本,选择支持的版本即可。https://downloads.mysql.com/archives/community/。我这边选择的是默认最新版本:8.0.34,下载好直接安装,一路 Next 到底,记住自己输入的 Root 密码!!!确认好当前数据库是否已经运行起来了,启动 Workbench 查看状态。1.创建数据库数据库存在字符集选择,不同的字符集和校验规则,会对存储数据产生影响,所以大家可以自行查询,按照自己存储数据原则选择,我这里默认选最广泛的。确认好,就选择右下角的应用按钮。2.创建表和属性选项解答:PRIMARY KEY 是表中的一个或多个列的组合,它用于唯一标识表中的每一行。Not NULL 和 Unique 就不解释,就是直译的那个意思。GENERATED 生成列是表中的一种特殊类型的列,它的值不是从插入语句中获取的,而是根据其他列的值通过一个表达式或函数生成的。CREATE TABLE people (    first_name VARCHAR(100),    last_name VARCHAR(100),    full_name VARCHAR(200) AS (CONCAT(first_name, , last_name)));UNSIGNED 这个数值类型就只能存储正数(包括零),不会存储负数。ZEROFILL 将数值类型的字段的前面填充零,他会自动使字段变为 UNSIGNED,直到该字段达到声明的长度,如:00007BINARY 用于存储二进制字符串,如声明一个字段为 BINARY(5),那么存储在这个字段中的字符串都将被处理为长度为 5 的二进制字符串。◦如尝试存储一个长度为 3 的字符串,那么它将在右侧用两个空字节填充。◦如果你尝试存储一个长度为 6 的字符串,那么它将被截断为长度为 5◦主要用途是存储那些需要按字节进行比较的数据,例如加密哈希值此外也可顺手创建一个索引,方便快速查找。CREATE TABLE `rrweb`.`test_sys_req_log` (  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,  `content` TEXT NOT NULL,  `l_level` INT UNSIGNED NOT NULL,  `l_category` VARCHAR(255) NOT NULL,  `l_created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,  `l_updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,  PRIMARY KEY (`id`),  UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,  INDEX `table_index` (`l_level` ASC, `l_category` ASC, `l_time` ASC) VISIBLE);3.连接数据库由于目前 node-oracledb 官方尚未提供针对 Apple Silicon 架构的预编译二进制文件。导致我们无法在 Mac M1 芯片上使用 TypeORM 链接数据库操作,它目前只支持 Mac x86 芯片。哎~折腾老半天,查阅各种文档,居然有这个坑,没关系我们换个方式打开。我们不得不放弃,从而选用 https://docs.nestjs.com/techniques/database#sequelize-integration 哐哐哐~一顿操作猛如虎,盘它!安装 Sequelize# 安装连接库npm install --save @nestjs/sequelize sequelize sequelize-typescript mysql2# 安装 typenpm install --save-dev @types/sequelize配置数据库基础信息// packages/node-server-demo/src/module/index.tsimport { Module } from @nestjs/common;import { UserModule } from ./user;import { LogModule } from ./log;import { Log } from @/entities/Log;import { SequelizeModule } from @nestjs/sequelize;@Module({  imports: [    SequelizeModule.forRoot({      dialect: mysql,      // 按数据库实际配置      host: 127.0.0.1,      // 按数据库实际配置      port: 3306,      // 按数据库实际配置      username: root,      // 按数据库实际配置      password: hello,      // 按数据库实际配置      database: world,      synchronize: true,      models: [Log],      autoLoadModels: true,    }),    LogModule,    UserModule,  ],})export class AppModule { }实体与数据库一一映射处理import { getNow } from @/common/date;import {  Model,  Table,  Column,  PrimaryKey,  DataType,} from sequelize-typescript;@Table({ tableName: test_sys_req_log })export class Log extends Model<Log> {  @PrimaryKey  @Column({    type: DataType.INTEGER,    autoIncrement: true,    field: id,  })  id: number;  @Column({ field: content, type: DataType.TEXT })  content: string;  @Column({ field: l_level, type: DataType.INTEGER })  level: number; // 3严重,2危险,1轻微  @Column({ field: l_category })  category: string; // 模块分类/来源分类  @Column({    field: l_created_at,    type: DataType.NOW,    defaultValue: getNow(),  })  createdAt: number;  @Column({    field: l_updated_at,    type: DataType.NOW,    defaultValue: getNow(),  })  updatedAt: number;}module 注册实体// packages/node-server-demo/src/module/log.tsimport { Module } from @nestjs/common;import { SequelizeModule } from @nestjs/sequelize;import { Log } from @/entities/Log;import LogServices, {  CreateLogService,  UpdateLogService,  DeleteLogService,  ReadLogService,} from @/service/log;import {  CreateLogController,  RemoveLogController,  UpdateLogController,} from @/controller/log;@Module({  imports: [SequelizeModule.forFeature([Log])],  providers: [    LogServices,    CreateLogService,    UpdateLogService,    DeleteLogService,    ReadLogService,  ],  controllers: [CreateLogController, RemoveLogController, UpdateLogController],})export class LogModule { }service 操作数据库处理数据import { Log } from @/entities/Log;import { Injectable } from @nestjs/common;import { AddLogDto } from @/dto/log;import { InjectModel } from @nestjs/sequelize;import { ResponseStatus } from @/types/BaseResponse;import { getErrRes, getSucVoidRes } from @/common/response;@Injectable()export class CreateLogService {  constructor(    @InjectModel(Log)    private logModel: typeof Log,) { }  async create(createLogDto: AddLogDto): Promise<ResponseStatus<null>> {    console.info(CreateLogService create > , createLogDto);    const { level = 1, content = , category = INFO } = createLogDto || { };    const str = content.trim();    if (!str) {      return getErrRes(500, 日志内容为空);    }    const item = {      level,      category,      // Tips: 为防止外部数据进行数据注入,我们可以对内容进行 encode 处理。      // content: encodeURIComponent(str),      content: str,    };    await this.logModel.create(item);    return getSucVoidRes();  }}一路操作猛如虎,回头一看嘿嘿嘿~终于,我们收到了来自外界的第一条数据!hello world!连接及创建数据成功!此时已经完成基础功能啦~第三步:实现CRUD基础功能剩下的内容,其实大家可以自行脑补了,就是调用数据库的操作逻辑。先说说什么是 CRUDC create 创建R read 读取U update 更新D delete 删除下面给个简单示例,大家看看,剩下就去找文档,实现业务逻辑即可:import { Injectable } from @nestjs/common;import { InjectModel } from @nestjs/sequelize;import { User } from ./user.model;@Injectable()export class UserService {  constructor(    @InjectModel(User)    private userModel: typeof User,) { }  // 创建新数据  async create(user: User) {    const newUser = await this.userModel.create(user);    return newUser;  }  // 查找所有数据  async findAll() {    return this.userModel.findAll();  }  // 按要求查找单个  async findOne(id: string) {    return this.userModel.findOne({ where: { id } });  }  // 按要求更新  async update(id: string, user: User) {    await this.userModel.update(user, { where: { id } });    return this.userModel.findOne({ where: { id } });  }  // 按要求删除  async delete(id: string) {    const user = await this.userModel.findOne({ where: { id } });    await user.destroy();  }}Tips: 进行删除的时候,我们可以进行假删除,两个数据库,一个是备份数据库,一个是主数据库。主数据库可以直接删除或者增加标识表示删除。备份数据库,可以不用删除只写入和更新操作,这样可以进行数据还原操作。此外,为了防止 SQL 数据库注入,大家需要对数据来源进行统一校验处理或者直接进行 encode 处理,对于重要数据可以直接进行 MD5 加密处理,防止数据库被直接下载泄露。关于 SQL 数据库的安全处理,网上教程有很多,大家找一找就可以啦~部署就比较简单了,我们就不需要一一赘述了,数据库可以用集团提供的云数据库,而 Nest 就是普通的 node 部署。-end-

很赞哦!(826)