@mikro-orm/core 7.0.0-dev.17 → 7.0.0-dev.170
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 +300 -274
- 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 +44 -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 +22 -9
- package/errors.js +56 -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 +369 -326
- 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 +17 -9
- package/metadata/MetadataValidator.js +93 -39
- 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 +490 -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 +185 -116
- package/typings.js +59 -44
- 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 +764 -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,35 +420,53 @@ 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
|
}
|
|
465
454
|
if (!prop.referencedColumnNames) {
|
|
466
455
|
prop.referencedColumnNames = fieldNames;
|
|
467
456
|
}
|
|
457
|
+
// Relations to composite PK targets need cascade update by default,
|
|
458
|
+
// since composite PKs are more likely to have mutable components
|
|
459
|
+
if (meta2.compositePK) {
|
|
460
|
+
prop.updateRule ??= 'cascade';
|
|
461
|
+
}
|
|
462
|
+
// Nullable relations default to 'set null' on delete - when the referenced
|
|
463
|
+
// entity is deleted, set the FK to null rather than failing
|
|
464
|
+
if (prop.nullable) {
|
|
465
|
+
prop.deleteRule ??= 'set null';
|
|
466
|
+
}
|
|
468
467
|
}
|
|
469
468
|
initOneToManyFields(prop) {
|
|
470
|
-
const meta2 =
|
|
469
|
+
const meta2 = prop.targetMeta;
|
|
471
470
|
if (!prop.joinColumns) {
|
|
472
471
|
prop.joinColumns = [this.namingStrategy.joinColumnName(prop.name)];
|
|
473
472
|
}
|
|
@@ -480,12 +479,17 @@ export class MetadataDiscovery {
|
|
|
480
479
|
const pks = Object.values(meta.properties).filter(prop => prop.primary);
|
|
481
480
|
meta.primaryKeys = pks.map(prop => prop.name);
|
|
482
481
|
meta.compositePK = pks.length > 1;
|
|
483
|
-
// FK used as PK, we need to cascade
|
|
484
|
-
|
|
485
|
-
|
|
482
|
+
// FK used as PK, we need to cascade - applies to both single FK-as-PK
|
|
483
|
+
// and composite PKs where all PKs are FKs (e.g., pivot-like entities)
|
|
484
|
+
const fkPks = pks.filter(pk => pk.kind !== ReferenceKind.SCALAR);
|
|
485
|
+
if (fkPks.length > 0 && fkPks.length === pks.length) {
|
|
486
|
+
for (const pk of fkPks) {
|
|
487
|
+
pk.deleteRule ??= 'cascade';
|
|
488
|
+
pk.updateRule ??= 'cascade';
|
|
489
|
+
}
|
|
486
490
|
}
|
|
487
491
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
488
|
-
this.validator.validateEntityDefinition(this.metadata, meta.
|
|
492
|
+
this.validator.validateEntityDefinition(this.metadata, meta.class, this.config.get('discovery'));
|
|
489
493
|
for (const prop of Object.values(meta.properties)) {
|
|
490
494
|
this.initNullability(prop);
|
|
491
495
|
this.applyNamingStrategy(meta, prop);
|
|
@@ -498,15 +502,21 @@ export class MetadataDiscovery {
|
|
|
498
502
|
}
|
|
499
503
|
this.initOwnColumns(meta);
|
|
500
504
|
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;
|
|
505
|
+
meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
|
|
506
|
+
if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
|
|
507
|
+
meta.properties[meta.serializedPrimaryKey].persist ??= false;
|
|
505
508
|
}
|
|
506
509
|
if (this.platform.usesPivotTable()) {
|
|
507
510
|
return Object.values(meta.properties)
|
|
508
511
|
.filter(prop => prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && prop.pivotTable)
|
|
509
|
-
.map(prop =>
|
|
512
|
+
.map(prop => {
|
|
513
|
+
const pivotMeta = this.definePivotTableEntity(meta, prop);
|
|
514
|
+
prop.pivotEntity = pivotMeta.class;
|
|
515
|
+
if (prop.inversedBy) {
|
|
516
|
+
prop.targetMeta.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
517
|
+
}
|
|
518
|
+
return pivotMeta;
|
|
519
|
+
});
|
|
510
520
|
}
|
|
511
521
|
return [];
|
|
512
522
|
}
|
|
@@ -523,8 +533,11 @@ export class MetadataDiscovery {
|
|
|
523
533
|
['mappedBy', 'inversedBy', 'pivotEntity'].forEach(type => {
|
|
524
534
|
const value = prop[type];
|
|
525
535
|
if (value instanceof Function) {
|
|
526
|
-
const meta2 = this.metadata.get(prop.
|
|
536
|
+
const meta2 = prop.targetMeta ?? this.metadata.get(prop.target);
|
|
527
537
|
prop[type] = value(meta2.properties)?.name;
|
|
538
|
+
if (type === 'pivotEntity' && value) {
|
|
539
|
+
prop[type] = value(meta2.properties);
|
|
540
|
+
}
|
|
528
541
|
if (prop[type] == null) {
|
|
529
542
|
throw MetadataError.fromWrongReference(meta, prop, type);
|
|
530
543
|
}
|
|
@@ -540,9 +553,9 @@ export class MetadataDiscovery {
|
|
|
540
553
|
}
|
|
541
554
|
else if (fks.length >= 2) {
|
|
542
555
|
[first, second] = fks;
|
|
543
|
-
/* v8 ignore next 3 */
|
|
544
556
|
}
|
|
545
557
|
else {
|
|
558
|
+
/* v8 ignore next */
|
|
546
559
|
return [];
|
|
547
560
|
}
|
|
548
561
|
// wrong FK order, first FK needs to point to the owning side
|
|
@@ -556,7 +569,9 @@ export class MetadataDiscovery {
|
|
|
556
569
|
return [first, second];
|
|
557
570
|
}
|
|
558
571
|
definePivotTableEntity(meta, prop) {
|
|
559
|
-
const pivotMeta =
|
|
572
|
+
const pivotMeta = prop.pivotEntity
|
|
573
|
+
? this.metadata.find(prop.pivotEntity)
|
|
574
|
+
: this.metadata.getByClassName(prop.pivotTable, false);
|
|
560
575
|
// ensure inverse side exists so we can join it when populating via pivot tables
|
|
561
576
|
if (!prop.inversedBy && prop.targetMeta) {
|
|
562
577
|
const inverseName = `${meta.className}_${prop.name}__inverse`;
|
|
@@ -565,6 +580,8 @@ export class MetadataDiscovery {
|
|
|
565
580
|
name: inverseName,
|
|
566
581
|
kind: ReferenceKind.MANY_TO_MANY,
|
|
567
582
|
type: meta.className,
|
|
583
|
+
target: meta.class,
|
|
584
|
+
targetMeta: meta,
|
|
568
585
|
mappedBy: prop.name,
|
|
569
586
|
pivotEntity: prop.pivotEntity,
|
|
570
587
|
pivotTable: prop.pivotTable,
|
|
@@ -573,55 +590,51 @@ export class MetadataDiscovery {
|
|
|
573
590
|
};
|
|
574
591
|
this.applyNamingStrategy(prop.targetMeta, inverseProp);
|
|
575
592
|
this.initCustomType(prop.targetMeta, inverseProp);
|
|
576
|
-
this.initRelation(inverseProp);
|
|
577
593
|
prop.targetMeta.properties[inverseName] = inverseProp;
|
|
578
594
|
}
|
|
579
595
|
if (pivotMeta) {
|
|
596
|
+
prop.pivotEntity = pivotMeta.class;
|
|
580
597
|
this.ensureCorrectFKOrderInPivotEntity(pivotMeta, prop);
|
|
581
598
|
return pivotMeta;
|
|
582
599
|
}
|
|
583
|
-
const exists = this.metadata.find(prop.pivotTable);
|
|
584
|
-
if (exists) {
|
|
585
|
-
prop.pivotEntity = exists.className;
|
|
586
|
-
return exists;
|
|
587
|
-
}
|
|
588
600
|
let tableName = prop.pivotTable;
|
|
589
601
|
let schemaName;
|
|
590
602
|
if (prop.pivotTable.includes('.')) {
|
|
591
603
|
[schemaName, tableName] = prop.pivotTable.split('.');
|
|
592
604
|
}
|
|
593
605
|
schemaName ??= meta.schema;
|
|
594
|
-
const
|
|
595
|
-
const
|
|
606
|
+
const targetMeta = prop.targetMeta;
|
|
607
|
+
const targetType = targetMeta.className;
|
|
608
|
+
const pivotMeta2 = new EntityMetadata({
|
|
596
609
|
name: prop.pivotTable,
|
|
597
610
|
className: prop.pivotTable,
|
|
598
611
|
collection: tableName,
|
|
599
612
|
schema: schemaName,
|
|
600
613
|
pivotTable: true,
|
|
601
614
|
});
|
|
602
|
-
prop.pivotEntity =
|
|
615
|
+
prop.pivotEntity = pivotMeta2.class;
|
|
603
616
|
if (prop.fixedOrder) {
|
|
604
|
-
const primaryProp = this.defineFixedOrderProperty(prop,
|
|
605
|
-
|
|
617
|
+
const primaryProp = this.defineFixedOrderProperty(prop, targetMeta);
|
|
618
|
+
pivotMeta2.properties[primaryProp.name] = primaryProp;
|
|
606
619
|
}
|
|
607
620
|
else {
|
|
608
|
-
|
|
621
|
+
pivotMeta2.compositePK = true;
|
|
609
622
|
}
|
|
610
623
|
// handle self-referenced m:n with same default field names
|
|
611
624
|
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.
|
|
625
|
+
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.tableName + '_1', name, meta.compositePK));
|
|
626
|
+
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.tableName + '_2', name, meta.compositePK));
|
|
614
627
|
if (prop.inversedBy) {
|
|
615
|
-
const prop2 =
|
|
628
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
616
629
|
prop2.inverseJoinColumns = prop.joinColumns;
|
|
617
630
|
prop2.joinColumns = prop.inverseJoinColumns;
|
|
618
631
|
}
|
|
619
632
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return this.metadata.set(
|
|
633
|
+
pivotMeta2.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.class, targetType + '_inverse', true, meta.className === targetType);
|
|
634
|
+
pivotMeta2.properties[targetType + '_inverse'] = this.definePivotProperty(prop, targetType + '_inverse', targetMeta.class, meta.name + '_owner', false, meta.className === targetType);
|
|
635
|
+
return this.metadata.set(pivotMeta2.class, EntitySchema.fromMetadata(pivotMeta2).init().meta);
|
|
623
636
|
}
|
|
624
|
-
defineFixedOrderProperty(prop,
|
|
637
|
+
defineFixedOrderProperty(prop, targetMeta) {
|
|
625
638
|
const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
|
|
626
639
|
const primaryProp = {
|
|
627
640
|
name: pk,
|
|
@@ -635,7 +648,7 @@ export class MetadataDiscovery {
|
|
|
635
648
|
this.initColumnType(primaryProp);
|
|
636
649
|
prop.fixedOrderColumn = pk;
|
|
637
650
|
if (prop.inversedBy) {
|
|
638
|
-
const prop2 =
|
|
651
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
639
652
|
prop2.fixedOrder = true;
|
|
640
653
|
prop2.fixedOrderColumn = pk;
|
|
641
654
|
}
|
|
@@ -644,7 +657,8 @@ export class MetadataDiscovery {
|
|
|
644
657
|
definePivotProperty(prop, name, type, inverse, owner, selfReferencing) {
|
|
645
658
|
const ret = {
|
|
646
659
|
name,
|
|
647
|
-
type,
|
|
660
|
+
type: Utils.className(type),
|
|
661
|
+
target: type,
|
|
648
662
|
kind: ReferenceKind.MANY_TO_ONE,
|
|
649
663
|
cascade: [Cascade.ALL],
|
|
650
664
|
fixedOrder: prop.fixedOrder,
|
|
@@ -654,11 +668,11 @@ export class MetadataDiscovery {
|
|
|
654
668
|
autoincrement: false,
|
|
655
669
|
updateRule: prop.updateRule,
|
|
656
670
|
deleteRule: prop.deleteRule,
|
|
671
|
+
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
657
672
|
};
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
673
|
+
const defaultRule = selfReferencing && !this.platform.supportsMultipleCascadePaths() ? 'no action' : 'cascade';
|
|
674
|
+
ret.updateRule ??= defaultRule;
|
|
675
|
+
ret.deleteRule ??= defaultRule;
|
|
662
676
|
const meta = this.metadata.get(type);
|
|
663
677
|
ret.targetMeta = meta;
|
|
664
678
|
ret.joinColumns = [];
|
|
@@ -701,7 +715,7 @@ export class MetadataDiscovery {
|
|
|
701
715
|
Object.values(meta.properties)
|
|
702
716
|
.filter(prop => prop.kind !== ReferenceKind.SCALAR && !prop.owner && prop.mappedBy)
|
|
703
717
|
.forEach(prop => {
|
|
704
|
-
const meta2 =
|
|
718
|
+
const meta2 = prop.targetMeta;
|
|
705
719
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
706
720
|
if (prop2 && !prop2.inversedBy) {
|
|
707
721
|
prop2.inversedBy = prop.name;
|
|
@@ -709,7 +723,7 @@ export class MetadataDiscovery {
|
|
|
709
723
|
});
|
|
710
724
|
}
|
|
711
725
|
defineBaseEntityProperties(meta) {
|
|
712
|
-
const base = meta.extends && this.metadata.get(
|
|
726
|
+
const base = meta.extends && this.metadata.get(meta.extends);
|
|
713
727
|
if (!base || base === meta) { // make sure we do not fall into infinite loop
|
|
714
728
|
return 0;
|
|
715
729
|
}
|
|
@@ -740,12 +754,9 @@ export class MetadataDiscovery {
|
|
|
740
754
|
Utils.keys(base.hooks).forEach(type => {
|
|
741
755
|
meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
|
|
742
756
|
});
|
|
743
|
-
if (meta.constructorParams
|
|
757
|
+
if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
|
|
744
758
|
meta.constructorParams = [...base.constructorParams];
|
|
745
759
|
}
|
|
746
|
-
if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
|
|
747
|
-
meta.toJsonParams = [...base.toJsonParams];
|
|
748
|
-
}
|
|
749
760
|
return order;
|
|
750
761
|
}
|
|
751
762
|
initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
|
|
@@ -821,22 +832,30 @@ export class MetadataDiscovery {
|
|
|
821
832
|
}
|
|
822
833
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
823
834
|
};
|
|
835
|
+
const isParentArray = (prop) => {
|
|
836
|
+
if (prop.array) {
|
|
837
|
+
return true;
|
|
838
|
+
}
|
|
839
|
+
return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
|
|
840
|
+
};
|
|
824
841
|
const rootProperty = getRootProperty(embeddedProp);
|
|
825
842
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
826
843
|
const object = isParentObject(embeddedProp);
|
|
844
|
+
const array = isParentArray(embeddedProp);
|
|
827
845
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
828
846
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
829
847
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
830
848
|
const glue = object ? '~' : '_';
|
|
831
849
|
for (const prop of Object.values(embeddable.properties)) {
|
|
832
850
|
const name = (embeddedProp.embeddedPath?.join(glue) ?? embeddedProp.fieldNames[0] + glue) + prop.name;
|
|
833
|
-
meta.properties[name] = Utils.copy(prop
|
|
851
|
+
meta.properties[name] = Utils.copy(prop);
|
|
834
852
|
meta.properties[name].name = name;
|
|
835
853
|
meta.properties[name].embedded = [embeddedProp.name, prop.name];
|
|
836
854
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
837
855
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
838
856
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
839
|
-
|
|
857
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
858
|
+
if (embeddedProp.nullable || refInArray) {
|
|
840
859
|
meta.properties[name].nullable = true;
|
|
841
860
|
}
|
|
842
861
|
if (meta.properties[name].fieldNames) {
|
|
@@ -866,14 +885,17 @@ export class MetadataDiscovery {
|
|
|
866
885
|
path = [embeddedProp.fieldNames[0]];
|
|
867
886
|
}
|
|
868
887
|
this.initFieldName(prop, true);
|
|
888
|
+
this.initRelation(prop);
|
|
869
889
|
path.push(prop.fieldNames[0]);
|
|
870
890
|
meta.properties[name].fieldNames = prop.fieldNames;
|
|
871
891
|
meta.properties[name].embeddedPath = path;
|
|
872
|
-
const
|
|
892
|
+
const targetProp = prop.targetMeta?.getPrimaryProp() ?? prop;
|
|
893
|
+
const fieldName = raw(this.platform.getSearchJsonPropertySQL(path.join('->'), targetProp.runtimeType ?? targetProp.type, true));
|
|
873
894
|
meta.properties[name].fieldNameRaw = fieldName.sql; // for querying in SQL drivers
|
|
874
895
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
875
896
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
876
897
|
meta.properties[name].object = true;
|
|
898
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
877
899
|
}
|
|
878
900
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
879
901
|
}
|
|
@@ -896,7 +918,7 @@ export class MetadataDiscovery {
|
|
|
896
918
|
}
|
|
897
919
|
initSingleTableInheritance(meta, metadata) {
|
|
898
920
|
if (meta.root !== meta && !meta.__processed) {
|
|
899
|
-
meta.root = metadata.find(m => m.
|
|
921
|
+
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
900
922
|
meta.root.__processed = true;
|
|
901
923
|
}
|
|
902
924
|
else {
|
|
@@ -905,17 +927,23 @@ export class MetadataDiscovery {
|
|
|
905
927
|
if (!meta.root.discriminatorColumn) {
|
|
906
928
|
return;
|
|
907
929
|
}
|
|
908
|
-
if (
|
|
930
|
+
if (meta.root.discriminatorMap) {
|
|
931
|
+
const map = meta.root.discriminatorMap;
|
|
932
|
+
Object.keys(map)
|
|
933
|
+
.filter(key => typeof map[key] === 'string')
|
|
934
|
+
.forEach(key => map[key] = this.metadata.getByClassName(map[key]).class);
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
909
937
|
meta.root.discriminatorMap = {};
|
|
910
938
|
const children = metadata
|
|
911
|
-
.filter(m => m.root.
|
|
939
|
+
.filter(m => m.root.class === meta.root.class && !m.abstract)
|
|
912
940
|
.sort((a, b) => a.className.localeCompare(b.className));
|
|
913
941
|
for (const m of children) {
|
|
914
942
|
const name = m.discriminatorValue ?? this.namingStrategy.classToTableName(m.className);
|
|
915
|
-
meta.root.discriminatorMap[name] = m.
|
|
943
|
+
meta.root.discriminatorMap[name] = m.class;
|
|
916
944
|
}
|
|
917
945
|
}
|
|
918
|
-
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([,
|
|
946
|
+
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([, cls]) => cls === meta.class)?.[0];
|
|
919
947
|
if (!meta.root.properties[meta.root.discriminatorColumn]) {
|
|
920
948
|
this.createDiscriminatorProperty(meta.root);
|
|
921
949
|
}
|
|
@@ -927,25 +955,44 @@ export class MetadataDiscovery {
|
|
|
927
955
|
let i = 1;
|
|
928
956
|
Object.values(meta.properties).forEach(prop => {
|
|
929
957
|
const newProp = { ...prop };
|
|
930
|
-
|
|
958
|
+
const rootProp = meta.root.properties[prop.name];
|
|
959
|
+
if (rootProp && (rootProp.type !== prop.type || (rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
931
960
|
const name = newProp.name;
|
|
932
961
|
this.initFieldName(newProp, newProp.object);
|
|
962
|
+
newProp.renamedFrom = name;
|
|
933
963
|
newProp.name = name + '_' + (i++);
|
|
934
964
|
meta.root.addProperty(newProp);
|
|
965
|
+
// Track all field variants and map discriminator values to field names
|
|
966
|
+
if (!rootProp.stiFieldNames) {
|
|
967
|
+
this.initFieldName(prop, prop.object);
|
|
968
|
+
this.initFieldName(rootProp, rootProp.object);
|
|
969
|
+
rootProp.stiFieldNames = [...rootProp.fieldNames];
|
|
970
|
+
rootProp.stiFieldNameMap = {};
|
|
971
|
+
// Find which discriminator owns the original fieldNames
|
|
972
|
+
for (const [discValue, childClass] of Object.entries(meta.root.discriminatorMap)) {
|
|
973
|
+
const childMeta = this.metadata.find(childClass);
|
|
974
|
+
if (childMeta?.properties[prop.name]?.fieldNames && compareArrays(childMeta.properties[prop.name].fieldNames, rootProp.fieldNames)) {
|
|
975
|
+
rootProp.stiFieldNameMap[discValue] = rootProp.fieldNames[0];
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
rootProp.stiFieldNameMap[meta.discriminatorValue] = prop.fieldNames[0];
|
|
981
|
+
rootProp.stiFieldNames.push(...prop.fieldNames);
|
|
935
982
|
newProp.nullable = true;
|
|
936
983
|
newProp.name = name;
|
|
937
984
|
newProp.hydrate = false;
|
|
938
985
|
newProp.inherited = true;
|
|
939
986
|
return;
|
|
940
987
|
}
|
|
941
|
-
if (prop.enum && prop.items &&
|
|
942
|
-
newProp.items = Utils.unique([...
|
|
988
|
+
if (prop.enum && prop.items && rootProp?.items) {
|
|
989
|
+
newProp.items = Utils.unique([...rootProp.items, ...prop.items]);
|
|
943
990
|
}
|
|
944
991
|
newProp.nullable = true;
|
|
945
|
-
newProp.inherited =
|
|
992
|
+
newProp.inherited = !rootProp;
|
|
946
993
|
meta.root.addProperty(newProp);
|
|
947
994
|
});
|
|
948
|
-
meta.
|
|
995
|
+
meta.tableName = meta.root.tableName;
|
|
949
996
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
950
997
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
951
998
|
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
@@ -967,7 +1014,7 @@ export class MetadataDiscovery {
|
|
|
967
1014
|
}
|
|
968
1015
|
}
|
|
969
1016
|
initCheckConstraints(meta) {
|
|
970
|
-
const map =
|
|
1017
|
+
const map = meta.createColumnMappingObject();
|
|
971
1018
|
for (const check of meta.checks) {
|
|
972
1019
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
973
1020
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -977,7 +1024,7 @@ export class MetadataDiscovery {
|
|
|
977
1024
|
}
|
|
978
1025
|
if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
|
|
979
1026
|
for (const prop of meta.props) {
|
|
980
|
-
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item =>
|
|
1027
|
+
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
|
|
981
1028
|
this.initFieldName(prop);
|
|
982
1029
|
meta.checks.push({
|
|
983
1030
|
name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
|
|
@@ -1002,38 +1049,28 @@ export class MetadataDiscovery {
|
|
|
1002
1049
|
}
|
|
1003
1050
|
return;
|
|
1004
1051
|
}
|
|
1005
|
-
const map =
|
|
1052
|
+
const map = meta.createColumnMappingObject();
|
|
1006
1053
|
if (prop.generated instanceof Function) {
|
|
1007
1054
|
prop.generated = prop.generated(map);
|
|
1008
1055
|
}
|
|
1009
1056
|
}
|
|
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) {
|
|
1057
|
+
getDefaultVersionValue(meta, prop) {
|
|
1019
1058
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1020
1059
|
return prop.defaultRaw;
|
|
1021
1060
|
}
|
|
1022
|
-
/* v8 ignore next
|
|
1061
|
+
/* v8 ignore next */
|
|
1023
1062
|
if (prop.default != null) {
|
|
1024
1063
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1025
1064
|
}
|
|
1026
|
-
|
|
1065
|
+
this.initCustomType(meta, prop, true);
|
|
1066
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1067
|
+
if (type === 'Date') {
|
|
1027
1068
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1028
1069
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1029
1070
|
}
|
|
1030
1071
|
return '1';
|
|
1031
1072
|
}
|
|
1032
1073
|
inferDefaultValue(meta, prop) {
|
|
1033
|
-
/* v8 ignore next 3 */
|
|
1034
|
-
if (!meta.class) {
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
1074
|
try {
|
|
1038
1075
|
// try to create two entity instances to detect the value is stable
|
|
1039
1076
|
const now = Date.now();
|
|
@@ -1061,12 +1098,12 @@ export class MetadataDiscovery {
|
|
|
1061
1098
|
return;
|
|
1062
1099
|
}
|
|
1063
1100
|
let val = prop.default;
|
|
1064
|
-
const raw =
|
|
1101
|
+
const raw = Raw.getKnownFragment(val);
|
|
1065
1102
|
if (raw) {
|
|
1066
1103
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1067
1104
|
return;
|
|
1068
1105
|
}
|
|
1069
|
-
if (
|
|
1106
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1070
1107
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1071
1108
|
}
|
|
1072
1109
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1094,13 +1131,13 @@ export class MetadataDiscovery {
|
|
|
1094
1131
|
if (prop.version) {
|
|
1095
1132
|
this.initDefaultValue(prop);
|
|
1096
1133
|
meta.versionProperty = prop.name;
|
|
1097
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1134
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1098
1135
|
}
|
|
1099
1136
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1100
1137
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1101
1138
|
}
|
|
1102
1139
|
}
|
|
1103
|
-
initCustomType(meta, prop) {
|
|
1140
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1104
1141
|
// `prop.type` might be actually instance of custom type class
|
|
1105
1142
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1106
1143
|
prop.customType = prop.type;
|
|
@@ -1108,41 +1145,61 @@ export class MetadataDiscovery {
|
|
|
1108
1145
|
}
|
|
1109
1146
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1110
1147
|
if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
|
|
1111
|
-
|
|
1112
|
-
|
|
1148
|
+
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1149
|
+
// we use just the type name, to have more performant hydration code
|
|
1150
|
+
const type = Utils.keys(t).find(type => {
|
|
1151
|
+
return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
|
|
1152
|
+
});
|
|
1153
|
+
if (type) {
|
|
1154
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
prop.customType = new prop.type();
|
|
1158
|
+
prop.type = prop.customType.constructor.name;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (simple) {
|
|
1162
|
+
return;
|
|
1113
1163
|
}
|
|
1114
1164
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1115
|
-
prop.customType = new
|
|
1165
|
+
prop.customType = new t.json();
|
|
1116
1166
|
}
|
|
1117
1167
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1118
|
-
prop.customType = new
|
|
1168
|
+
prop.customType = new t.json();
|
|
1169
|
+
}
|
|
1170
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1171
|
+
prop.customType = new t.json();
|
|
1119
1172
|
}
|
|
1120
1173
|
if (!prop.customType && prop.array && prop.items) {
|
|
1121
|
-
prop.customType = new
|
|
1174
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1175
|
+
}
|
|
1176
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1177
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1178
|
+
prop.customType = new t.json();
|
|
1122
1179
|
}
|
|
1123
1180
|
// for number arrays we make sure to convert the items to numbers
|
|
1124
1181
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1125
|
-
prop.customType = new
|
|
1182
|
+
prop.customType = new t.array(i => +i);
|
|
1126
1183
|
}
|
|
1127
1184
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1128
|
-
if (!prop.customType &&
|
|
1129
|
-
prop.customType = new
|
|
1185
|
+
if (!prop.customType && isArray) {
|
|
1186
|
+
prop.customType = new t.array();
|
|
1130
1187
|
}
|
|
1131
1188
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1132
|
-
prop.customType = new
|
|
1189
|
+
prop.customType = new t.blob();
|
|
1133
1190
|
}
|
|
1134
1191
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1135
|
-
prop.customType = new
|
|
1192
|
+
prop.customType = new t.uint8array();
|
|
1136
1193
|
}
|
|
1137
1194
|
const mappedType = this.getMappedType(prop);
|
|
1138
1195
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1139
|
-
[
|
|
1196
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1140
1197
|
.filter(type => mappedType instanceof type)
|
|
1141
|
-
.forEach(type => prop.customType = new type());
|
|
1198
|
+
.forEach((type) => prop.customType = new type());
|
|
1142
1199
|
}
|
|
1143
1200
|
if (prop.customType && !prop.columnTypes) {
|
|
1144
1201
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1145
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1202
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1146
1203
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1147
1204
|
}
|
|
1148
1205
|
else {
|
|
@@ -1162,16 +1219,16 @@ export class MetadataDiscovery {
|
|
|
1162
1219
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1163
1220
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1164
1221
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1165
|
-
if (prop.customType instanceof
|
|
1222
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1166
1223
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1167
1224
|
}
|
|
1168
1225
|
}
|
|
1169
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1226
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1170
1227
|
prop.type = prop.customType.name;
|
|
1171
1228
|
}
|
|
1172
|
-
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
1229
|
+
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && prop.targetMeta.compositePK) {
|
|
1173
1230
|
prop.customTypes = [];
|
|
1174
|
-
for (const pk of
|
|
1231
|
+
for (const pk of prop.targetMeta.getPrimaryProps()) {
|
|
1175
1232
|
if (pk.customType) {
|
|
1176
1233
|
prop.customTypes.push(pk.customType);
|
|
1177
1234
|
prop.hasConvertToJSValueSQL ||= !!pk.customType.convertToJSValueSQL && pk.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
@@ -1183,7 +1240,7 @@ export class MetadataDiscovery {
|
|
|
1183
1240
|
}
|
|
1184
1241
|
}
|
|
1185
1242
|
}
|
|
1186
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1243
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1187
1244
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1188
1245
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1189
1246
|
}
|
|
@@ -1201,19 +1258,25 @@ export class MetadataDiscovery {
|
|
|
1201
1258
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1202
1259
|
return;
|
|
1203
1260
|
}
|
|
1204
|
-
|
|
1205
|
-
prop.
|
|
1261
|
+
// 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
|
|
1262
|
+
const meta2 = this.metadata.find(prop.target) ?? this.metadata.getByClassName(prop.type);
|
|
1263
|
+
// If targetKey is specified, use that property instead of PKs for referencedPKs
|
|
1264
|
+
prop.referencedPKs = prop.targetKey ? [prop.targetKey] : meta2.primaryKeys;
|
|
1206
1265
|
prop.targetMeta = meta2;
|
|
1207
1266
|
if (!prop.formula && prop.persist === false && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.embedded) {
|
|
1208
|
-
prop.formula =
|
|
1267
|
+
prop.formula = table => `${table}.${this.platform.quoteIdentifier(prop.fieldNames[0])}`;
|
|
1209
1268
|
}
|
|
1210
1269
|
}
|
|
1211
1270
|
initColumnType(prop) {
|
|
1212
1271
|
this.initUnsigned(prop);
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
prop.
|
|
1216
|
-
|
|
1272
|
+
// Get the target properties for FK relations - use targetKey property if specified, otherwise PKs
|
|
1273
|
+
const targetProps = prop.targetMeta
|
|
1274
|
+
? (prop.targetKey ? [prop.targetMeta.properties[prop.targetKey]] : prop.targetMeta.getPrimaryProps())
|
|
1275
|
+
: [];
|
|
1276
|
+
targetProps.map(targetProp => {
|
|
1277
|
+
prop.length ??= targetProp.length;
|
|
1278
|
+
prop.precision ??= targetProp.precision;
|
|
1279
|
+
prop.scale ??= targetProp.scale;
|
|
1217
1280
|
});
|
|
1218
1281
|
if (prop.kind === ReferenceKind.SCALAR && (prop.type == null || prop.type === 'object') && prop.columnTypes?.[0]) {
|
|
1219
1282
|
delete prop.type;
|
|
@@ -1226,8 +1289,7 @@ export class MetadataDiscovery {
|
|
|
1226
1289
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1227
1290
|
const mappedType = this.getMappedType(prop);
|
|
1228
1291
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1229
|
-
if (mappedType instanceof
|
|
1230
|
-
&& !prop.columnTypes
|
|
1292
|
+
if (mappedType instanceof t.unknown
|
|
1231
1293
|
// it could be a runtime type from reflect-metadata
|
|
1232
1294
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
1233
1295
|
// or it might be inferred via ts-morph to some generic type alias
|
|
@@ -1240,23 +1302,28 @@ export class MetadataDiscovery {
|
|
|
1240
1302
|
}
|
|
1241
1303
|
return;
|
|
1242
1304
|
}
|
|
1243
|
-
|
|
1305
|
+
/* v8 ignore next */
|
|
1306
|
+
if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
|
|
1244
1307
|
prop.columnTypes = [this.platform.getJsonDeclarationSQL()];
|
|
1245
1308
|
return;
|
|
1246
1309
|
}
|
|
1247
|
-
const targetMeta =
|
|
1310
|
+
const targetMeta = prop.targetMeta;
|
|
1248
1311
|
prop.columnTypes = [];
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1312
|
+
// Use targetKey property if specified, otherwise use primary key properties
|
|
1313
|
+
const referencedProps = prop.targetKey
|
|
1314
|
+
? [targetMeta.properties[prop.targetKey]]
|
|
1315
|
+
: targetMeta.getPrimaryProps();
|
|
1316
|
+
for (const referencedProp of referencedProps) {
|
|
1317
|
+
this.initCustomType(targetMeta, referencedProp);
|
|
1318
|
+
this.initColumnType(referencedProp);
|
|
1319
|
+
const mappedType = this.getMappedType(referencedProp);
|
|
1320
|
+
let columnTypes = referencedProp.columnTypes;
|
|
1321
|
+
if (referencedProp.autoincrement) {
|
|
1322
|
+
columnTypes = [mappedType.getColumnType({ ...referencedProp, autoincrement: false }, this.platform)];
|
|
1256
1323
|
}
|
|
1257
1324
|
prop.columnTypes.push(...columnTypes);
|
|
1258
|
-
if (!targetMeta.compositePK) {
|
|
1259
|
-
prop.customType =
|
|
1325
|
+
if (!targetMeta.compositePK || prop.targetKey) {
|
|
1326
|
+
prop.customType = referencedProp.customType;
|
|
1260
1327
|
}
|
|
1261
1328
|
}
|
|
1262
1329
|
}
|
|
@@ -1270,7 +1337,7 @@ export class MetadataDiscovery {
|
|
|
1270
1337
|
t = 'enum';
|
|
1271
1338
|
}
|
|
1272
1339
|
else if (prop.enum) {
|
|
1273
|
-
t = prop.items?.every(item =>
|
|
1340
|
+
t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
|
|
1274
1341
|
}
|
|
1275
1342
|
if (t === 'Date') {
|
|
1276
1343
|
t = 'datetime';
|
|
@@ -1294,7 +1361,7 @@ export class MetadataDiscovery {
|
|
|
1294
1361
|
return;
|
|
1295
1362
|
}
|
|
1296
1363
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
1297
|
-
const meta2 =
|
|
1364
|
+
const meta2 = prop.targetMeta;
|
|
1298
1365
|
prop.unsigned = meta2.getPrimaryProps().some(pk => {
|
|
1299
1366
|
this.initUnsigned(pk);
|
|
1300
1367
|
return pk.unsigned;
|
|
@@ -1309,30 +1376,6 @@ export class MetadataDiscovery {
|
|
|
1309
1376
|
prop.index ??= true;
|
|
1310
1377
|
}
|
|
1311
1378
|
}
|
|
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
1379
|
shouldForceConstructorUsage(meta) {
|
|
1337
1380
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
1338
1381
|
if (Array.isArray(forceConstructor)) {
|