Remove describe.only
from spec file as this was stopping all other
spec files from running.
Closes #7284
1 | 10 |
import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; |
2 | 10 |
import {SapDriver} from "../driver/sap/SapDriver"; |
3 | 10 |
import {EntityMetadata} from "../metadata/EntityMetadata"; |
4 | 10 |
import {ColumnMetadata} from "../metadata/ColumnMetadata"; |
5 | 10 |
import {IndexMetadata} from "../metadata/IndexMetadata"; |
6 | 10 |
import {RelationMetadata} from "../metadata/RelationMetadata"; |
7 | 10 |
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata"; |
8 |
import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage"; |
|
9 |
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs"; |
|
10 | 10 |
import {RelationIdMetadata} from "../metadata/RelationIdMetadata"; |
11 | 10 |
import {RelationCountMetadata} from "../metadata/RelationCountMetadata"; |
12 | 10 |
import { EventListenerTypes } from "../metadata/types/EventListenerTypes"; |
13 | 10 |
import {MetadataUtils} from "./MetadataUtils"; |
14 |
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs"; |
|
15 | 10 |
import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder"; |
16 | 10 |
import {ClosureJunctionEntityMetadataBuilder} from "./ClosureJunctionEntityMetadataBuilder"; |
17 | 10 |
import {RelationJoinColumnBuilder} from "./RelationJoinColumnBuilder"; |
18 |
import {Connection} from "../connection/Connection"; |
|
19 | 10 |
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata"; |
20 | 10 |
import {UniqueMetadata} from "../metadata/UniqueMetadata"; |
21 | 10 |
import {MysqlDriver} from "../driver/mysql/MysqlDriver"; |
22 | 10 |
import {CheckMetadata} from "../metadata/CheckMetadata"; |
23 | 10 |
import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; |
24 | 10 |
import {PostgresDriver} from "../driver/postgres/PostgresDriver"; |
25 | 10 |
import {ExclusionMetadata} from "../metadata/ExclusionMetadata"; |
26 | 10 |
import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; |
27 |
|
|
28 |
/**
|
|
29 |
* Builds EntityMetadata objects and all its sub-metadatas.
|
|
30 |
*/
|
|
31 | 10 |
export class EntityMetadataBuilder { |
32 |
|
|
33 |
// -------------------------------------------------------------------------
|
|
34 |
// Protected Properties
|
|
35 |
// -------------------------------------------------------------------------
|
|
36 |
|
|
37 |
/**
|
|
38 |
* Used to build entity metadatas of the junction entities.
|
|
39 |
*/
|
|
40 |
protected junctionEntityMetadataBuilder: JunctionEntityMetadataBuilder; |
|
41 |
|
|
42 |
/**
|
|
43 |
* Used to build entity metadatas of the closure junction entities.
|
|
44 |
*/
|
|
45 |
protected closureJunctionEntityMetadataBuilder: ClosureJunctionEntityMetadataBuilder; |
|
46 |
|
|
47 |
/**
|
|
48 |
* Used to build join columns of the relations.
|
|
49 |
*/
|
|
50 |
protected relationJoinColumnBuilder: RelationJoinColumnBuilder; |
|
51 |
|
|
52 |
// -------------------------------------------------------------------------
|
|
53 |
// Constructor
|
|
54 |
// -------------------------------------------------------------------------
|
|
55 |
|
|
56 | 10 |
constructor(private connection: Connection, |
57 | 10 |
private metadataArgsStorage: MetadataArgsStorage) { |
58 |
|
|
59 | 10 |
this.junctionEntityMetadataBuilder = new JunctionEntityMetadataBuilder(connection); |
60 | 10 |
this.closureJunctionEntityMetadataBuilder = new ClosureJunctionEntityMetadataBuilder(connection); |
61 | 10 |
this.relationJoinColumnBuilder = new RelationJoinColumnBuilder(connection); |
62 |
}
|
|
63 |
|
|
64 |
// -------------------------------------------------------------------------
|
|
65 |
// Public Methods
|
|
66 |
// -------------------------------------------------------------------------
|
|
67 |
|
|
68 |
/**
|
|
69 |
* Builds a complete entity metadatas for the given entity classes.
|
|
70 |
*/
|
|
71 | 10 |
build(entityClasses?: Function[]): EntityMetadata[] { |
72 |
|
|
73 |
// if entity classes to filter entities by are given then do filtering, otherwise use all
|
|
74 | 10 |
const allTables = entityClasses ? this.metadataArgsStorage.filterTables(entityClasses) : this.metadataArgsStorage.tables; |
75 |
|
|
76 |
// filter out table metadata args for those we really create entity metadatas and tables in the db
|
|
77 | 10 |
const realTables = allTables.filter(table => table.type === "regular" || table.type === "closure" || table.type === "entity-child" || table.type === "view"); |
78 |
|
|
79 |
// create entity metadatas for a user defined entities (marked with @Entity decorator or loaded from entity schemas)
|
|
80 | 10 |
const entityMetadatas = realTables.map(tableArgs => this.createEntityMetadata(tableArgs)); |
81 |
|
|
82 |
// compute parent entity metadatas for table inheritance
|
|
83 | 10 |
entityMetadatas.forEach(entityMetadata => this.computeParentEntityMetadata(entityMetadatas, entityMetadata)); |
84 |
|
|
85 |
// after all metadatas created we set child entity metadatas for table inheritance
|
|
86 | 10 |
entityMetadatas.forEach(metadata => { |
87 | 10 |
metadata.childEntityMetadatas = entityMetadatas.filter(childMetadata => { |
88 | 10 |
return metadata.target instanceof Function |
89 | 10 |
&& childMetadata.target instanceof Function |
90 | 10 |
&& MetadataUtils.isInherited(childMetadata.target, metadata.target); |
91 |
});
|
|
92 |
});
|
|
93 |
|
|
94 |
// build entity metadata (step0), first for non-single-table-inherited entity metadatas (dependant)
|
|
95 | 10 |
entityMetadatas
|
96 | 10 |
.filter(entityMetadata => entityMetadata.tableType !== "entity-child") |
97 | 10 |
.forEach(entityMetadata => entityMetadata.build()); |
98 |
|
|
99 |
// build entity metadata (step0), now for single-table-inherited entity metadatas (dependant)
|
|
100 | 10 |
entityMetadatas
|
101 | 10 |
.filter(entityMetadata => entityMetadata.tableType === "entity-child") |
102 | 10 |
.forEach(entityMetadata => entityMetadata.build()); |
103 |
|
|
104 |
// compute entity metadata columns, relations, etc. first for the regular, non-single-table-inherited entity metadatas
|
|
105 | 10 |
entityMetadatas
|
106 | 10 |
.filter(entityMetadata => entityMetadata.tableType !== "entity-child") |
107 | 10 |
.forEach(entityMetadata => this.computeEntityMetadataStep1(entityMetadatas, entityMetadata)); |
108 |
|
|
109 |
// then do it for single table inheritance children (since they are depend on their parents to be built)
|
|
110 | 10 |
entityMetadatas
|
111 | 10 |
.filter(entityMetadata => entityMetadata.tableType === "entity-child") |
112 | 10 |
.forEach(entityMetadata => this.computeEntityMetadataStep1(entityMetadatas, entityMetadata)); |
113 |
|
|
114 |
// calculate entity metadata computed properties and all its sub-metadatas
|
|
115 | 10 |
entityMetadatas.forEach(entityMetadata => this.computeEntityMetadataStep2(entityMetadata)); |
116 |
|
|
117 |
// calculate entity metadata's inverse properties
|
|
118 | 10 |
entityMetadatas.forEach(entityMetadata => this.computeInverseProperties(entityMetadata, entityMetadatas)); |
119 |
|
|
120 |
// go through all entity metadatas and create foreign keys / junction entity metadatas for their relations
|
|
121 | 10 |
entityMetadatas
|
122 | 10 |
.filter(entityMetadata => entityMetadata.tableType !== "entity-child") |
123 | 10 |
.forEach(entityMetadata => { |
124 |
|
|
125 |
// create entity's relations join columns (for many-to-one and one-to-one owner)
|
|
126 | 10 |
entityMetadata.relations.filter(relation => relation.isOneToOne || relation.isManyToOne).forEach(relation => { |
127 | 10 |
const joinColumns = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName); |
128 | 10 |
const { foreignKey, uniqueConstraint } = this.relationJoinColumnBuilder.build(joinColumns, relation); // create a foreign key based on its metadata args |
129 | 10 |
if (foreignKey) { |
130 | 10 |
relation.registerForeignKeys(foreignKey); // push it to the relation and thus register there a join column |
131 | 10 |
entityMetadata.foreignKeys.push(foreignKey); |
132 |
}
|
|
133 | 10 |
if (uniqueConstraint) { |
134 | 10 |
if (this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver |
135 | 10 |
|| this.connection.driver instanceof SqlServerDriver || this.connection.driver instanceof SapDriver) { |
136 | 10 |
const index = new IndexMetadata({ |
137 |
entityMetadata: uniqueConstraint.entityMetadata, |
|
138 |
columns: uniqueConstraint.columns, |
|
139 |
args: { |
|
140 |
target: uniqueConstraint.target!, |
|
141 |
name: uniqueConstraint.name, |
|
142 |
unique: true, |
|
143 |
synchronize: true |
|
144 |
}
|
|
145 |
});
|
|
146 |
|
|
147 | 10 |
if (this.connection.driver instanceof SqlServerDriver) { |
148 | 6 |
index.where = index.columns.map(column => { |
149 | 6 |
return `${this.connection.driver.escape(column.databaseName)} IS NOT NULL`; |
150 |
}).join(" AND "); |
|
151 |
}
|
|
152 |
|
|
153 | 10 |
if (relation.embeddedMetadata) { |
154 | 6 |
relation.embeddedMetadata.indices.push(index); |
155 |
} else { |
|
156 | 10 |
relation.entityMetadata.ownIndices.push(index); |
157 |
}
|
|
158 | 10 |
this.computeEntityMetadataStep2(entityMetadata); |
159 |
|
|
160 |
} else { |
|
161 | 10 |
if (relation.embeddedMetadata) { |
162 | 10 |
relation.embeddedMetadata.uniques.push(uniqueConstraint); |
163 |
} else { |
|
164 | 10 |
relation.entityMetadata.ownUniques.push(uniqueConstraint); |
165 |
}
|
|
166 | 10 |
this.computeEntityMetadataStep2(entityMetadata); |
167 |
}
|
|
168 |
}
|
|
169 |
|
|
170 | 10 |
if (foreignKey && this.connection.driver instanceof CockroachDriver) { |
171 | 2 |
const index = new IndexMetadata({ |
172 |
entityMetadata: relation.entityMetadata, |
|
173 |
columns: foreignKey.columns, |
|
174 |
args: { |
|
175 |
target: relation.entityMetadata.target!, |
|
176 |
synchronize: true |
|
177 |
}
|
|
178 |
});
|
|
179 | 2 |
if (relation.embeddedMetadata) { |
180 | 2 |
relation.embeddedMetadata.indices.push(index); |
181 |
} else { |
|
182 | 2 |
relation.entityMetadata.ownIndices.push(index); |
183 |
}
|
|
184 | 2 |
this.computeEntityMetadataStep2(entityMetadata); |
185 |
}
|
|
186 |
});
|
|
187 |
|
|
188 |
// create junction entity metadatas for entity many-to-many relations
|
|
189 | 10 |
entityMetadata.relations.filter(relation => relation.isManyToMany).forEach(relation => { |
190 | 10 |
const joinTable = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName)!; |
191 | 10 |
if (!joinTable) return; // no join table set - no need to do anything (it means this is many-to-many inverse side) |
192 |
|
|
193 |
// here we create a junction entity metadata for a new junction table of many-to-many relation
|
|
194 | 10 |
const junctionEntityMetadata = this.junctionEntityMetadataBuilder.build(relation, joinTable); |
195 | 10 |
relation.registerForeignKeys(...junctionEntityMetadata.foreignKeys); |
196 | 10 |
relation.registerJunctionEntityMetadata(junctionEntityMetadata); |
197 |
|
|
198 |
// compute new entity metadata properties and push it to entity metadatas pool
|
|
199 | 10 |
this.computeEntityMetadataStep2(junctionEntityMetadata); |
200 | 10 |
this.computeInverseProperties(junctionEntityMetadata, entityMetadatas); |
201 | 10 |
entityMetadatas.push(junctionEntityMetadata); |
202 |
});
|
|
203 |
|
|
204 |
});
|
|
205 |
|
|
206 |
// update entity metadata depend properties
|
|
207 | 10 |
entityMetadatas
|
208 | 10 |
.forEach(entityMetadata => { |
209 | 10 |
entityMetadata.relationsWithJoinColumns = entityMetadata.relations.filter(relation => relation.isWithJoinColumn); |
210 | 10 |
entityMetadata.hasNonNullableRelations = entityMetadata.relationsWithJoinColumns.some(relation => !relation.isNullable || relation.isPrimary); |
211 |
});
|
|
212 |
|
|
213 |
// generate closure junction tables for all closure tables
|
|
214 | 10 |
entityMetadatas
|
215 | 10 |
.filter(metadata => metadata.treeType === "closure-table") |
216 | 10 |
.forEach(entityMetadata => { |
217 | 10 |
const closureJunctionEntityMetadata = this.closureJunctionEntityMetadataBuilder.build(entityMetadata); |
218 | 10 |
entityMetadata.closureJunctionTable = closureJunctionEntityMetadata; |
219 | 10 |
this.computeEntityMetadataStep2(closureJunctionEntityMetadata); |
220 | 10 |
this.computeInverseProperties(closureJunctionEntityMetadata, entityMetadatas); |
221 | 10 |
entityMetadatas.push(closureJunctionEntityMetadata); |
222 |
});
|
|
223 |
|
|
224 |
// generate keys for tables with single-table inheritance
|
|
225 | 10 |
entityMetadatas
|
226 | 10 |
.filter(metadata => metadata.inheritancePattern === "STI" && metadata.discriminatorColumn) |
227 | 10 |
.forEach(entityMetadata => this.createKeysForTableInheritance(entityMetadata)); |
228 |
|
|
229 |
// build all indices (need to do it after relations and their join columns are built)
|
|
230 | 10 |
entityMetadatas.forEach(entityMetadata => { |
231 | 10 |
entityMetadata.indices.forEach(index => index.build(this.connection.namingStrategy)); |
232 |
});
|
|
233 |
|
|
234 |
// build all unique constraints (need to do it after relations and their join columns are built)
|
|
235 | 10 |
entityMetadatas.forEach(entityMetadata => { |
236 | 10 |
entityMetadata.uniques.forEach(unique => unique.build(this.connection.namingStrategy)); |
237 |
});
|
|
238 |
|
|
239 |
// build all check constraints
|
|
240 | 10 |
entityMetadatas.forEach(entityMetadata => { |
241 | 10 |
entityMetadata.checks.forEach(check => check.build(this.connection.namingStrategy)); |
242 |
});
|
|
243 |
|
|
244 |
// build all exclusion constraints
|
|
245 | 10 |
entityMetadatas.forEach(entityMetadata => { |
246 | 10 |
entityMetadata.exclusions.forEach(exclusion => exclusion.build(this.connection.namingStrategy)); |
247 |
});
|
|
248 |
|
|
249 |
// add lazy initializer for entity relations
|
|
250 | 10 |
entityMetadatas
|
251 | 10 |
.filter(metadata => metadata.target instanceof Function) |
252 | 10 |
.forEach(entityMetadata => { |
253 | 10 |
entityMetadata.relations |
254 | 10 |
.filter(relation => relation.isLazy) |
255 | 10 |
.forEach(relation => { |
256 | 10 |
this.connection.relationLoader.enableLazyLoad(relation, (entityMetadata.target as Function).prototype); |
257 |
});
|
|
258 |
});
|
|
259 |
|
|
260 | 10 |
entityMetadatas.forEach(entityMetadata => { |
261 | 10 |
entityMetadata.columns.forEach(column => { |
262 |
// const target = column.embeddedMetadata ? column.embeddedMetadata.type : column.target;
|
|
263 | 10 |
const generated = this.metadataArgsStorage.findGenerated(column.target, column.propertyName); |
264 | 10 |
if (generated) { |
265 | 10 |
column.isGenerated = true; |
266 | 10 |
column.generationStrategy = generated.strategy; |
267 | 10 |
if (generated.strategy === "uuid") { |
268 | 10 |
column.type = "uuid"; |
269 | 10 |
} else if (generated.strategy === "rowid") { |
270 | 2 |
column.type = "int"; |
271 |
} else { |
|
272 | 10 |
column.type = column.type || Number; |
273 |
}
|
|
274 | 10 |
column.build(this.connection); |
275 | 10 |
this.computeEntityMetadataStep2(entityMetadata); |
276 |
}
|
|
277 |
});
|
|
278 |
|
|
279 |
});
|
|
280 |
|
|
281 | 10 |
return entityMetadatas; |
282 |
}
|
|
283 |
|
|
284 |
// -------------------------------------------------------------------------
|
|
285 |
// Protected Methods
|
|
286 |
// -------------------------------------------------------------------------
|
|
287 |
|
|
288 |
/**
|
|
289 |
* Creates entity metadata from the given table args.
|
|
290 |
* Creates column, relation, etc. metadatas for everything this entity metadata owns.
|
|
291 |
*/
|
|
292 | 10 |
protected createEntityMetadata(tableArgs: TableMetadataArgs): EntityMetadata { |
293 |
|
|
294 |
// we take all "inheritance tree" from a target entity to collect all stored metadata args
|
|
295 |
// (by decorators or inside entity schemas). For example for target Post < ContentModel < Unit
|
|
296 |
// it will be an array of [Post, ContentModel, Unit] and we can then get all metadata args of those classes
|
|
297 | 10 |
const inheritanceTree: any[] = tableArgs.target instanceof Function |
298 | 10 |
? MetadataUtils.getInheritanceTree(tableArgs.target) |
299 | 10 |
: [tableArgs.target]; // todo: implement later here inheritance for string-targets |
300 |
|
|
301 | 10 |
const tableInheritance = this.metadataArgsStorage.findInheritanceType(tableArgs.target); |
302 | 10 |
const tableTree = this.metadataArgsStorage.findTree(tableArgs.target); |
303 |
|
|
304 |
// if single table inheritance used, we need to copy all children columns in to parent table
|
|
305 |
let singleTableChildrenTargets: any[]; |
|
306 | 10 |
if ((tableInheritance && tableInheritance.pattern === "STI") || tableArgs.type === "entity-child") { |
307 | 10 |
singleTableChildrenTargets = this.metadataArgsStorage |
308 |
.filterSingleTableChildren(tableArgs.target) |
|
309 | 10 |
.map(args => args.target) |
310 | 10 |
.filter(target => target instanceof Function); |
311 |
|
|
312 | 10 |
inheritanceTree.push(...singleTableChildrenTargets); |
313 |
}
|
|
314 |
|
|
315 | 10 |
return new EntityMetadata({ |
316 |
connection: this.connection, |
|
317 |
args: tableArgs, |
|
318 |
inheritanceTree: inheritanceTree, |
|
319 |
tableTree: tableTree, |
|
320 | 10 |
inheritancePattern: tableInheritance ? tableInheritance.pattern : undefined |
321 |
});
|
|
322 |
}
|
|
323 |
|
|
324 | 10 |
protected computeParentEntityMetadata(allEntityMetadatas: EntityMetadata[], entityMetadata: EntityMetadata) { |
325 |
|
|
326 |
// after all metadatas created we set parent entity metadata for table inheritance
|
|
327 | 10 |
if (entityMetadata.tableType === "entity-child") { |
328 | 10 |
entityMetadata.parentEntityMetadata = allEntityMetadatas.find(allEntityMetadata => { |
329 | 10 |
return allEntityMetadata.inheritanceTree.indexOf(entityMetadata.target as Function) !== -1 && allEntityMetadata.inheritancePattern === "STI"; |
330 |
})!; |
|
331 |
}
|
|
332 |
}
|
|
333 |
|
|
334 | 10 |
protected computeEntityMetadataStep1(allEntityMetadatas: EntityMetadata[], entityMetadata: EntityMetadata) { |
335 |
|
|
336 | 10 |
const entityInheritance = this.metadataArgsStorage.findInheritanceType(entityMetadata.target); |
337 |
|
|
338 | 10 |
const discriminatorValue = this.metadataArgsStorage.findDiscriminatorValue(entityMetadata.target); |
339 |
|
|
340 | 10 |
if (typeof discriminatorValue !== "undefined") { |
341 | 10 |
entityMetadata.discriminatorValue = discriminatorValue.value; |
342 |
} else { |
|
343 | 10 |
entityMetadata.discriminatorValue = (entityMetadata.target as any).name; |
344 |
}
|
|
345 |
|
|
346 |
// if single table inheritance is used, we need to mark all embedded columns as nullable
|
|
347 | 10 |
entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(entityMetadata.inheritanceTree)) |
348 | 10 |
.map((embedded: EmbeddedMetadata): EmbeddedMetadata => { |
349 | 10 |
if (entityMetadata.inheritancePattern === "STI") { |
350 | 10 |
embedded.columns = embedded.columns.map((column: ColumnMetadata): ColumnMetadata => { |
351 | 10 |
column.isNullable = true; |
352 | 10 |
return column; |
353 |
});
|
|
354 |
}
|
|
355 | 10 |
return embedded; |
356 |
});
|
|
357 |
|
|
358 | 10 |
entityMetadata.ownColumns = this.metadataArgsStorage |
359 |
.filterColumns(entityMetadata.inheritanceTree) |
|
360 | 10 |
.map(args => { |
361 |
|
|
362 |
// for single table children we reuse columns created for their parents
|
|
363 | 10 |
if (entityMetadata.tableType === "entity-child") |
364 | 10 |
return entityMetadata.parentEntityMetadata.ownColumns.find(column => column.propertyName === args.propertyName)!; |
365 |
|
|
366 | 10 |
const column = new ColumnMetadata({ connection: this.connection, entityMetadata, args }); |
367 |
|
|
368 |
// if single table inheritance used, we need to mark all inherit table columns as nullable
|
|
369 | 10 |
const columnInSingleTableInheritedChild = allEntityMetadatas.find(otherEntityMetadata => otherEntityMetadata.tableType === "entity-child" && otherEntityMetadata.target === args.target); |
370 | 10 |
if (columnInSingleTableInheritedChild) |
371 | 10 |
column.isNullable = true; |
372 | 10 |
return column; |
373 |
});
|
|
374 |
|
|
375 |
// for table inheritance we need to add a discriminator column
|
|
376 |
//
|
|
377 | 10 |
if (entityInheritance && entityInheritance.column) { |
378 | 10 |
const discriminatorColumnName = entityInheritance.column && entityInheritance.column.name ? entityInheritance.column.name : "type"; |
379 | 10 |
let discriminatorColumn = entityMetadata.ownColumns.find(column => column.propertyName === discriminatorColumnName); |
380 | 10 |
if (!discriminatorColumn) { |
381 | 10 |
discriminatorColumn = new ColumnMetadata({ |
382 |
connection: this.connection, |
|
383 |
entityMetadata: entityMetadata, |
|
384 |
args: { |
|
385 |
target: entityMetadata.target, |
|
386 |
mode: "virtual", |
|
387 |
propertyName: discriminatorColumnName, |
|
388 | 10 |
options: entityInheritance.column || { |
389 |
name: discriminatorColumnName, |
|
390 |
type: "varchar", |
|
391 |
nullable: false |
|
392 |
}
|
|
393 |
}
|
|
394 |
});
|
|
395 | 10 |
discriminatorColumn.isVirtual = true; |
396 | 10 |
discriminatorColumn.isDiscriminator = true; |
397 | 10 |
entityMetadata.ownColumns.push(discriminatorColumn); |
398 |
} else { |
|
399 | 10 |
discriminatorColumn.isDiscriminator = true; |
400 |
}
|
|
401 |
}
|
|
402 |
|
|
403 |
// add discriminator column to the child entity metadatas
|
|
404 |
// discriminator column will not be there automatically since we are creating it in the code above
|
|
405 | 10 |
if (entityMetadata.tableType === "entity-child") { |
406 | 10 |
const discriminatorColumn = entityMetadata.parentEntityMetadata.ownColumns.find(column => column.isDiscriminator); |
407 | 10 |
if (discriminatorColumn && !entityMetadata.ownColumns.find(column => column === discriminatorColumn)) { |
408 | 10 |
entityMetadata.ownColumns.push(discriminatorColumn); |
409 |
}
|
|
410 |
}
|
|
411 |
|
|
412 | 10 |
const { namingStrategy } = this.connection; |
413 |
|
|
414 |
// check if tree is used then we need to add extra columns for specific tree types
|
|
415 | 10 |
if (entityMetadata.treeType === "materialized-path") { |
416 | 10 |
entityMetadata.ownColumns.push(new ColumnMetadata({ |
417 |
connection: this.connection, |
|
418 |
entityMetadata: entityMetadata, |
|
419 |
materializedPath: true, |
|
420 |
args: { |
|
421 |
target: entityMetadata.target, |
|
422 |
mode: "virtual", |
|
423 |
propertyName: "mpath", |
|
424 |
options: /*tree.column || */ { |
|
425 |
name: namingStrategy.materializedPathColumnName, |
|
426 |
type: "varchar", |
|
427 |
nullable: true, |
|
428 |
default: "" |
|
429 |
}
|
|
430 |
}
|
|
431 |
}));
|
|
432 |
|
|
433 | 10 |
} else if (entityMetadata.treeType === "nested-set") { |
434 | 10 |
const { left, right } = namingStrategy.nestedSetColumnNames; |
435 | 10 |
entityMetadata.ownColumns.push(new ColumnMetadata({ |
436 |
connection: this.connection, |
|
437 |
entityMetadata: entityMetadata, |
|
438 |
nestedSetLeft: true, |
|
439 |
args: { |
|
440 |
target: entityMetadata.target, |
|
441 |
mode: "virtual", |
|
442 |
propertyName: left, |
|
443 |
options: /*tree.column || */ { |
|
444 |
name: left, |
|
445 |
type: "integer", |
|
446 |
nullable: false, |
|
447 |
default: 1 |
|
448 |
}
|
|
449 |
}
|
|
450 |
}));
|
|
451 | 10 |
entityMetadata.ownColumns.push(new ColumnMetadata({ |
452 |
connection: this.connection, |
|
453 |
entityMetadata: entityMetadata, |
|
454 |
nestedSetRight: true, |
|
455 |
args: { |
|
456 |
target: entityMetadata.target, |
|
457 |
mode: "virtual", |
|
458 |
propertyName: right, |
|
459 |
options: /*tree.column || */ { |
|
460 |
name: right, |
|
461 |
type: "integer", |
|
462 |
nullable: false, |
|
463 |
default: 2 |
|
464 |
}
|
|
465 |
}
|
|
466 |
}));
|
|
467 |
}
|
|
468 |
|
|
469 | 10 |
entityMetadata.ownRelations = this.metadataArgsStorage.filterRelations(entityMetadata.inheritanceTree).map(args => { |
470 |
|
|
471 |
// for single table children we reuse relations created for their parents
|
|
472 | 10 |
if (entityMetadata.tableType === "entity-child") |
473 | 10 |
return entityMetadata.parentEntityMetadata.ownRelations.find(relation => relation.propertyName === args.propertyName)!; |
474 |
|
|
475 | 10 |
return new RelationMetadata({ entityMetadata, args }); |
476 |
});
|
|
477 | 10 |
entityMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(entityMetadata.inheritanceTree).map(args => { |
478 |
|
|
479 |
// for single table children we reuse relation ids created for their parents
|
|
480 | 10 |
if (entityMetadata.tableType === "entity-child") |
481 |
return entityMetadata.parentEntityMetadata.relationIds.find(relationId => relationId.propertyName === args.propertyName)!; |
|
482 |
|
|
483 | 10 |
return new RelationIdMetadata({ entityMetadata, args }); |
484 |
});
|
|
485 | 10 |
entityMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(entityMetadata.inheritanceTree).map(args => { |
486 |
|
|
487 |
// for single table children we reuse relation counts created for their parents
|
|
488 | 10 |
if (entityMetadata.tableType === "entity-child") |
489 |
return entityMetadata.parentEntityMetadata.relationCounts.find(relationCount => relationCount.propertyName === args.propertyName)!; |
|
490 |
|
|
491 | 10 |
return new RelationCountMetadata({ entityMetadata, args }); |
492 |
});
|
|
493 | 10 |
entityMetadata.ownListeners = this.metadataArgsStorage.filterListeners(entityMetadata.inheritanceTree).map(args => { |
494 | 10 |
return new EntityListenerMetadata({ entityMetadata: entityMetadata, args: args }); |
495 |
});
|
|
496 | 10 |
entityMetadata.checks = this.metadataArgsStorage.filterChecks(entityMetadata.inheritanceTree).map(args => { |
497 | 10 |
return new CheckMetadata({ entityMetadata, args }); |
498 |
});
|
|
499 |
|
|
500 |
// Only PostgreSQL supports exclusion constraints.
|
|
501 | 10 |
if (this.connection.driver instanceof PostgresDriver) { |
502 | 6 |
entityMetadata.exclusions = this.metadataArgsStorage.filterExclusions(entityMetadata.inheritanceTree).map(args => { |
503 | 6 |
return new ExclusionMetadata({ entityMetadata, args }); |
504 |
});
|
|
505 |
}
|
|
506 |
|
|
507 | 10 |
if (this.connection.driver instanceof CockroachDriver) { |
508 | 2 |
entityMetadata.ownIndices = this.metadataArgsStorage.filterIndices(entityMetadata.inheritanceTree) |
509 | 2 |
.filter(args => !args.unique) |
510 | 2 |
.map(args => { |
511 | 2 |
return new IndexMetadata({entityMetadata, args}); |
512 |
});
|
|
513 |
|
|
514 | 2 |
const uniques = this.metadataArgsStorage.filterIndices(entityMetadata.inheritanceTree) |
515 | 2 |
.filter(args => args.unique) |
516 | 2 |
.map(args => { |
517 | 2 |
return new UniqueMetadata({ |
518 |
entityMetadata: entityMetadata, |
|
519 |
args: { |
|
520 |
target: args.target, |
|
521 |
name: args.name, |
|
522 |
columns: args.columns, |
|
523 |
}
|
|
524 |
});
|
|
525 |
});
|
|
526 | 2 |
entityMetadata.ownUniques.push(...uniques); |
527 |
|
|
528 |
} else { |
|
529 | 10 |
entityMetadata.ownIndices = this.metadataArgsStorage.filterIndices(entityMetadata.inheritanceTree).map(args => { |
530 | 8 |
return new IndexMetadata({entityMetadata, args}); |
531 |
});
|
|
532 |
}
|
|
533 |
|
|
534 |
// Mysql and SAP HANA stores unique constraints as unique indices.
|
|
535 | 10 |
if (this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver || this.connection.driver instanceof SapDriver) { |
536 | 10 |
const indices = this.metadataArgsStorage.filterUniques(entityMetadata.inheritanceTree).map(args => { |
537 | 6 |
return new IndexMetadata({ |
538 |
entityMetadata: entityMetadata, |
|
539 |
args: { |
|
540 |
target: args.target, |
|
541 |
name: args.name, |
|
542 |
columns: args.columns, |
|
543 |
unique: true, |
|
544 |
synchronize: true |
|
545 |
}
|
|
546 |
});
|
|
547 |
});
|
|
548 | 10 |
entityMetadata.ownIndices.push(...indices); |
549 |
|
|
550 |
} else { |
|
551 | 10 |
const uniques = this.metadataArgsStorage.filterUniques(entityMetadata.inheritanceTree).map(args => { |
552 | 10 |
return new UniqueMetadata({ entityMetadata, args }); |
553 |
});
|
|
554 | 10 |
entityMetadata.ownUniques.push(...uniques); |
555 |
}
|
|
556 |
}
|
|
557 |
|
|
558 |
/**
|
|
559 |
* Creates from the given embedded metadata args real embedded metadatas with its columns and relations,
|
|
560 |
* and does the same for all its sub-embeddeds (goes recursively).
|
|
561 |
*/
|
|
562 | 10 |
protected createEmbeddedsRecursively(entityMetadata: EntityMetadata, embeddedArgs: EmbeddedMetadataArgs[]): EmbeddedMetadata[] { |
563 | 10 |
return embeddedArgs.map(embeddedArgs => { |
564 | 10 |
const embeddedMetadata = new EmbeddedMetadata({ entityMetadata: entityMetadata, args: embeddedArgs }); |
565 | 10 |
const targets = MetadataUtils.getInheritanceTree(embeddedMetadata.type); |
566 |
|
|
567 | 10 |
embeddedMetadata.columns = this.metadataArgsStorage.filterColumns(targets).map(args => { |
568 | 10 |
return new ColumnMetadata({ connection: this.connection, entityMetadata, embeddedMetadata, args}); |
569 |
});
|
|
570 | 10 |
embeddedMetadata.relations = this.metadataArgsStorage.filterRelations(targets).map(args => { |
571 | 10 |
return new RelationMetadata({ entityMetadata, embeddedMetadata, args }); |
572 |
});
|
|
573 | 10 |
embeddedMetadata.listeners = this.metadataArgsStorage.filterListeners(targets).map(args => { |
574 | 10 |
return new EntityListenerMetadata({ entityMetadata, embeddedMetadata, args }); |
575 |
});
|
|
576 | 10 |
embeddedMetadata.indices = this.metadataArgsStorage.filterIndices(targets).map(args => { |
577 | 10 |
return new IndexMetadata({ entityMetadata, embeddedMetadata, args }); |
578 |
});
|
|
579 | 10 |
embeddedMetadata.uniques = this.metadataArgsStorage.filterUniques(targets).map(args => { |
580 | 6 |
return new UniqueMetadata({ entityMetadata, embeddedMetadata, args }); |
581 |
});
|
|
582 | 10 |
embeddedMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(targets).map(args => { |
583 |
return new RelationIdMetadata({ entityMetadata, args }); |
|
584 |
});
|
|
585 | 10 |
embeddedMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(targets).map(args => { |
586 |
return new RelationCountMetadata({ entityMetadata, args }); |
|
587 |
});
|
|
588 | 10 |
embeddedMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(targets)); |
589 | 10 |
embeddedMetadata.embeddeds.forEach(subEmbedded => subEmbedded.parentEmbeddedMetadata = embeddedMetadata); |
590 | 10 |
entityMetadata.allEmbeddeds.push(embeddedMetadata); |
591 | 10 |
return embeddedMetadata; |
592 |
});
|
|
593 |
}
|
|
594 |
|
|
595 |
/**
|
|
596 |
* Computes all entity metadata's computed properties, and all its sub-metadatas (relations, columns, embeds, etc).
|
|
597 |
*/
|
|
598 | 10 |
protected computeEntityMetadataStep2(entityMetadata: EntityMetadata) { |
599 | 10 |
entityMetadata.embeddeds.forEach(embedded => embedded.build(this.connection)); |
600 | 10 |
entityMetadata.embeddeds.forEach(embedded => { |
601 | 10 |
embedded.columnsFromTree.forEach(column => column.build(this.connection)); |
602 | 10 |
embedded.relationsFromTree.forEach(relation => relation.build()); |
603 |
});
|
|
604 | 10 |
entityMetadata.ownColumns.forEach(column => column.build(this.connection)); |
605 | 10 |
entityMetadata.ownRelations.forEach(relation => relation.build()); |
606 | 10 |
entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations); |
607 | 10 |
entityMetadata.eagerRelations = entityMetadata.relations.filter(relation => relation.isEager); |
608 | 10 |
entityMetadata.lazyRelations = entityMetadata.relations.filter(relation => relation.isLazy); |
609 | 10 |
entityMetadata.oneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOne); |
610 | 10 |
entityMetadata.oneToManyRelations = entityMetadata.relations.filter(relation => relation.isOneToMany); |
611 | 10 |
entityMetadata.manyToOneRelations = entityMetadata.relations.filter(relation => relation.isManyToOne); |
612 | 10 |
entityMetadata.manyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToMany); |
613 | 10 |
entityMetadata.ownerOneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOneOwner); |
614 | 10 |
entityMetadata.ownerManyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToManyOwner); |
615 | 10 |
entityMetadata.treeParentRelation = entityMetadata.relations.find(relation => relation.isTreeParent); |
616 | 10 |
entityMetadata.treeChildrenRelation = entityMetadata.relations.find(relation => relation.isTreeChildren); |
617 | 10 |
entityMetadata.columns = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), entityMetadata.ownColumns); |
618 | 10 |
entityMetadata.listeners = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.listenersFromTree), entityMetadata.ownListeners); |
619 | 10 |
entityMetadata.afterLoadListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_LOAD); |
620 | 10 |
entityMetadata.afterInsertListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_INSERT); |
621 | 10 |
entityMetadata.afterUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_UPDATE); |
622 | 10 |
entityMetadata.afterRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_REMOVE); |
623 | 10 |
entityMetadata.beforeInsertListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_INSERT); |
624 | 10 |
entityMetadata.beforeUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_UPDATE); |
625 | 10 |
entityMetadata.beforeRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_REMOVE); |
626 | 10 |
entityMetadata.indices = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.indicesFromTree), entityMetadata.ownIndices); |
627 | 10 |
entityMetadata.uniques = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.uniquesFromTree), entityMetadata.ownUniques); |
628 | 10 |
entityMetadata.primaryColumns = entityMetadata.columns.filter(column => column.isPrimary); |
629 | 10 |
entityMetadata.nonVirtualColumns = entityMetadata.columns.filter(column => !column.isVirtual); |
630 | 10 |
entityMetadata.ancestorColumns = entityMetadata.columns.filter(column => column.closureType === "ancestor"); |
631 | 10 |
entityMetadata.descendantColumns = entityMetadata.columns.filter(column => column.closureType === "descendant"); |
632 | 10 |
entityMetadata.hasMultiplePrimaryKeys = entityMetadata.primaryColumns.length > 1; |
633 | 10 |
entityMetadata.generatedColumns = entityMetadata.columns.filter(column => column.isGenerated || column.isObjectId); |
634 | 10 |
entityMetadata.hasUUIDGeneratedColumns = entityMetadata.columns.filter(column => column.isGenerated || column.generationStrategy === "uuid").length > 0; |
635 | 10 |
entityMetadata.createDateColumn = entityMetadata.columns.find(column => column.isCreateDate); |
636 | 10 |
entityMetadata.updateDateColumn = entityMetadata.columns.find(column => column.isUpdateDate); |
637 | 10 |
entityMetadata.deleteDateColumn = entityMetadata.columns.find(column => column.isDeleteDate); |
638 | 10 |
entityMetadata.versionColumn = entityMetadata.columns.find(column => column.isVersion); |
639 | 10 |
entityMetadata.discriminatorColumn = entityMetadata.columns.find(column => column.isDiscriminator); |
640 | 10 |
entityMetadata.treeLevelColumn = entityMetadata.columns.find(column => column.isTreeLevel); |
641 | 10 |
entityMetadata.nestedSetLeftColumn = entityMetadata.columns.find(column => column.isNestedSetLeft); |
642 | 10 |
entityMetadata.nestedSetRightColumn = entityMetadata.columns.find(column => column.isNestedSetRight); |
643 | 10 |
entityMetadata.materializedPathColumn = entityMetadata.columns.find(column => column.isMaterializedPath); |
644 | 10 |
entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.isObjectId); |
645 | 10 |
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.connection.namingStrategy)); |
646 | 10 |
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap(); |
647 | 10 |
entityMetadata.relationIds.forEach(relationId => relationId.build()); |
648 | 10 |
entityMetadata.relationCounts.forEach(relationCount => relationCount.build()); |
649 | 10 |
entityMetadata.embeddeds.forEach(embedded => { |
650 | 10 |
embedded.relationIdsFromTree.forEach(relationId => relationId.build()); |
651 | 10 |
embedded.relationCountsFromTree.forEach(relationCount => relationCount.build()); |
652 |
});
|
|
653 |
}
|
|
654 |
|
|
655 |
/**
|
|
656 |
* Computes entity metadata's relations inverse side properties.
|
|
657 |
*/
|
|
658 | 10 |
protected computeInverseProperties(entityMetadata: EntityMetadata, entityMetadatas: EntityMetadata[]) { |
659 | 10 |
entityMetadata.relations.forEach(relation => { |
660 |
|
|
661 |
// compute inverse side (related) entity metadatas for all relation metadatas
|
|
662 | 10 |
const inverseEntityMetadata = entityMetadatas.find(m => m.target === relation.type || (typeof relation.type === "string" && m.targetName === relation.type)); |
663 | 10 |
if (!inverseEntityMetadata) |
664 |
throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.propertyPath + " was not found. Check if you specified a correct entity object and if it's connected in the connection options."); |
|
665 |
|
|
666 | 10 |
relation.inverseEntityMetadata = inverseEntityMetadata; |
667 | 10 |
relation.inverseSidePropertyPath = relation.buildInverseSidePropertyPath(); |
668 |
|
|
669 |
// and compute inverse relation and mark if it has such
|
|
670 | 10 |
relation.inverseRelation = inverseEntityMetadata.relations.find(foundRelation => foundRelation.propertyPath === relation.inverseSidePropertyPath); |
671 |
});
|
|
672 |
}
|
|
673 |
|
|
674 |
/**
|
|
675 |
* Creates indices for the table of single table inheritance.
|
|
676 |
*/
|
|
677 | 10 |
protected createKeysForTableInheritance(entityMetadata: EntityMetadata) { |
678 | 10 |
entityMetadata.indices.push( |
679 |
new IndexMetadata({ |
|
680 |
entityMetadata: entityMetadata, |
|
681 |
columns: [entityMetadata.discriminatorColumn!], |
|
682 |
args: { |
|
683 |
target: entityMetadata.target, |
|
684 |
unique: false |
|
685 |
}
|
|
686 |
}),
|
|
687 |
);
|
|
688 |
}
|
|
689 |
|
|
690 | 10 |
}
|
Read our documentation on viewing source code .