1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var tslib_1 = require("tslib");
|
4 | var PromiseUtils_1 = require("../util/PromiseUtils");
|
5 | var SubjectTopoligicalSorter_1 = require("./SubjectTopoligicalSorter");
|
6 | var SubjectChangedColumnsComputer_1 = require("./SubjectChangedColumnsComputer");
|
7 | var SubjectWithoutIdentifierError_1 = require("../error/SubjectWithoutIdentifierError");
|
8 | var SubjectRemovedAndUpdatedError_1 = require("../error/SubjectRemovedAndUpdatedError");
|
9 | var MongoQueryRunner_1 = require("../driver/mongodb/MongoQueryRunner");
|
10 | var MongoDriver_1 = require("../driver/mongodb/MongoDriver");
|
11 | var BroadcasterResult_1 = require("../subscriber/BroadcasterResult");
|
12 | var OracleDriver_1 = require("../driver/oracle/OracleDriver");
|
13 | var NestedSetSubjectExecutor_1 = require("./tree/NestedSetSubjectExecutor");
|
14 | var ClosureSubjectExecutor_1 = require("./tree/ClosureSubjectExecutor");
|
15 | var MaterializedPathSubjectExecutor_1 = require("./tree/MaterializedPathSubjectExecutor");
|
16 | var OrmUtils_1 = require("../util/OrmUtils");
|
17 | /**
|
18 | * Executes all database operations (inserts, updated, deletes) that must be executed
|
19 | * with given persistence subjects.
|
20 | */
|
21 | var SubjectExecutor = /** @class */ (function () {
|
22 | // -------------------------------------------------------------------------
|
23 | // Constructor
|
24 | // -------------------------------------------------------------------------
|
25 | function SubjectExecutor(queryRunner, subjects, options) {
|
26 | // -------------------------------------------------------------------------
|
27 | // Public Properties
|
28 | // -------------------------------------------------------------------------
|
29 | /**
|
30 | * Indicates if executor has any operations to execute (e.g. has insert / update / delete operations to be executed).
|
31 | */
|
32 | this.hasExecutableOperations = false;
|
33 | /**
|
34 | * Subjects that must be inserted.
|
35 | */
|
36 | this.insertSubjects = [];
|
37 | /**
|
38 | * Subjects that must be updated.
|
39 | */
|
40 | this.updateSubjects = [];
|
41 | /**
|
42 | * Subjects that must be removed.
|
43 | */
|
44 | this.removeSubjects = [];
|
45 | this.queryRunner = queryRunner;
|
46 | this.allSubjects = subjects;
|
47 | this.options = options;
|
48 | this.validate();
|
49 | this.recompute();
|
50 | }
|
51 | // -------------------------------------------------------------------------
|
52 | // Public Methods
|
53 | // -------------------------------------------------------------------------
|
54 | /**
|
55 | * Executes all operations over given array of subjects.
|
56 | * Executes queries using given query runner.
|
57 | */
|
58 | SubjectExecutor.prototype.execute = function () {
|
59 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
60 | var broadcasterResult;
|
61 | return tslib_1.__generator(this, function (_a) {
|
62 | switch (_a.label) {
|
63 | case 0:
|
64 | broadcasterResult = undefined;
|
65 | if (!(!this.options || this.options.listeners !== false)) return [3 /*break*/, 2];
|
66 | // console.time(".broadcastBeforeEventsForAll");
|
67 | broadcasterResult = this.broadcastBeforeEventsForAll();
|
68 | if (!(broadcasterResult.promises.length > 0)) return [3 /*break*/, 2];
|
69 | return [4 /*yield*/, Promise.all(broadcasterResult.promises)];
|
70 | case 1:
|
71 | _a.sent();
|
72 | _a.label = 2;
|
73 | case 2:
|
74 | // since event listeners and subscribers can call save methods and/or trigger entity changes we need to recompute operational subjects
|
75 | // recompute only in the case if any listener or subscriber was really executed
|
76 | if (broadcasterResult && broadcasterResult.count > 0) {
|
77 | // console.time(".recompute");
|
78 | this.insertSubjects.forEach(function (subject) { return subject.recompute(); });
|
79 | this.updateSubjects.forEach(function (subject) { return subject.recompute(); });
|
80 | this.removeSubjects.forEach(function (subject) { return subject.recompute(); });
|
81 | this.recompute();
|
82 | // console.timeEnd(".recompute");
|
83 | }
|
84 | // make sure our insert subjects are sorted (using topological sorting) to make cascade inserts work properly
|
85 | // console.timeEnd("prepare");
|
86 | // execute all insert operations
|
87 | // console.time(".insertion");
|
88 | this.insertSubjects = new SubjectTopoligicalSorter_1.SubjectTopoligicalSorter(this.insertSubjects).sort("insert");
|
89 | return [4 /*yield*/, this.executeInsertOperations()];
|
90 | case 3:
|
91 | _a.sent();
|
92 | // console.timeEnd(".insertion");
|
93 | // recompute update operations since insertion can create updation operations for the
|
94 | // properties it wasn't able to handle on its own (referenced columns)
|
95 | this.updateSubjects = this.allSubjects.filter(function (subject) { return subject.mustBeUpdated; });
|
96 | // execute update operations
|
97 | // console.time(".updation");
|
98 | return [4 /*yield*/, this.executeUpdateOperations()];
|
99 | case 4:
|
100 | // execute update operations
|
101 | // console.time(".updation");
|
102 | _a.sent();
|
103 | // console.timeEnd(".updation");
|
104 | // make sure our remove subjects are sorted (using topological sorting) when multiple entities are passed for the removal
|
105 | // console.time(".removal");
|
106 | this.removeSubjects = new SubjectTopoligicalSorter_1.SubjectTopoligicalSorter(this.removeSubjects).sort("delete");
|
107 | return [4 /*yield*/, this.executeRemoveOperations()];
|
108 | case 5:
|
109 | _a.sent();
|
110 | // console.timeEnd(".removal");
|
111 | // update all special columns in persisted entities, like inserted id or remove ids from the removed entities
|
112 | // console.time(".updateSpecialColumnsInPersistedEntities");
|
113 | return [4 /*yield*/, this.updateSpecialColumnsInPersistedEntities()];
|
114 | case 6:
|
115 | // console.timeEnd(".removal");
|
116 | // update all special columns in persisted entities, like inserted id or remove ids from the removed entities
|
117 | // console.time(".updateSpecialColumnsInPersistedEntities");
|
118 | _a.sent();
|
119 | if (!(!this.options || this.options.listeners !== false)) return [3 /*break*/, 8];
|
120 | // console.time(".broadcastAfterEventsForAll");
|
121 | broadcasterResult = this.broadcastAfterEventsForAll();
|
122 | if (!(broadcasterResult.promises.length > 0)) return [3 /*break*/, 8];
|
123 | return [4 /*yield*/, Promise.all(broadcasterResult.promises)];
|
124 | case 7:
|
125 | _a.sent();
|
126 | _a.label = 8;
|
127 | case 8: return [2 /*return*/];
|
128 | }
|
129 | });
|
130 | });
|
131 | };
|
132 | // -------------------------------------------------------------------------
|
133 | // Protected Methods
|
134 | // -------------------------------------------------------------------------
|
135 | /**
|
136 | * Validates all given subjects.
|
137 | */
|
138 | SubjectExecutor.prototype.validate = function () {
|
139 | this.allSubjects.forEach(function (subject) {
|
140 | if (subject.mustBeUpdated && subject.mustBeRemoved)
|
141 | throw new SubjectRemovedAndUpdatedError_1.SubjectRemovedAndUpdatedError(subject);
|
142 | });
|
143 | };
|
144 | /**
|
145 | * Performs entity re-computations - finds changed columns, re-builds insert/update/remove subjects.
|
146 | */
|
147 | SubjectExecutor.prototype.recompute = function () {
|
148 | new SubjectChangedColumnsComputer_1.SubjectChangedColumnsComputer().compute(this.allSubjects);
|
149 | this.insertSubjects = this.allSubjects.filter(function (subject) { return subject.mustBeInserted; });
|
150 | this.updateSubjects = this.allSubjects.filter(function (subject) { return subject.mustBeUpdated; });
|
151 | this.removeSubjects = this.allSubjects.filter(function (subject) { return subject.mustBeRemoved; });
|
152 | this.hasExecutableOperations = this.insertSubjects.length > 0 || this.updateSubjects.length > 0 || this.removeSubjects.length > 0;
|
153 | };
|
154 | /**
|
155 | * Broadcasts "BEFORE_INSERT", "BEFORE_UPDATE", "BEFORE_REMOVE" events for all given subjects.
|
156 | */
|
157 | SubjectExecutor.prototype.broadcastBeforeEventsForAll = function () {
|
158 | var _this = this;
|
159 | var result = new BroadcasterResult_1.BroadcasterResult();
|
160 | if (this.insertSubjects.length)
|
161 | this.insertSubjects.forEach(function (subject) { return _this.queryRunner.broadcaster.broadcastBeforeInsertEvent(result, subject.metadata, subject.entity); });
|
162 | if (this.updateSubjects.length)
|
163 | this.updateSubjects.forEach(function (subject) { return _this.queryRunner.broadcaster.broadcastBeforeUpdateEvent(result, subject.metadata, subject.entity, subject.databaseEntity, subject.diffColumns, subject.diffRelations); });
|
164 | if (this.removeSubjects.length)
|
165 | this.removeSubjects.forEach(function (subject) { return _this.queryRunner.broadcaster.broadcastBeforeRemoveEvent(result, subject.metadata, subject.entity, subject.databaseEntity); });
|
166 | return result;
|
167 | };
|
168 | /**
|
169 | * Broadcasts "AFTER_INSERT", "AFTER_UPDATE", "AFTER_REMOVE" events for all given subjects.
|
170 | * Returns void if there wasn't any listener or subscriber executed.
|
171 | * Note: this method has a performance-optimized code organization.
|
172 | */
|
173 | SubjectExecutor.prototype.broadcastAfterEventsForAll = function () {
|
174 | var _this = this;
|
175 | var result = new BroadcasterResult_1.BroadcasterResult();
|
176 | if (this.insertSubjects.length)
|
177 | this.insertSubjects.forEach(function (subject) { return _this.queryRunner.broadcaster.broadcastAfterInsertEvent(result, subject.metadata, subject.entity); });
|
178 | if (this.updateSubjects.length)
|
179 | this.updateSubjects.forEach(function (subject) { return _this.queryRunner.broadcaster.broadcastAfterUpdateEvent(result, subject.metadata, subject.entity, subject.databaseEntity, subject.diffColumns, subject.diffRelations); });
|
180 | if (this.removeSubjects.length)
|
181 | this.removeSubjects.forEach(function (subject) { return _this.queryRunner.broadcaster.broadcastAfterRemoveEvent(result, subject.metadata, subject.entity, subject.databaseEntity); });
|
182 | return result;
|
183 | };
|
184 | /**
|
185 | * Executes insert operations.
|
186 | */
|
187 | SubjectExecutor.prototype.executeInsertOperations = function () {
|
188 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
189 | var _a, groupedInsertSubjects, groupedInsertSubjectKeys;
|
190 | var _this = this;
|
191 | return tslib_1.__generator(this, function (_b) {
|
192 | switch (_b.label) {
|
193 | case 0:
|
194 | _a = tslib_1.__read(this.groupBulkSubjects(this.insertSubjects, "insert"), 2), groupedInsertSubjects = _a[0], groupedInsertSubjectKeys = _a[1];
|
195 | // then we run insertion in the sequential order which is important since we have an ordered subjects
|
196 | return [4 /*yield*/, PromiseUtils_1.PromiseUtils.runInSequence(groupedInsertSubjectKeys, function (groupName) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
197 | var subjects, bulkInsertMaps, bulkInsertSubjects, singleInsertSubjects, manager, insertResult_1, insertResult_2;
|
198 | var _this = this;
|
199 | return tslib_1.__generator(this, function (_a) {
|
200 | switch (_a.label) {
|
201 | case 0:
|
202 | subjects = groupedInsertSubjects[groupName];
|
203 | bulkInsertMaps = [];
|
204 | bulkInsertSubjects = [];
|
205 | singleInsertSubjects = [];
|
206 | if (this.queryRunner.connection.driver instanceof MongoDriver_1.MongoDriver) {
|
207 | subjects.forEach(function (subject) {
|
208 | if (subject.metadata.createDateColumn && subject.entity) {
|
209 | subject.entity[subject.metadata.createDateColumn.databaseName] = new Date();
|
210 | }
|
211 | if (subject.metadata.updateDateColumn && subject.entity) {
|
212 | subject.entity[subject.metadata.updateDateColumn.databaseName] = new Date();
|
213 | }
|
214 | subject.createValueSetAndPopChangeMap();
|
215 | bulkInsertSubjects.push(subject);
|
216 | bulkInsertMaps.push(subject.entity);
|
217 | });
|
218 | }
|
219 | else if (this.queryRunner.connection.driver instanceof OracleDriver_1.OracleDriver) {
|
220 | subjects.forEach(function (subject) {
|
221 | singleInsertSubjects.push(subject);
|
222 | });
|
223 | }
|
224 | else {
|
225 | subjects.forEach(function (subject) {
|
226 | // we do not insert in bulk in following cases:
|
227 | // - when there is no values in insert (only defaults are inserted), since we cannot use DEFAULT VALUES expression for multiple inserted rows
|
228 | // - when entity is a tree table, since tree tables require extra operation per each inserted row
|
229 | // - when oracle is used, since oracle's bulk insertion is very bad
|
230 | if (subject.changeMaps.length === 0 ||
|
231 | subject.metadata.treeType ||
|
232 | _this.queryRunner.connection.driver instanceof OracleDriver_1.OracleDriver) {
|
233 | singleInsertSubjects.push(subject);
|
234 | }
|
235 | else {
|
236 | bulkInsertSubjects.push(subject);
|
237 | bulkInsertMaps.push(subject.createValueSetAndPopChangeMap());
|
238 | }
|
239 | });
|
240 | }
|
241 | if (!(this.queryRunner instanceof MongoQueryRunner_1.MongoQueryRunner)) return [3 /*break*/, 2];
|
242 | manager = this.queryRunner.manager;
|
243 | return [4 /*yield*/, manager.insert(subjects[0].metadata.target, bulkInsertMaps)];
|
244 | case 1:
|
245 | insertResult_1 = _a.sent();
|
246 | subjects.forEach(function (subject, index) {
|
247 | subject.identifier = insertResult_1.identifiers[index];
|
248 | subject.generatedMap = insertResult_1.generatedMaps[index];
|
249 | subject.insertedValueSet = bulkInsertMaps[index];
|
250 | });
|
251 | return [3 /*break*/, 6];
|
252 | case 2:
|
253 | if (!(bulkInsertMaps.length > 0)) return [3 /*break*/, 4];
|
254 | return [4 /*yield*/, this.queryRunner
|
255 | .manager
|
256 | .createQueryBuilder()
|
257 | .insert()
|
258 | .into(subjects[0].metadata.target)
|
259 | .values(bulkInsertMaps)
|
260 | .updateEntity(this.options && this.options.reload === false ? false : true)
|
261 | .callListeners(false)
|
262 | .execute()];
|
263 | case 3:
|
264 | insertResult_2 = _a.sent();
|
265 | bulkInsertSubjects.forEach(function (subject, index) {
|
266 | subject.identifier = insertResult_2.identifiers[index];
|
267 | subject.generatedMap = insertResult_2.generatedMaps[index];
|
268 | subject.insertedValueSet = bulkInsertMaps[index];
|
269 | });
|
270 | _a.label = 4;
|
271 | case 4:
|
272 | if (!(singleInsertSubjects.length > 0)) return [3 /*break*/, 6];
|
273 | return [4 /*yield*/, PromiseUtils_1.PromiseUtils.runInSequence(singleInsertSubjects, function (subject) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
274 | return tslib_1.__generator(this, function (_a) {
|
275 | switch (_a.label) {
|
276 | case 0:
|
277 | subject.insertedValueSet = subject.createValueSetAndPopChangeMap(); // important to have because query builder sets inserted values into it
|
278 | if (!(subject.metadata.treeType === "nested-set")) return [3 /*break*/, 2];
|
279 | return [4 /*yield*/, new NestedSetSubjectExecutor_1.NestedSetSubjectExecutor(this.queryRunner).insert(subject)];
|
280 | case 1:
|
281 | _a.sent();
|
282 | _a.label = 2;
|
283 | case 2: return [4 /*yield*/, this.queryRunner
|
284 | .manager
|
285 | .createQueryBuilder()
|
286 | .insert()
|
287 | .into(subject.metadata.target)
|
288 | .values(subject.insertedValueSet)
|
289 | .updateEntity(this.options && this.options.reload === false ? false : true)
|
290 | .callListeners(false)
|
291 | .execute()
|
292 | .then(function (insertResult) {
|
293 | subject.identifier = insertResult.identifiers[0];
|
294 | subject.generatedMap = insertResult.generatedMaps[0];
|
295 | })];
|
296 | case 3:
|
297 | _a.sent();
|
298 | if (!(subject.metadata.treeType === "closure-table")) return [3 /*break*/, 5];
|
299 | return [4 /*yield*/, new ClosureSubjectExecutor_1.ClosureSubjectExecutor(this.queryRunner).insert(subject)];
|
300 | case 4:
|
301 | _a.sent();
|
302 | return [3 /*break*/, 7];
|
303 | case 5:
|
304 | if (!(subject.metadata.treeType === "materialized-path")) return [3 /*break*/, 7];
|
305 | return [4 /*yield*/, new MaterializedPathSubjectExecutor_1.MaterializedPathSubjectExecutor(this.queryRunner).insert(subject)];
|
306 | case 6:
|
307 | _a.sent();
|
308 | _a.label = 7;
|
309 | case 7: return [2 /*return*/];
|
310 | }
|
311 | });
|
312 | }); })];
|
313 | case 5:
|
314 | _a.sent();
|
315 | _a.label = 6;
|
316 | case 6:
|
317 | subjects.forEach(function (subject) {
|
318 | if (subject.generatedMap) {
|
319 | subject.metadata.columns.forEach(function (column) {
|
320 | var value = column.getEntityValue(subject.generatedMap);
|
321 | if (value !== undefined && value !== null) {
|
322 | var preparedValue = _this.queryRunner.connection.driver.prepareHydratedValue(value, column);
|
323 | column.setEntityValue(subject.generatedMap, preparedValue);
|
324 | }
|
325 | });
|
326 | }
|
327 | });
|
328 | return [2 /*return*/];
|
329 | }
|
330 | });
|
331 | }); })];
|
332 | case 1:
|
333 | // then we run insertion in the sequential order which is important since we have an ordered subjects
|
334 | _b.sent();
|
335 | return [2 /*return*/];
|
336 | }
|
337 | });
|
338 | });
|
339 | };
|
340 | /**
|
341 | * Updates all given subjects in the database.
|
342 | */
|
343 | SubjectExecutor.prototype.executeUpdateOperations = function () {
|
344 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
345 | var _this = this;
|
346 | return tslib_1.__generator(this, function (_a) {
|
347 | switch (_a.label) {
|
348 | case 0: return [4 /*yield*/, Promise.all(this.updateSubjects.map(function (subject) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
349 | var partialEntity, manager, updateMap, updateQueryBuilder, updateResult;
|
350 | var _this = this;
|
351 | return tslib_1.__generator(this, function (_a) {
|
352 | switch (_a.label) {
|
353 | case 0:
|
354 | if (!subject.identifier)
|
355 | throw new SubjectWithoutIdentifierError_1.SubjectWithoutIdentifierError(subject);
|
356 | if (!(this.queryRunner instanceof MongoQueryRunner_1.MongoQueryRunner)) return [3 /*break*/, 2];
|
357 | partialEntity = OrmUtils_1.OrmUtils.mergeDeep({}, subject.entity);
|
358 | if (subject.metadata.objectIdColumn && subject.metadata.objectIdColumn.propertyName) {
|
359 | delete partialEntity[subject.metadata.objectIdColumn.propertyName];
|
360 | }
|
361 | if (subject.metadata.createDateColumn && subject.metadata.createDateColumn.propertyName) {
|
362 | delete partialEntity[subject.metadata.createDateColumn.propertyName];
|
363 | }
|
364 | if (subject.metadata.updateDateColumn && subject.metadata.updateDateColumn.propertyName) {
|
365 | partialEntity[subject.metadata.updateDateColumn.propertyName] = new Date();
|
366 | }
|
367 | manager = this.queryRunner.manager;
|
368 | return [4 /*yield*/, manager.update(subject.metadata.target, subject.identifier, partialEntity)];
|
369 | case 1:
|
370 | _a.sent();
|
371 | return [3 /*break*/, 4];
|
372 | case 2:
|
373 | updateMap = subject.createValueSetAndPopChangeMap();
|
374 | updateQueryBuilder = this.queryRunner
|
375 | .manager
|
376 | .createQueryBuilder()
|
377 | .update(subject.metadata.target)
|
378 | .set(updateMap)
|
379 | .updateEntity(this.options && this.options.reload === false ? false : true)
|
380 | .callListeners(false);
|
381 | if (subject.entity) {
|
382 | updateQueryBuilder.whereEntity(subject.identifier);
|
383 | }
|
384 | else { // in this case identifier is just conditions object to update by
|
385 | updateQueryBuilder.where(subject.identifier);
|
386 | }
|
387 | return [4 /*yield*/, updateQueryBuilder.execute()];
|
388 | case 3:
|
389 | updateResult = _a.sent();
|
390 | subject.generatedMap = updateResult.generatedMaps[0];
|
391 | if (subject.generatedMap) {
|
392 | subject.metadata.columns.forEach(function (column) {
|
393 | var value = column.getEntityValue(subject.generatedMap);
|
394 | if (value !== undefined && value !== null) {
|
395 | var preparedValue = _this.queryRunner.connection.driver.prepareHydratedValue(value, column);
|
396 | column.setEntityValue(subject.generatedMap, preparedValue);
|
397 | }
|
398 | });
|
399 | }
|
400 | _a.label = 4;
|
401 | case 4: return [2 /*return*/];
|
402 | }
|
403 | });
|
404 | }); }))];
|
405 | case 1:
|
406 | _a.sent();
|
407 | return [2 /*return*/];
|
408 | }
|
409 | });
|
410 | });
|
411 | };
|
412 | /**
|
413 | * Removes all given subjects from the database.
|
414 | *
|
415 | * todo: we need to apply topological sort here as well
|
416 | */
|
417 | SubjectExecutor.prototype.executeRemoveOperations = function () {
|
418 | return tslib_1.__awaiter(this, void 0, void 0, function () {
|
419 | var _a, groupedRemoveSubjects, groupedRemoveSubjectKeys;
|
420 | var _this = this;
|
421 | return tslib_1.__generator(this, function (_b) {
|
422 | switch (_b.label) {
|
423 | case 0:
|
424 | _a = tslib_1.__read(this.groupBulkSubjects(this.removeSubjects, "delete"), 2), groupedRemoveSubjects = _a[0], groupedRemoveSubjectKeys = _a[1];
|
425 | return [4 /*yield*/, PromiseUtils_1.PromiseUtils.runInSequence(groupedRemoveSubjectKeys, function (groupName) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
426 | var subjects, deleteMaps, manager;
|
427 | return tslib_1.__generator(this, function (_a) {
|
428 | switch (_a.label) {
|
429 | case 0:
|
430 | subjects = groupedRemoveSubjects[groupName];
|
431 | deleteMaps = subjects.map(function (subject) {
|
432 | if (!subject.identifier)
|
433 | throw new SubjectWithoutIdentifierError_1.SubjectWithoutIdentifierError(subject);
|
434 | return subject.identifier;
|
435 | });
|
436 | if (!(this.queryRunner instanceof MongoQueryRunner_1.MongoQueryRunner)) return [3 /*break*/, 2];
|
437 | manager = this.queryRunner.manager;
|
438 | return [4 /*yield*/, manager.delete(subjects[0].metadata.target, deleteMaps)];
|
439 | case 1:
|
440 | _a.sent();
|
441 | return [3 /*break*/, 4];
|
442 | case 2:
|
443 | // here we execute our deletion query
|
444 | // we don't need to specify entities and set update entity to true since the only thing query builder
|
445 | // will do for use is a primary keys deletion which is handled by us later once persistence is finished
|
446 | // also, we disable listeners because we call them on our own in persistence layer
|
447 | return [4 /*yield*/, this.queryRunner
|
448 | .manager
|
449 | .createQueryBuilder()
|
450 | .delete()
|
451 | .from(subjects[0].metadata.target)
|
452 | .where(deleteMaps)
|
453 | .callListeners(false)
|
454 | .execute()];
|
455 | case 3:
|
456 | // here we execute our deletion query
|
457 | // we don't need to specify entities and set update entity to true since the only thing query builder
|
458 | // will do for use is a primary keys deletion which is handled by us later once persistence is finished
|
459 | // also, we disable listeners because we call them on our own in persistence layer
|
460 | _a.sent();
|
461 | _a.label = 4;
|
462 | case 4: return [2 /*return*/];
|
463 | }
|
464 | });
|
465 | }); })];
|
466 | case 1:
|
467 | _b.sent();
|
468 | return [2 /*return*/];
|
469 | }
|
470 | });
|
471 | });
|
472 | };
|
473 | /**
|
474 | * Updates all special columns of the saving entities (create date, update date, version, etc.).
|
475 | * Also updates nullable columns and columns with default values.
|
476 | */
|
477 | SubjectExecutor.prototype.updateSpecialColumnsInPersistedEntities = function () {
|
478 | var _this = this;
|
479 | // update inserted entity properties
|
480 | if (this.insertSubjects.length)
|
481 | this.updateSpecialColumnsInInsertedAndUpdatedEntities(this.insertSubjects);
|
482 | // update updated entity properties
|
483 | if (this.updateSubjects.length)
|
484 | this.updateSpecialColumnsInInsertedAndUpdatedEntities(this.updateSubjects);
|
485 | // remove ids from the entities that were removed
|
486 | if (this.removeSubjects.length) {
|
487 | this.removeSubjects.forEach(function (subject) {
|
488 | if (!subject.entity)
|
489 | return;
|
490 | subject.metadata.primaryColumns.forEach(function (primaryColumn) {
|
491 | primaryColumn.setEntityValue(subject.entity, undefined);
|
492 | });
|
493 | });
|
494 | }
|
495 | // other post-persist updations
|
496 | this.allSubjects.forEach(function (subject) {
|
497 | if (!subject.entity)
|
498 | return;
|
499 | subject.metadata.relationIds.forEach(function (relationId) {
|
500 | relationId.setValue(subject.entity);
|
501 | });
|
502 | // mongo _id remove
|
503 | if (_this.queryRunner instanceof MongoQueryRunner_1.MongoQueryRunner) {
|
504 | if (subject.metadata.objectIdColumn
|
505 | && subject.metadata.objectIdColumn.databaseName
|
506 | && subject.metadata.objectIdColumn.databaseName !== subject.metadata.objectIdColumn.propertyName) {
|
507 | delete subject.entity[subject.metadata.objectIdColumn.databaseName];
|
508 | }
|
509 | }
|
510 | });
|
511 | };
|
512 | /**
|
513 | * Updates all special columns of the saving entities (create date, update date, version, etc.).
|
514 | * Also updates nullable columns and columns with default values.
|
515 | */
|
516 | SubjectExecutor.prototype.updateSpecialColumnsInInsertedAndUpdatedEntities = function (subjects) {
|
517 | var _this = this;
|
518 | subjects.forEach(function (subject) {
|
519 | if (!subject.entity)
|
520 | return;
|
521 | // set values to "null" for nullable columns that did not have values
|
522 | subject.metadata.columns.forEach(function (column) {
|
523 | // if table inheritance is used make sure this column is not child's column
|
524 | if (subject.metadata.childEntityMetadatas.length > 0 && subject.metadata.childEntityMetadatas.map(function (metadata) { return metadata.target; }).indexOf(column.target) !== -1)
|
525 | return;
|
526 | // entities does not have virtual columns
|
527 | if (column.isVirtual)
|
528 | return;
|
529 | // update nullable columns
|
530 | if (column.isNullable) {
|
531 | var columnValue = column.getEntityValue(subject.entity);
|
532 | if (columnValue === undefined)
|
533 | column.setEntityValue(subject.entity, null);
|
534 | }
|
535 | // update relational columns
|
536 | if (subject.updatedRelationMaps.length > 0) {
|
537 | subject.updatedRelationMaps.forEach(function (updatedRelationMap) {
|
538 | updatedRelationMap.relation.joinColumns.forEach(function (column) {
|
539 | if (column.isVirtual === true)
|
540 | return;
|
541 | column.setEntityValue(subject.entity, updatedRelationMap.value instanceof Object ? column.referencedColumn.getEntityValue(updatedRelationMap.value) : updatedRelationMap.value);
|
542 | });
|
543 | });
|
544 | }
|
545 | });
|
546 | // merge into entity all generated values returned by a database
|
547 | if (subject.generatedMap)
|
548 | _this.queryRunner.manager.merge(subject.metadata.target, subject.entity, subject.generatedMap);
|
549 | });
|
550 | };
|
551 | /**
|
552 | * Groups subjects by metadata names (by tables) to make bulk insertions and deletions possible.
|
553 | * However there are some limitations with bulk insertions of data into tables with generated (increment) columns
|
554 | * in some drivers. Some drivers like mysql and sqlite does not support returning multiple generated columns
|
555 | * after insertion and can only return a single generated column value, that's why its not possible to do bulk insertion,
|
556 | * because it breaks insertion result's generatedMap and leads to problems when this subject is used in other subjects saves.
|
557 | * That's why we only support bulking in junction tables for those drivers.
|
558 | *
|
559 | * Other drivers like postgres and sql server support RETURNING / OUTPUT statement which allows to return generated
|
560 | * id for each inserted row, that's why bulk insertion is not limited to junction tables in there.
|
561 | */
|
562 | SubjectExecutor.prototype.groupBulkSubjects = function (subjects, type) {
|
563 | var group = {};
|
564 | var keys = [];
|
565 | var groupingAllowed = type === "delete" || this.queryRunner.connection.driver.isReturningSqlSupported();
|
566 | subjects.forEach(function (subject, index) {
|
567 | var key = groupingAllowed || subject.metadata.isJunction ? subject.metadata.name : subject.metadata.name + "_" + index;
|
568 | if (!group[key]) {
|
569 | group[key] = [subject];
|
570 | keys.push(key);
|
571 | }
|
572 | else {
|
573 | group[key].push(subject);
|
574 | }
|
575 | });
|
576 | return [group, keys];
|
577 | };
|
578 | return SubjectExecutor;
|
579 | }());
|
580 | exports.SubjectExecutor = SubjectExecutor;
|
581 |
|
582 | //# sourceMappingURL=SubjectExecutor.js.map
|