- 后端
- 微服务
【免费下载链接】loopback-next
LoopBack makes it easy to build modern API applications that require complex integrations.
本篇指南讲解 LoopBack 4(loopback-next)中lb4 discover命令的完整用法:它如何从已有的关系型数据库(MySQL、PostgreSQL、Oracle、SQL Server 等)反向发现表结构,并自动生成带@model、@property装饰器的 TypeScript 模型文件。读完后你将掌握lb4 discover全部命令行参数、交互式提示项、生成产物结构,以及命令在 discover 生成器 中的实现原理与测试验证方式。
概览:什么是模型发现(Discovery)
LoopBack 简化了从现有关系数据库创建模型的过程。这一过程称为discovery,官方文档(Discovering-models.md)列出的受支持连接器包括:
- Cassandra
- MySQL
- Oracle
- PostgreSQL
- SQL Server
- IBM DashDB
- IBM Db2 (for Linux, Unix, Windows)
- IBM Db2 for i
- IBM Db2 for z/OS
- SAP HANA —— 非官方支持
通过运行lb4 discover命令即可从受支持的数据源(datasource)中发现模型。该命令由 CLI 注册入口 cli.js 挂载为lb4 discover(Yeoman 生成器注册在generators/discover目录)。
重要前提:LoopBack 项目必须先用
npm run build构建,把数据源定义文件转译到dist/datasources/目录下。从源码看,model-discoverer.js 中定义了DEFAULT_DATASOURCE_DIRECTORY = './dist/datasources',并且只扫描其中以.datasource.js结尾的文件;若数据源不存在,命令会抛出Cannot find datasource "<name>" in ./dist/datasources错误(这一点在 discover.integration.js 集成测试中被显式断言)。
命令行选项
lb4 discover支持的选项如下,默认值与 discover 生成器 中option()的定义一致:
| 选项 | 说明 | 默认值 |
|---|---|---|
--dataSource或--datasource(别名--ds) | 指定有效数据源名称,跳过数据源选择提示 | 无(交互式选择) |
--views | 是否发现数据库视图(views) | true |
--relations | 是否创建关系(relations) | false |
--all | 跳过模型选择提示,发现全部模型 | false |
--outDir | 生成的*.model.ts文件输出目录 | src/models |
--schema | 指定数据源中发现模型的 schema | 空字符串 |
--models | 指定要生成的模型,例如--models=table1,table2 | 无 |
--optionalId | 是否把生成模型的 Id 属性标记为不必填(not required) | false |
--connectorDiscoveryOptions | 以 JSON 字符串向连接器透传发现选项 | 无 |
关于--connectorDiscoveryOptions:文档给出的例子是向loopback-mysql-connector传入--connectorDiscoveryOptions='{"treatTINYINT1AsTinyInt":false}',使连接器把tinyint当作boolean处理。在源码中,该参数在getAllModelDefs()里被JSON.parse解析后展开进每次discoverSchema的选项中;discover.integration.js 中还专门有一条用例验证treatTINYINT1AsTinyInt: false时的生成结果快照。
交互式提示(Interactive Prompts)
根据传入的选项不同,工具可能提示你输入以下内容(提示逻辑见 discover/index.js 的promptDataSource、promptModelChoices、promptColNamingConvention方法):
- Name of the connector to discover:列出所有可用连接器(数据源)供选择。该列表来自
loadAllDatasources(),它会读取dist/datasources目录并实例化每个数据源。 - Name of the models to discover:多选(checkbox)列出可用模型。源码中该提示带有校验:至少要选择 1 个模型,防止用户误按 ENTER(而不是 SPACE)跳过选择。
- Database column naming convention:默认情况下 LoopBack 会把发现的模型属性转换为
camelCase,这也是推荐做法。也可以选择与数据库列名保持完全一致(noCase)。源码中的提示选项文案为Camel case (exampleColumn) (Recommended)与No conversion (EXAMPLE_COLUMN);若选择禁用驼峰转换,CLI 会用红色警告输出:禁用 camel case 后,你可能需要在关系定义中显式指定这些自定义属性名。各关系文档的 Relation Metadata 小节 介绍了如何自定义关系端点名称。
使用--all、--models、--dataSource等选项可以跳过相应提示,实现非交互式的批量发现,这也适合脚本化场景。
命令执行流程与源码实现
从 discover/index.js 的生成器实现看,lb4 discover的完整调用链是:
- 加载数据源:
setOptions()若检测到--dataSource/--datasource,调用 model-discoverer.js 的loadDataSourceByName(),遍历dist/datasources下所有*.datasource.js,按ds.name匹配并实例化导出的第一个类;未指定时由loadAllDatasources()构建候选列表供提示选择。 - 列出可发现的模型:
discoverModelInfos()调用ds.discoverModelDefinitions({views, schema})(见discoverModelNames()),拿到模型名列表后按字母序排序。 - 确定目标模型:
promptModelChoices()处理--all(直接取全部)与--models(按逗号拆分、在列表中查找匹配项)。 - 命名约定:
promptColNamingConvention()决定disableCamelCase标志。 - 逐个发现模型定义:
getAllModelDefs()对每个模型调用ds.discoverSchema(modelName, {schema, disableCamelCase, associations: <--relations>, ...connectorDiscoveryOptions})(封装在discoverSingleModel()中)。若指定--optionalId,会把所有id属性(包括复合主键中的多个 id)的required置为false。 - 渲染模板生成文件:
scaffold()把发现结果转换为模板数据并写入目标文件。
其中第 6 步的关键转换在 import-discovered-model.js:importDiscoveredModel()把 juggler 发现的定义映射为 LB4 模型模板数据——类名取pascalCase(modelName),基类固定为Entity,并开启allowAdditionalProperties。每个属性经importPropertyDefinition()处理:通过findBuiltinType归一化类型(Number→'number'、Date→'date'、Binary→Buffer等),再经sanitizeProperty()清理null字段、并把连接器元数据对象(如postgresql块)序列化为单行字符串,避免模板输出[object Object]。最终由 model.ts.ejs 模板渲染出*.model.ts文件。
--relations的实现细节
开启--relations时,scaffold()会额外做三件事:
- 把
settings.relations中每条关系挂到对应外键属性上,渲染为@belongsTo(() => TargetModel)装饰器; - 把关系目标模型写入
import {...} from '.'的引用(自引用关系会被剔除); - 在
@model的settings中写入foreignKeys(包含name、entity、entityKey、foreignKey字段,其中entityKey取自目标模型id === 1的属性名)。
仓库中的测试快照(discover.integration.snapshots.js)展示了开启--relations后生成的Doctor模型形态:
import {Entity, model, property, belongsTo} from '@loopback/repository'; @model({ settings: { foreignKeys: { doctorRel: {name: 'doctorRel', entity: 'Doctor', entityKey: 'id', foreignKey: 'reportsTo'} } } }) export class Doctor extends Entity { @property({ type: 'number', scale: 0, id: 1, }) id?: number; @property({ type: 'string', length: 45, }) name?: string; @belongsTo(() => Doctor) reportsTo?: number; // ... }注意:只有当关系目标模型也在本次发现的模型集合内时,才会真正生成关系,否则会被跳过。
生成产物示例
回答完所有提示后,CLI 会生成所选模型的*.model.ts文件。以 PostgreSQL 连接器为例,文档给出的生成模型如下:
@model({ settings: { postgresql: {schema: 'public', table: 'mymodel'}, }, }) export class My extends Entity { @property({ type: 'number', required: false, scale: 0, id: true, postgresql: { columnName: 'my_id', dataType: 'integer', ... }, }) my_id: number; @property({ type: 'string', required: true, length: 100, postgresql: { columnName: 'my_name', dataType: 'character varying', dataLength: 100, ... }, }) my_name: string;每个属性都会带上连接器专属的元数据块(columnName、dataType、dataLength、nullable等),模型settings中则记录 schema 与表名。这些元数据正是sanitizeProperty()将连接器对象序列化后写入模板的结果,也是后续手动调整列映射的基础。
列名与属性名不一致时
数据库列名可以与属性名不同:只要属性定义了<连接器名>.columnName字段且其与数据库列名一致,就可以随意修改属性名。由于 LB4 偏好 camel case,推荐把属性命名为驼峰形式:
@model({ settings: { postgresql: {schema: 'public', table: 'mymodel'}, }, }) export class MyModel extends Entity { @property({ type: 'number', required: false, scale: 0, id: 1, postgresql: { columnName: 'my_id', dataType: 'integer', ... }, }) myId: number; // different from the column name @property({ type: 'string', required: true, length: 100, postgresql: { columnName: 'my_name', dataType: 'character varying', dataLength: 100, ... }, }) myName: string; // different from the column name }枚举(enum)类型的处理
源码scaffold()中还有一段针对枚举列的特判:当连接器返回的属性类型以'enum开头时,CLI 会将其改写为String/string,并把可选值提取为jsonSchema: {enum: [...]}。单测 import-discovered-model.test.js 中的用例imports enum properties验证了该行为:带jsonSchema: {enum: ['INPATIENT', 'OUTPATIENT']}的属性会被转换为type: 'string'、tsType: 'string',且jsonSchema保留为枚举数组字符串。
测试与验证
该命令的行为由两层测试保障:
- 集成测试discover.integration.js:在一个 mock 的 LoopBack 4 项目中运行完整生成器,覆盖
--all --dataSource、--datasource(小写 s 变体)、--outDir、--views/--schema过滤、禁用 camel case 保持列名、--optionalId、treatTINYINT1AsTinyInt、--relations、--models指定单个模型等场景,并与 快照文件 比对生成内容。其使用的假数据源 mem.datasource.js.txt 模拟了discoverModelDefinitions(支持按views、schema过滤)与discoverSchema两个连接器方法——这也是真实连接器必须实现的能力接口。 - 单元测试import-discovered-model.test.js:验证从发现结果到模板数据的类型映射,包括
Date、Number、String、Boolean、Binary、数值主键(id: 1)以及连接器元数据对象到 TypeScript 字符串的转换。
小结
lb4 discover是 LoopBack 4 中面向“已有数据库、反向建模型”场景的核心工具:它依赖构建产物dist/datasources/*.datasource.js连接数据源,通过连接器的discoverModelDefinitions/discoverSchema能力读取表与列信息,再经importDiscoveredModel()与model.ts.ejs模板渲染成带连接器元数据的@model类。配合--all、--models、--schema、--relations、--optionalId与--connectorDiscoveryOptions等选项,你可以灵活地在交互式与非交互式模式下完成批量模型生成,并借助生成的columnName元数据自由调整属性命名。
- 后端
- 微服务
【免费下载链接】loopback-next
LoopBack makes it easy to build modern API applications that require complex integrations.
相关推荐
LoopBack 4 Controller 生成器(lb4 controller)完全指南:从空控制器到 REST CRUD 控制器
LoopBack 4 Controller 生成器(lb4 controller)完全指南:从空控制器到 REST CRUD 控制器 本篇技术指南围绕 Loop
后端微服务LoopBack 4 应用生成器(lb4 app)完全指南:从零脚手架一个可运行的 REST API 项目
LoopBack 4 应用生成器(lb4 app)完全指南:从零脚手架一个可运行的 REST API 项目 本指南围绕 LoopBack 4 CLI 的核心命令
后端微服务Bogus数据模型设计:从实体类到关联数据生成
Bogus数据模型设计:从实体类到关联数据生成 在软件开发中,测试数据的生成往往耗费大量时间。手动编写测试数据不仅效率低下,还难以覆盖各种边界情况。Bogus作
开发工具测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考