今天这篇保姆级教程,手把手教你用NestJS + TypeORM 自定义数据源,实现一套代码无缝切换达梦数据库和MySQL!全程实战代码,从环境搭建到CRUD完整示例,让你在国产化项目中既能享受达梦的合规性,又能保留MySQL的开发便利性。
数据库连接模块可以打包直接复用。
如果你觉得有用,请关注、点赞👍、收藏🌟、评论💬支持一下!文末还有粉丝福利!
1. 先搞懂核心原理
NestJS 本身不实现 ORM,而是通过模块化集成 TypeORM。但官方@nestjs/typeorm模块对达梦这种非原生数据库支持不够友好,容易遇到oracledb驱动冲突、方言包补丁失效等问题。
我们的解决方案:
放弃
@nestjs/typeorm,改用自定义提供者手动创建数据源。通过环境变量
DB_TYPE动态选择创建MySQL 数据源(DataSource)或达梦数据源(DmdbDataSource)。将创建好的数据源实例通过
'DATA_SOURCE'令牌注入到各个服务中。
优势:
✅ 彻底避免驱动冲突,连接更稳定
✅ 一套 Service 代码,无需修改即可操作两种数据库
✅ 完全掌控数据库连接生命周期
2. 环境准备与依赖安装
Node.js 版本要求
推荐 Node.js 16.x 或 18.x LTS(达梦驱动对高版本支持不完善)
可用
nvm或nvm-windows管理版本
创建项目并安装依赖
bash
# 创建 NestJS 项目 nest new nest-dm-mysql-demo cd nest-dm-mysql-demo # 安装必要依赖(注意:不安装 @nestjs/typeorm) npm install @nestjs/config typeorm mysql2 dmdb typeorm-dm reflect-metadata依赖说明:
@nestjs/config:读取环境变量typeorm:TypeORM 核心(被typeorm-dm依赖)mysql2:MySQL 驱动dmdb:达梦官方 Node.js 驱动typeorm-dm:达梦方言包(提供DmdbDataSource)
3. 配置环境变量(核心!)
在项目根目录创建.env文件,通过DB_TYPE控制使用哪种数据库:
env
# 数据库类型:mysql 或 dm DB_TYPE=mysql # 通用数据库连接信息 DB_HOST=localhost DB_USERNAME=root DB_PASSWORD=123456 # MySQL 专用配置 MYSQL_PORT=3306 MYSQL_DB=test_db # 达梦专用配置 DM_PORT=5236 DM_SCHEMA=TEST # 达梦模式名,通常与用户名相同切换数据库只需修改DB_TYPE这一个变量!
4. 数据库模块:动态创建数据源(关键代码)
创建src/database/database.module.ts,这是整个方案的核心:
import { Module, Global, Logger } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { DataSource } from 'typeorm'; import { DmdbDataSource } from 'typeorm-dm'; import { User } from '../user/user.entity'; @Global() @Module({ imports: [ConfigModule], providers: [ { provide: 'DATA_SOURCE', useFactory: async (configService: ConfigService) => { const logger = new Logger('DatabaseModule'); const dbType = configService.get<string>('DB_TYPE'); let dataSource: DataSource; if (dbType === 'mysql') { // MySQL 数据源 dataSource = new DataSource({ type: 'mysql', host: configService.get<string>('DB_HOST'), port: configService.get<number>('MYSQL_PORT'), username: configService.get<string>('DB_USERNAME'), password: configService.get<string>('DB_PASSWORD'), database: configService.get<string>('MYSQL_DB'), entities: [User], synchronize: true, // 开发环境使用,生产请关闭 logging: false, }); } else if (dbType === 'dm') { // 达梦数据源 dataSource = new DmdbDataSource({ type: 'oracle', innerType: 'dmdb', host: configService.get<string>('DB_HOST'), port: configService.get<number>('DM_PORT'), username: configService.get<string>('DB_USERNAME'), password: configService.get<string>('DB_PASSWORD'), schema: configService.get<string>('DM_SCHEMA'), entities: [User], synchronize: true, logging: false, extra: { connectTimeout: 30000, loginEncrypt: false, // 避免达梦加密错误 }, }); } else { throw new Error(`不支持的数据库类型: ${dbType}`); } await dataSource.initialize(); logger.log(`${dbType} 数据库连接成功!`); return dataSource; }, inject: [ConfigService], }, ], exports: ['DATA_SOURCE'], }) export class DatabaseModule {}要点说明:
type: 'oracle'和innerType: 'dmdb'是达梦方言包的固定写法。loginEncrypt: false用于解决达梦加密错误[6071]。entities数组注册了所有实体,可随时添加。
5. 定义用户实体
创建src/user/user.entity.ts:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('t_user') export class User { @PrimaryGeneratedColumn() id: number; @Column({ length: 50 }) name: string; @Column({ type: 'int', nullable: true }) age: number; }注意:实体定义完全兼容两种数据库,无需任何修改。
6. 用户模块与服务
用户模块src/user/user.module.ts
typescript
import { Module } from '@nestjs/common'; import { UserService } from './user.service'; import { UserController } from './user.controller'; @Module({ providers: [UserService], controllers: [UserController], }) export class UserModule {}用户服务src/user/user.service.ts
typescript
import { Injectable, Inject, NotFoundException } from '@nestjs/common'; import { DataSource, Repository } from 'typeorm'; import { User } from './user.entity'; @Injectable() export class UserService { private userRepository: Repository<User>; constructor(@Inject('DATA_SOURCE') private dataSource: DataSource) { this.userRepository = this.dataSource.getRepository(User); } async create(userData: Partial<User>): Promise<User> { const user = this.userRepository.create(userData); return this.userRepository.save(user); } async findAll(): Promise<User[]> { return this.userRepository.find(); } async findOne(id: number): Promise<User | null> { return this.userRepository.findOneBy({ id }); } async update(id: number, updateData: Partial<User>): Promise<User> { const user = await this.findOne(id); if (!user) { throw new NotFoundException(`用户 ID ${id} 不存在`); } Object.assign(user, updateData); return this.userRepository.save(user); } async remove(id: number): Promise<void> { const result = await this.userRepository.delete(id); if (result.affected === 0) { throw new NotFoundException(`用户 ID ${id} 不存在`); } } }关键点:服务中注入的是DataSource类型,无论底层是 MySQL 还是达梦,都能正确获取Repository。
用户控制器src/user/user.controller.ts
typescript
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'; import { UserService } from './user.service'; import { User } from './user.entity'; @Controller('users') export class UserController { constructor(private readonly userService: UserService) {} @Post() create(@Body() userData: Partial<User>): Promise<User> { return this.userService.create(userData); } @Get() findAll(): Promise<User[]> { return this.userService.findAll(); } @Get(':id') findOne(@Param('id') id: string): Promise<User | null> { return this.userService.findOne(+id); } @Put(':id') update(@Param('id') id: string, @Body() updateData: Partial<User>): Promise<User> { return this.userService.update(+id, updateData); } @Delete(':id') remove(@Param('id') id: string): Promise<void> { return this.userService.remove(+id); } }7. 根模块整合
修改src/app.module.ts,导入数据库模块和用户模块:
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { DatabaseModule } from './database/database.module'; import { UserModule } from './user/user.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env', }), DatabaseModule, UserModule, ], }) export class AppModule {}8. 运行测试
启动应用
bash
npm run start:dev
看到如下日志表示连接成功:
text
[Nest] ... LOG [DatabaseModule] mysql 数据库连接成功! 或 [Nest] ... LOG [DatabaseModule] dm 数据库连接成功!
测试 CRUD 接口
| 方法 | 路径 | 功能 | 请求体示例 |
|---|---|---|---|
| POST | /users | 创建用户 | {"name": "张三", "age": 30} |
| GET | /users | 获取所有用户 | - |
| GET | /users/:id | 获取单个用户 | - |
| PUT | /users/:id | 更新用户 | {"age": 35} |
| DELETE | /users/:id | 删除用户 | - |
使用 Postman 或 curl 测试:
bash
# 创建用户 curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name":"李四","age":25}' # 查询所有用户 curl http://localhost:3000/users9. 常见问题与解决方案
❌ 问题1:达梦连接时报[6071] 消息加密失败
解决:在达梦配置的extra中添加loginEncrypt: false。
❌ 问题2:启动后仍提示Oracle package not found
原因:typeorm-dm的补丁未生效,或错误地安装了@nestjs/typeorm。
解决:确保卸载@nestjs/typeorm,并删除node_modules重新安装。
❌ 问题3:MySQL 连接时区问题
解决:在 MySQL 配置中添加timezone: 'Z'或timezone: '+08:00'。
❌ 问题4:Node.js 版本过高导致编译失败
解决:降级到 Node.js 16 或 18 LTS。
如果你觉得有用,请点赞👍、收藏🌟、评论💬支持一下!文末还有粉丝福利!