@mikro-orm/core 7.0.0-dev.16 → 7.0.0-dev.160
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +99 -57
- package/EntityManager.js +292 -262
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +103 -143
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +8 -7
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +0 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +16 -7
- package/connections/Connection.js +23 -14
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +80 -35
- package/drivers/IDatabaseDriver.d.ts +43 -17
- package/entity/BaseEntity.d.ts +2 -2
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +94 -29
- package/entity/Collection.js +434 -97
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +13 -1
- package/entity/EntityFactory.js +84 -53
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +35 -15
- package/entity/EntityLoader.d.ts +6 -6
- package/entity/EntityLoader.js +109 -72
- package/entity/EntityRepository.d.ts +24 -4
- package/entity/EntityRepository.js +8 -2
- package/entity/Reference.d.ts +6 -5
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +2 -7
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +580 -0
- package/entity/defineEntity.js +533 -0
- package/entity/index.d.ts +3 -2
- package/entity/index.js +3 -2
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +16 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +21 -5
- package/enums.js +15 -1
- package/errors.d.ts +21 -9
- package/errors.js +53 -21
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +52 -33
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +1 -0
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/index.d.ts +1 -0
- package/logging/index.js +1 -0
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +18 -22
- package/metadata/EntitySchema.js +59 -34
- package/metadata/MetadataDiscovery.d.ts +6 -10
- package/metadata/MetadataDiscovery.js +348 -319
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +46 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +70 -37
- package/metadata/MetadataValidator.d.ts +12 -9
- package/metadata/MetadataValidator.js +69 -38
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +484 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +8 -4
- package/naming-strategy/AbstractNamingStrategy.js +8 -2
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +14 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +18 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +6 -13
- package/platforms/Platform.js +17 -43
- package/serialization/EntitySerializer.d.ts +5 -0
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +28 -18
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +8 -6
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +3 -3
- package/types/DoubleType.js +2 -2
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +174 -114
- package/typings.js +55 -42
- package/unit-of-work/ChangeSet.d.ts +2 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +1 -3
- package/unit-of-work/ChangeSetComputer.js +26 -13
- package/unit-of-work/ChangeSetPersister.d.ts +5 -4
- package/unit-of-work/ChangeSetPersister.js +70 -34
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +23 -3
- package/unit-of-work/UnitOfWork.js +175 -98
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +18 -16
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +754 -207
- package/utils/Configuration.js +146 -190
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +27 -11
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +64 -30
- package/utils/EntityComparator.d.ts +13 -9
- package/utils/EntityComparator.js +101 -42
- package/utils/QueryHelper.d.ts +14 -6
- package/utils/QueryHelper.js +87 -25
- package/utils/RawQueryFragment.d.ts +48 -25
- package/utils/RawQueryFragment.js +66 -70
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +13 -126
- package/utils/Utils.js +100 -391
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +97 -0
- package/utils/fs-utils.d.ts +32 -0
- package/utils/fs-utils.js +178 -0
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +55 -4
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -32
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -26
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -13
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -402
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/exports.d.ts +0 -24
- package/exports.js +0 -23
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { basename, extname } from 'node:path';
|
|
2
|
-
import globby from 'globby';
|
|
3
1
|
import { EntityMetadata, } from '../typings.js';
|
|
4
|
-
import { Utils } from '../utils/Utils.js';
|
|
2
|
+
import { compareArrays, Utils } from '../utils/Utils.js';
|
|
5
3
|
import { MetadataValidator } from './MetadataValidator.js';
|
|
4
|
+
import { MetadataProvider } from './MetadataProvider.js';
|
|
6
5
|
import { MetadataStorage } from './MetadataStorage.js';
|
|
7
6
|
import { EntitySchema } from './EntitySchema.js';
|
|
8
7
|
import { Cascade, ReferenceKind } from '../enums.js';
|
|
9
8
|
import { MetadataError } from '../errors.js';
|
|
10
|
-
import {
|
|
9
|
+
import { t, Type } from '../types/index.js';
|
|
11
10
|
import { colors } from '../logging/colors.js';
|
|
12
|
-
import { raw,
|
|
11
|
+
import { raw, Raw } from '../utils/RawQueryFragment.js';
|
|
12
|
+
import { BaseEntity } from '../entity/BaseEntity.js';
|
|
13
13
|
export class MetadataDiscovery {
|
|
14
14
|
metadata;
|
|
15
15
|
platform;
|
|
16
16
|
config;
|
|
17
17
|
namingStrategy;
|
|
18
18
|
metadataProvider;
|
|
19
|
-
cache;
|
|
20
19
|
logger;
|
|
21
20
|
schemaHelper;
|
|
22
21
|
validator = new MetadataValidator();
|
|
@@ -27,13 +26,14 @@ export class MetadataDiscovery {
|
|
|
27
26
|
this.config = config;
|
|
28
27
|
this.namingStrategy = this.config.getNamingStrategy();
|
|
29
28
|
this.metadataProvider = this.config.getMetadataProvider();
|
|
30
|
-
this.cache = this.config.getMetadataCacheAdapter();
|
|
31
29
|
this.logger = this.config.getLogger();
|
|
32
30
|
this.schemaHelper = this.platform.getSchemaHelper();
|
|
33
31
|
}
|
|
34
32
|
async discover(preferTs = true) {
|
|
33
|
+
this.discovered.length = 0;
|
|
35
34
|
const startTime = Date.now();
|
|
36
|
-
this.
|
|
35
|
+
const suffix = this.metadataProvider.constructor === MetadataProvider ? '' : `, using ${colors.cyan(this.metadataProvider.constructor.name)}`;
|
|
36
|
+
this.logger.log('discovery', `ORM entity discovery started${suffix}`);
|
|
37
37
|
await this.findEntities(preferTs);
|
|
38
38
|
for (const meta of this.discovered) {
|
|
39
39
|
/* v8 ignore next */
|
|
@@ -47,10 +47,13 @@ export class MetadataDiscovery {
|
|
|
47
47
|
await this.config.get('discovery').afterDiscovered?.(storage, this.platform);
|
|
48
48
|
return storage;
|
|
49
49
|
}
|
|
50
|
-
discoverSync(
|
|
50
|
+
discoverSync() {
|
|
51
|
+
this.discovered.length = 0;
|
|
51
52
|
const startTime = Date.now();
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
53
|
+
const suffix = this.metadataProvider.constructor === MetadataProvider ? '' : `, using ${colors.cyan(this.metadataProvider.constructor.name)}`;
|
|
54
|
+
this.logger.log('discovery', `ORM entity discovery started${suffix} in sync mode`);
|
|
55
|
+
const refs = this.config.get('entities');
|
|
56
|
+
this.discoverReferences(refs);
|
|
54
57
|
for (const meta of this.discovered) {
|
|
55
58
|
/* v8 ignore next */
|
|
56
59
|
void this.config.get('discovery').onMetadata?.(meta, this.platform);
|
|
@@ -70,15 +73,48 @@ export class MetadataDiscovery {
|
|
|
70
73
|
.sort((a, b) => b.root.name.localeCompare(a.root.name))
|
|
71
74
|
.forEach(meta => {
|
|
72
75
|
this.platform.validateMetadata(meta);
|
|
73
|
-
discovered.set(meta.
|
|
76
|
+
discovered.set(meta.class, meta);
|
|
74
77
|
});
|
|
78
|
+
for (const meta of discovered) {
|
|
79
|
+
meta.root = discovered.get(meta.root.class);
|
|
80
|
+
}
|
|
75
81
|
return discovered;
|
|
76
82
|
}
|
|
83
|
+
initAccessors(meta) {
|
|
84
|
+
for (const prop of Object.values(meta.properties)) {
|
|
85
|
+
if (!prop.accessor || meta.properties[prop.accessor]) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
|
|
89
|
+
if (desc?.get || desc?.set) {
|
|
90
|
+
this.initFieldName(prop);
|
|
91
|
+
const accessor = prop.name;
|
|
92
|
+
prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
|
|
93
|
+
if (prop.accessor === true) {
|
|
94
|
+
prop.getter = prop.setter = true;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
prop.getter = prop.setter = false;
|
|
98
|
+
}
|
|
99
|
+
prop.accessor = accessor;
|
|
100
|
+
prop.serializedName ??= accessor;
|
|
101
|
+
Utils.renameKey(meta.properties, accessor, prop.name);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const name = prop.name;
|
|
105
|
+
prop.name = prop.accessor;
|
|
106
|
+
this.initFieldName(prop);
|
|
107
|
+
prop.serializedName ??= prop.accessor;
|
|
108
|
+
prop.name = name;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
77
112
|
processDiscoveredEntities(discovered) {
|
|
78
113
|
for (const meta of discovered) {
|
|
79
114
|
let i = 1;
|
|
80
115
|
Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
|
|
81
116
|
Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
|
|
117
|
+
this.initAccessors(meta);
|
|
82
118
|
}
|
|
83
119
|
// ignore base entities (not annotated with @Entity)
|
|
84
120
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
@@ -86,66 +122,61 @@ export class MetadataDiscovery {
|
|
|
86
122
|
filtered.sort((a, b) => !a.embeddable === !b.embeddable ? 0 : (a.embeddable ? 1 : -1));
|
|
87
123
|
filtered.forEach(meta => this.initSingleTableInheritance(meta, filtered));
|
|
88
124
|
filtered.forEach(meta => this.defineBaseEntityProperties(meta));
|
|
89
|
-
filtered.forEach(meta =>
|
|
125
|
+
filtered.forEach(meta => {
|
|
126
|
+
const newMeta = EntitySchema.fromMetadata(meta).init().meta;
|
|
127
|
+
return this.metadata.set(newMeta.class, newMeta);
|
|
128
|
+
});
|
|
90
129
|
filtered.forEach(meta => this.initAutoincrement(meta));
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
const forEachProp = (cb) => {
|
|
131
|
+
filtered.forEach(meta => Object.values(meta.properties).forEach(prop => cb(meta, prop)));
|
|
132
|
+
};
|
|
133
|
+
forEachProp((m, p) => this.initFactoryField(m, p));
|
|
134
|
+
forEachProp((_m, p) => this.initRelation(p));
|
|
135
|
+
forEachProp((m, p) => this.initEmbeddables(m, p));
|
|
136
|
+
forEachProp((_m, p) => this.initFieldName(p));
|
|
137
|
+
forEachProp((m, p) => this.initVersionProperty(m, p));
|
|
138
|
+
forEachProp((m, p) => this.initCustomType(m, p));
|
|
139
|
+
forEachProp((m, p) => this.initGeneratedColumn(m, p));
|
|
97
140
|
filtered.forEach(meta => this.initAutoincrement(meta)); // once again after we init custom types
|
|
98
141
|
filtered.forEach(meta => this.initCheckConstraints(meta));
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
prop.trackChanges = false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
filtered.forEach(meta => Object.values(meta.properties).forEach(prop => this.initIndexes(meta, prop)));
|
|
142
|
+
forEachProp((_m, p) => {
|
|
143
|
+
this.initDefaultValue(p);
|
|
144
|
+
this.inferTypeFromDefault(p);
|
|
145
|
+
this.initRelation(p);
|
|
146
|
+
this.initColumnType(p);
|
|
147
|
+
});
|
|
148
|
+
forEachProp((m, p) => this.initIndexes(m, p));
|
|
111
149
|
filtered.forEach(meta => this.autoWireBidirectionalProperties(meta));
|
|
112
|
-
filtered.forEach(meta => this.findReferencingProperties(meta, filtered));
|
|
113
150
|
for (const meta of filtered) {
|
|
114
151
|
discovered.push(...this.processEntity(meta));
|
|
115
152
|
}
|
|
116
153
|
discovered.forEach(meta => meta.sync(true));
|
|
117
|
-
|
|
118
|
-
// override the path in the options, so we can log it from the CLI in `cache:generate` command
|
|
119
|
-
if (combinedCachePath) {
|
|
120
|
-
this.config.get('metadataCache').combined = combinedCachePath;
|
|
121
|
-
}
|
|
154
|
+
this.metadataProvider.combineCache();
|
|
122
155
|
return discovered.map(meta => {
|
|
123
|
-
meta = this.metadata.get(meta.
|
|
156
|
+
meta = this.metadata.get(meta.class);
|
|
124
157
|
meta.sync(true);
|
|
158
|
+
this.findReferencingProperties(meta, filtered);
|
|
125
159
|
return meta;
|
|
126
160
|
});
|
|
127
161
|
}
|
|
128
|
-
findEntities(preferTs
|
|
129
|
-
this.
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const paths =
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
162
|
+
async findEntities(preferTs) {
|
|
163
|
+
const { entities, entitiesTs, baseDir } = this.config.getAll();
|
|
164
|
+
const targets = (preferTs && entitiesTs.length > 0) ? entitiesTs : entities;
|
|
165
|
+
const processed = [];
|
|
166
|
+
const paths = [];
|
|
167
|
+
for (const entity of targets) {
|
|
168
|
+
if (typeof entity === 'string') {
|
|
169
|
+
paths.push(entity);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
processed.push(entity);
|
|
137
173
|
}
|
|
138
|
-
return this.discoverDirectories(paths).then(() => {
|
|
139
|
-
this.discoverReferences(refs);
|
|
140
|
-
this.discoverMissingTargets();
|
|
141
|
-
this.validator.validateDiscovered(this.discovered, options);
|
|
142
|
-
return this.discovered;
|
|
143
|
-
});
|
|
144
174
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
175
|
+
if (paths.length > 0) {
|
|
176
|
+
const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
|
|
177
|
+
processed.push(...await discoverEntities(paths, { baseDir }));
|
|
178
|
+
}
|
|
179
|
+
return this.discoverReferences(processed);
|
|
149
180
|
}
|
|
150
181
|
discoverMissingTargets() {
|
|
151
182
|
const unwrap = (type) => type
|
|
@@ -154,17 +185,22 @@ export class MetadataDiscovery {
|
|
|
154
185
|
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
155
186
|
const missing = [];
|
|
156
187
|
this.discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
157
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.pivotEntity
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
188
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.pivotEntity) {
|
|
189
|
+
const pivotEntity = prop.pivotEntity;
|
|
190
|
+
const target = typeof pivotEntity === 'function' && !pivotEntity.prototype
|
|
191
|
+
? pivotEntity()
|
|
192
|
+
: pivotEntity;
|
|
193
|
+
if (!this.discovered.find(m => m.className === Utils.className(target))) {
|
|
194
|
+
missing.push(target);
|
|
195
|
+
}
|
|
162
196
|
}
|
|
163
|
-
if (prop.kind !== ReferenceKind.SCALAR
|
|
164
|
-
const target = typeof prop.entity === 'function'
|
|
197
|
+
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
198
|
+
const target = typeof prop.entity === 'function' && !prop.entity.prototype
|
|
165
199
|
? prop.entity()
|
|
166
200
|
: prop.type;
|
|
167
|
-
|
|
201
|
+
if (!unwrap(prop.type).split(/ ?\| ?/).every(type => this.discovered.find(m => m.className === type))) {
|
|
202
|
+
missing.push(...Utils.asArray(target));
|
|
203
|
+
}
|
|
168
204
|
}
|
|
169
205
|
}));
|
|
170
206
|
if (missing.length > 0) {
|
|
@@ -173,123 +209,100 @@ export class MetadataDiscovery {
|
|
|
173
209
|
}
|
|
174
210
|
tryDiscoverTargets(targets) {
|
|
175
211
|
for (const target of targets) {
|
|
176
|
-
|
|
177
|
-
|
|
212
|
+
const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
|
|
213
|
+
if (isDiscoverable && target.name && !this.metadata.has(target)) {
|
|
214
|
+
this.discoverReferences([target], false);
|
|
178
215
|
this.discoverMissingTargets();
|
|
179
216
|
}
|
|
180
217
|
}
|
|
181
218
|
}
|
|
182
|
-
|
|
183
|
-
paths = paths.map(path => Utils.normalizePath(path));
|
|
184
|
-
const files = await globby(paths, { cwd: Utils.normalizePath(this.config.get('baseDir')) });
|
|
185
|
-
this.logger.log('discovery', `- processing ${colors.cyan('' + files.length)} files`);
|
|
186
|
-
const found = [];
|
|
187
|
-
for (const filepath of files) {
|
|
188
|
-
const filename = basename(filepath);
|
|
189
|
-
if (!filename.match(/\.[cm]?[jt]s$/) ||
|
|
190
|
-
filename.endsWith('.js.map') ||
|
|
191
|
-
filename.match(/\.d\.[cm]?ts/) ||
|
|
192
|
-
filename.startsWith('.') ||
|
|
193
|
-
filename.match(/index\.[cm]?[jt]s$/)) {
|
|
194
|
-
this.logger.log('discovery', `- ignoring file ${filename}`);
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
const name = this.namingStrategy.getClassName(filename);
|
|
198
|
-
const path = Utils.normalizePath(this.config.get('baseDir'), filepath);
|
|
199
|
-
const targets = await this.getEntityClassOrSchema(path, name);
|
|
200
|
-
for (const target of targets) {
|
|
201
|
-
if (!(target instanceof Function) && !(target instanceof EntitySchema)) {
|
|
202
|
-
this.logger.log('discovery', `- ignoring file ${filename}`);
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
const entity = this.prepare(target);
|
|
206
|
-
const schema = this.getSchema(entity, path);
|
|
207
|
-
const meta = schema.init().meta;
|
|
208
|
-
this.metadata.set(meta.className, meta);
|
|
209
|
-
found.push([schema, path]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
for (const [schema, path] of found) {
|
|
213
|
-
this.discoverEntity(schema, path);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
discoverReferences(refs) {
|
|
219
|
+
discoverReferences(refs, validate = true) {
|
|
217
220
|
const found = [];
|
|
218
221
|
for (const entity of refs) {
|
|
219
|
-
|
|
222
|
+
if (typeof entity === 'string') {
|
|
223
|
+
throw new Error('Folder based discovery requires the async `MikroORM.init()` method.');
|
|
224
|
+
}
|
|
225
|
+
const schema = this.getSchema(entity);
|
|
220
226
|
const meta = schema.init().meta;
|
|
221
|
-
this.metadata.set(meta.
|
|
227
|
+
this.metadata.set(meta.class, meta);
|
|
222
228
|
found.push(schema);
|
|
223
229
|
}
|
|
224
230
|
// discover parents (base entities) automatically
|
|
225
231
|
for (const meta of this.metadata) {
|
|
226
232
|
let parent = meta.extends;
|
|
227
|
-
if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.
|
|
228
|
-
this.discoverReferences([parent]);
|
|
233
|
+
if (parent instanceof EntitySchema && !this.metadata.has(parent.init().meta.class)) {
|
|
234
|
+
this.discoverReferences([parent], false);
|
|
235
|
+
}
|
|
236
|
+
if (typeof parent === 'function' && parent.name && !this.metadata.has(parent)) {
|
|
237
|
+
this.discoverReferences([parent], false);
|
|
229
238
|
}
|
|
230
|
-
/* v8 ignore next
|
|
239
|
+
/* v8 ignore next */
|
|
231
240
|
if (!meta.class) {
|
|
232
241
|
continue;
|
|
233
242
|
}
|
|
234
243
|
parent = Object.getPrototypeOf(meta.class);
|
|
235
|
-
if
|
|
236
|
-
|
|
244
|
+
// Skip if parent is the auto-generated base class for the same entity (from setClass usage)
|
|
245
|
+
if (parent.name !== '' && parent.name !== meta.className && !this.metadata.has(parent) && parent !== BaseEntity) {
|
|
246
|
+
this.discoverReferences([parent], false);
|
|
237
247
|
}
|
|
238
248
|
}
|
|
239
249
|
for (const schema of found) {
|
|
240
250
|
this.discoverEntity(schema);
|
|
241
251
|
}
|
|
252
|
+
this.discoverMissingTargets();
|
|
253
|
+
if (validate) {
|
|
254
|
+
this.validator.validateDiscovered(this.discovered, this.config.get('discovery'));
|
|
255
|
+
}
|
|
242
256
|
return this.discovered.filter(meta => found.find(m => m.name === meta.className));
|
|
243
257
|
}
|
|
244
|
-
reset(
|
|
245
|
-
const exists = this.discovered.findIndex(m => m.className === className);
|
|
258
|
+
reset(entityName) {
|
|
259
|
+
const exists = this.discovered.findIndex(m => m.class === entityName || m.className === Utils.className(entityName));
|
|
246
260
|
if (exists !== -1) {
|
|
247
|
-
this.metadata.reset(this.discovered[exists].
|
|
261
|
+
this.metadata.reset(this.discovered[exists].class);
|
|
248
262
|
this.discovered.splice(exists, 1);
|
|
249
263
|
}
|
|
250
264
|
}
|
|
251
|
-
|
|
252
|
-
/* v8 ignore next 3 */
|
|
253
|
-
if ('schema' in entity && entity.schema instanceof EntitySchema) {
|
|
254
|
-
return entity.schema;
|
|
255
|
-
}
|
|
265
|
+
getSchema(entity) {
|
|
256
266
|
if (EntitySchema.REGISTRY.has(entity)) {
|
|
257
|
-
|
|
267
|
+
entity = EntitySchema.REGISTRY.get(entity);
|
|
258
268
|
}
|
|
259
|
-
return entity;
|
|
260
|
-
}
|
|
261
|
-
getSchema(entity, filepath) {
|
|
262
269
|
if (entity instanceof EntitySchema) {
|
|
263
|
-
if (filepath) {
|
|
264
|
-
// initialize global metadata for given entity
|
|
265
|
-
MetadataStorage.getMetadata(entity.meta.className, filepath);
|
|
266
|
-
}
|
|
267
270
|
const meta = Utils.copy(entity.meta, false);
|
|
268
271
|
return EntitySchema.fromMetadata(meta);
|
|
269
272
|
}
|
|
270
273
|
const path = entity[MetadataStorage.PATH_SYMBOL];
|
|
271
274
|
if (path) {
|
|
272
275
|
const meta = Utils.copy(MetadataStorage.getMetadata(entity.name, path), false);
|
|
273
|
-
meta.path =
|
|
274
|
-
this.metadata.set(entity
|
|
276
|
+
meta.path = path;
|
|
277
|
+
this.metadata.set(entity, meta);
|
|
275
278
|
}
|
|
276
|
-
const exists = this.metadata.has(entity
|
|
277
|
-
const meta = this.metadata.get(entity
|
|
279
|
+
const exists = this.metadata.has(entity);
|
|
280
|
+
const meta = this.metadata.get(entity, true);
|
|
278
281
|
meta.abstract ??= !(exists && meta.name);
|
|
279
282
|
const schema = EntitySchema.fromMetadata(meta);
|
|
280
283
|
schema.setClass(entity);
|
|
281
284
|
return schema;
|
|
282
285
|
}
|
|
283
|
-
|
|
284
|
-
|
|
286
|
+
getRootEntity(meta) {
|
|
287
|
+
const base = meta.extends && this.metadata.find(meta.extends);
|
|
288
|
+
if (!base || base === meta) { // make sure we do not fall into infinite loop
|
|
289
|
+
return meta;
|
|
290
|
+
}
|
|
291
|
+
const root = this.getRootEntity(base);
|
|
292
|
+
if (root.discriminatorColumn) {
|
|
293
|
+
return root;
|
|
294
|
+
}
|
|
295
|
+
return meta;
|
|
296
|
+
}
|
|
297
|
+
discoverEntity(schema) {
|
|
285
298
|
const meta = schema.meta;
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
const
|
|
299
|
+
const path = meta.path;
|
|
300
|
+
this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
301
|
+
const root = this.getRootEntity(meta);
|
|
302
|
+
schema.meta.path = meta.path;
|
|
303
|
+
const cache = this.metadataProvider.getCachedMetadata(meta, root);
|
|
289
304
|
if (cache) {
|
|
290
305
|
this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
|
|
291
|
-
this.metadataProvider.loadFromCache(meta, cache);
|
|
292
|
-
meta.root = root;
|
|
293
306
|
this.discovered.push(meta);
|
|
294
307
|
return;
|
|
295
308
|
}
|
|
@@ -298,50 +311,18 @@ export class MetadataDiscovery {
|
|
|
298
311
|
this.inferDefaultValue(meta, prop);
|
|
299
312
|
}
|
|
300
313
|
// if the definition is using EntitySchema we still want it to go through the metadata provider to validate no types are missing
|
|
301
|
-
this.metadataProvider.loadEntityMetadata(meta
|
|
302
|
-
if (!meta.
|
|
314
|
+
this.metadataProvider.loadEntityMetadata(meta);
|
|
315
|
+
if (!meta.tableName && meta.name) {
|
|
303
316
|
const entityName = root.discriminatorColumn ? root.name : meta.name;
|
|
304
|
-
meta.
|
|
317
|
+
meta.tableName = this.namingStrategy.classToTableName(entityName);
|
|
305
318
|
}
|
|
306
|
-
|
|
307
|
-
this.saveToCache(meta);
|
|
319
|
+
this.metadataProvider.saveToCache(meta);
|
|
308
320
|
meta.root = root;
|
|
309
321
|
this.discovered.push(meta);
|
|
310
322
|
}
|
|
311
|
-
saveToCache(meta) {
|
|
312
|
-
if (!this.metadataProvider.useCache()) {
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
const copy = Utils.copy(meta, false);
|
|
316
|
-
for (const prop of copy.props) {
|
|
317
|
-
if (Type.isMappedType(prop.type)) {
|
|
318
|
-
Reflect.deleteProperty(prop, 'type');
|
|
319
|
-
Reflect.deleteProperty(prop, 'customType');
|
|
320
|
-
}
|
|
321
|
-
if (prop.default) {
|
|
322
|
-
const raw = RawQueryFragment.getKnownFragment(prop.default);
|
|
323
|
-
if (raw) {
|
|
324
|
-
prop.defaultRaw ??= this.platform.formatQuery(raw.sql, raw.params);
|
|
325
|
-
Reflect.deleteProperty(prop, 'default');
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
Reflect.deleteProperty(prop, 'targetMeta');
|
|
329
|
-
}
|
|
330
|
-
[
|
|
331
|
-
'prototype', 'props', 'referencingProperties', 'propertyOrder', 'relations',
|
|
332
|
-
'concurrencyCheckKeys', 'checks',
|
|
333
|
-
].forEach(key => delete copy[key]);
|
|
334
|
-
// base entity without properties might not have path, but nothing to cache there
|
|
335
|
-
if (meta.path) {
|
|
336
|
-
this.cache.set(meta.className + extname(meta.path), copy, meta.path);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
323
|
initNullability(prop) {
|
|
340
|
-
if (prop.kind === ReferenceKind.MANY_TO_ONE) {
|
|
341
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
|
|
342
|
-
}
|
|
343
324
|
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
344
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner
|
|
325
|
+
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
|
|
345
326
|
}
|
|
346
327
|
return Utils.defaultValue(prop, 'nullable', prop.optional);
|
|
347
328
|
}
|
|
@@ -376,7 +357,7 @@ export class MetadataDiscovery {
|
|
|
376
357
|
if (prop.joinColumns.length !== prop.columnTypes.length) {
|
|
377
358
|
prop.columnTypes = prop.joinColumns.flatMap(field => {
|
|
378
359
|
const matched = meta.props.find(p => p.fieldNames?.includes(field));
|
|
379
|
-
/* v8 ignore next
|
|
360
|
+
/* v8 ignore next */
|
|
380
361
|
if (!matched) {
|
|
381
362
|
throw MetadataError.fromWrongForeignKey(meta, prop, 'columnTypes');
|
|
382
363
|
}
|
|
@@ -403,7 +384,7 @@ export class MetadataDiscovery {
|
|
|
403
384
|
}
|
|
404
385
|
}
|
|
405
386
|
initManyToOneFieldName(prop, name) {
|
|
406
|
-
const meta2 =
|
|
387
|
+
const meta2 = prop.targetMeta;
|
|
407
388
|
const ret = [];
|
|
408
389
|
for (const primaryKey of meta2.primaryKeys) {
|
|
409
390
|
this.initFieldName(meta2.properties[primaryKey]);
|
|
@@ -414,11 +395,11 @@ export class MetadataDiscovery {
|
|
|
414
395
|
return ret;
|
|
415
396
|
}
|
|
416
397
|
initManyToManyFieldName(prop, name) {
|
|
417
|
-
const meta2 =
|
|
398
|
+
const meta2 = prop.targetMeta;
|
|
418
399
|
return meta2.primaryKeys.map(() => this.namingStrategy.propertyToColumnName(name));
|
|
419
400
|
}
|
|
420
401
|
initManyToManyFields(meta, prop) {
|
|
421
|
-
const meta2 =
|
|
402
|
+
const meta2 = prop.targetMeta;
|
|
422
403
|
Utils.defaultValue(prop, 'fixedOrder', !!prop.fixedOrderColumn);
|
|
423
404
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
424
405
|
const props = Object.values(pivotMeta?.properties ?? {});
|
|
@@ -439,26 +420,34 @@ export class MetadataDiscovery {
|
|
|
439
420
|
prop.inverseJoinColumns ??= second.fieldNames;
|
|
440
421
|
}
|
|
441
422
|
if (!prop.pivotTable && prop.owner && this.platform.usesPivotTable()) {
|
|
442
|
-
prop.pivotTable = this.namingStrategy.joinTableName(meta.
|
|
423
|
+
prop.pivotTable = this.namingStrategy.joinTableName(meta.className, meta2.tableName, prop.name, meta.tableName);
|
|
443
424
|
}
|
|
444
425
|
if (prop.mappedBy) {
|
|
445
426
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
446
427
|
this.initManyToManyFields(meta2, prop2);
|
|
447
428
|
prop.pivotTable = prop2.pivotTable;
|
|
448
|
-
prop.pivotEntity = prop2.pivotEntity
|
|
429
|
+
prop.pivotEntity = prop2.pivotEntity;
|
|
449
430
|
prop.fixedOrder = prop2.fixedOrder;
|
|
450
431
|
prop.fixedOrderColumn = prop2.fixedOrderColumn;
|
|
451
432
|
prop.joinColumns = prop2.inverseJoinColumns;
|
|
452
433
|
prop.inverseJoinColumns = prop2.joinColumns;
|
|
453
434
|
}
|
|
454
435
|
prop.referencedColumnNames ??= Utils.flatten(meta.primaryKeys.map(primaryKey => meta.properties[primaryKey].fieldNames));
|
|
455
|
-
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.namingStrategy.joinKeyColumnName(meta.root.className, referencedColumnName, meta.compositePK));
|
|
436
|
+
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.namingStrategy.joinKeyColumnName(meta.root.className, referencedColumnName, meta.compositePK, meta.root.tableName));
|
|
456
437
|
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop, meta2.root.className);
|
|
457
438
|
}
|
|
458
439
|
initManyToOneFields(prop) {
|
|
459
|
-
const meta2 =
|
|
460
|
-
|
|
461
|
-
|
|
440
|
+
const meta2 = prop.targetMeta;
|
|
441
|
+
let fieldNames;
|
|
442
|
+
// If targetKey is specified, use that property's field names instead of PKs
|
|
443
|
+
if (prop.targetKey) {
|
|
444
|
+
const targetProp = meta2.properties[prop.targetKey];
|
|
445
|
+
fieldNames = targetProp.fieldNames;
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
fieldNames = Utils.flatten(meta2.primaryKeys.map(primaryKey => meta2.properties[primaryKey].fieldNames));
|
|
449
|
+
}
|
|
450
|
+
Utils.defaultValue(prop, 'referencedTableName', meta2.tableName);
|
|
462
451
|
if (!prop.joinColumns) {
|
|
463
452
|
prop.joinColumns = fieldNames.map(fieldName => this.namingStrategy.joinKeyColumnName(prop.name, fieldName, fieldNames.length > 1));
|
|
464
453
|
}
|
|
@@ -467,7 +456,7 @@ export class MetadataDiscovery {
|
|
|
467
456
|
}
|
|
468
457
|
}
|
|
469
458
|
initOneToManyFields(prop) {
|
|
470
|
-
const meta2 =
|
|
459
|
+
const meta2 = prop.targetMeta;
|
|
471
460
|
if (!prop.joinColumns) {
|
|
472
461
|
prop.joinColumns = [this.namingStrategy.joinColumnName(prop.name)];
|
|
473
462
|
}
|
|
@@ -485,7 +474,7 @@ export class MetadataDiscovery {
|
|
|
485
474
|
pks[0].deleteRule ??= 'cascade';
|
|
486
475
|
}
|
|
487
476
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
488
|
-
this.validator.validateEntityDefinition(this.metadata, meta.
|
|
477
|
+
this.validator.validateEntityDefinition(this.metadata, meta.class, this.config.get('discovery'));
|
|
489
478
|
for (const prop of Object.values(meta.properties)) {
|
|
490
479
|
this.initNullability(prop);
|
|
491
480
|
this.applyNamingStrategy(meta, prop);
|
|
@@ -498,15 +487,21 @@ export class MetadataDiscovery {
|
|
|
498
487
|
}
|
|
499
488
|
this.initOwnColumns(meta);
|
|
500
489
|
meta.simplePK = pks.length === 1 && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType && pks[0].runtimeType !== 'Date';
|
|
501
|
-
meta.serializedPrimaryKey
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
serializedPKProp.persist = false;
|
|
490
|
+
meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
|
|
491
|
+
if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
|
|
492
|
+
meta.properties[meta.serializedPrimaryKey].persist ??= false;
|
|
505
493
|
}
|
|
506
494
|
if (this.platform.usesPivotTable()) {
|
|
507
495
|
return Object.values(meta.properties)
|
|
508
496
|
.filter(prop => prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && prop.pivotTable)
|
|
509
|
-
.map(prop =>
|
|
497
|
+
.map(prop => {
|
|
498
|
+
const pivotMeta = this.definePivotTableEntity(meta, prop);
|
|
499
|
+
prop.pivotEntity = pivotMeta.class;
|
|
500
|
+
if (prop.inversedBy) {
|
|
501
|
+
prop.targetMeta.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
502
|
+
}
|
|
503
|
+
return pivotMeta;
|
|
504
|
+
});
|
|
510
505
|
}
|
|
511
506
|
return [];
|
|
512
507
|
}
|
|
@@ -523,8 +518,11 @@ export class MetadataDiscovery {
|
|
|
523
518
|
['mappedBy', 'inversedBy', 'pivotEntity'].forEach(type => {
|
|
524
519
|
const value = prop[type];
|
|
525
520
|
if (value instanceof Function) {
|
|
526
|
-
const meta2 = this.metadata.get(prop.
|
|
521
|
+
const meta2 = prop.targetMeta ?? this.metadata.get(prop.target);
|
|
527
522
|
prop[type] = value(meta2.properties)?.name;
|
|
523
|
+
if (type === 'pivotEntity' && value) {
|
|
524
|
+
prop[type] = value(meta2.properties);
|
|
525
|
+
}
|
|
528
526
|
if (prop[type] == null) {
|
|
529
527
|
throw MetadataError.fromWrongReference(meta, prop, type);
|
|
530
528
|
}
|
|
@@ -540,9 +538,9 @@ export class MetadataDiscovery {
|
|
|
540
538
|
}
|
|
541
539
|
else if (fks.length >= 2) {
|
|
542
540
|
[first, second] = fks;
|
|
543
|
-
/* v8 ignore next 3 */
|
|
544
541
|
}
|
|
545
542
|
else {
|
|
543
|
+
/* v8 ignore next */
|
|
546
544
|
return [];
|
|
547
545
|
}
|
|
548
546
|
// wrong FK order, first FK needs to point to the owning side
|
|
@@ -556,7 +554,9 @@ export class MetadataDiscovery {
|
|
|
556
554
|
return [first, second];
|
|
557
555
|
}
|
|
558
556
|
definePivotTableEntity(meta, prop) {
|
|
559
|
-
const pivotMeta =
|
|
557
|
+
const pivotMeta = prop.pivotEntity
|
|
558
|
+
? this.metadata.find(prop.pivotEntity)
|
|
559
|
+
: this.metadata.getByClassName(prop.pivotTable, false);
|
|
560
560
|
// ensure inverse side exists so we can join it when populating via pivot tables
|
|
561
561
|
if (!prop.inversedBy && prop.targetMeta) {
|
|
562
562
|
const inverseName = `${meta.className}_${prop.name}__inverse`;
|
|
@@ -565,6 +565,8 @@ export class MetadataDiscovery {
|
|
|
565
565
|
name: inverseName,
|
|
566
566
|
kind: ReferenceKind.MANY_TO_MANY,
|
|
567
567
|
type: meta.className,
|
|
568
|
+
target: meta.class,
|
|
569
|
+
targetMeta: meta,
|
|
568
570
|
mappedBy: prop.name,
|
|
569
571
|
pivotEntity: prop.pivotEntity,
|
|
570
572
|
pivotTable: prop.pivotTable,
|
|
@@ -573,55 +575,51 @@ export class MetadataDiscovery {
|
|
|
573
575
|
};
|
|
574
576
|
this.applyNamingStrategy(prop.targetMeta, inverseProp);
|
|
575
577
|
this.initCustomType(prop.targetMeta, inverseProp);
|
|
576
|
-
this.initRelation(inverseProp);
|
|
577
578
|
prop.targetMeta.properties[inverseName] = inverseProp;
|
|
578
579
|
}
|
|
579
580
|
if (pivotMeta) {
|
|
581
|
+
prop.pivotEntity = pivotMeta.class;
|
|
580
582
|
this.ensureCorrectFKOrderInPivotEntity(pivotMeta, prop);
|
|
581
583
|
return pivotMeta;
|
|
582
584
|
}
|
|
583
|
-
const exists = this.metadata.find(prop.pivotTable);
|
|
584
|
-
if (exists) {
|
|
585
|
-
prop.pivotEntity = exists.className;
|
|
586
|
-
return exists;
|
|
587
|
-
}
|
|
588
585
|
let tableName = prop.pivotTable;
|
|
589
586
|
let schemaName;
|
|
590
587
|
if (prop.pivotTable.includes('.')) {
|
|
591
588
|
[schemaName, tableName] = prop.pivotTable.split('.');
|
|
592
589
|
}
|
|
593
590
|
schemaName ??= meta.schema;
|
|
594
|
-
const
|
|
595
|
-
const
|
|
591
|
+
const targetMeta = prop.targetMeta;
|
|
592
|
+
const targetType = targetMeta.className;
|
|
593
|
+
const pivotMeta2 = new EntityMetadata({
|
|
596
594
|
name: prop.pivotTable,
|
|
597
595
|
className: prop.pivotTable,
|
|
598
596
|
collection: tableName,
|
|
599
597
|
schema: schemaName,
|
|
600
598
|
pivotTable: true,
|
|
601
599
|
});
|
|
602
|
-
prop.pivotEntity =
|
|
600
|
+
prop.pivotEntity = pivotMeta2.class;
|
|
603
601
|
if (prop.fixedOrder) {
|
|
604
|
-
const primaryProp = this.defineFixedOrderProperty(prop,
|
|
605
|
-
|
|
602
|
+
const primaryProp = this.defineFixedOrderProperty(prop, targetMeta);
|
|
603
|
+
pivotMeta2.properties[primaryProp.name] = primaryProp;
|
|
606
604
|
}
|
|
607
605
|
else {
|
|
608
|
-
|
|
606
|
+
pivotMeta2.compositePK = true;
|
|
609
607
|
}
|
|
610
608
|
// handle self-referenced m:n with same default field names
|
|
611
609
|
if (meta.className === targetType && prop.joinColumns.every((joinColumn, idx) => joinColumn === prop.inverseJoinColumns[idx])) {
|
|
612
|
-
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.
|
|
613
|
-
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.
|
|
610
|
+
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.tableName + '_1', name, meta.compositePK));
|
|
611
|
+
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.tableName + '_2', name, meta.compositePK));
|
|
614
612
|
if (prop.inversedBy) {
|
|
615
|
-
const prop2 =
|
|
613
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
616
614
|
prop2.inverseJoinColumns = prop.joinColumns;
|
|
617
615
|
prop2.joinColumns = prop.inverseJoinColumns;
|
|
618
616
|
}
|
|
619
617
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return this.metadata.set(
|
|
618
|
+
pivotMeta2.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.class, targetType + '_inverse', true, meta.className === targetType);
|
|
619
|
+
pivotMeta2.properties[targetType + '_inverse'] = this.definePivotProperty(prop, targetType + '_inverse', targetMeta.class, meta.name + '_owner', false, meta.className === targetType);
|
|
620
|
+
return this.metadata.set(pivotMeta2.class, EntitySchema.fromMetadata(pivotMeta2).init().meta);
|
|
623
621
|
}
|
|
624
|
-
defineFixedOrderProperty(prop,
|
|
622
|
+
defineFixedOrderProperty(prop, targetMeta) {
|
|
625
623
|
const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
|
|
626
624
|
const primaryProp = {
|
|
627
625
|
name: pk,
|
|
@@ -635,7 +633,7 @@ export class MetadataDiscovery {
|
|
|
635
633
|
this.initColumnType(primaryProp);
|
|
636
634
|
prop.fixedOrderColumn = pk;
|
|
637
635
|
if (prop.inversedBy) {
|
|
638
|
-
const prop2 =
|
|
636
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
639
637
|
prop2.fixedOrder = true;
|
|
640
638
|
prop2.fixedOrderColumn = pk;
|
|
641
639
|
}
|
|
@@ -644,7 +642,8 @@ export class MetadataDiscovery {
|
|
|
644
642
|
definePivotProperty(prop, name, type, inverse, owner, selfReferencing) {
|
|
645
643
|
const ret = {
|
|
646
644
|
name,
|
|
647
|
-
type,
|
|
645
|
+
type: Utils.className(type),
|
|
646
|
+
target: type,
|
|
648
647
|
kind: ReferenceKind.MANY_TO_ONE,
|
|
649
648
|
cascade: [Cascade.ALL],
|
|
650
649
|
fixedOrder: prop.fixedOrder,
|
|
@@ -654,6 +653,7 @@ export class MetadataDiscovery {
|
|
|
654
653
|
autoincrement: false,
|
|
655
654
|
updateRule: prop.updateRule,
|
|
656
655
|
deleteRule: prop.deleteRule,
|
|
656
|
+
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
657
657
|
};
|
|
658
658
|
if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
|
|
659
659
|
ret.updateRule ??= 'no action';
|
|
@@ -701,7 +701,7 @@ export class MetadataDiscovery {
|
|
|
701
701
|
Object.values(meta.properties)
|
|
702
702
|
.filter(prop => prop.kind !== ReferenceKind.SCALAR && !prop.owner && prop.mappedBy)
|
|
703
703
|
.forEach(prop => {
|
|
704
|
-
const meta2 =
|
|
704
|
+
const meta2 = prop.targetMeta;
|
|
705
705
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
706
706
|
if (prop2 && !prop2.inversedBy) {
|
|
707
707
|
prop2.inversedBy = prop.name;
|
|
@@ -709,7 +709,7 @@ export class MetadataDiscovery {
|
|
|
709
709
|
});
|
|
710
710
|
}
|
|
711
711
|
defineBaseEntityProperties(meta) {
|
|
712
|
-
const base = meta.extends && this.metadata.get(
|
|
712
|
+
const base = meta.extends && this.metadata.get(meta.extends);
|
|
713
713
|
if (!base || base === meta) { // make sure we do not fall into infinite loop
|
|
714
714
|
return 0;
|
|
715
715
|
}
|
|
@@ -740,12 +740,9 @@ export class MetadataDiscovery {
|
|
|
740
740
|
Utils.keys(base.hooks).forEach(type => {
|
|
741
741
|
meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
|
|
742
742
|
});
|
|
743
|
-
if (meta.constructorParams
|
|
743
|
+
if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
|
|
744
744
|
meta.constructorParams = [...base.constructorParams];
|
|
745
745
|
}
|
|
746
|
-
if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
|
|
747
|
-
meta.toJsonParams = [...base.toJsonParams];
|
|
748
|
-
}
|
|
749
746
|
return order;
|
|
750
747
|
}
|
|
751
748
|
initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
|
|
@@ -821,22 +818,30 @@ export class MetadataDiscovery {
|
|
|
821
818
|
}
|
|
822
819
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
823
820
|
};
|
|
821
|
+
const isParentArray = (prop) => {
|
|
822
|
+
if (prop.array) {
|
|
823
|
+
return true;
|
|
824
|
+
}
|
|
825
|
+
return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
|
|
826
|
+
};
|
|
824
827
|
const rootProperty = getRootProperty(embeddedProp);
|
|
825
828
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
826
829
|
const object = isParentObject(embeddedProp);
|
|
830
|
+
const array = isParentArray(embeddedProp);
|
|
827
831
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
828
832
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
829
833
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
830
834
|
const glue = object ? '~' : '_';
|
|
831
835
|
for (const prop of Object.values(embeddable.properties)) {
|
|
832
836
|
const name = (embeddedProp.embeddedPath?.join(glue) ?? embeddedProp.fieldNames[0] + glue) + prop.name;
|
|
833
|
-
meta.properties[name] = Utils.copy(prop
|
|
837
|
+
meta.properties[name] = Utils.copy(prop);
|
|
834
838
|
meta.properties[name].name = name;
|
|
835
839
|
meta.properties[name].embedded = [embeddedProp.name, prop.name];
|
|
836
840
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
837
841
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
838
842
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
839
|
-
|
|
843
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
844
|
+
if (embeddedProp.nullable || refInArray) {
|
|
840
845
|
meta.properties[name].nullable = true;
|
|
841
846
|
}
|
|
842
847
|
if (meta.properties[name].fieldNames) {
|
|
@@ -866,14 +871,17 @@ export class MetadataDiscovery {
|
|
|
866
871
|
path = [embeddedProp.fieldNames[0]];
|
|
867
872
|
}
|
|
868
873
|
this.initFieldName(prop, true);
|
|
874
|
+
this.initRelation(prop);
|
|
869
875
|
path.push(prop.fieldNames[0]);
|
|
870
876
|
meta.properties[name].fieldNames = prop.fieldNames;
|
|
871
877
|
meta.properties[name].embeddedPath = path;
|
|
872
|
-
const
|
|
878
|
+
const targetProp = prop.targetMeta?.getPrimaryProp() ?? prop;
|
|
879
|
+
const fieldName = raw(this.platform.getSearchJsonPropertySQL(path.join('->'), targetProp.runtimeType ?? targetProp.type, true));
|
|
873
880
|
meta.properties[name].fieldNameRaw = fieldName.sql; // for querying in SQL drivers
|
|
874
881
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
875
882
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
876
883
|
meta.properties[name].object = true;
|
|
884
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
877
885
|
}
|
|
878
886
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
879
887
|
}
|
|
@@ -896,7 +904,7 @@ export class MetadataDiscovery {
|
|
|
896
904
|
}
|
|
897
905
|
initSingleTableInheritance(meta, metadata) {
|
|
898
906
|
if (meta.root !== meta && !meta.__processed) {
|
|
899
|
-
meta.root = metadata.find(m => m.
|
|
907
|
+
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
900
908
|
meta.root.__processed = true;
|
|
901
909
|
}
|
|
902
910
|
else {
|
|
@@ -905,17 +913,23 @@ export class MetadataDiscovery {
|
|
|
905
913
|
if (!meta.root.discriminatorColumn) {
|
|
906
914
|
return;
|
|
907
915
|
}
|
|
908
|
-
if (
|
|
916
|
+
if (meta.root.discriminatorMap) {
|
|
917
|
+
const map = meta.root.discriminatorMap;
|
|
918
|
+
Object.keys(map)
|
|
919
|
+
.filter(key => typeof map[key] === 'string')
|
|
920
|
+
.forEach(key => map[key] = this.metadata.getByClassName(map[key]).class);
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
909
923
|
meta.root.discriminatorMap = {};
|
|
910
924
|
const children = metadata
|
|
911
|
-
.filter(m => m.root.
|
|
925
|
+
.filter(m => m.root.class === meta.root.class && !m.abstract)
|
|
912
926
|
.sort((a, b) => a.className.localeCompare(b.className));
|
|
913
927
|
for (const m of children) {
|
|
914
928
|
const name = m.discriminatorValue ?? this.namingStrategy.classToTableName(m.className);
|
|
915
|
-
meta.root.discriminatorMap[name] = m.
|
|
929
|
+
meta.root.discriminatorMap[name] = m.class;
|
|
916
930
|
}
|
|
917
931
|
}
|
|
918
|
-
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([,
|
|
932
|
+
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([, cls]) => cls === meta.class)?.[0];
|
|
919
933
|
if (!meta.root.properties[meta.root.discriminatorColumn]) {
|
|
920
934
|
this.createDiscriminatorProperty(meta.root);
|
|
921
935
|
}
|
|
@@ -927,25 +941,44 @@ export class MetadataDiscovery {
|
|
|
927
941
|
let i = 1;
|
|
928
942
|
Object.values(meta.properties).forEach(prop => {
|
|
929
943
|
const newProp = { ...prop };
|
|
930
|
-
|
|
944
|
+
const rootProp = meta.root.properties[prop.name];
|
|
945
|
+
if (rootProp && (rootProp.type !== prop.type || (rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
931
946
|
const name = newProp.name;
|
|
932
947
|
this.initFieldName(newProp, newProp.object);
|
|
948
|
+
newProp.renamedFrom = name;
|
|
933
949
|
newProp.name = name + '_' + (i++);
|
|
934
950
|
meta.root.addProperty(newProp);
|
|
951
|
+
// Track all field variants and map discriminator values to field names
|
|
952
|
+
if (!rootProp.stiFieldNames) {
|
|
953
|
+
this.initFieldName(prop, prop.object);
|
|
954
|
+
this.initFieldName(rootProp, rootProp.object);
|
|
955
|
+
rootProp.stiFieldNames = [...rootProp.fieldNames];
|
|
956
|
+
rootProp.stiFieldNameMap = {};
|
|
957
|
+
// Find which discriminator owns the original fieldNames
|
|
958
|
+
for (const [discValue, childClass] of Object.entries(meta.root.discriminatorMap)) {
|
|
959
|
+
const childMeta = this.metadata.find(childClass);
|
|
960
|
+
if (childMeta?.properties[prop.name]?.fieldNames && compareArrays(childMeta.properties[prop.name].fieldNames, rootProp.fieldNames)) {
|
|
961
|
+
rootProp.stiFieldNameMap[discValue] = rootProp.fieldNames[0];
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
rootProp.stiFieldNameMap[meta.discriminatorValue] = prop.fieldNames[0];
|
|
967
|
+
rootProp.stiFieldNames.push(...prop.fieldNames);
|
|
935
968
|
newProp.nullable = true;
|
|
936
969
|
newProp.name = name;
|
|
937
970
|
newProp.hydrate = false;
|
|
938
971
|
newProp.inherited = true;
|
|
939
972
|
return;
|
|
940
973
|
}
|
|
941
|
-
if (prop.enum && prop.items &&
|
|
942
|
-
newProp.items = Utils.unique([...
|
|
974
|
+
if (prop.enum && prop.items && rootProp?.items) {
|
|
975
|
+
newProp.items = Utils.unique([...rootProp.items, ...prop.items]);
|
|
943
976
|
}
|
|
944
977
|
newProp.nullable = true;
|
|
945
|
-
newProp.inherited =
|
|
978
|
+
newProp.inherited = !rootProp;
|
|
946
979
|
meta.root.addProperty(newProp);
|
|
947
980
|
});
|
|
948
|
-
meta.
|
|
981
|
+
meta.tableName = meta.root.tableName;
|
|
949
982
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
950
983
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
951
984
|
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
@@ -967,7 +1000,7 @@ export class MetadataDiscovery {
|
|
|
967
1000
|
}
|
|
968
1001
|
}
|
|
969
1002
|
initCheckConstraints(meta) {
|
|
970
|
-
const map =
|
|
1003
|
+
const map = meta.createColumnMappingObject();
|
|
971
1004
|
for (const check of meta.checks) {
|
|
972
1005
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
973
1006
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -977,7 +1010,7 @@ export class MetadataDiscovery {
|
|
|
977
1010
|
}
|
|
978
1011
|
if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
|
|
979
1012
|
for (const prop of meta.props) {
|
|
980
|
-
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item =>
|
|
1013
|
+
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
|
|
981
1014
|
this.initFieldName(prop);
|
|
982
1015
|
meta.checks.push({
|
|
983
1016
|
name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
|
|
@@ -1002,38 +1035,28 @@ export class MetadataDiscovery {
|
|
|
1002
1035
|
}
|
|
1003
1036
|
return;
|
|
1004
1037
|
}
|
|
1005
|
-
const map =
|
|
1038
|
+
const map = meta.createColumnMappingObject();
|
|
1006
1039
|
if (prop.generated instanceof Function) {
|
|
1007
1040
|
prop.generated = prop.generated(map);
|
|
1008
1041
|
}
|
|
1009
1042
|
}
|
|
1010
|
-
|
|
1011
|
-
return Object.values(meta.properties).reduce((o, prop) => {
|
|
1012
|
-
if (prop.fieldNames) {
|
|
1013
|
-
o[prop.name] = prop.fieldNames[0];
|
|
1014
|
-
}
|
|
1015
|
-
return o;
|
|
1016
|
-
}, {});
|
|
1017
|
-
}
|
|
1018
|
-
getDefaultVersionValue(prop) {
|
|
1043
|
+
getDefaultVersionValue(meta, prop) {
|
|
1019
1044
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1020
1045
|
return prop.defaultRaw;
|
|
1021
1046
|
}
|
|
1022
|
-
/* v8 ignore next
|
|
1047
|
+
/* v8 ignore next */
|
|
1023
1048
|
if (prop.default != null) {
|
|
1024
1049
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1025
1050
|
}
|
|
1026
|
-
|
|
1051
|
+
this.initCustomType(meta, prop, true);
|
|
1052
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1053
|
+
if (type === 'Date') {
|
|
1027
1054
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1028
1055
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1029
1056
|
}
|
|
1030
1057
|
return '1';
|
|
1031
1058
|
}
|
|
1032
1059
|
inferDefaultValue(meta, prop) {
|
|
1033
|
-
/* v8 ignore next 3 */
|
|
1034
|
-
if (!meta.class) {
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
1060
|
try {
|
|
1038
1061
|
// try to create two entity instances to detect the value is stable
|
|
1039
1062
|
const now = Date.now();
|
|
@@ -1061,12 +1084,12 @@ export class MetadataDiscovery {
|
|
|
1061
1084
|
return;
|
|
1062
1085
|
}
|
|
1063
1086
|
let val = prop.default;
|
|
1064
|
-
const raw =
|
|
1087
|
+
const raw = Raw.getKnownFragment(val);
|
|
1065
1088
|
if (raw) {
|
|
1066
1089
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1067
1090
|
return;
|
|
1068
1091
|
}
|
|
1069
|
-
if (
|
|
1092
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1070
1093
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1071
1094
|
}
|
|
1072
1095
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1094,13 +1117,13 @@ export class MetadataDiscovery {
|
|
|
1094
1117
|
if (prop.version) {
|
|
1095
1118
|
this.initDefaultValue(prop);
|
|
1096
1119
|
meta.versionProperty = prop.name;
|
|
1097
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1120
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1098
1121
|
}
|
|
1099
1122
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1100
1123
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1101
1124
|
}
|
|
1102
1125
|
}
|
|
1103
|
-
initCustomType(meta, prop) {
|
|
1126
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1104
1127
|
// `prop.type` might be actually instance of custom type class
|
|
1105
1128
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1106
1129
|
prop.customType = prop.type;
|
|
@@ -1108,41 +1131,61 @@ export class MetadataDiscovery {
|
|
|
1108
1131
|
}
|
|
1109
1132
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1110
1133
|
if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
|
|
1111
|
-
|
|
1112
|
-
|
|
1134
|
+
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1135
|
+
// we use just the type name, to have more performant hydration code
|
|
1136
|
+
const type = Utils.keys(t).find(type => {
|
|
1137
|
+
return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
|
|
1138
|
+
});
|
|
1139
|
+
if (type) {
|
|
1140
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
prop.customType = new prop.type();
|
|
1144
|
+
prop.type = prop.customType.constructor.name;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
if (simple) {
|
|
1148
|
+
return;
|
|
1113
1149
|
}
|
|
1114
1150
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1115
|
-
prop.customType = new
|
|
1151
|
+
prop.customType = new t.json();
|
|
1116
1152
|
}
|
|
1117
1153
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1118
|
-
prop.customType = new
|
|
1154
|
+
prop.customType = new t.json();
|
|
1155
|
+
}
|
|
1156
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1157
|
+
prop.customType = new t.json();
|
|
1119
1158
|
}
|
|
1120
1159
|
if (!prop.customType && prop.array && prop.items) {
|
|
1121
|
-
prop.customType = new
|
|
1160
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1161
|
+
}
|
|
1162
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1163
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1164
|
+
prop.customType = new t.json();
|
|
1122
1165
|
}
|
|
1123
1166
|
// for number arrays we make sure to convert the items to numbers
|
|
1124
1167
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1125
|
-
prop.customType = new
|
|
1168
|
+
prop.customType = new t.array(i => +i);
|
|
1126
1169
|
}
|
|
1127
1170
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1128
|
-
if (!prop.customType &&
|
|
1129
|
-
prop.customType = new
|
|
1171
|
+
if (!prop.customType && isArray) {
|
|
1172
|
+
prop.customType = new t.array();
|
|
1130
1173
|
}
|
|
1131
1174
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1132
|
-
prop.customType = new
|
|
1175
|
+
prop.customType = new t.blob();
|
|
1133
1176
|
}
|
|
1134
1177
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1135
|
-
prop.customType = new
|
|
1178
|
+
prop.customType = new t.uint8array();
|
|
1136
1179
|
}
|
|
1137
1180
|
const mappedType = this.getMappedType(prop);
|
|
1138
1181
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1139
|
-
[
|
|
1182
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1140
1183
|
.filter(type => mappedType instanceof type)
|
|
1141
|
-
.forEach(type => prop.customType = new type());
|
|
1184
|
+
.forEach((type) => prop.customType = new type());
|
|
1142
1185
|
}
|
|
1143
1186
|
if (prop.customType && !prop.columnTypes) {
|
|
1144
1187
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1145
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1188
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1146
1189
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1147
1190
|
}
|
|
1148
1191
|
else {
|
|
@@ -1162,16 +1205,16 @@ export class MetadataDiscovery {
|
|
|
1162
1205
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1163
1206
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1164
1207
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1165
|
-
if (prop.customType instanceof
|
|
1208
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1166
1209
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1167
1210
|
}
|
|
1168
1211
|
}
|
|
1169
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1212
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1170
1213
|
prop.type = prop.customType.name;
|
|
1171
1214
|
}
|
|
1172
|
-
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
1215
|
+
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && prop.targetMeta.compositePK) {
|
|
1173
1216
|
prop.customTypes = [];
|
|
1174
|
-
for (const pk of
|
|
1217
|
+
for (const pk of prop.targetMeta.getPrimaryProps()) {
|
|
1175
1218
|
if (pk.customType) {
|
|
1176
1219
|
prop.customTypes.push(pk.customType);
|
|
1177
1220
|
prop.hasConvertToJSValueSQL ||= !!pk.customType.convertToJSValueSQL && pk.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
@@ -1183,7 +1226,7 @@ export class MetadataDiscovery {
|
|
|
1183
1226
|
}
|
|
1184
1227
|
}
|
|
1185
1228
|
}
|
|
1186
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1229
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1187
1230
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1188
1231
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1189
1232
|
}
|
|
@@ -1201,19 +1244,25 @@ export class MetadataDiscovery {
|
|
|
1201
1244
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1202
1245
|
return;
|
|
1203
1246
|
}
|
|
1204
|
-
|
|
1205
|
-
prop.
|
|
1247
|
+
// when the target is a polymorphic embedded entity, `prop.target` is an array of classes, we need to get the metadata by the type name instead
|
|
1248
|
+
const meta2 = this.metadata.find(prop.target) ?? this.metadata.getByClassName(prop.type);
|
|
1249
|
+
// If targetKey is specified, use that property instead of PKs for referencedPKs
|
|
1250
|
+
prop.referencedPKs = prop.targetKey ? [prop.targetKey] : meta2.primaryKeys;
|
|
1206
1251
|
prop.targetMeta = meta2;
|
|
1207
1252
|
if (!prop.formula && prop.persist === false && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.embedded) {
|
|
1208
|
-
prop.formula =
|
|
1253
|
+
prop.formula = table => `${table}.${this.platform.quoteIdentifier(prop.fieldNames[0])}`;
|
|
1209
1254
|
}
|
|
1210
1255
|
}
|
|
1211
1256
|
initColumnType(prop) {
|
|
1212
1257
|
this.initUnsigned(prop);
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
prop.
|
|
1216
|
-
|
|
1258
|
+
// Get the target properties for FK relations - use targetKey property if specified, otherwise PKs
|
|
1259
|
+
const targetProps = prop.targetMeta
|
|
1260
|
+
? (prop.targetKey ? [prop.targetMeta.properties[prop.targetKey]] : prop.targetMeta.getPrimaryProps())
|
|
1261
|
+
: [];
|
|
1262
|
+
targetProps.map(targetProp => {
|
|
1263
|
+
prop.length ??= targetProp.length;
|
|
1264
|
+
prop.precision ??= targetProp.precision;
|
|
1265
|
+
prop.scale ??= targetProp.scale;
|
|
1217
1266
|
});
|
|
1218
1267
|
if (prop.kind === ReferenceKind.SCALAR && (prop.type == null || prop.type === 'object') && prop.columnTypes?.[0]) {
|
|
1219
1268
|
delete prop.type;
|
|
@@ -1226,8 +1275,7 @@ export class MetadataDiscovery {
|
|
|
1226
1275
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1227
1276
|
const mappedType = this.getMappedType(prop);
|
|
1228
1277
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1229
|
-
if (mappedType instanceof
|
|
1230
|
-
&& !prop.columnTypes
|
|
1278
|
+
if (mappedType instanceof t.unknown
|
|
1231
1279
|
// it could be a runtime type from reflect-metadata
|
|
1232
1280
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
1233
1281
|
// or it might be inferred via ts-morph to some generic type alias
|
|
@@ -1240,23 +1288,28 @@ export class MetadataDiscovery {
|
|
|
1240
1288
|
}
|
|
1241
1289
|
return;
|
|
1242
1290
|
}
|
|
1243
|
-
|
|
1291
|
+
/* v8 ignore next */
|
|
1292
|
+
if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
|
|
1244
1293
|
prop.columnTypes = [this.platform.getJsonDeclarationSQL()];
|
|
1245
1294
|
return;
|
|
1246
1295
|
}
|
|
1247
|
-
const targetMeta =
|
|
1296
|
+
const targetMeta = prop.targetMeta;
|
|
1248
1297
|
prop.columnTypes = [];
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1298
|
+
// Use targetKey property if specified, otherwise use primary key properties
|
|
1299
|
+
const referencedProps = prop.targetKey
|
|
1300
|
+
? [targetMeta.properties[prop.targetKey]]
|
|
1301
|
+
: targetMeta.getPrimaryProps();
|
|
1302
|
+
for (const referencedProp of referencedProps) {
|
|
1303
|
+
this.initCustomType(targetMeta, referencedProp);
|
|
1304
|
+
this.initColumnType(referencedProp);
|
|
1305
|
+
const mappedType = this.getMappedType(referencedProp);
|
|
1306
|
+
let columnTypes = referencedProp.columnTypes;
|
|
1307
|
+
if (referencedProp.autoincrement) {
|
|
1308
|
+
columnTypes = [mappedType.getColumnType({ ...referencedProp, autoincrement: false }, this.platform)];
|
|
1256
1309
|
}
|
|
1257
1310
|
prop.columnTypes.push(...columnTypes);
|
|
1258
|
-
if (!targetMeta.compositePK) {
|
|
1259
|
-
prop.customType =
|
|
1311
|
+
if (!targetMeta.compositePK || prop.targetKey) {
|
|
1312
|
+
prop.customType = referencedProp.customType;
|
|
1260
1313
|
}
|
|
1261
1314
|
}
|
|
1262
1315
|
}
|
|
@@ -1270,7 +1323,7 @@ export class MetadataDiscovery {
|
|
|
1270
1323
|
t = 'enum';
|
|
1271
1324
|
}
|
|
1272
1325
|
else if (prop.enum) {
|
|
1273
|
-
t = prop.items?.every(item =>
|
|
1326
|
+
t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
|
|
1274
1327
|
}
|
|
1275
1328
|
if (t === 'Date') {
|
|
1276
1329
|
t = 'datetime';
|
|
@@ -1294,7 +1347,7 @@ export class MetadataDiscovery {
|
|
|
1294
1347
|
return;
|
|
1295
1348
|
}
|
|
1296
1349
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
1297
|
-
const meta2 =
|
|
1350
|
+
const meta2 = prop.targetMeta;
|
|
1298
1351
|
prop.unsigned = meta2.getPrimaryProps().some(pk => {
|
|
1299
1352
|
this.initUnsigned(pk);
|
|
1300
1353
|
return pk.unsigned;
|
|
@@ -1309,30 +1362,6 @@ export class MetadataDiscovery {
|
|
|
1309
1362
|
prop.index ??= true;
|
|
1310
1363
|
}
|
|
1311
1364
|
}
|
|
1312
|
-
async getEntityClassOrSchema(path, name) {
|
|
1313
|
-
const exports = await Utils.dynamicImport(path);
|
|
1314
|
-
const targets = Object.values(exports)
|
|
1315
|
-
.filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
|
|
1316
|
-
// ignore class implementations that are linked from an EntitySchema
|
|
1317
|
-
for (const item of targets) {
|
|
1318
|
-
if (item instanceof EntitySchema) {
|
|
1319
|
-
targets.forEach((item2, idx) => {
|
|
1320
|
-
if (item.meta.class === item2) {
|
|
1321
|
-
targets.splice(idx, 1);
|
|
1322
|
-
}
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
if (targets.length > 0) {
|
|
1327
|
-
return targets;
|
|
1328
|
-
}
|
|
1329
|
-
const target = exports.default ?? exports[name];
|
|
1330
|
-
/* v8 ignore next 3 */
|
|
1331
|
-
if (!target) {
|
|
1332
|
-
throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
|
|
1333
|
-
}
|
|
1334
|
-
return [target];
|
|
1335
|
-
}
|
|
1336
1365
|
shouldForceConstructorUsage(meta) {
|
|
1337
1366
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
1338
1367
|
if (Array.isArray(forceConstructor)) {
|