@pangza/orm 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +2 -2
  2. package/dist/BaseMapper.d.ts +112 -0
  3. package/dist/BaseMapper.d.ts.map +1 -0
  4. package/dist/BaseMapper.js +653 -0
  5. package/dist/BaseMapper.js.map +1 -0
  6. package/dist/DataSource.d.ts +61 -0
  7. package/dist/DataSource.d.ts.map +1 -0
  8. package/dist/DataSource.js +350 -0
  9. package/dist/DataSource.js.map +1 -0
  10. package/dist/Db.d.ts +67 -0
  11. package/dist/Db.d.ts.map +1 -0
  12. package/dist/Db.js +222 -0
  13. package/dist/Db.js.map +1 -0
  14. package/dist/IdType.d.ts +22 -0
  15. package/dist/IdType.d.ts.map +1 -0
  16. package/dist/IdType.js +34 -0
  17. package/dist/IdType.js.map +1 -0
  18. package/dist/QueryWrapper.d.ts +110 -0
  19. package/dist/QueryWrapper.d.ts.map +1 -0
  20. package/dist/QueryWrapper.js +343 -0
  21. package/dist/QueryWrapper.js.map +1 -0
  22. package/dist/config.d.ts +12 -0
  23. package/dist/config.d.ts.map +1 -0
  24. package/dist/config.js +80 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/dialect.d.ts +41 -0
  27. package/dist/dialect.d.ts.map +1 -0
  28. package/dist/dialect.js +171 -0
  29. package/dist/dialect.js.map +1 -0
  30. package/dist/enable.d.ts +12 -0
  31. package/dist/enable.d.ts.map +1 -0
  32. package/dist/enable.js +31 -0
  33. package/dist/enable.js.map +1 -0
  34. package/dist/entityDecorators.d.ts +77 -0
  35. package/dist/entityDecorators.d.ts.map +1 -0
  36. package/dist/entityDecorators.js +150 -0
  37. package/dist/entityDecorators.js.map +1 -0
  38. package/dist/errors.d.ts +11 -0
  39. package/dist/errors.d.ts.map +1 -0
  40. package/dist/errors.js +22 -0
  41. package/dist/errors.js.map +1 -0
  42. package/dist/fieldSelect.d.ts +49 -0
  43. package/dist/fieldSelect.d.ts.map +1 -0
  44. package/dist/fieldSelect.js +53 -0
  45. package/dist/fieldSelect.js.map +1 -0
  46. package/dist/fill.d.ts +8 -0
  47. package/dist/fill.d.ts.map +1 -0
  48. package/dist/fill.js +47 -0
  49. package/dist/fill.js.map +1 -0
  50. package/dist/flag.d.ts +2 -0
  51. package/dist/flag.d.ts.map +1 -0
  52. package/dist/flag.js +8 -0
  53. package/dist/flag.js.map +1 -0
  54. package/dist/hydrate.d.ts +38 -0
  55. package/dist/hydrate.d.ts.map +1 -0
  56. package/dist/hydrate.js +325 -0
  57. package/dist/hydrate.js.map +1 -0
  58. package/dist/identifier.d.ts +14 -0
  59. package/dist/identifier.d.ts.map +1 -0
  60. package/dist/identifier.js +51 -0
  61. package/dist/identifier.js.map +1 -0
  62. package/dist/index.d.ts +41 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +122 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/relation.d.ts +72 -0
  67. package/dist/relation.d.ts.map +1 -0
  68. package/dist/relation.js +150 -0
  69. package/dist/relation.js.map +1 -0
  70. package/dist/resultMap.d.ts +7 -0
  71. package/dist/resultMap.d.ts.map +1 -0
  72. package/dist/resultMap.js +25 -0
  73. package/dist/resultMap.js.map +1 -0
  74. package/dist/security.d.ts +16 -0
  75. package/dist/security.d.ts.map +1 -0
  76. package/dist/security.js +50 -0
  77. package/dist/security.js.map +1 -0
  78. package/dist/sqlDecorators.d.ts +60 -0
  79. package/dist/sqlDecorators.d.ts.map +1 -0
  80. package/dist/sqlDecorators.js +147 -0
  81. package/dist/sqlDecorators.js.map +1 -0
  82. package/dist/sqlLog.d.ts +30 -0
  83. package/dist/sqlLog.d.ts.map +1 -0
  84. package/dist/sqlLog.js +183 -0
  85. package/dist/sqlLog.js.map +1 -0
  86. package/dist/sqlTemplate.d.ts +38 -0
  87. package/dist/sqlTemplate.d.ts.map +1 -0
  88. package/dist/sqlTemplate.js +112 -0
  89. package/dist/sqlTemplate.js.map +1 -0
  90. package/dist/table.d.ts +17 -0
  91. package/dist/table.d.ts.map +1 -0
  92. package/dist/table.js +55 -0
  93. package/dist/table.js.map +1 -0
  94. package/dist/tableSearch.d.ts +72 -0
  95. package/dist/tableSearch.d.ts.map +1 -0
  96. package/dist/tableSearch.js +232 -0
  97. package/dist/tableSearch.js.map +1 -0
  98. package/dist/transactional.d.ts +27 -0
  99. package/dist/transactional.d.ts.map +1 -0
  100. package/dist/transactional.js +57 -0
  101. package/dist/transactional.js.map +1 -0
  102. package/dist/types.d.ts +155 -0
  103. package/dist/types.d.ts.map +1 -0
  104. package/dist/types.js +3 -0
  105. package/dist/types.js.map +1 -0
  106. package/dist/xml/MapperXml.d.ts +24 -0
  107. package/dist/xml/MapperXml.d.ts.map +1 -0
  108. package/dist/xml/MapperXml.js +92 -0
  109. package/dist/xml/MapperXml.js.map +1 -0
  110. package/dist/xml/execute.d.ts +12 -0
  111. package/dist/xml/execute.d.ts.map +1 -0
  112. package/dist/xml/execute.js +54 -0
  113. package/dist/xml/execute.js.map +1 -0
  114. package/dist/xml/index.d.ts +11 -0
  115. package/dist/xml/index.d.ts.map +1 -0
  116. package/dist/xml/index.js +29 -0
  117. package/dist/xml/index.js.map +1 -0
  118. package/dist/xml/loadMappers.d.ts +14 -0
  119. package/dist/xml/loadMappers.d.ts.map +1 -0
  120. package/dist/xml/loadMappers.js +171 -0
  121. package/dist/xml/loadMappers.js.map +1 -0
  122. package/dist/xml/parseMapperXml.d.ts +6 -0
  123. package/dist/xml/parseMapperXml.d.ts.map +1 -0
  124. package/dist/xml/parseMapperXml.js +225 -0
  125. package/dist/xml/parseMapperXml.js.map +1 -0
  126. package/dist/xml/registry.d.ts +15 -0
  127. package/dist/xml/registry.d.ts.map +1 -0
  128. package/dist/xml/registry.js +57 -0
  129. package/dist/xml/registry.js.map +1 -0
  130. package/dist/xml/renderSql.d.ts +6 -0
  131. package/dist/xml/renderSql.d.ts.map +1 -0
  132. package/dist/xml/renderSql.js +187 -0
  133. package/dist/xml/renderSql.js.map +1 -0
  134. package/dist/xml/types.d.ts +52 -0
  135. package/dist/xml/types.d.ts.map +1 -0
  136. package/dist/xml/types.js +3 -0
  137. package/dist/xml/types.js.map +1 -0
  138. package/dist/xml/xmlMapper.d.ts +16 -0
  139. package/dist/xml/xmlMapper.d.ts.map +1 -0
  140. package/dist/xml/xmlMapper.js +49 -0
  141. package/dist/xml/xmlMapper.js.map +1 -0
  142. package/package.json +35 -4
@@ -0,0 +1,653 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BaseMapper = void 0;
7
+ const Db_1 = __importDefault(require("./Db"));
8
+ const config_1 = require("./config");
9
+ const entityDecorators_1 = require("./entityDecorators");
10
+ const errors_1 = require("./errors");
11
+ const fill_1 = require("./fill");
12
+ const hydrate_1 = require("./hydrate");
13
+ const identifier_1 = require("./identifier");
14
+ const IdType_1 = require("./IdType");
15
+ const QueryWrapper_1 = require("./QueryWrapper");
16
+ const resultMap_1 = require("./resultMap");
17
+ const security_1 = require("./security");
18
+ /**
19
+ * 通用 Mapper(对齐 MyBatis-Plus BaseMapper)
20
+ * 支持逻辑删除、自动填充、乐观锁(均可配置开关)。
21
+ */
22
+ class BaseMapper {
23
+ constructor(options) {
24
+ this.columnNames = null;
25
+ const global = (0, config_1.getOrmConfig)();
26
+ const datasource = (options.datasource ?? "default").trim() || "default";
27
+ this.opts = {
28
+ table: (0, security_1.assertIdent)(options.table),
29
+ idField: (0, security_1.assertIdent)(options.idField ?? "id"),
30
+ datasource,
31
+ logicDeleteExplicit: options.logicDelete,
32
+ logicDeleteField: (0, security_1.assertIdent)(options.logicDeleteField ?? global.logicDeleteField),
33
+ logicDeleteValue: options.logicDeleteValue ?? global.logicDeleteValue,
34
+ logicNotDeleteValue: options.logicNotDeleteValue ?? global.logicNotDeleteValue,
35
+ optimisticLockExplicit: options.optimisticLock,
36
+ versionField: (0, security_1.assertIdent)(options.versionField ?? global.versionField),
37
+ fill: options.fill ?? true,
38
+ updateNullStrategy: options.updateNullStrategy ?? "INCLUDE",
39
+ entity: options.entity,
40
+ };
41
+ this.dialect = Db_1.default.getDialect(datasource);
42
+ }
43
+ /** 更新字段是否参与 SET(undefined 永不参与;null 受 updateNullStrategy 控制) */
44
+ shouldWriteUpdateValue(value) {
45
+ if (value === undefined)
46
+ return false;
47
+ if (value === null && this.opts.updateNullStrategy === "IGNORE") {
48
+ return false;
49
+ }
50
+ return true;
51
+ }
52
+ async query(sql, params = []) {
53
+ return Db_1.default.queryOn(this.opts.datasource, sql, params);
54
+ }
55
+ async tx(fn) {
56
+ return Db_1.default.transactionOn(this.opts.datasource, fn);
57
+ }
58
+ cfg() {
59
+ return (0, config_1.getOrmConfig)();
60
+ }
61
+ /** 是否启用逻辑删除(表级优先,否则读全局) */
62
+ isLogicDelete() {
63
+ return this.opts.logicDeleteExplicit ?? this.cfg().logicDelete;
64
+ }
65
+ /** 是否启用乐观锁(表级优先,否则读全局) */
66
+ isOptimisticLock() {
67
+ return this.opts.optimisticLockExplicit ?? this.cfg().optimisticLock;
68
+ }
69
+ /** 未删除条件(对齐 MyBatis-Plus:字段 = 未删除值) */
70
+ notDeletedClause() {
71
+ const col = this.colSql(this.opts.logicDeleteField);
72
+ return {
73
+ sql: `${col} = ?`,
74
+ params: [this.opts.logicNotDeleteValue],
75
+ };
76
+ }
77
+ tableSql() {
78
+ return this.dialect.quoteIdent(this.opts.table);
79
+ }
80
+ idSql() {
81
+ return this.dialect.quoteIdent(this.opts.idField);
82
+ }
83
+ colSql(name) {
84
+ return this.dialect.quoteIdent((0, security_1.assertIdent)(name));
85
+ }
86
+ /**
87
+ * 把实体属性名映射为列名,并去掉 exist:false 字段,
88
+ * 避免 createTime / create_time 双键写入 SQL。
89
+ */
90
+ normalizeEntityRow(row) {
91
+ if (!this.opts.entity)
92
+ return row;
93
+ const fields = (0, entityDecorators_1.getEntityFields)(this.opts.entity);
94
+ if (!fields.length)
95
+ return row;
96
+ const out = { ...row };
97
+ for (const f of fields) {
98
+ if (!f.exist) {
99
+ delete out[f.property];
100
+ delete out[f.column];
101
+ continue;
102
+ }
103
+ if (f.property !== f.column &&
104
+ out[f.property] !== undefined &&
105
+ out[f.column] === undefined) {
106
+ out[f.column] = out[f.property];
107
+ }
108
+ if (f.property !== f.column) {
109
+ delete out[f.property];
110
+ }
111
+ }
112
+ return out;
113
+ }
114
+ async getColumnNames() {
115
+ if (this.columnNames)
116
+ return this.columnNames;
117
+ const { sql, params } = this.dialect.listColumnsSql(this.opts.table);
118
+ const rows = (await this.query(sql, params));
119
+ const names = this.dialect
120
+ .parseColumnNames(rows)
121
+ .filter(Boolean);
122
+ this.columnNames = new Set(names);
123
+ return this.columnNames;
124
+ }
125
+ /** 表上不存在的自动填充字段直接丢掉,避免 Unknown column */
126
+ async stripMissingFillFields(row) {
127
+ const cols = await this.getColumnNames();
128
+ const cfg = this.cfg();
129
+ for (const field of [
130
+ cfg.fillCreateTime,
131
+ cfg.fillUpdateTime,
132
+ cfg.fillCreateBy,
133
+ cfg.fillUpdateBy,
134
+ ]) {
135
+ if (field && row[field] !== undefined && !cols.has(field)) {
136
+ delete row[field];
137
+ }
138
+ }
139
+ }
140
+ async assertLogicDeleteColumn() {
141
+ if (!this.isLogicDelete())
142
+ return;
143
+ const cols = await this.getColumnNames();
144
+ const field = this.opts.logicDeleteField;
145
+ if (!cols.has(field)) {
146
+ throw new Error(`[ORM] 表 ${this.opts.table}(数据源 ${this.opts.datasource})没有逻辑删除列 ${field}。` +
147
+ `请 ALTER TABLE 增加该列,或 @Mapper / table(..., { logicDelete: false })。` +
148
+ `当前表列: ${[...cols].join(", ") || "(空)"}`);
149
+ }
150
+ }
151
+ /** 追加未删除条件(不修改调用方 wrapper) */
152
+ withLogic(wrapper) {
153
+ const src = wrapper ?? new QueryWrapper_1.QueryWrapper(this.dialect);
154
+ if (!this.isLogicDelete() || src.isIgnoreLogicDelete()) {
155
+ return src;
156
+ }
157
+ return src
158
+ .clone()
159
+ .eq(this.opts.logicDeleteField, this.opts.logicNotDeleteValue);
160
+ }
161
+ hydrateOpts(query) {
162
+ if (query?.relations === false) {
163
+ return {
164
+ datasource: this.opts.datasource,
165
+ relations: false,
166
+ properties: [],
167
+ };
168
+ }
169
+ if (Array.isArray(query?.relations)) {
170
+ return {
171
+ datasource: this.opts.datasource,
172
+ relations: query.relations,
173
+ includeLazy: true,
174
+ properties: query.relations,
175
+ };
176
+ }
177
+ return { datasource: this.opts.datasource };
178
+ }
179
+ normalizeListArgs(wrapperOrOptions, limitOrOptions) {
180
+ if (wrapperOrOptions &&
181
+ typeof wrapperOrOptions === "object" &&
182
+ !(wrapperOrOptions instanceof QueryWrapper_1.QueryWrapper) &&
183
+ ("relations" in wrapperOrOptions || "limit" in wrapperOrOptions)) {
184
+ return { options: wrapperOrOptions };
185
+ }
186
+ if (typeof limitOrOptions === "number") {
187
+ return {
188
+ wrapper: wrapperOrOptions,
189
+ options: { limit: limitOrOptions },
190
+ };
191
+ }
192
+ return {
193
+ wrapper: wrapperOrOptions,
194
+ options: limitOrOptions ?? {},
195
+ };
196
+ }
197
+ /** 按主键查询 */
198
+ async selectById(id, options) {
199
+ return this.selectOne(new QueryWrapper_1.QueryWrapper(this.dialect).eq(this.opts.idField, id), options);
200
+ }
201
+ /** 条件查询单条 */
202
+ async selectOne(wrapper, options) {
203
+ const list = await this.selectList(wrapper, {
204
+ ...options,
205
+ limit: options?.limit ?? 1,
206
+ });
207
+ return list[0] ?? null;
208
+ }
209
+ /** WHERE + GROUP BY + HAVING + ORDER BY + LAST(去多余空格) */
210
+ fragTail(frag) {
211
+ return `${frag.whereSql} ${frag.groupSql} ${frag.havingSql} ${frag.orderSql} ${frag.lastSql}`
212
+ .replace(/\s+/g, " ")
213
+ .trim();
214
+ }
215
+ /** 有 GROUP BY 时用子查询计组数,否则普通 COUNT */
216
+ countSqlFromFrag(frag) {
217
+ if (frag.groupSql) {
218
+ const inner = `${frag.whereSql} ${frag.groupSql} ${frag.havingSql} ${frag.lastSql}`
219
+ .replace(/\s+/g, " ")
220
+ .trim();
221
+ return `SELECT COUNT(1) AS cnt FROM (SELECT 1 FROM ${this.tableSql()} ${inner}) AS _g`;
222
+ }
223
+ return `SELECT COUNT(1) AS cnt FROM ${this.tableSql()} ${frag.whereSql} ${frag.lastSql}`
224
+ .replace(/\s+/g, " ")
225
+ .trim();
226
+ }
227
+ /**
228
+ * 条件查询列表。
229
+ * @param limitOrOptions 数字 limit,或 `{ limit, relations }`
230
+ */
231
+ async selectList(wrapper, limitOrOptions) {
232
+ const { wrapper: w, options } = this.normalizeListArgs(wrapper, limitOrOptions);
233
+ await this.assertLogicDeleteColumn();
234
+ const frag = this.withLogic(w).build();
235
+ let sql = `SELECT ${frag.selectSql} FROM ${this.tableSql()} ${this.fragTail(frag)}`.trim();
236
+ const params = [...frag.params];
237
+ const limit = options.limit;
238
+ if (limit != null) {
239
+ const n = (0, security_1.assertSafeUint)(limit, "limit");
240
+ const hasOrder = /\bORDER\s+BY\b/i.test(sql);
241
+ sql = this.dialect.appendLimitOffset(sql, n, 0, hasOrder);
242
+ }
243
+ const rows = (0, resultMap_1.mapResultRows)((await this.query(sql, params)));
244
+ return (0, hydrate_1.hydrateFieldSelects)(this.opts.entity, rows, this.hydrateOpts(options));
245
+ }
246
+ /** 条件计数(含 GROUP BY 时返回分组行数) */
247
+ async selectCount(wrapper) {
248
+ await this.assertLogicDeleteColumn();
249
+ const frag = this.withLogic(wrapper).build();
250
+ const sql = this.countSqlFromFrag(frag);
251
+ const rows = await this.query(sql, frag.params);
252
+ return Number(rows[0]?.cnt ?? rows[0]?.CNT ?? 0);
253
+ }
254
+ /** 分页查询 */
255
+ async selectPage(page = {}, wrapper, options) {
256
+ const current = (0, security_1.assertSafeUint)(Math.max(1, page.current ?? 1), "current");
257
+ const size = (0, security_1.assertSafeUint)(Math.max(1, page.size ?? 10), "size");
258
+ const offset = (0, security_1.assertSafeUint)((current - 1) * size, "offset");
259
+ await this.assertLogicDeleteColumn();
260
+ const frag = this.withLogic(wrapper).build();
261
+ const countSql = this.countSqlFromFrag(frag);
262
+ let listSql = `SELECT ${frag.selectSql} FROM ${this.tableSql()} ${this.fragTail(frag)}`.trim();
263
+ const hasOrder = /\bORDER\s+BY\b/i.test(listSql);
264
+ listSql = this.dialect.appendLimitOffset(listSql, size, offset, hasOrder);
265
+ const [countRows, listRows] = await Promise.all([
266
+ this.query(countSql, frag.params),
267
+ this.query(listSql, frag.params),
268
+ ]);
269
+ const records = (0, resultMap_1.mapResultRows)(listRows);
270
+ return {
271
+ records: await (0, hydrate_1.hydrateFieldSelects)(this.opts.entity, records, this.hydrateOpts(options)),
272
+ total: Number(countRows[0]?.cnt ?? countRows[0]?.CNT ?? 0),
273
+ current,
274
+ size,
275
+ };
276
+ }
277
+ /** 解析有效主键策略 */
278
+ resolveIdType() {
279
+ const idMeta = this.opts.entity
280
+ ? (0, entityDecorators_1.getEntityIdField)(this.opts.entity)
281
+ : undefined;
282
+ const annotated = idMeta?.idType ?? IdType_1.IdType.NONE;
283
+ if (annotated !== IdType_1.IdType.NONE)
284
+ return annotated;
285
+ return (0, IdType_1.resolveIdType)(this.cfg().idType ?? IdType_1.IdType.AUTO);
286
+ }
287
+ /** 插入前按 IdType 填充 / 剥离主键 */
288
+ async applyIdOnInsert(row, entity) {
289
+ const idType = this.resolveIdType();
290
+ const col = this.opts.idField;
291
+ const prop = this.opts.entity != null
292
+ ? ((0, entityDecorators_1.getEntityIdField)(this.opts.entity)?.property ?? col)
293
+ : col;
294
+ const current = row[col] !== undefined
295
+ ? row[col]
296
+ : row[prop] !== undefined
297
+ ? row[prop]
298
+ : entity?.[prop] ?? entity?.[col];
299
+ if (idType === IdType_1.IdType.AUTO) {
300
+ // 数据库自增:不写入 id 列(已有值则保留,兼容显式指定)
301
+ if (current === undefined || current === null || current === "") {
302
+ delete row[col];
303
+ if (prop !== col)
304
+ delete row[prop];
305
+ }
306
+ return;
307
+ }
308
+ if (idType === IdType_1.IdType.INPUT) {
309
+ return;
310
+ }
311
+ if (current !== undefined && current !== null && current !== "") {
312
+ row[col] = current;
313
+ return;
314
+ }
315
+ const gen = (0, identifier_1.getIdentifierGenerator)();
316
+ if (idType === IdType_1.IdType.ASSIGN_ID) {
317
+ row[col] = await gen.nextId(entity);
318
+ return;
319
+ }
320
+ if (idType === IdType_1.IdType.ASSIGN_UUID) {
321
+ row[col] = await gen.nextUUID(entity);
322
+ }
323
+ }
324
+ /** 把生成的主键回写到原始实体 */
325
+ writeBackId(entity, normalized, insertId) {
326
+ const col = this.opts.idField;
327
+ const prop = this.opts.entity != null
328
+ ? ((0, entityDecorators_1.getEntityIdField)(this.opts.entity)?.property ?? col)
329
+ : col;
330
+ const idType = this.resolveIdType();
331
+ let value = normalized[col];
332
+ if ((value === undefined || value === null) &&
333
+ idType === IdType_1.IdType.AUTO &&
334
+ insertId) {
335
+ value = insertId;
336
+ }
337
+ if (value === undefined || value === null)
338
+ return;
339
+ entity[prop] = value;
340
+ if (prop !== col) {
341
+ entity[col] = value;
342
+ }
343
+ }
344
+ /** 插入,返回自增 id(若有);ASSIGN_* 时尽量回写实体并返回可解析的数字 */
345
+ async insert(entity) {
346
+ await this.assertLogicDeleteColumn();
347
+ const normalized = await this.prepareInsertRow(entity);
348
+ const insertId = await this.insertOne(normalized);
349
+ this.writeBackId(entity, normalized, insertId);
350
+ const back = entity[this.opts.idField];
351
+ if (insertId)
352
+ return insertId;
353
+ if (typeof back === "number")
354
+ return back;
355
+ if (typeof back === "string" && /^\d+$/.test(back))
356
+ return Number(back);
357
+ return 0;
358
+ }
359
+ /**
360
+ * 批量插入,返回影响行数。
361
+ * 多行一条 INSERT,按 `orm.batch.insertSize`(默认 200)分批。
362
+ */
363
+ async saveBatch(entities) {
364
+ const list = (entities ?? []).filter((item) => item != null && typeof item === "object");
365
+ if (list.length === 0)
366
+ return 0;
367
+ await this.assertLogicDeleteColumn();
368
+ return this.tx(async () => {
369
+ const rows = [];
370
+ for (const entity of list) {
371
+ const normalized = await this.prepareInsertRow(entity);
372
+ rows.push(normalized);
373
+ // ASSIGN_* 已写入 normalized,回写实体;AUTO 批量不回填单条 id
374
+ this.writeBackId(entity, normalized, 0);
375
+ }
376
+ const batchSize = Math.max(1, this.cfg().batchInsertSize ?? 200);
377
+ let affected = 0;
378
+ for (let i = 0; i < rows.length; i += batchSize) {
379
+ affected += await this.insertMany(rows.slice(i, i + batchSize));
380
+ }
381
+ return affected;
382
+ });
383
+ }
384
+ async prepareInsertRow(entity) {
385
+ const row = { ...entity };
386
+ await (0, fill_1.applyInsertFill)(row, this.cfg(), this.opts.fill);
387
+ await (0, entityDecorators_1.applyEntityFieldFills)(this.opts.entity, row, "insert");
388
+ await this.applyIdOnInsert(row, entity);
389
+ await this.stripMissingFillFields(row);
390
+ const normalized = this.normalizeEntityRow(row);
391
+ if (this.isLogicDelete() &&
392
+ normalized[this.opts.logicDeleteField] === undefined) {
393
+ normalized[this.opts.logicDeleteField] = this.opts.logicNotDeleteValue;
394
+ }
395
+ if (this.isOptimisticLock() &&
396
+ normalized[this.opts.versionField] === undefined) {
397
+ normalized[this.opts.versionField] = 1;
398
+ }
399
+ return normalized;
400
+ }
401
+ async insertOne(normalized) {
402
+ const keys = Object.keys(normalized).filter((k) => normalized[k] !== undefined);
403
+ if (keys.length === 0) {
404
+ throw new Error("insert 实体不能为空");
405
+ }
406
+ const safeKeys = keys.map(security_1.assertIdent);
407
+ const cols = safeKeys.map((k) => this.dialect.quoteIdent(k)).join(", ");
408
+ const placeholders = safeKeys.map(() => "?").join(", ");
409
+ const values = safeKeys.map((k) => normalized[k]);
410
+ const baseSql = `INSERT INTO ${this.tableSql()} (${cols}) VALUES (${placeholders})`;
411
+ const prepared = this.dialect.prepareInsert(baseSql, this.opts.idField);
412
+ const result = await this.query(prepared.sql, values);
413
+ return this.dialect.extractInsertId(result, this.opts.idField, prepared.returning);
414
+ }
415
+ /** 多行同一列集合;缺省列写 NULL */
416
+ async insertMany(rows) {
417
+ const keySet = new Set();
418
+ for (const row of rows) {
419
+ for (const key of Object.keys(row)) {
420
+ if (row[key] !== undefined)
421
+ keySet.add(key);
422
+ }
423
+ }
424
+ const keys = [...keySet];
425
+ if (keys.length === 0) {
426
+ throw new Error("saveBatch 实体不能为空");
427
+ }
428
+ const safeKeys = keys.map(security_1.assertIdent);
429
+ const cols = safeKeys.map((k) => this.dialect.quoteIdent(k)).join(", ");
430
+ const rowPlaceholder = `(${safeKeys.map(() => "?").join(", ")})`;
431
+ const values = [];
432
+ for (const row of rows) {
433
+ for (const key of safeKeys) {
434
+ values.push(row[key] === undefined ? null : row[key]);
435
+ }
436
+ }
437
+ const sql = `INSERT INTO ${this.tableSql()} (${cols}) VALUES ${rows
438
+ .map(() => rowPlaceholder)
439
+ .join(", ")}`;
440
+ const result = await this.query(sql, values);
441
+ return this.dialect.extractAffectedRows(result);
442
+ }
443
+ /** 按主键更新(忽略 undefined 字段) */
444
+ async updateById(entity) {
445
+ const id = entity[this.opts.idField];
446
+ if (id === undefined || id === null) {
447
+ throw new Error(`updateById 缺少主键 ${this.opts.idField}`);
448
+ }
449
+ const row = { ...entity };
450
+ await (0, fill_1.applyUpdateFill)(row, this.cfg(), this.opts.fill);
451
+ await (0, entityDecorators_1.applyEntityFieldFills)(this.opts.entity, row, "update");
452
+ await this.stripMissingFillFields(row);
453
+ const normalized = this.normalizeEntityRow(row);
454
+ await this.assertLogicDeleteColumn();
455
+ const version = this.isOptimisticLock() &&
456
+ normalized[this.opts.versionField] !== undefined
457
+ ? normalized[this.opts.versionField]
458
+ : undefined;
459
+ const keys = Object.keys(normalized).filter((k) => k !== this.opts.idField &&
460
+ k !== this.opts.versionField &&
461
+ this.shouldWriteUpdateValue(normalized[k]));
462
+ const sets = keys.map((k) => `${this.colSql(k)} = ?`);
463
+ const values = keys.map((k) => normalized[k]);
464
+ if (version !== undefined) {
465
+ sets.push(`${this.colSql(this.opts.versionField)} = ${this.colSql(this.opts.versionField)} + 1`);
466
+ }
467
+ if (sets.length === 0)
468
+ return 0;
469
+ let where = `${this.idSql()} = ?`;
470
+ const whereParams = [id];
471
+ if (version !== undefined) {
472
+ where += ` AND ${this.colSql(this.opts.versionField)} = ?`;
473
+ whereParams.push(version);
474
+ }
475
+ if (this.isLogicDelete()) {
476
+ const nd = this.notDeletedClause();
477
+ where += ` AND ${nd.sql}`;
478
+ whereParams.push(...nd.params);
479
+ }
480
+ const sql = `UPDATE ${this.tableSql()} SET ${sets.join(", ")} WHERE ${where}`;
481
+ const result = await this.query(sql, [...values, ...whereParams]);
482
+ const affected = this.dialect.extractAffectedRows(result);
483
+ if (version !== undefined && affected === 0) {
484
+ throw new errors_1.OptimisticLockError();
485
+ }
486
+ return affected;
487
+ }
488
+ /**
489
+ * 有主键且库中存在则 updateById,否则 insert。
490
+ * 返回影响行数(insert / update 语义与各自方法一致)。
491
+ */
492
+ async saveOrUpdate(entity) {
493
+ const id = entity[this.opts.idField];
494
+ if (id === undefined || id === null || id === "") {
495
+ return this.insert(entity);
496
+ }
497
+ const existing = await this.selectById(id);
498
+ if (!existing) {
499
+ return this.insert(entity);
500
+ }
501
+ return this.updateById(entity);
502
+ }
503
+ /**
504
+ * 按主键批量更新(逐条 updateById,同一事务)。
505
+ * 分批大小取 `orm.batch.updateSize`(默认 200)。
506
+ */
507
+ async updateBatch(entities) {
508
+ const list = (entities ?? []).filter((item) => item != null && typeof item === "object");
509
+ if (list.length === 0)
510
+ return 0;
511
+ const batchSize = Math.max(1, this.cfg().batchUpdateSize ?? 200);
512
+ return this.tx(async () => {
513
+ let affected = 0;
514
+ for (let i = 0; i < list.length; i += batchSize) {
515
+ const chunk = list.slice(i, i + batchSize);
516
+ for (const entity of chunk) {
517
+ affected += await this.updateById(entity);
518
+ }
519
+ }
520
+ return affected;
521
+ });
522
+ }
523
+ /** 条件更新 */
524
+ async update(entity, wrapper) {
525
+ await this.assertLogicDeleteColumn();
526
+ const frag = this.withLogic(wrapper).build();
527
+ if (!frag.whereSql) {
528
+ throw new Error("update 必须带 WHERE 条件,防止全表更新");
529
+ }
530
+ const row = { ...entity };
531
+ await (0, fill_1.applyUpdateFill)(row, this.cfg(), this.opts.fill);
532
+ await (0, entityDecorators_1.applyEntityFieldFills)(this.opts.entity, row, "update");
533
+ await this.stripMissingFillFields(row);
534
+ const normalized = this.normalizeEntityRow(row);
535
+ const version = this.isOptimisticLock() &&
536
+ normalized[this.opts.versionField] !== undefined
537
+ ? normalized[this.opts.versionField]
538
+ : undefined;
539
+ const keys = Object.keys(normalized).filter((k) => k !== this.opts.versionField &&
540
+ this.shouldWriteUpdateValue(normalized[k]));
541
+ const sets = keys.map((k) => `${this.colSql(k)} = ?`);
542
+ const values = keys.map((k) => normalized[k]);
543
+ if (version !== undefined) {
544
+ sets.push(`${this.colSql(this.opts.versionField)} = ${this.colSql(this.opts.versionField)} + 1`);
545
+ }
546
+ if (sets.length === 0)
547
+ return 0;
548
+ let whereSql = frag.whereSql;
549
+ const params = [...frag.params];
550
+ if (version !== undefined) {
551
+ whereSql += ` AND ${this.colSql(this.opts.versionField)} = ?`;
552
+ params.push(version);
553
+ }
554
+ const sql = `UPDATE ${this.tableSql()} SET ${sets.join(", ")} ${whereSql} ${frag.lastSql}`.trim();
555
+ const result = await this.query(sql, [...values, ...params]);
556
+ const affected = this.dialect.extractAffectedRows(result);
557
+ if (version !== undefined && affected === 0) {
558
+ throw new errors_1.OptimisticLockError();
559
+ }
560
+ return affected;
561
+ }
562
+ /** 按主键删除(开启逻辑删除时改为 UPDATE) */
563
+ async deleteById(id) {
564
+ return this.deleteByIds([id]);
565
+ }
566
+ /**
567
+ * 按主键批量删除,返回影响行数。
568
+ * 开启逻辑删除时一条 UPDATE ... IN (...),否则物理 DELETE。
569
+ */
570
+ async deleteByIds(ids) {
571
+ return this.deleteByIdsInternal(ids, false);
572
+ }
573
+ /**
574
+ * 按主键物理删除(始终 DELETE,忽略逻辑删除开关)。
575
+ * 用于彻底清除或回收站清空。
576
+ */
577
+ async physicalDeleteById(id) {
578
+ return this.physicalDeleteByIds([id]);
579
+ }
580
+ /** 按主键批量物理删除 */
581
+ async physicalDeleteByIds(ids) {
582
+ return this.deleteByIdsInternal(ids, true);
583
+ }
584
+ /** 条件删除(开启逻辑删除时改为 UPDATE) */
585
+ async delete(wrapper) {
586
+ return this.deleteByWrapper(wrapper, false);
587
+ }
588
+ /**
589
+ * 条件物理删除(始终 DELETE)。
590
+ * 必须带 WHERE;不会自动附加「未删除」条件(可删已逻辑删除行)。
591
+ */
592
+ async physicalDelete(wrapper) {
593
+ return this.deleteByWrapper(wrapper, true);
594
+ }
595
+ async deleteByIdsInternal(ids, physical) {
596
+ const list = (ids ?? []).filter((id) => id !== undefined && id !== null);
597
+ if (list.length === 0)
598
+ return 0;
599
+ if (!physical) {
600
+ await this.assertLogicDeleteColumn();
601
+ }
602
+ const batchSize = Math.max(1, this.cfg().batchInSize ?? 500);
603
+ return this.tx(async () => {
604
+ let affected = 0;
605
+ for (let i = 0; i < list.length; i += batchSize) {
606
+ const chunk = list.slice(i, i + batchSize);
607
+ const placeholders = chunk.map(() => "?").join(", ");
608
+ if (physical || !this.isLogicDelete()) {
609
+ const sql = `DELETE FROM ${this.tableSql()} WHERE ${this.idSql()} IN (${placeholders})`;
610
+ const result = await this.query(sql, chunk);
611
+ affected += this.dialect.extractAffectedRows(result);
612
+ continue;
613
+ }
614
+ const nd = this.notDeletedClause();
615
+ affected += await this.logicDeleteBy(`${this.idSql()} IN (${placeholders}) AND ${nd.sql}`, [...chunk, ...nd.params]);
616
+ }
617
+ return affected;
618
+ });
619
+ }
620
+ async deleteByWrapper(wrapper, physical) {
621
+ if (!physical) {
622
+ await this.assertLogicDeleteColumn();
623
+ }
624
+ const frag = physical
625
+ ? (wrapper ?? new QueryWrapper_1.QueryWrapper(this.dialect)).build()
626
+ : this.withLogic(wrapper).build();
627
+ if (!frag.whereSql) {
628
+ throw new Error(`${physical ? "physicalDelete" : "delete"} 必须带 WHERE 条件,防止全表删除`);
629
+ }
630
+ if (physical || !this.isLogicDelete()) {
631
+ const sql = `DELETE FROM ${this.tableSql()} ${frag.whereSql} ${frag.lastSql}`.trim();
632
+ const result = await this.query(sql, frag.params);
633
+ return this.dialect.extractAffectedRows(result);
634
+ }
635
+ return this.logicDeleteBy(frag.whereSql.replace(/^WHERE\s+/i, ""), frag.params);
636
+ }
637
+ async logicDeleteBy(whereBody, whereParams) {
638
+ const patch = {
639
+ [this.opts.logicDeleteField]: this.opts.logicDeleteValue,
640
+ };
641
+ await (0, fill_1.applyUpdateFill)(patch, this.cfg(), this.opts.fill);
642
+ await (0, entityDecorators_1.applyEntityFieldFills)(this.opts.entity, patch, "update");
643
+ await this.stripMissingFillFields(patch);
644
+ const keys = Object.keys(patch).filter((k) => patch[k] !== undefined);
645
+ const sets = keys.map((k) => `${this.colSql(k)} = ?`).join(", ");
646
+ const values = keys.map((k) => patch[k]);
647
+ const sql = `UPDATE ${this.tableSql()} SET ${sets} WHERE ${whereBody}`;
648
+ const result = await this.query(sql, [...values, ...whereParams]);
649
+ return this.dialect.extractAffectedRows(result);
650
+ }
651
+ }
652
+ exports.BaseMapper = BaseMapper;
653
+ //# sourceMappingURL=BaseMapper.js.map