nestjs实战(五): 连接达梦数据库的两种方式(完整实战教程)
还在为 NestJS 项目接入达梦数据库发愁想用 TypeORM 优雅操作国产数据库却不知道如何配置今天这篇教程全程围绕 NestJS 与达梦数据库的集成展开从原生驱动连接到 TypeORM 整合详细解释每一步流程和核心代码并以 DMHR.CITY 表为例进行完整演示。1. 先搞懂为什么需要两种方式连接达梦达梦数据库是国内主流的国产关系型数据库但在 Node.js 生态中官方驱动dmdb是 C 插件TypeScript 类型支持不完善。NestJS 作为企业级框架追求代码规范与类型安全这就产生了两种常见方案方案适用场景优点缺点原生驱动方案简单查询、快速原型轻量、直接、无额外依赖类型不安全需手动管理连接TypeORM 方案复杂业务、团队协作ORM 映射、类型安全、迁移方便需配置typeorm-dm扩展2. 准备工作达梦数据库环境搭建2.1 安装达梦数据库从达梦官网下载对应操作系统的 DM8 安装包完成安装。本地测试可使用 Dockerbashdocker run -d -p 5236:5236 \ -e DM_USERSYSDBA \ -e DM_PASSWORDAa123456 \ --name dm8 \ dm8:latest2.2 确认表结构达梦数据库自带示例模式 DMHR包含 CITY 表和 REGION 表REGION 表地区表sqlCREATE TABLE DMHR.REGION ( REGION_ID INT PRIMARY KEY, REGION_NAME VARCHAR(50) ); INSERT INTO DMHR.REGION VALUES (1, 华北地区); INSERT INTO DMHR.REGION VALUES (2, 华东地区); INSERT INTO DMHR.REGION VALUES (3, 华南地区);CITY 表城市表REGION_ID 为外键2.3 安装依赖bashnpm install dmdb typeorm nestjs/typeorm reflect-metadata typeorm-dm注意dmdb是 C 插件需要 Node.js 编译环境Python、Visual Studio Build Tools 或 g。3. 方案一原生驱动连接详细流程3.1 核心代码解析javascriptconst dmdb require(dmdb); class NativeDbService { constructor() { this.logger new Logger(NativeDbService); this.pool null; } async onModuleInit() { try { // 【关键点1】解决 OpenSSL 兼容性问题 process.env.NODE_OPTIONS --openssl-legacy-provider; // 【关键点2】创建连接池 this.pool await dmdb.createPool({ connectionString: dm://SYSDBA:Aa123456localhost:5236/DAMENG?schemaDMHR, poolMax: 10, // 最大连接数 poolMin: 1, // 最小连接数 poolTimeout: 60, // 连接超时时间秒 }); // 【关键点3】测试连接 let testConn; try { testConn await this.pool.getConnection(); this.logger.log(✅ 原生驱动连接成功DMHR 模式); } finally { if (testConn) testConn.release(); // 必须释放连接 } } catch (error) { this.logger.error(❌ 原生驱动连接失败, error); throw error; } } // 【关键点4】获取连接 async getConnection() { return await this.pool.getConnection(); } // 【关键点5】执行 SQL 查询自动释放连接 async query(sql, params []) { const conn await this.getConnection(); try { if (params params.length 0) { const result await conn.execute(sql, params); return result; } else { const result await conn.execute(sql); return result; } } finally { conn.release(); // 无论成功失败都要释放连接 } } // 【关键点6】解析查询结果 async getAllCities() { const sql SELECT * FROM DMHR.CITY ORDER BY CITY_ID; const result await this.query(sql); // 达梦驱动返回的结果格式{ metaData: [...], rows: [...] } if (result result.rows) { const cities result.rows.map(row { const city {}; result.metaData.forEach((col, index) { city[col.name] row[index]; }); return city; }); return cities; } return result; } async onModuleDestroy() { if (this.pool) await this.pool.close(); // 关闭连接池 } }3.2 流程详解步骤1导入驱动使用require(dmdb)而非import因为dmdb没有 TypeScript 类型定义这样可以完全避开 TypeScript 的类型检查步骤2创建连接池createPool是异步方法返回连接池实例connectionString格式dm://用户名:密码主机:端口/数据库?schema模式名连接池参数poolMax最大连接数根据业务并发量设置poolMin最小连接数保持一定数量的活跃连接poolTimeout获取连接的超时时间步骤3测试连接从连接池获取一个连接执行简单查询验证连接有效性重要使用try/finally确保连接被释放步骤4执行查询从连接池获取连接执行 SQL 语句必须释放连接否则连接池会耗尽步骤5结果解析达梦驱动返回{ metaData, rows }格式metaData字段信息数组每个元素有name属性rows数据行数组每行是字段值的数组需要手动转换为对象数组4. 方案二TypeORM 整合详细流程4.1 核心代码解析javascriptconst { DmdbDataSource } require(typeorm-dm); const { EntitySchema } require(typeorm); require(reflect-metadata); // 【关键点1】定义实体使用 EntitySchema const CitySchema new EntitySchema({ name: City, // 实体名称 tableName: CITY, // 数据库表名 schema: DMHR, // 数据库模式名 columns: { CITY_ID: { primary: true, // 主键 type: varchar, // 字段类型 length: 3, // 长度限制 name: CITY_ID // 数据库字段名 }, CITY_NAME: { type: varchar, length: 50, name: CITY_NAME }, REGION_ID: { type: int, name: REGION_ID } } }); // 【关键点2】配置数据源 const typeormConfig { type: oracle, // 达梦兼容 Oracle 协议 innerType: dmdb, // 指定为达梦驱动 host: localhost, port: 5236, username: SYSDBA, password: Aa123456, schema: DMHR, // 默认模式 entities: [CitySchema, RegionSchema], synchronize: false, // 生产环境必须为 false logging: true, // 打印 SQL 日志 extra: { connectTimeout: 30000, } }; // 【关键点3】创建数据源实例 const AppDataSource new DmdbDataSource(typeormConfig); class TypeormDbService { constructor() { this.logger new Logger(TypeormDbService); this.dataSource null; } async onModuleInit() { try { // 【关键点4】初始化数据源 await AppDataSource.initialize(); this.dataSource AppDataSource; this.logger.log(✅ TypeORM 连接成功); // 测试连接 const cityRepo this.dataSource.getRepository(City); const cityCount await cityRepo.count(); this.logger.log(CITY 表中共有 ${cityCount} 条记录); } catch (error) { this.logger.error(❌ TypeORM 连接失败, error); throw error; } } // 【关键点5】使用 Repository 进行 CRUD async getAllCities() { const cityRepo this.dataSource.getRepository(City); return await cityRepo.find({ order: { CITY_ID: ASC } }); } async createCity(cityData) { const cityRepo this.dataSource.getRepository(City); const newCity cityRepo.create(cityData); return await cityRepo.save(newCity); } async onModuleDestroy() { if (this.dataSource) await this.dataSource.destroy(); } }4.2 流程详解步骤1定义实体EntitySchema 方式name实体名称用于getRepository(City)获取tableName数据库实际表名注意大小写schema数据库模式名达梦区分模式columns字段映射指定数据库字段名与实体属性的对应关系步骤2配置数据源type: oracle达梦兼容 Oracle 协议innerType: dmdb指定使用达梦驱动synchronize: false生产环境必须关闭避免误操作表结构logging: true开启后控制台打印执行的 SQL 语句便于调试步骤3初始化数据源initialize()是异步方法会建立数据库连接只有初始化成功后才能进行后续操作步骤4获取 RepositorygetRepository(City)根据实体名称获取操作对象Repository 提供find、findOne、save、update、delete等方法步骤5CRUD 操作create()创建实体实例不保存到数据库save()保存实体插入或更新find()查询多条记录findOne()查询单条记录update()更新记录delete()删除记录5. 完整代码运行/** * NestJS 连接达梦数据库的两种方式单文件实现 * 操作表DMHR.CITY实际表结构CITY_ID CHAR(3), CITY_NAME VARCHAR(50), REGION_ID INT * 注意REGION_ID 是外键必须存在于 DMHR.REGION 表中 */ const { Injectable, Logger } require(nestjs/common); // 方式一原生驱动连接 const dmdb require(dmdb); class NativeDbService { constructor() { this.logger new Logger(NativeDbService); this.pool null; } async onModuleInit() { try { process.env.NODE_OPTIONS --openssl-legacy-provider; this.pool await dmdb.createPool({ connectionString: dm://SYSDBA:Aa123456localhost:5236/DAMENG?schemaDMHR, poolMax: 10, poolMin: 1, poolTimeout: 60, }); let testConn; try { testConn await this.pool.getConnection(); this.logger.log(✅ 原生驱动连接成功DMHR 模式); } finally { if (testConn) testConn.release(); } } catch (error) { this.logger.error(❌ 原生驱动连接失败, error); throw error; } } async getConnection() { return await this.pool.getConnection(); } async onModuleDestroy() { if (this.pool) await this.pool.close(); } async query(sql, params []) { const conn await this.getConnection(); try { if (params params.length 0) { const result await conn.execute(sql, params); return result; } else { const result await conn.execute(sql); return result; } } finally { conn.release(); } } // 查询 REGION 表获取有效的 REGION_ID async getAllRegions() { const sql SELECT * FROM DMHR.REGION ORDER BY REGION_ID; const result await this.query(sql); if (result result.rows) { const regions result.rows.map(row { const region {}; result.metaData.forEach((col, index) { region[col.name] row[index]; }); return region; }); return regions; } return []; } // CITY 表专用方法 async getAllCities() { const sql SELECT * FROM DMHR.CITY ORDER BY CITY_ID; const result await this.query(sql); if (result result.rows) { const cities result.rows.map(row { const city {}; result.metaData.forEach((col, index) { city[col.name] row[index]; }); return city; }); return cities; } return result; } async getCityById(cityId) { const sql SELECT * FROM DMHR.CITY WHERE CITY_ID ${cityId}; const result await this.query(sql); if (result result.rows result.rows.length 0) { const city {}; result.metaData.forEach((col, index) { city[col.name] result.rows[0][index]; }); return city; } return null; } async getCitiesByRegion(regionId) { const sql SELECT * FROM DMHR.CITY WHERE REGION_ID ${regionId} ORDER BY CITY_ID; const result await this.query(sql); if (result result.rows) { const cities result.rows.map(row { const city {}; result.metaData.forEach((col, index) { city[col.name] row[index]; }); return city; }); return cities; } return []; } async createCity(cityData) { if (cityData.CITY_ID.length 3) { throw new Error(CITY_ID 长度不能超过 3 个字符当前: ${cityData.CITY_ID}); } const sql INSERT INTO DMHR.CITY (CITY_ID, CITY_NAME, REGION_ID) VALUES (${cityData.CITY_ID}, ${cityData.CITY_NAME}, ${cityData.REGION_ID}); const result await this.query(sql); return result; } async updateCity(cityId, cityData) { const sql UPDATE DMHR.CITY SET CITY_NAME ${cityData.CITY_NAME}, REGION_ID ${cityData.REGION_ID} WHERE CITY_ID ${cityId}; const result await this.query(sql); return result; } async deleteCity(cityId) { const sql DELETE FROM DMHR.CITY WHERE CITY_ID ${cityId}; const result await this.query(sql); return result; } } // 方式二TypeORM 整合 const { DmdbDataSource } require(typeorm-dm); const { EntitySchema } require(typeorm); require(reflect-metadata); // 使用 EntitySchema 定义 City 实体 const CitySchema new EntitySchema({ name: City, tableName: CITY, schema: DMHR, columns: { CITY_ID: { primary: true, type: varchar, length: 3, name: CITY_ID }, CITY_NAME: { type: varchar, length: 50, name: CITY_NAME }, REGION_ID: { type: int, name: REGION_ID } } }); // Region 实体用于查询有效的外键值 const RegionSchema new EntitySchema({ name: Region, tableName: REGION, schema: DMHR, columns: { REGION_ID: { primary: true, type: int, name: REGION_ID }, REGION_NAME: { type: varchar, length: 50, name: REGION_NAME } } }); // TypeORM 数据源配置 const typeormConfig { type: oracle, innerType: dmdb, host: localhost, port: 5236, username: SYSDBA, password: Aa123456, schema: DMHR, entities: [CitySchema, RegionSchema], synchronize: false, logging: true, extra: { connectTimeout: 30000, } }; // 创建 TypeORM 数据源实例 const AppDataSource new DmdbDataSource(typeormConfig); // TypeORM 服务封装 class TypeormDbService { constructor() { this.logger new Logger(TypeormDbService); this.dataSource null; } async onModuleInit() { try { await AppDataSource.initialize(); this.dataSource AppDataSource; this.logger.log(✅ TypeORM 连接成功DMHR 模式); const regionRepo this.dataSource.getRepository(Region); const regionCount await regionRepo.count(); this.logger.log(REGION 表中共有 ${regionCount} 条记录); const cityRepo this.dataSource.getRepository(City); const cityCount await cityRepo.count(); this.logger.log(CITY 表中共有 ${cityCount} 条记录); } catch (error) { this.logger.error(❌ TypeORM 连接失败, error); throw error; } } async onModuleDestroy() { if (this.dataSource) await this.dataSource.destroy(); } // CITY 表专用方法 async getAllCities() { const cityRepo this.dataSource.getRepository(City); return await cityRepo.find({ order: { CITY_ID: ASC } }); } async getCityById(cityId) { const cityRepo this.dataSource.getRepository(City); return await cityRepo.findOne({ where: { CITY_ID: cityId } }); } async getCitiesByRegion(regionId) { const cityRepo this.dataSource.getRepository(City); return await cityRepo.find({ where: { REGION_ID: regionId }, order: { CITY_ID: ASC } }); } async createCity(cityData) { if (cityData.CITY_ID.length 3) { throw new Error(CITY_ID 长度不能超过 3 个字符当前: ${cityData.CITY_ID}); } const cityRepo this.dataSource.getRepository(City); const newCity cityRepo.create(cityData); return await cityRepo.save(newCity); } async updateCity(cityId, cityData) { const cityRepo this.dataSource.getRepository(City); await cityRepo.update(cityId, cityData); return await this.getCityById(cityId); } async deleteCity(cityId) { const cityRepo this.dataSource.getRepository(City); const result await cityRepo.delete(cityId); return result.affected 0; } // REGION 表专用方法 async getAllRegions() { const regionRepo this.dataSource.getRepository(Region); return await regionRepo.find({ order: { REGION_ID: ASC } }); } async getRegionById(regionId) { const regionRepo this.dataSource.getRepository(Region); return await regionRepo.findOne({ where: { REGION_ID: regionId } }); } } // 业务服务 class CityService { constructor(nativeDbService, typeormDbService) { this.nativeDbService nativeDbService; this.typeormDbService typeormDbService; this.logger new Logger(CityService); } async queryCitiesWithNative() { try { this.logger.log(使用原生驱动查询所有城市...); const cities await this.nativeDbService.getAllCities(); this.logger.log(原生驱动查询到 ${cities.length} 个城市); return cities; } catch (error) { this.logger.error(原生驱动查询失败, error); throw error; } } async queryCitiesWithTypeorm() { try { this.logger.log(使用 TypeORM 查询所有城市...); const cities await this.typeormDbService.getAllCities(); this.logger.log(TypeORM 查询到 ${cities.length} 个城市); return cities; } catch (error) { this.logger.error(TypeORM 查询失败, error); throw error; } } async getValidRegions() { try { this.logger.log(查询有效的地区列表...); const regions await this.typeormDbService.getAllRegions(); return regions; } catch (error) { this.logger.error(查询地区失败, error); throw error; } } async createCityWithTypeorm(cityData) { try { this.logger.log(使用 TypeORM 创建城市${cityData.CITY_NAME}); const newCity await this.typeormDbService.createCity(cityData); this.logger.log(创建成功ID${newCity.CITY_ID}); return newCity; } catch (error) { this.logger.error(TypeORM 创建城市失败, error); throw error; } } async deleteCityWithTypeorm(cityId) { try { this.logger.log(使用 TypeORM 删除城市 ID: ${cityId}); const success await this.typeormDbService.deleteCity(cityId); this.logger.log(success ? 删除成功 : 删除失败可能不存在); return success; } catch (error) { this.logger.error(TypeORM 删除城市失败, error); throw error; } } } // 测试函数 async function testNativeDriver() { console.log(\n 测试方式一原生驱动DMHR.CITY ); const nativeService new NativeDbService(); try { await nativeService.onModuleInit(); // 0. 先查询有效的 REGION_ID console.log(\n--- 0. 查询有效的地区列表 ---); const regions await nativeService.getAllRegions(); console.log(有效地区, regions); // 使用第一个有效的 REGION_ID const validRegionId regions.length 0 ? regions[0].REGION_ID : 1; console.log(使用地区 ID: ${validRegionId}); // 1. 查询所有城市 console.log(\n--- 1. 查询所有城市 ---); const allCities await nativeService.getAllCities(); console.log(所有城市, allCities); // 2. 根据 ID 查询城市 console.log(\n--- 2. 根据 ID 查询城市 ---); const city await nativeService.getCityById(BJ); console.log(IDBJ 的城市, city); // 3. 根据地区查询城市 console.log(\n--- 3. 根据地区查询城市 ---); const regionCities await nativeService.getCitiesByRegion(validRegionId); console.log(地区 ID${validRegionId} 的城市, regionCities); // 4. 新增测试城市使用有效的 REGION_ID console.log(\n--- 4. 新增测试城市 ---); const testCityId T1; await nativeService.createCity({ CITY_ID: testCityId, CITY_NAME: 测试城市, REGION_ID: validRegionId }); console.log(新增城市成功ID: ${testCityId}); // 5. 查询验证新增 console.log(\n--- 5. 验证新增 ---); const testCity await nativeService.getCityById(testCityId); console.log(新增的城市, testCity); // 6. 删除城市 console.log(\n--- 6. 删除城市 ---); await nativeService.deleteCity(testCityId); console.log(删除成功); // 7. 验证删除 console.log(\n--- 7. 验证删除 ---); const deletedCity await nativeService.getCityById(testCityId); console.log(删除后的查询结果, deletedCity); } catch (error) { console.error(原生驱动测试失败, error); } finally { await nativeService.onModuleDestroy(); } } async function testTypeorm() { console.log(\n 测试方式二TypeORMDMHR.CITY ); const typeormService new TypeormDbService(); try { await typeormService.onModuleInit(); // 0. 获取有效的地区列表 console.log(\n--- 0. 查询有效的地区列表 ---); const regions await typeormService.getAllRegions(); console.log(有效地区, regions); const validRegionId regions.length 0 ? regions[0].REGION_ID : 1; console.log(使用地区 ID: ${validRegionId}); // 1. 查询所有城市 console.log(\n--- 1. 查询所有城市 ---); const allCities await typeormService.getAllCities(); console.log(所有城市, allCities); // 2. 根据 ID 查询城市 console.log(\n--- 2. 根据 ID 查询城市 ---); const city await typeormService.getCityById(BJ); console.log(IDBJ 的城市, city); // 3. 根据地区查询城市 console.log(\n--- 3. 根据地区查询城市 ---); const regionCities await typeormService.getCitiesByRegion(validRegionId); console.log(地区 ID${validRegionId} 的城市, regionCities); // 4. 新增测试城市使用有效的 REGION_ID console.log(\n--- 4. 新增测试城市 ---); const testCityId T2; const newCity await typeormService.createCity({ CITY_ID: testCityId, CITY_NAME: TypeORM测试城市, REGION_ID: validRegionId }); console.log(新增城市, newCity); // 5. 删除城市 console.log(\n--- 5. 删除城市 ---); const deleted await typeormService.deleteCity(testCityId); console.log(deleted ? 删除成功 : 删除失败); // 6. 验证删除 console.log(\n--- 6. 验证删除 ---); const finalCity await typeormService.getCityById(testCityId); console.log(删除后的查询结果, finalCity); } catch (error) { console.error(TypeORM 测试失败, error); } finally { await typeormService.onModuleDestroy(); } } async function testMixedUsage() { console.log(\n 测试混合使用DMHR.CITY ); const nativeService new NativeDbService(); const typeormService new TypeormDbService(); const cityService new CityService(nativeService, typeormService); try { await Promise.all([ nativeService.onModuleInit(), typeormService.onModuleInit() ]); // 0. 获取有效的地区列表 console.log(\n--- 0. 查询有效的地区列表 ---); const regions await cityService.getValidRegions(); console.log(有效地区, regions); const validRegionId regions.length 0 ? regions[0].REGION_ID : 1; console.log(使用地区 ID: ${validRegionId}); // 1. 使用 TypeORM 新增城市使用有效的 REGION_ID console.log(\n--- 1. 使用 TypeORM 新增城市 ---); const testCityId M1; await cityService.createCityWithTypeorm({ CITY_ID: testCityId, CITY_NAME: 混合测试城市, REGION_ID: validRegionId }); // 2. 使用原生驱动查询所有城市 console.log(\n--- 2. 使用原生驱动查询所有城市 ---); const nativeCities await cityService.queryCitiesWithNative(); console.log(原生驱动查询到 ${nativeCities.length} 个城市); // 3. 使用 TypeORM 查询所有城市 console.log(\n--- 3. 使用 TypeORM 查询所有城市 ---); const typeormCities await cityService.queryCitiesWithTypeorm(); console.log(TypeORM 查询到 ${typeormCities.length} 个城市); // 4. 使用 TypeORM 删除城市 console.log(\n--- 4. 使用 TypeORM 删除城市 ---); await cityService.deleteCityWithTypeorm(testCityId); // 5. 验证删除 console.log(\n--- 5. 验证删除 ---); const finalCities await cityService.queryCitiesWithTypeorm(); console.log(最终城市数量${finalCities.length}); } catch (error) { console.error(混合使用测试失败, error); } finally { await Promise.all([ nativeService.onModuleDestroy(), typeormService.onModuleDestroy() ]); } } async function main() { console.log( 达梦数据库连接测试启动); console.log(数据库配置localhost:5236, 用户:SYSDBA, 密码:Aa123456, 模式:DMHR); console.log(操作表DMHR.CITY外键约束REGION_ID 必须存在于 DMHR.REGION 表); const testMode process.argv[2] || all; switch (testMode) { case native: await testNativeDriver(); break; case typeorm: await testTypeorm(); break; case mixed: await testMixedUsage(); break; case all: default: await testNativeDriver(); await testTypeorm(); await testMixedUsage(); break; } console.log(\n✨ 所有测试完成); } if (require.main module) { main().catch(console.error); } module.exports { NativeDbService, TypeormDbService, CityService, typeormConfig, AppDataSource };将完整代码保存为dm-database.js运行测试bash# 运行所有测试 node dm-database.js # 只测试原生驱动 node dm-database.js native # 只测试 TypeORM node dm-database.js typeorm # 测试混合使用 node dm-database.js mixed预期输出text原生方式orm方式6. 常见问题与避坑指南6.1dmdb安装失败错误node-gyp编译失败解决bash# Windows npm install -g windows-build-tools # Ubuntu sudo apt-get install build-essential6.2 Node.js 版本兼容问题错误ERR_OSSL_EVP_UNSUPPORTED解决在代码开头设置环境变量javascriptprocess.env.NODE_OPTIONS --openssl-legacy-provider;6.3 外键约束问题错误违反引用约束[CITY_REG_FK]原因插入的 REGION_ID 在 REGION 表中不存在解决先查询有效的 REGION_IDjavascriptconst regions await nativeService.getAllRegions(); const validRegionId regions[0].REGION_ID;6.4 表名大小写问题达梦数据库对表名大小写敏感SQL 语句中使用大写sqlSELECT * FROM DMHR.CITY -- 正确 SELECT * FROM dmhr.city -- 可能出错6.5 连接池耗尽原因获取连接后没有释放解决使用try/finally确保释放javascriptconst conn await pool.getConnection(); try { await conn.execute(sql); } finally { conn.release(); // 必须执行 }6.6 TypeORM 实体找不到错误No metadata for City was found解决确保实体正确注册到entities数组使用EntitySchema方式定义实体确保name与getRepository参数一致7. 核心总结方案优点缺点推荐场景原生驱动性能好、轻量类型不安全、手动管理高性能、简单查询TypeORM类型安全、开发快性能略低、内存占用高复杂业务、团队协作接入三要素正确安装dmdb驱动配置正确的连接字符串注意 schema 参数处理好外键约束和表名大小写运行命令bash# 安装依赖 npm install dmdb typeorm nestjs/typeorm reflect-metadata typeorm-dm # 运行测试 node dm-database.js附录完整代码文件完整的dm-database.js文件已在上文提供可直接复制保存运行。代码包含原生驱动连接实现NativeDbService 类TypeORM 整合实现TypeormDbService 类业务服务封装CityService 类完整的测试函数三种测试模式导出供其他模块使用的接口
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2433898.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!