UNPKG

39.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4var PromiseUtils_1 = require("../util/PromiseUtils");
5var SubjectTopoligicalSorter_1 = require("./SubjectTopoligicalSorter");
6var SubjectChangedColumnsComputer_1 = require("./SubjectChangedColumnsComputer");
7var SubjectWithoutIdentifierError_1 = require("../error/SubjectWithoutIdentifierError");
8var SubjectRemovedAndUpdatedError_1 = require("../error/SubjectRemovedAndUpdatedError");
9var MongoQueryRunner_1 = require("../driver/mongodb/MongoQueryRunner");
10var MongoDriver_1 = require("../driver/mongodb/MongoDriver");
11var BroadcasterResult_1 = require("../subscriber/BroadcasterResult");
12var OracleDriver_1 = require("../driver/oracle/OracleDriver");
13var NestedSetSubjectExecutor_1 = require("./tree/NestedSetSubjectExecutor");
14var ClosureSubjectExecutor_1 = require("./tree/ClosureSubjectExecutor");
15var MaterializedPathSubjectExecutor_1 = require("./tree/MaterializedPathSubjectExecutor");
16var OrmUtils_1 = require("../util/OrmUtils");
17/**
18 * Executes all database operations (inserts, updated, deletes) that must be executed
19 * with given persistence subjects.
20 */
21var 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}());
580exports.SubjectExecutor = SubjectExecutor;
581
582//# sourceMappingURL=SubjectExecutor.js.map