@stingerloom/orm 0.1.0
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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/DatabaseClient.d.ts +23 -0
- package/dist/DatabaseClient.d.ts.map +1 -0
- package/dist/DatabaseClient.js +125 -0
- package/dist/DatabaseClient.js.map +1 -0
- package/dist/core/BaseEntityManager.d.ts +29 -0
- package/dist/core/BaseEntityManager.d.ts.map +1 -0
- package/dist/core/BaseEntityManager.js +7 -0
- package/dist/core/BaseEntityManager.js.map +1 -0
- package/dist/core/BaseInsertQueryBuilder.d.ts +8 -0
- package/dist/core/BaseInsertQueryBuilder.d.ts.map +1 -0
- package/dist/core/BaseInsertQueryBuilder.js +3 -0
- package/dist/core/BaseInsertQueryBuilder.js.map +1 -0
- package/dist/core/BaseRawQueryBuilder.d.ts +33 -0
- package/dist/core/BaseRawQueryBuilder.d.ts.map +1 -0
- package/dist/core/BaseRawQueryBuilder.js +3 -0
- package/dist/core/BaseRawQueryBuilder.js.map +1 -0
- package/dist/core/BaseRepository.d.ts +37 -0
- package/dist/core/BaseRepository.d.ts.map +1 -0
- package/dist/core/BaseRepository.js +77 -0
- package/dist/core/BaseRepository.js.map +1 -0
- package/dist/core/BaseResultTransformer.d.ts +11 -0
- package/dist/core/BaseResultTransformer.d.ts.map +1 -0
- package/dist/core/BaseResultTransformer.js +3 -0
- package/dist/core/BaseResultTransformer.js.map +1 -0
- package/dist/core/Conditions.d.ts +33 -0
- package/dist/core/Conditions.d.ts.map +1 -0
- package/dist/core/Conditions.js +154 -0
- package/dist/core/Conditions.js.map +1 -0
- package/dist/core/CursorPagination.d.ts +19 -0
- package/dist/core/CursorPagination.d.ts.map +1 -0
- package/dist/core/CursorPagination.js +27 -0
- package/dist/core/CursorPagination.js.map +1 -0
- package/dist/core/DatabaseClientOptions.d.ts +41 -0
- package/dist/core/DatabaseClientOptions.d.ts.map +1 -0
- package/dist/core/DatabaseClientOptions.js +3 -0
- package/dist/core/DatabaseClientOptions.js.map +1 -0
- package/dist/core/EntityEventEmitter.d.ts +14 -0
- package/dist/core/EntityEventEmitter.d.ts.map +1 -0
- package/dist/core/EntityEventEmitter.js +35 -0
- package/dist/core/EntityEventEmitter.js.map +1 -0
- package/dist/core/EntityManager.d.ts +115 -0
- package/dist/core/EntityManager.d.ts.map +1 -0
- package/dist/core/EntityManager.js +2347 -0
- package/dist/core/EntityManager.js.map +1 -0
- package/dist/core/EntitySubscriber.d.ts +31 -0
- package/dist/core/EntitySubscriber.d.ts.map +1 -0
- package/dist/core/EntitySubscriber.js +3 -0
- package/dist/core/EntitySubscriber.js.map +1 -0
- package/dist/core/EntityValidator.d.ts +6 -0
- package/dist/core/EntityValidator.d.ts.map +1 -0
- package/dist/core/EntityValidator.js +43 -0
- package/dist/core/EntityValidator.js.map +1 -0
- package/dist/core/ExplainResult.d.ts +9 -0
- package/dist/core/ExplainResult.d.ts.map +1 -0
- package/dist/core/ExplainResult.js +3 -0
- package/dist/core/ExplainResult.js.map +1 -0
- package/dist/core/IConnector.d.ts +18 -0
- package/dist/core/IConnector.d.ts.map +1 -0
- package/dist/core/IConnector.js +7 -0
- package/dist/core/IConnector.js.map +1 -0
- package/dist/core/LazyLoader.d.ts +6 -0
- package/dist/core/LazyLoader.d.ts.map +1 -0
- package/dist/core/LazyLoader.js +94 -0
- package/dist/core/LazyLoader.js.map +1 -0
- package/dist/core/MyClassConstructor.d.ts +4 -0
- package/dist/core/MyClassConstructor.d.ts.map +1 -0
- package/dist/core/MyClassConstructor.js +3 -0
- package/dist/core/MyClassConstructor.js.map +1 -0
- package/dist/core/QueryTracker.d.ts +37 -0
- package/dist/core/QueryTracker.d.ts.map +1 -0
- package/dist/core/QueryTracker.js +111 -0
- package/dist/core/QueryTracker.js.map +1 -0
- package/dist/core/RawQueryBuilder.d.ts +40 -0
- package/dist/core/RawQueryBuilder.d.ts.map +1 -0
- package/dist/core/RawQueryBuilder.js +211 -0
- package/dist/core/RawQueryBuilder.js.map +1 -0
- package/dist/core/RawQueryBuilderFactory.d.ts +6 -0
- package/dist/core/RawQueryBuilderFactory.d.ts.map +1 -0
- package/dist/core/RawQueryBuilderFactory.js +14 -0
- package/dist/core/RawQueryBuilderFactory.js.map +1 -0
- package/dist/core/ResultTransformer.d.ts +24 -0
- package/dist/core/ResultTransformer.d.ts.map +1 -0
- package/dist/core/ResultTransformer.js +134 -0
- package/dist/core/ResultTransformer.js.map +1 -0
- package/dist/core/ResultTransformerFactory.d.ts +5 -0
- package/dist/core/ResultTransformerFactory.d.ts.map +1 -0
- package/dist/core/ResultTransformerFactory.js +11 -0
- package/dist/core/ResultTransformerFactory.js.map +1 -0
- package/dist/core/SelectUtils.d.ts +14 -0
- package/dist/core/SelectUtils.d.ts.map +1 -0
- package/dist/core/SelectUtils.js +19 -0
- package/dist/core/SelectUtils.js.map +1 -0
- package/dist/core/deserializer/ClassTransformerDeserializer.d.ts +7 -0
- package/dist/core/deserializer/ClassTransformerDeserializer.d.ts.map +1 -0
- package/dist/core/deserializer/ClassTransformerDeserializer.js +14 -0
- package/dist/core/deserializer/ClassTransformerDeserializer.js.map +1 -0
- package/dist/core/deserializer/DeserializeEntity.d.ts +7 -0
- package/dist/core/deserializer/DeserializeEntity.d.ts.map +1 -0
- package/dist/core/deserializer/DeserializeEntity.js +17 -0
- package/dist/core/deserializer/DeserializeEntity.js.map +1 -0
- package/dist/core/deserializer/DeserializeOptions.d.ts +9 -0
- package/dist/core/deserializer/DeserializeOptions.d.ts.map +1 -0
- package/dist/core/deserializer/DeserializeOptions.js +3 -0
- package/dist/core/deserializer/DeserializeOptions.js.map +1 -0
- package/dist/core/deserializer/Deserializer.d.ts +6 -0
- package/dist/core/deserializer/Deserializer.d.ts.map +1 -0
- package/dist/core/deserializer/Deserializer.js +3 -0
- package/dist/core/deserializer/Deserializer.js.map +1 -0
- package/dist/core/deserializer/DeserializerRegistry.d.ts +13 -0
- package/dist/core/deserializer/DeserializerRegistry.d.ts.map +1 -0
- package/dist/core/deserializer/DeserializerRegistry.js +26 -0
- package/dist/core/deserializer/DeserializerRegistry.js.map +1 -0
- package/dist/core/deserializer/index.d.ts +6 -0
- package/dist/core/deserializer/index.d.ts.map +1 -0
- package/dist/core/deserializer/index.js +22 -0
- package/dist/core/deserializer/index.js.map +1 -0
- package/dist/core/generators/SchemaDiff.d.ts +34 -0
- package/dist/core/generators/SchemaDiff.d.ts.map +1 -0
- package/dist/core/generators/SchemaDiff.js +231 -0
- package/dist/core/generators/SchemaDiff.js.map +1 -0
- package/dist/core/generators/SchemaDiffMigrationGenerator.d.ts +11 -0
- package/dist/core/generators/SchemaDiffMigrationGenerator.d.ts.map +1 -0
- package/dist/core/generators/SchemaDiffMigrationGenerator.js +108 -0
- package/dist/core/generators/SchemaDiffMigrationGenerator.js.map +1 -0
- package/dist/core/generators/SchemaGenerator.d.ts +37 -0
- package/dist/core/generators/SchemaGenerator.d.ts.map +1 -0
- package/dist/core/generators/SchemaGenerator.js +379 -0
- package/dist/core/generators/SchemaGenerator.js.map +1 -0
- package/dist/core/generators/index.d.ts +4 -0
- package/dist/core/generators/index.d.ts.map +1 -0
- package/dist/core/generators/index.js +20 -0
- package/dist/core/generators/index.js.map +1 -0
- package/dist/core/index.d.ts +20 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +36 -0
- package/dist/core/index.js.map +1 -0
- package/dist/decorators/Column.d.ts +19 -0
- package/dist/decorators/Column.d.ts.map +1 -0
- package/dist/decorators/Column.js +53 -0
- package/dist/decorators/Column.js.map +1 -0
- package/dist/decorators/CustomColumn.d.ts +6 -0
- package/dist/decorators/CustomColumn.d.ts.map +1 -0
- package/dist/decorators/CustomColumn.js +13 -0
- package/dist/decorators/CustomColumn.js.map +1 -0
- package/dist/decorators/DeletedAt.d.ts +3 -0
- package/dist/decorators/DeletedAt.d.ts.map +1 -0
- package/dist/decorators/DeletedAt.js +16 -0
- package/dist/decorators/DeletedAt.js.map +1 -0
- package/dist/decorators/Entity.d.ts +16 -0
- package/dist/decorators/Entity.d.ts.map +1 -0
- package/dist/decorators/Entity.js +32 -0
- package/dist/decorators/Entity.js.map +1 -0
- package/dist/decorators/Hooks.d.ts +14 -0
- package/dist/decorators/Hooks.d.ts.map +1 -0
- package/dist/decorators/Hooks.js +36 -0
- package/dist/decorators/Hooks.js.map +1 -0
- package/dist/decorators/Indexer.d.ts +11 -0
- package/dist/decorators/Indexer.d.ts.map +1 -0
- package/dist/decorators/Indexer.js +21 -0
- package/dist/decorators/Indexer.js.map +1 -0
- package/dist/decorators/InjectEntityManager.d.ts +3 -0
- package/dist/decorators/InjectEntityManager.d.ts.map +1 -0
- package/dist/decorators/InjectEntityManager.js +13 -0
- package/dist/decorators/InjectEntityManager.js.map +1 -0
- package/dist/decorators/InjectRepository.d.ts +4 -0
- package/dist/decorators/InjectRepository.d.ts.map +1 -0
- package/dist/decorators/InjectRepository.js +22 -0
- package/dist/decorators/InjectRepository.js.map +1 -0
- package/dist/decorators/ManyToMany.d.ts +21 -0
- package/dist/decorators/ManyToMany.d.ts.map +1 -0
- package/dist/decorators/ManyToMany.js +29 -0
- package/dist/decorators/ManyToMany.js.map +1 -0
- package/dist/decorators/ManyToOne.d.ts +27 -0
- package/dist/decorators/ManyToOne.d.ts.map +1 -0
- package/dist/decorators/ManyToOne.js +35 -0
- package/dist/decorators/ManyToOne.js.map +1 -0
- package/dist/decorators/OneToMany.d.ts +17 -0
- package/dist/decorators/OneToMany.d.ts.map +1 -0
- package/dist/decorators/OneToMany.js +29 -0
- package/dist/decorators/OneToMany.js.map +1 -0
- package/dist/decorators/OneToOne.d.ts +20 -0
- package/dist/decorators/OneToOne.d.ts.map +1 -0
- package/dist/decorators/OneToOne.js +30 -0
- package/dist/decorators/OneToOne.js.map +1 -0
- package/dist/decorators/PrimaryColumn.d.ts +3 -0
- package/dist/decorators/PrimaryColumn.d.ts.map +1 -0
- package/dist/decorators/PrimaryColumn.js +14 -0
- package/dist/decorators/PrimaryColumn.js.map +1 -0
- package/dist/decorators/PrimaryGeneratedColumn.d.ts +3 -0
- package/dist/decorators/PrimaryGeneratedColumn.d.ts.map +1 -0
- package/dist/decorators/PrimaryGeneratedColumn.js +17 -0
- package/dist/decorators/PrimaryGeneratedColumn.js.map +1 -0
- package/dist/decorators/Transactional.d.ts +15 -0
- package/dist/decorators/Transactional.d.ts.map +1 -0
- package/dist/decorators/Transactional.js +76 -0
- package/dist/decorators/Transactional.js.map +1 -0
- package/dist/decorators/UniqueIndex.d.ts +7 -0
- package/dist/decorators/UniqueIndex.d.ts.map +1 -0
- package/dist/decorators/UniqueIndex.js +12 -0
- package/dist/decorators/UniqueIndex.js.map +1 -0
- package/dist/decorators/Validation.d.ts +15 -0
- package/dist/decorators/Validation.d.ts.map +1 -0
- package/dist/decorators/Validation.js +64 -0
- package/dist/decorators/Validation.js.map +1 -0
- package/dist/decorators/Version.d.ts +3 -0
- package/dist/decorators/Version.d.ts.map +1 -0
- package/dist/decorators/Version.js +17 -0
- package/dist/decorators/Version.js.map +1 -0
- package/dist/decorators/index.d.ts +19 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +35 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/dialects/ConnectionLeakDetector.d.ts +19 -0
- package/dist/dialects/ConnectionLeakDetector.d.ts.map +1 -0
- package/dist/dialects/ConnectionLeakDetector.js +50 -0
- package/dist/dialects/ConnectionLeakDetector.js.map +1 -0
- package/dist/dialects/EntityNotFound.d.ts +4 -0
- package/dist/dialects/EntityNotFound.d.ts.map +1 -0
- package/dist/dialects/EntityNotFound.js +10 -0
- package/dist/dialects/EntityNotFound.js.map +1 -0
- package/dist/dialects/FindOption.d.ts +20 -0
- package/dist/dialects/FindOption.d.ts.map +1 -0
- package/dist/dialects/FindOption.js +3 -0
- package/dist/dialects/FindOption.js.map +1 -0
- package/dist/dialects/IConnection.d.ts +7 -0
- package/dist/dialects/IConnection.d.ts.map +1 -0
- package/dist/dialects/IConnection.js +3 -0
- package/dist/dialects/IConnection.js.map +1 -0
- package/dist/dialects/IDataSource.d.ts +12 -0
- package/dist/dialects/IDataSource.d.ts.map +1 -0
- package/dist/dialects/IDataSource.js +3 -0
- package/dist/dialects/IDataSource.js.map +1 -0
- package/dist/dialects/IOrderBy.d.ts +4 -0
- package/dist/dialects/IOrderBy.d.ts.map +1 -0
- package/dist/dialects/IOrderBy.js +3 -0
- package/dist/dialects/IOrderBy.js.map +1 -0
- package/dist/dialects/IQueryEngine.d.ts +15 -0
- package/dist/dialects/IQueryEngine.d.ts.map +1 -0
- package/dist/dialects/IQueryEngine.js +7 -0
- package/dist/dialects/IQueryEngine.js.map +1 -0
- package/dist/dialects/ISelectOption.d.ts +7 -0
- package/dist/dialects/ISelectOption.d.ts.map +1 -0
- package/dist/dialects/ISelectOption.js +3 -0
- package/dist/dialects/ISelectOption.js.map +1 -0
- package/dist/dialects/ITenantMigrationRunner.d.ts +16 -0
- package/dist/dialects/ITenantMigrationRunner.d.ts.map +1 -0
- package/dist/dialects/ITenantMigrationRunner.js +3 -0
- package/dist/dialects/ITenantMigrationRunner.js.map +1 -0
- package/dist/dialects/ITxEngine.d.ts +9 -0
- package/dist/dialects/ITxEngine.d.ts.map +1 -0
- package/dist/dialects/ITxEngine.js +3 -0
- package/dist/dialects/ITxEngine.js.map +1 -0
- package/dist/dialects/IsolationLevel.d.ts +2 -0
- package/dist/dialects/IsolationLevel.d.ts.map +1 -0
- package/dist/dialects/IsolationLevel.js +3 -0
- package/dist/dialects/IsolationLevel.js.map +1 -0
- package/dist/dialects/ReplicationRouter.d.ts +28 -0
- package/dist/dialects/ReplicationRouter.d.ts.map +1 -0
- package/dist/dialects/ReplicationRouter.js +64 -0
- package/dist/dialects/ReplicationRouter.js.map +1 -0
- package/dist/dialects/SqlDriver.d.ts +43 -0
- package/dist/dialects/SqlDriver.d.ts.map +1 -0
- package/dist/dialects/SqlDriver.js +3 -0
- package/dist/dialects/SqlDriver.js.map +1 -0
- package/dist/dialects/TransactionSessionManager.d.ts +21 -0
- package/dist/dialects/TransactionSessionManager.d.ts.map +1 -0
- package/dist/dialects/TransactionSessionManager.js +112 -0
- package/dist/dialects/TransactionSessionManager.js.map +1 -0
- package/dist/dialects/index.d.ts +32 -0
- package/dist/dialects/index.d.ts.map +1 -0
- package/dist/dialects/index.js +65 -0
- package/dist/dialects/index.js.map +1 -0
- package/dist/dialects/mysql/BaseSchema.d.ts +9 -0
- package/dist/dialects/mysql/BaseSchema.d.ts.map +1 -0
- package/dist/dialects/mysql/BaseSchema.js +3 -0
- package/dist/dialects/mysql/BaseSchema.js.map +1 -0
- package/dist/dialects/mysql/Connection.d.ts +9 -0
- package/dist/dialects/mysql/Connection.d.ts.map +1 -0
- package/dist/dialects/mysql/Connection.js +3 -0
- package/dist/dialects/mysql/Connection.js.map +1 -0
- package/dist/dialects/mysql/ConnectionNotFound.d.ts +4 -0
- package/dist/dialects/mysql/ConnectionNotFound.d.ts.map +1 -0
- package/dist/dialects/mysql/ConnectionNotFound.js +10 -0
- package/dist/dialects/mysql/ConnectionNotFound.js.map +1 -0
- package/dist/dialects/mysql/MySqlConnectionError.d.ts +4 -0
- package/dist/dialects/mysql/MySqlConnectionError.d.ts.map +1 -0
- package/dist/dialects/mysql/MySqlConnectionError.js +10 -0
- package/dist/dialects/mysql/MySqlConnectionError.js.map +1 -0
- package/dist/dialects/mysql/MySqlConnector.d.ts +26 -0
- package/dist/dialects/mysql/MySqlConnector.d.ts.map +1 -0
- package/dist/dialects/mysql/MySqlConnector.js +195 -0
- package/dist/dialects/mysql/MySqlConnector.js.map +1 -0
- package/dist/dialects/mysql/MySqlDataSource.d.ts +17 -0
- package/dist/dialects/mysql/MySqlDataSource.d.ts.map +1 -0
- package/dist/dialects/mysql/MySqlDataSource.js +63 -0
- package/dist/dialects/mysql/MySqlDataSource.js.map +1 -0
- package/dist/dialects/mysql/MySqlDriver.d.ts +49 -0
- package/dist/dialects/mysql/MySqlDriver.d.ts.map +1 -0
- package/dist/dialects/mysql/MySqlDriver.js +260 -0
- package/dist/dialects/mysql/MySqlDriver.js.map +1 -0
- package/dist/dialects/mysql/MySqlTenantMigrationRunner.d.ts +11 -0
- package/dist/dialects/mysql/MySqlTenantMigrationRunner.d.ts.map +1 -0
- package/dist/dialects/mysql/MySqlTenantMigrationRunner.js +31 -0
- package/dist/dialects/mysql/MySqlTenantMigrationRunner.js.map +1 -0
- package/dist/dialects/mysql/MysqlConnection.d.ts +12 -0
- package/dist/dialects/mysql/MysqlConnection.d.ts.map +1 -0
- package/dist/dialects/mysql/MysqlConnection.js +26 -0
- package/dist/dialects/mysql/MysqlConnection.js.map +1 -0
- package/dist/dialects/mysql/PoolNotFound.d.ts +4 -0
- package/dist/dialects/mysql/PoolNotFound.d.ts.map +1 -0
- package/dist/dialects/mysql/PoolNotFound.js +10 -0
- package/dist/dialects/mysql/PoolNotFound.js.map +1 -0
- package/dist/dialects/mysql/index.d.ts +10 -0
- package/dist/dialects/mysql/index.d.ts.map +1 -0
- package/dist/dialects/mysql/index.js +26 -0
- package/dist/dialects/mysql/index.js.map +1 -0
- package/dist/dialects/postgres/ConnectionNotFound.d.ts +4 -0
- package/dist/dialects/postgres/ConnectionNotFound.d.ts.map +1 -0
- package/dist/dialects/postgres/ConnectionNotFound.js +10 -0
- package/dist/dialects/postgres/ConnectionNotFound.js.map +1 -0
- package/dist/dialects/postgres/PoolNotFound.d.ts +4 -0
- package/dist/dialects/postgres/PoolNotFound.d.ts.map +1 -0
- package/dist/dialects/postgres/PoolNotFound.js +10 -0
- package/dist/dialects/postgres/PoolNotFound.js.map +1 -0
- package/dist/dialects/postgres/PostgresConnection.d.ts +12 -0
- package/dist/dialects/postgres/PostgresConnection.d.ts.map +1 -0
- package/dist/dialects/postgres/PostgresConnection.js +26 -0
- package/dist/dialects/postgres/PostgresConnection.js.map +1 -0
- package/dist/dialects/postgres/PostgresConnectionError.d.ts +4 -0
- package/dist/dialects/postgres/PostgresConnectionError.d.ts.map +1 -0
- package/dist/dialects/postgres/PostgresConnectionError.js +10 -0
- package/dist/dialects/postgres/PostgresConnectionError.js.map +1 -0
- package/dist/dialects/postgres/PostgresConnector.d.ts +24 -0
- package/dist/dialects/postgres/PostgresConnector.d.ts.map +1 -0
- package/dist/dialects/postgres/PostgresConnector.js +158 -0
- package/dist/dialects/postgres/PostgresConnector.js.map +1 -0
- package/dist/dialects/postgres/PostgresDataSource.d.ts +18 -0
- package/dist/dialects/postgres/PostgresDataSource.d.ts.map +1 -0
- package/dist/dialects/postgres/PostgresDataSource.js +66 -0
- package/dist/dialects/postgres/PostgresDataSource.js.map +1 -0
- package/dist/dialects/postgres/PostgresDriver.d.ts +71 -0
- package/dist/dialects/postgres/PostgresDriver.d.ts.map +1 -0
- package/dist/dialects/postgres/PostgresDriver.js +440 -0
- package/dist/dialects/postgres/PostgresDriver.js.map +1 -0
- package/dist/dialects/postgres/PostgresTenantMigrationRunner.d.ts +18 -0
- package/dist/dialects/postgres/PostgresTenantMigrationRunner.d.ts.map +1 -0
- package/dist/dialects/postgres/PostgresTenantMigrationRunner.js +78 -0
- package/dist/dialects/postgres/PostgresTenantMigrationRunner.js.map +1 -0
- package/dist/dialects/postgres/index.d.ts +8 -0
- package/dist/dialects/postgres/index.d.ts.map +1 -0
- package/dist/dialects/postgres/index.js +26 -0
- package/dist/dialects/postgres/index.js.map +1 -0
- package/dist/dialects/sqlite/ConnectionNotFound.d.ts +4 -0
- package/dist/dialects/sqlite/ConnectionNotFound.d.ts.map +1 -0
- package/dist/dialects/sqlite/ConnectionNotFound.js +10 -0
- package/dist/dialects/sqlite/ConnectionNotFound.js.map +1 -0
- package/dist/dialects/sqlite/SqliteConnection.d.ts +12 -0
- package/dist/dialects/sqlite/SqliteConnection.d.ts.map +1 -0
- package/dist/dialects/sqlite/SqliteConnection.js +23 -0
- package/dist/dialects/sqlite/SqliteConnection.js.map +1 -0
- package/dist/dialects/sqlite/SqliteConnectionError.d.ts +4 -0
- package/dist/dialects/sqlite/SqliteConnectionError.d.ts.map +1 -0
- package/dist/dialects/sqlite/SqliteConnectionError.js +10 -0
- package/dist/dialects/sqlite/SqliteConnectionError.js.map +1 -0
- package/dist/dialects/sqlite/SqliteConnector.d.ts +24 -0
- package/dist/dialects/sqlite/SqliteConnector.d.ts.map +1 -0
- package/dist/dialects/sqlite/SqliteConnector.js +119 -0
- package/dist/dialects/sqlite/SqliteConnector.js.map +1 -0
- package/dist/dialects/sqlite/SqliteDataSource.d.ts +18 -0
- package/dist/dialects/sqlite/SqliteDataSource.d.ts.map +1 -0
- package/dist/dialects/sqlite/SqliteDataSource.js +56 -0
- package/dist/dialects/sqlite/SqliteDataSource.js.map +1 -0
- package/dist/dialects/sqlite/SqliteDriver.d.ts +49 -0
- package/dist/dialects/sqlite/SqliteDriver.d.ts.map +1 -0
- package/dist/dialects/sqlite/SqliteDriver.js +234 -0
- package/dist/dialects/sqlite/SqliteDriver.js.map +1 -0
- package/dist/dialects/sqlite/SqliteTenantMigrationRunner.d.ts +11 -0
- package/dist/dialects/sqlite/SqliteTenantMigrationRunner.d.ts.map +1 -0
- package/dist/dialects/sqlite/SqliteTenantMigrationRunner.js +31 -0
- package/dist/dialects/sqlite/SqliteTenantMigrationRunner.js.map +1 -0
- package/dist/dialects/sqlite/index.d.ts +7 -0
- package/dist/dialects/sqlite/index.d.ts.map +1 -0
- package/dist/dialects/sqlite/index.js +23 -0
- package/dist/dialects/sqlite/index.js.map +1 -0
- package/dist/errors/AdvisoryLockError.d.ts +5 -0
- package/dist/errors/AdvisoryLockError.d.ts.map +1 -0
- package/dist/errors/AdvisoryLockError.js +13 -0
- package/dist/errors/AdvisoryLockError.js.map +1 -0
- package/dist/errors/DatabaseConnectionFailedError.d.ts +5 -0
- package/dist/errors/DatabaseConnectionFailedError.d.ts.map +1 -0
- package/dist/errors/DatabaseConnectionFailedError.js +11 -0
- package/dist/errors/DatabaseConnectionFailedError.js.map +1 -0
- package/dist/errors/DatabaseNotConnectedError.d.ts +5 -0
- package/dist/errors/DatabaseNotConnectedError.d.ts.map +1 -0
- package/dist/errors/DatabaseNotConnectedError.js +11 -0
- package/dist/errors/DatabaseNotConnectedError.js.map +1 -0
- package/dist/errors/DeleteWithoutConditionsError.d.ts +5 -0
- package/dist/errors/DeleteWithoutConditionsError.d.ts.map +1 -0
- package/dist/errors/DeleteWithoutConditionsError.js +13 -0
- package/dist/errors/DeleteWithoutConditionsError.js.map +1 -0
- package/dist/errors/EntityMetadataNotFoundError.d.ts +5 -0
- package/dist/errors/EntityMetadataNotFoundError.d.ts.map +1 -0
- package/dist/errors/EntityMetadataNotFoundError.js +13 -0
- package/dist/errors/EntityMetadataNotFoundError.js.map +1 -0
- package/dist/errors/EntityNotFoundError.d.ts +5 -0
- package/dist/errors/EntityNotFoundError.d.ts.map +1 -0
- package/dist/errors/EntityNotFoundError.js +13 -0
- package/dist/errors/EntityNotFoundError.js.map +1 -0
- package/dist/errors/Exception.d.ts +5 -0
- package/dist/errors/Exception.d.ts.map +1 -0
- package/dist/errors/Exception.js +11 -0
- package/dist/errors/Exception.js.map +1 -0
- package/dist/errors/InvalidQueryError.d.ts +5 -0
- package/dist/errors/InvalidQueryError.d.ts.map +1 -0
- package/dist/errors/InvalidQueryError.js +13 -0
- package/dist/errors/InvalidQueryError.js.map +1 -0
- package/dist/errors/NotSupportedDatabaseTypeError.d.ts +5 -0
- package/dist/errors/NotSupportedDatabaseTypeError.d.ts.map +1 -0
- package/dist/errors/NotSupportedDatabaseTypeError.js +11 -0
- package/dist/errors/NotSupportedDatabaseTypeError.js.map +1 -0
- package/dist/errors/OrmError.d.ts +6 -0
- package/dist/errors/OrmError.d.ts.map +1 -0
- package/dist/errors/OrmError.js +12 -0
- package/dist/errors/OrmError.js.map +1 -0
- package/dist/errors/OrmErrorCode.d.ts +16 -0
- package/dist/errors/OrmErrorCode.d.ts.map +1 -0
- package/dist/errors/OrmErrorCode.js +20 -0
- package/dist/errors/OrmErrorCode.js.map +1 -0
- package/dist/errors/PrimaryKeyNotFoundError.d.ts +5 -0
- package/dist/errors/PrimaryKeyNotFoundError.d.ts.map +1 -0
- package/dist/errors/PrimaryKeyNotFoundError.js +13 -0
- package/dist/errors/PrimaryKeyNotFoundError.js.map +1 -0
- package/dist/errors/QueryTimeoutError.d.ts +5 -0
- package/dist/errors/QueryTimeoutError.d.ts.map +1 -0
- package/dist/errors/QueryTimeoutError.js +13 -0
- package/dist/errors/QueryTimeoutError.js.map +1 -0
- package/dist/errors/TransactionError.d.ts +5 -0
- package/dist/errors/TransactionError.d.ts.map +1 -0
- package/dist/errors/TransactionError.js +13 -0
- package/dist/errors/TransactionError.js.map +1 -0
- package/dist/errors/ValidationError.d.ts +6 -0
- package/dist/errors/ValidationError.d.ts.map +1 -0
- package/dist/errors/ValidationError.js +13 -0
- package/dist/errors/ValidationError.js.map +1 -0
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +32 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata/LayeredMetadataScanner.d.ts +32 -0
- package/dist/metadata/LayeredMetadataScanner.d.ts.map +1 -0
- package/dist/metadata/LayeredMetadataScanner.js +119 -0
- package/dist/metadata/LayeredMetadataScanner.js.map +1 -0
- package/dist/metadata/LayeredMetadataStore.d.ts +30 -0
- package/dist/metadata/LayeredMetadataStore.d.ts.map +1 -0
- package/dist/metadata/LayeredMetadataStore.js +150 -0
- package/dist/metadata/LayeredMetadataStore.js.map +1 -0
- package/dist/metadata/MetadataContext.d.ts +11 -0
- package/dist/metadata/MetadataContext.d.ts.map +1 -0
- package/dist/metadata/MetadataContext.js +23 -0
- package/dist/metadata/MetadataContext.js.map +1 -0
- package/dist/metadata/MetadataLayer.d.ts +27 -0
- package/dist/metadata/MetadataLayer.d.ts.map +1 -0
- package/dist/metadata/MetadataLayer.js +78 -0
- package/dist/metadata/MetadataLayer.js.map +1 -0
- package/dist/metadata/MetadataPath.d.ts +24 -0
- package/dist/metadata/MetadataPath.d.ts.map +1 -0
- package/dist/metadata/MetadataPath.js +104 -0
- package/dist/metadata/MetadataPath.js.map +1 -0
- package/dist/metadata/MultiTenantMetadataManager.d.ts +27 -0
- package/dist/metadata/MultiTenantMetadataManager.d.ts.map +1 -0
- package/dist/metadata/MultiTenantMetadataManager.js +69 -0
- package/dist/metadata/MultiTenantMetadataManager.js.map +1 -0
- package/dist/metadata/index.d.ts +7 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/index.js +23 -0
- package/dist/metadata/index.js.map +1 -0
- package/dist/migration/Migration.d.ts +11 -0
- package/dist/migration/Migration.d.ts.map +1 -0
- package/dist/migration/Migration.js +10 -0
- package/dist/migration/Migration.js.map +1 -0
- package/dist/migration/MigrationCli.d.ts +25 -0
- package/dist/migration/MigrationCli.d.ts.map +1 -0
- package/dist/migration/MigrationCli.js +108 -0
- package/dist/migration/MigrationCli.js.map +1 -0
- package/dist/migration/MigrationRunner.d.ts +47 -0
- package/dist/migration/MigrationRunner.d.ts.map +1 -0
- package/dist/migration/MigrationRunner.js +182 -0
- package/dist/migration/MigrationRunner.js.map +1 -0
- package/dist/migration/index.d.ts +4 -0
- package/dist/migration/index.d.ts.map +1 -0
- package/dist/migration/index.js +20 -0
- package/dist/migration/index.js.map +1 -0
- package/dist/scanner/ColumnScanner.d.ts +16 -0
- package/dist/scanner/ColumnScanner.d.ts.map +1 -0
- package/dist/scanner/ColumnScanner.js +30 -0
- package/dist/scanner/ColumnScanner.js.map +1 -0
- package/dist/scanner/EntityScanner.d.ts +14 -0
- package/dist/scanner/EntityScanner.d.ts.map +1 -0
- package/dist/scanner/EntityScanner.js +38 -0
- package/dist/scanner/EntityScanner.js.map +1 -0
- package/dist/scanner/ManyToManyScanner.d.ts +9 -0
- package/dist/scanner/ManyToManyScanner.d.ts.map +1 -0
- package/dist/scanner/ManyToManyScanner.js +39 -0
- package/dist/scanner/ManyToManyScanner.js.map +1 -0
- package/dist/scanner/ManyToOneScanner.d.ts +9 -0
- package/dist/scanner/ManyToOneScanner.d.ts.map +1 -0
- package/dist/scanner/ManyToOneScanner.js +38 -0
- package/dist/scanner/ManyToOneScanner.js.map +1 -0
- package/dist/scanner/MetadataScanner.d.ts +45 -0
- package/dist/scanner/MetadataScanner.d.ts.map +1 -0
- package/dist/scanner/MetadataScanner.js +200 -0
- package/dist/scanner/MetadataScanner.js.map +1 -0
- package/dist/scanner/OneToManyScanner.d.ts +9 -0
- package/dist/scanner/OneToManyScanner.d.ts.map +1 -0
- package/dist/scanner/OneToManyScanner.js +39 -0
- package/dist/scanner/OneToManyScanner.js.map +1 -0
- package/dist/scanner/OneToOneScanner.d.ts +9 -0
- package/dist/scanner/OneToOneScanner.d.ts.map +1 -0
- package/dist/scanner/OneToOneScanner.js +39 -0
- package/dist/scanner/OneToOneScanner.js.map +1 -0
- package/dist/scanner/index.d.ts +9 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +27 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/types/CascadeType.d.ts +5 -0
- package/dist/types/CascadeType.d.ts.map +1 -0
- package/dist/types/CascadeType.js +19 -0
- package/dist/types/CascadeType.js.map +1 -0
- package/dist/types/ColumnPaths.d.ts +7 -0
- package/dist/types/ColumnPaths.d.ts.map +1 -0
- package/dist/types/ColumnPaths.js +3 -0
- package/dist/types/ColumnPaths.js.map +1 -0
- package/dist/types/DeepPartial.d.ts +4 -0
- package/dist/types/DeepPartial.d.ts.map +1 -0
- package/dist/types/DeepPartial.js +3 -0
- package/dist/types/DeepPartial.js.map +1 -0
- package/dist/types/DeleteResult.d.ts +4 -0
- package/dist/types/DeleteResult.d.ts.map +1 -0
- package/dist/types/DeleteResult.js +3 -0
- package/dist/types/DeleteResult.js.map +1 -0
- package/dist/types/EntityResult.d.ts +3 -0
- package/dist/types/EntityResult.d.ts.map +1 -0
- package/dist/types/EntityResult.js +3 -0
- package/dist/types/EntityResult.js.map +1 -0
- package/dist/types/FindCondition.d.ts +16 -0
- package/dist/types/FindCondition.d.ts.map +1 -0
- package/dist/types/FindCondition.js +3 -0
- package/dist/types/FindCondition.js.map +1 -0
- package/dist/types/OrderByOption.d.ts +5 -0
- package/dist/types/OrderByOption.d.ts.map +1 -0
- package/dist/types/OrderByOption.js +3 -0
- package/dist/types/OrderByOption.js.map +1 -0
- package/dist/types/QueryResult.d.ts +5 -0
- package/dist/types/QueryResult.d.ts.map +1 -0
- package/dist/types/QueryResult.js +3 -0
- package/dist/types/QueryResult.js.map +1 -0
- package/dist/types/SchemaOption.d.ts +3 -0
- package/dist/types/SchemaOption.d.ts.map +1 -0
- package/dist/types/SchemaOption.js +3 -0
- package/dist/types/SchemaOption.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +25 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/Logger.d.ts +15 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +75 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/ReflectManager.d.ts +12 -0
- package/dist/utils/ReflectManager.d.ts.map +1 -0
- package/dist/utils/ReflectManager.js +34 -0
- package/dist/utils/ReflectManager.js.map +1 -0
- package/dist/utils/camelToSnakeCase.d.ts +2 -0
- package/dist/utils/camelToSnakeCase.d.ts.map +1 -0
- package/dist/utils/camelToSnakeCase.js +7 -0
- package/dist/utils/camelToSnakeCase.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +22 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/scanner.d.ts +2 -0
- package/dist/utils/scanner.d.ts.map +1 -0
- package/dist/utils/scanner.js +8 -0
- package/dist/utils/scanner.js.map +1 -0
- package/dist/utils/types.d.ts +5 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +3 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,2347 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.EntityManager = void 0;
|
|
40
|
+
require("reflect-metadata");
|
|
41
|
+
const utils_1 = require("../utils");
|
|
42
|
+
const scanner_1 = require("../scanner");
|
|
43
|
+
const typedi_1 = __importDefault(require("typedi"));
|
|
44
|
+
const DatabaseClient_1 = require("../DatabaseClient");
|
|
45
|
+
const MySqlDriver_1 = require("../dialects/mysql/MySqlDriver");
|
|
46
|
+
const PostgresDriver_1 = require("../dialects/postgres/PostgresDriver");
|
|
47
|
+
const SqliteDriver_1 = require("../dialects/sqlite/SqliteDriver");
|
|
48
|
+
const SchemaGenerator_1 = require("./generators/SchemaGenerator");
|
|
49
|
+
const Indexer_1 = require("../decorators/Indexer");
|
|
50
|
+
const UniqueIndex_1 = require("../decorators/UniqueIndex");
|
|
51
|
+
const TransactionSessionManager_1 = require("../dialects/TransactionSessionManager");
|
|
52
|
+
const MySqlDataSource_1 = require("../dialects/mysql/MySqlDataSource");
|
|
53
|
+
const PostgresDataSource_1 = require("../dialects/postgres/PostgresDataSource");
|
|
54
|
+
const SqliteDataSource_1 = require("../dialects/sqlite/SqliteDataSource");
|
|
55
|
+
const sql_template_tag_1 = __importStar(require("sql-template-tag"));
|
|
56
|
+
const decorators_1 = require("../decorators");
|
|
57
|
+
const BaseRepository_1 = require("./BaseRepository");
|
|
58
|
+
const EntityNotFound_1 = require("../dialects/EntityNotFound");
|
|
59
|
+
const RawQueryBuilderFactory_1 = require("./RawQueryBuilderFactory");
|
|
60
|
+
const Conditions_1 = require("./Conditions");
|
|
61
|
+
const ResultTransformerFactory_1 = require("./ResultTransformerFactory");
|
|
62
|
+
const MetadataContext_1 = require("../metadata/MetadataContext");
|
|
63
|
+
const LazyLoader_1 = require("./LazyLoader");
|
|
64
|
+
const CascadeType_1 = require("../types/CascadeType");
|
|
65
|
+
const EntityValidator_1 = require("./EntityValidator");
|
|
66
|
+
const EntityEventEmitter_1 = require("./EntityEventEmitter");
|
|
67
|
+
const EntityMetadataNotFoundError_1 = require("../errors/EntityMetadataNotFoundError");
|
|
68
|
+
const InvalidQueryError_1 = require("../errors/InvalidQueryError");
|
|
69
|
+
const PrimaryKeyNotFoundError_1 = require("../errors/PrimaryKeyNotFoundError");
|
|
70
|
+
const DeleteWithoutConditionsError_1 = require("../errors/DeleteWithoutConditionsError");
|
|
71
|
+
const NotSupportedDatabaseTypeError_1 = require("../errors/NotSupportedDatabaseTypeError");
|
|
72
|
+
const QueryTracker_1 = require("./QueryTracker");
|
|
73
|
+
const CursorPagination_1 = require("./CursorPagination");
|
|
74
|
+
const ReplicationRouter_1 = require("../dialects/ReplicationRouter");
|
|
75
|
+
class EntityManager {
|
|
76
|
+
constructor() {
|
|
77
|
+
this._entities = [];
|
|
78
|
+
this.logger = new utils_1.Logger(EntityManager.name);
|
|
79
|
+
this.dirtyEntities = new Set();
|
|
80
|
+
this.eventEmitter = new EntityEventEmitter_1.EntityEventEmitter();
|
|
81
|
+
this.subscribers = [];
|
|
82
|
+
this.queryTracker = null;
|
|
83
|
+
this.replicationRouter = null;
|
|
84
|
+
this.connectionName = "default";
|
|
85
|
+
}
|
|
86
|
+
async register(databaseClientOptions, connectionName = "default") {
|
|
87
|
+
await this.connect(databaseClientOptions, connectionName);
|
|
88
|
+
await this.registerEntities();
|
|
89
|
+
}
|
|
90
|
+
get client() {
|
|
91
|
+
return DatabaseClient_1.DatabaseClient.getInstance();
|
|
92
|
+
}
|
|
93
|
+
get connection() {
|
|
94
|
+
const c = this.client;
|
|
95
|
+
if (typeof c.getConnection === "function") {
|
|
96
|
+
return c.getConnection(this.connectionName);
|
|
97
|
+
}
|
|
98
|
+
return c.getConnection();
|
|
99
|
+
}
|
|
100
|
+
getConnectionName() {
|
|
101
|
+
return this.connectionName;
|
|
102
|
+
}
|
|
103
|
+
async connect(databaseClientOptions, connectionName = "default") {
|
|
104
|
+
this.connectionName = connectionName;
|
|
105
|
+
const client = this.client;
|
|
106
|
+
const connector = await client.connect(databaseClientOptions, connectionName);
|
|
107
|
+
const { schema, queryTimeout, replication } = databaseClientOptions;
|
|
108
|
+
const dbType = (typeof client.getType === "function"
|
|
109
|
+
? client.getType(connectionName)
|
|
110
|
+
: client.type);
|
|
111
|
+
this.dbType = dbType;
|
|
112
|
+
switch (dbType) {
|
|
113
|
+
case "mariadb":
|
|
114
|
+
case "mysql":
|
|
115
|
+
this.driver = new MySqlDriver_1.MySqlDriver(connector, dbType);
|
|
116
|
+
this.dataSource = new MySqlDataSource_1.MySqlDataSource(connector);
|
|
117
|
+
break;
|
|
118
|
+
case "postgres":
|
|
119
|
+
this.driver = new PostgresDriver_1.PostgresDriver(connector, dbType, schema);
|
|
120
|
+
this.dataSource = new PostgresDataSource_1.PostgresDataSource(connector);
|
|
121
|
+
break;
|
|
122
|
+
case "sqlite":
|
|
123
|
+
this.driver = new SqliteDriver_1.SqliteDriver(connector);
|
|
124
|
+
this.dataSource = new SqliteDataSource_1.SqliteDataSource(connector);
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
throw new NotSupportedDatabaseTypeError_1.NotSupportedDatabaseTypeError();
|
|
128
|
+
}
|
|
129
|
+
this.initQueryTracker(databaseClientOptions);
|
|
130
|
+
const isTimeoutSupported = queryTimeout && queryTimeout > 0;
|
|
131
|
+
if (isTimeoutSupported) {
|
|
132
|
+
this.defaultQueryTimeout = queryTimeout;
|
|
133
|
+
}
|
|
134
|
+
if (replication) {
|
|
135
|
+
this.replicationRouter = new ReplicationRouter_1.ReplicationRouter(replication);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
initQueryTracker(options) {
|
|
139
|
+
const logging = options.logging;
|
|
140
|
+
if (typeof logging === "object" && logging !== null) {
|
|
141
|
+
const loggingOpts = logging;
|
|
142
|
+
if (loggingOpts.enableQueryTracking === false) {
|
|
143
|
+
this.queryTracker = null;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (loggingOpts.nPlusOne || loggingOpts.slowQueryMs) {
|
|
147
|
+
this.queryTracker = new QueryTracker_1.QueryTracker({
|
|
148
|
+
slowQueryMs: loggingOpts.slowQueryMs ?? null,
|
|
149
|
+
enabled: loggingOpts.enableQueryTracking ?? true,
|
|
150
|
+
maxLogEntries: loggingOpts.maxLogEntries,
|
|
151
|
+
ttlMs: loggingOpts.ttlMs,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
getQueryLog() {
|
|
157
|
+
return this.queryTracker?.getLog() ?? [];
|
|
158
|
+
}
|
|
159
|
+
getQueryTracker() {
|
|
160
|
+
return this.queryTracker;
|
|
161
|
+
}
|
|
162
|
+
beginTrackQuery() {
|
|
163
|
+
this.queryTracker?.beginQuery();
|
|
164
|
+
}
|
|
165
|
+
trackQuery(entityName, sqlText, durationMs) {
|
|
166
|
+
this.queryTracker?.endQuery();
|
|
167
|
+
this.queryTracker?.track(entityName, sqlText, durationMs);
|
|
168
|
+
}
|
|
169
|
+
getReadNode(useMaster) {
|
|
170
|
+
if (!this.replicationRouter)
|
|
171
|
+
return null;
|
|
172
|
+
if (useMaster)
|
|
173
|
+
return this.replicationRouter.getWriteNode();
|
|
174
|
+
return this.replicationRouter.getReadNode();
|
|
175
|
+
}
|
|
176
|
+
getWriteNode() {
|
|
177
|
+
if (!this.replicationRouter)
|
|
178
|
+
return null;
|
|
179
|
+
return this.replicationRouter.getWriteNode();
|
|
180
|
+
}
|
|
181
|
+
get isReplicationEnabled() {
|
|
182
|
+
return this.replicationRouter !== null;
|
|
183
|
+
}
|
|
184
|
+
getReplicationRouter() {
|
|
185
|
+
return this.replicationRouter;
|
|
186
|
+
}
|
|
187
|
+
on(event, listener) {
|
|
188
|
+
this.eventEmitter.on(event, listener);
|
|
189
|
+
}
|
|
190
|
+
off(event, listener) {
|
|
191
|
+
this.eventEmitter.off(event, listener);
|
|
192
|
+
}
|
|
193
|
+
removeAllListeners() {
|
|
194
|
+
this.eventEmitter.removeAllListeners();
|
|
195
|
+
}
|
|
196
|
+
addSubscriber(subscriber) {
|
|
197
|
+
this.subscribers.push(subscriber);
|
|
198
|
+
}
|
|
199
|
+
removeSubscriber(subscriber) {
|
|
200
|
+
const idx = this.subscribers.indexOf(subscriber);
|
|
201
|
+
if (idx !== -1) {
|
|
202
|
+
this.subscribers.splice(idx, 1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async notifySubscribers(entityClass, method, arg) {
|
|
206
|
+
for (const sub of this.subscribers) {
|
|
207
|
+
if (sub.listenTo() === entityClass && typeof sub[method] === "function") {
|
|
208
|
+
await sub[method](arg);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async notifyTransactionSubscribers(method) {
|
|
213
|
+
for (const sub of this.subscribers) {
|
|
214
|
+
if (typeof sub[method] === "function") {
|
|
215
|
+
await sub[method]();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async propagateShutdown(options) {
|
|
220
|
+
const gracefulTimeoutMs = options?.gracefulTimeoutMs ?? 0;
|
|
221
|
+
const closeConnections = options?.closeConnections ?? false;
|
|
222
|
+
let allQueriesCompleted = true;
|
|
223
|
+
if (gracefulTimeoutMs > 0 && this.queryTracker) {
|
|
224
|
+
const activeCount = this.queryTracker.activeQueryCount;
|
|
225
|
+
if (activeCount > 0) {
|
|
226
|
+
this.logger.info(`[Shutdown] Waiting for ${activeCount} active queries (timeout: ${gracefulTimeoutMs}ms)...`);
|
|
227
|
+
allQueriesCompleted = await this.queryTracker.waitForQueries(gracefulTimeoutMs);
|
|
228
|
+
if (!allQueriesCompleted) {
|
|
229
|
+
this.logger.warn(`[Shutdown] Timed out waiting for active queries. Forcing shutdown.`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
this.removeAllListeners();
|
|
234
|
+
this.subscribers.length = 0;
|
|
235
|
+
this.dirtyEntities.clear();
|
|
236
|
+
this.queryTracker?.reset();
|
|
237
|
+
this.queryTracker = null;
|
|
238
|
+
if (this.replicationRouter) {
|
|
239
|
+
this.replicationRouter.resetFailedSlaves();
|
|
240
|
+
this.replicationRouter = null;
|
|
241
|
+
}
|
|
242
|
+
if (closeConnections) {
|
|
243
|
+
try {
|
|
244
|
+
await this.client.close(this.connectionName);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
this.logger.warn(`[Shutdown] Error closing connection '${this.connectionName}': ${err}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return allQueriesCompleted;
|
|
251
|
+
}
|
|
252
|
+
getNameStrategy(clazz) {
|
|
253
|
+
return clazz.name;
|
|
254
|
+
}
|
|
255
|
+
resolveEntityMetadata(entity) {
|
|
256
|
+
const context = MetadataContext_1.MetadataContext.isActive()
|
|
257
|
+
? MetadataContext_1.MetadataContext.getCurrentTenant()
|
|
258
|
+
: "public";
|
|
259
|
+
const entityScanner = typedi_1.default.get(scanner_1.EntityScanner);
|
|
260
|
+
const layeredMetadata = entityScanner.scan(entity);
|
|
261
|
+
if (layeredMetadata) {
|
|
262
|
+
return layeredMetadata;
|
|
263
|
+
}
|
|
264
|
+
const reflectMetadata = Reflect.getMetadata(decorators_1.ENTITY_TOKEN, entity);
|
|
265
|
+
if (reflectMetadata) {
|
|
266
|
+
this.logger.warn(`[resolveEntityMetadata] "${entity.name}" resolved via Reflect.getMetadata fallback (context: "${context}"). ` +
|
|
267
|
+
`This entity was not found in the layered store.`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
this.logger.error(`[resolveEntityMetadata] "${entity.name}" not found in any metadata source (context: "${context}")`);
|
|
271
|
+
}
|
|
272
|
+
return reflectMetadata ?? null;
|
|
273
|
+
}
|
|
274
|
+
getDeletedAtColumn(entity) {
|
|
275
|
+
const column = Reflect.getMetadata(decorators_1.DELETED_AT_TOKEN, entity);
|
|
276
|
+
return column ?? null;
|
|
277
|
+
}
|
|
278
|
+
async runHooks(entity, item, event) {
|
|
279
|
+
const hooks = Reflect.getMetadata(decorators_1.HOOK_TOKEN, entity);
|
|
280
|
+
if (!hooks || hooks.length === 0)
|
|
281
|
+
return;
|
|
282
|
+
for (const hook of hooks) {
|
|
283
|
+
if (hook.event !== event)
|
|
284
|
+
continue;
|
|
285
|
+
const method = item[hook.methodName];
|
|
286
|
+
if (typeof method === "function") {
|
|
287
|
+
await method.call(item);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
createProxy(entity) {
|
|
292
|
+
return new Proxy(entity, {
|
|
293
|
+
set: (target, prop, value) => {
|
|
294
|
+
target[prop] = value;
|
|
295
|
+
this.dirtyEntities.add(target);
|
|
296
|
+
return true;
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
async registerEntities() {
|
|
301
|
+
const entityScanner = typedi_1.default.get(scanner_1.EntityScanner);
|
|
302
|
+
const entities = entityScanner.makeEntities();
|
|
303
|
+
let entity;
|
|
304
|
+
const { synchronize } = this.client.getOptions();
|
|
305
|
+
if (synchronize && this.isPostgres() && this.driver) {
|
|
306
|
+
const pgDriver = this.driver;
|
|
307
|
+
const hasSchema = await pgDriver.hasSchema();
|
|
308
|
+
if (!hasSchema || hasSchema.length === 0) {
|
|
309
|
+
await pgDriver.createSchema();
|
|
310
|
+
await pgDriver.setSearchPath();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const entityList = [];
|
|
314
|
+
while ((entity = entities.next())) {
|
|
315
|
+
if (entity.done) {
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
const metadata = entity.value;
|
|
319
|
+
const TargetEntity = metadata.target;
|
|
320
|
+
let tableName = metadata.name;
|
|
321
|
+
if (!tableName) {
|
|
322
|
+
tableName = this.getNameStrategy(TargetEntity);
|
|
323
|
+
}
|
|
324
|
+
if (!utils_1.ReflectManager.isEntity(TargetEntity)) {
|
|
325
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(tableName ?? "Unknown");
|
|
326
|
+
}
|
|
327
|
+
if (synchronize) {
|
|
328
|
+
const hasTable = await this.driver?.hasTable(tableName);
|
|
329
|
+
if (!hasTable || hasTable.length === 0) {
|
|
330
|
+
await this.driver?.createTable(tableName, metadata.columns);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
entityList.push({ TargetEntity, tableName, metadata });
|
|
334
|
+
}
|
|
335
|
+
if (synchronize) {
|
|
336
|
+
for (const { TargetEntity, tableName } of entityList) {
|
|
337
|
+
await this.registerForeignKeys(TargetEntity, tableName);
|
|
338
|
+
await this.registerIndex(TargetEntity, tableName);
|
|
339
|
+
await this.registerUniqueIndexes(TargetEntity, tableName);
|
|
340
|
+
}
|
|
341
|
+
await this.registerManyToManyJoinTables(entityList.map((e) => e.TargetEntity));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async registerUniqueIndexes(TargetEntity, tableName) {
|
|
345
|
+
const uniqueIndexes = Reflect.getMetadata(UniqueIndex_1.UNIQUE_INDEX_TOKEN, TargetEntity);
|
|
346
|
+
if (!uniqueIndexes || uniqueIndexes.length === 0)
|
|
347
|
+
return;
|
|
348
|
+
for (const uq of uniqueIndexes) {
|
|
349
|
+
const indexName = uq.name ?? `uq_${tableName}_${uq.columns.join("_")}`;
|
|
350
|
+
const indexes = (await this.driver?.getIndexes(tableName));
|
|
351
|
+
let isExist = false;
|
|
352
|
+
for (const idx of indexes || []) {
|
|
353
|
+
const existingIndexName = idx["Key_name"] ?? idx["Field"] ?? idx["name"];
|
|
354
|
+
if (existingIndexName === indexName) {
|
|
355
|
+
isExist = true;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (!isExist) {
|
|
360
|
+
await this.driver?.addCompositeUniqueIndex(tableName, uq.columns, indexName);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async registerManyToManyJoinTables(entities) {
|
|
365
|
+
const processedTables = new Set();
|
|
366
|
+
for (const entity of entities) {
|
|
367
|
+
const m2mMeta = (Reflect.getMetadata(decorators_1.MANY_TO_MANY_TOKEN, entity) ??
|
|
368
|
+
[]);
|
|
369
|
+
for (const rel of m2mMeta) {
|
|
370
|
+
if (!rel.joinTable)
|
|
371
|
+
continue;
|
|
372
|
+
const { name: joinTableName, joinColumn, inverseJoinColumn, } = rel.joinTable;
|
|
373
|
+
if (processedTables.has(joinTableName))
|
|
374
|
+
continue;
|
|
375
|
+
processedTables.add(joinTableName);
|
|
376
|
+
const ownerEntityMeta = Reflect.getMetadata(decorators_1.ENTITY_TOKEN, entity);
|
|
377
|
+
const ownerTable = ownerEntityMeta?.name ?? entity.name;
|
|
378
|
+
const relatedEntity = rel.getRelatedEntity();
|
|
379
|
+
const relatedEntityMeta = Reflect.getMetadata(decorators_1.ENTITY_TOKEN, relatedEntity);
|
|
380
|
+
const relatedTable = relatedEntityMeta?.name ?? relatedEntity.name;
|
|
381
|
+
const hasTable = await this.driver?.hasTable(joinTableName);
|
|
382
|
+
if (!hasTable || hasTable.length === 0) {
|
|
383
|
+
const wJoinTable = this.wrap(joinTableName);
|
|
384
|
+
const wJoinCol = this.wrap(joinColumn);
|
|
385
|
+
const wInvCol = this.wrap(inverseJoinColumn);
|
|
386
|
+
let ddl = `CREATE TABLE IF NOT EXISTS ${wJoinTable} (${wJoinCol} INT NOT NULL, ${wInvCol} INT NOT NULL, PRIMARY KEY (${wJoinCol}, ${wInvCol}))`;
|
|
387
|
+
if (this.isMySqlFamily())
|
|
388
|
+
ddl += " ENGINE=InnoDB";
|
|
389
|
+
await this.driver?.executeRaw(ddl);
|
|
390
|
+
}
|
|
391
|
+
const ownerColumns = (Reflect.getMetadata(decorators_1.COLUMN_TOKEN, entity.prototype) ?? []);
|
|
392
|
+
const ownerPk = ownerColumns.find((c) => c.options?.primary)?.name;
|
|
393
|
+
const relatedColumns = (Reflect.getMetadata(decorators_1.COLUMN_TOKEN, relatedEntity.prototype) ?? []);
|
|
394
|
+
const relatedPk = relatedColumns.find((c) => c.options?.primary)?.name;
|
|
395
|
+
const ownerFkName = SchemaGenerator_1.SchemaGenerator.generateForeignKeyName(joinTableName, joinColumn, ownerTable);
|
|
396
|
+
if (ownerPk &&
|
|
397
|
+
this.driver &&
|
|
398
|
+
!(await this.driver.hasForeignKey(joinTableName, ownerFkName))) {
|
|
399
|
+
const ddl = `ALTER TABLE ${this.wrap(joinTableName)} ADD CONSTRAINT ${ownerFkName} FOREIGN KEY (${this.wrap(joinColumn)}) REFERENCES ${this.wrap(ownerTable)}(${this.wrap(ownerPk)}) ON DELETE CASCADE ON UPDATE CASCADE`;
|
|
400
|
+
await this.driver.executeRaw(ddl);
|
|
401
|
+
}
|
|
402
|
+
const relatedFkName = SchemaGenerator_1.SchemaGenerator.generateForeignKeyName(joinTableName, inverseJoinColumn, relatedTable);
|
|
403
|
+
if (relatedPk &&
|
|
404
|
+
this.driver &&
|
|
405
|
+
!(await this.driver.hasForeignKey(joinTableName, relatedFkName))) {
|
|
406
|
+
const ddl = `ALTER TABLE ${this.wrap(joinTableName)} ADD CONSTRAINT ${relatedFkName} FOREIGN KEY (${this.wrap(inverseJoinColumn)}) REFERENCES ${this.wrap(relatedTable)}(${this.wrap(relatedPk)}) ON DELETE CASCADE ON UPDATE CASCADE`;
|
|
407
|
+
await this.driver.executeRaw(ddl);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
resolveManyToOneMetadata(entity) {
|
|
413
|
+
const manyToOneScanner = typedi_1.default.get(scanner_1.ManyToOneScanner);
|
|
414
|
+
const allRelations = manyToOneScanner
|
|
415
|
+
.allMetadata()
|
|
416
|
+
.filter((rel) => rel.target === entity);
|
|
417
|
+
if (allRelations.length > 0) {
|
|
418
|
+
return this.resolveJoinColumnsFromColumnMeta(entity, allRelations);
|
|
419
|
+
}
|
|
420
|
+
const reflectMetadata = Reflect.getMetadata(decorators_1.MANY_TO_ONE_TOKEN, entity) ??
|
|
421
|
+
Reflect.getMetadata(decorators_1.MANY_TO_ONE_TOKEN, entity.prototype);
|
|
422
|
+
if (reflectMetadata && reflectMetadata.length > 0) {
|
|
423
|
+
this.logger.warn(`[resolveManyToOneMetadata] "${entity.name}" ManyToOne resolved via Reflect.getMetadata fallback.`);
|
|
424
|
+
return this.resolveJoinColumnsFromColumnMeta(entity, reflectMetadata);
|
|
425
|
+
}
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
resolveJoinColumnsFromColumnMeta(entity, relations) {
|
|
429
|
+
const columnsMeta = Reflect.getMetadata(decorators_1.COLUMN_TOKEN, entity) ??
|
|
430
|
+
Reflect.getMetadata(decorators_1.COLUMN_TOKEN, entity.prototype) ??
|
|
431
|
+
[];
|
|
432
|
+
if (columnsMeta.length === 0) {
|
|
433
|
+
return relations;
|
|
434
|
+
}
|
|
435
|
+
return relations.map((rel) => {
|
|
436
|
+
if (rel.joinColumn)
|
|
437
|
+
return rel;
|
|
438
|
+
const fkPropertyName = `${rel.columnName}Id`;
|
|
439
|
+
const matchingColumn = columnsMeta.find((col) => col.propertyKey === fkPropertyName);
|
|
440
|
+
if (!matchingColumn)
|
|
441
|
+
return rel;
|
|
442
|
+
const resolvedJoinColumn = matchingColumn.name ?? fkPropertyName;
|
|
443
|
+
return {
|
|
444
|
+
...rel,
|
|
445
|
+
joinColumn: resolvedJoinColumn,
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
resolveOneToManyMetadata(entity) {
|
|
450
|
+
const oneToManyScanner = typedi_1.default.get(scanner_1.OneToManyScanner);
|
|
451
|
+
const allRelations = oneToManyScanner
|
|
452
|
+
.allMetadata()
|
|
453
|
+
.filter((rel) => rel.target === entity);
|
|
454
|
+
if (allRelations.length > 0) {
|
|
455
|
+
return allRelations;
|
|
456
|
+
}
|
|
457
|
+
const reflectMetadata = Reflect.getMetadata(decorators_1.ONE_TO_MANY_TOKEN, entity) ??
|
|
458
|
+
Reflect.getMetadata(decorators_1.ONE_TO_MANY_TOKEN, entity.prototype);
|
|
459
|
+
if (reflectMetadata && reflectMetadata.length > 0) {
|
|
460
|
+
this.logger.warn(`[resolveOneToManyMetadata] "${entity.name}" OneToMany resolved via Reflect.getMetadata fallback.`);
|
|
461
|
+
return reflectMetadata;
|
|
462
|
+
}
|
|
463
|
+
return [];
|
|
464
|
+
}
|
|
465
|
+
async loadOneToManyRelations(entity, parentResults, relations) {
|
|
466
|
+
const oneToManyMeta = this.resolveOneToManyMetadata(entity);
|
|
467
|
+
if (oneToManyMeta.length === 0)
|
|
468
|
+
return;
|
|
469
|
+
const parentMetadata = this.resolveEntityMetadata(entity);
|
|
470
|
+
if (!parentMetadata)
|
|
471
|
+
return;
|
|
472
|
+
const pk = parentMetadata.columns.find((column) => column.options?.primary);
|
|
473
|
+
if (!pk)
|
|
474
|
+
return;
|
|
475
|
+
const parents = Array.isArray(parentResults)
|
|
476
|
+
? parentResults
|
|
477
|
+
: [parentResults];
|
|
478
|
+
for (const rel of oneToManyMeta) {
|
|
479
|
+
if (!relations.includes(rel.propertyKey))
|
|
480
|
+
continue;
|
|
481
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
482
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
483
|
+
if (!relatedMetadata)
|
|
484
|
+
continue;
|
|
485
|
+
const manyToOneItems = this.resolveManyToOneMetadata(RelatedEntity);
|
|
486
|
+
const matchingRelation = manyToOneItems.find((m) => m.columnName === rel.mappedBy);
|
|
487
|
+
const fkColumn = matchingRelation?.joinColumn ?? rel.mappedBy;
|
|
488
|
+
for (const parent of parents) {
|
|
489
|
+
const parentId = parent[pk.name];
|
|
490
|
+
if (parentId === undefined || parentId === null)
|
|
491
|
+
continue;
|
|
492
|
+
const children = await this.find(RelatedEntity, {
|
|
493
|
+
where: { [fkColumn]: parentId },
|
|
494
|
+
});
|
|
495
|
+
if (children === undefined) {
|
|
496
|
+
parent[rel.propertyKey] = [];
|
|
497
|
+
}
|
|
498
|
+
else if (Array.isArray(children)) {
|
|
499
|
+
parent[rel.propertyKey] = children;
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
parent[rel.propertyKey] = [children];
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
resolveManyToManyMetadata(entity) {
|
|
508
|
+
const manyToManyScanner = typedi_1.default.get(scanner_1.ManyToManyScanner);
|
|
509
|
+
const allRelations = manyToManyScanner
|
|
510
|
+
.allMetadata()
|
|
511
|
+
.filter((rel) => rel.target === entity);
|
|
512
|
+
if (allRelations.length > 0) {
|
|
513
|
+
return allRelations;
|
|
514
|
+
}
|
|
515
|
+
const reflectMetadata = Reflect.getMetadata(decorators_1.MANY_TO_MANY_TOKEN, entity) ??
|
|
516
|
+
Reflect.getMetadata(decorators_1.MANY_TO_MANY_TOKEN, entity.prototype);
|
|
517
|
+
if (reflectMetadata && reflectMetadata.length > 0) {
|
|
518
|
+
this.logger.warn(`[resolveManyToManyMetadata] "${entity.name}" ManyToMany resolved via Reflect.getMetadata fallback.`);
|
|
519
|
+
return reflectMetadata;
|
|
520
|
+
}
|
|
521
|
+
return [];
|
|
522
|
+
}
|
|
523
|
+
resolveOneToOneMetadata(entity) {
|
|
524
|
+
const oneToOneScanner = typedi_1.default.get(scanner_1.OneToOneScanner);
|
|
525
|
+
const allRelations = oneToOneScanner
|
|
526
|
+
.allMetadata()
|
|
527
|
+
.filter((rel) => rel.target === entity);
|
|
528
|
+
if (allRelations.length > 0) {
|
|
529
|
+
return this.resolveJoinColumnsFromColumnMetaForOneToOne(entity, allRelations);
|
|
530
|
+
}
|
|
531
|
+
const reflectMetadata = Reflect.getMetadata(decorators_1.ONE_TO_ONE_TOKEN, entity) ??
|
|
532
|
+
Reflect.getMetadata(decorators_1.ONE_TO_ONE_TOKEN, entity.prototype);
|
|
533
|
+
if (reflectMetadata && reflectMetadata.length > 0) {
|
|
534
|
+
this.logger.warn(`[resolveOneToOneMetadata] "${entity.name}" OneToOne resolved via Reflect.getMetadata fallback.`);
|
|
535
|
+
return this.resolveJoinColumnsFromColumnMetaForOneToOne(entity, reflectMetadata);
|
|
536
|
+
}
|
|
537
|
+
return [];
|
|
538
|
+
}
|
|
539
|
+
resolveJoinColumnsFromColumnMetaForOneToOne(entity, relations) {
|
|
540
|
+
const columnsMeta = Reflect.getMetadata(decorators_1.COLUMN_TOKEN, entity) ??
|
|
541
|
+
Reflect.getMetadata(decorators_1.COLUMN_TOKEN, entity.prototype) ??
|
|
542
|
+
[];
|
|
543
|
+
if (columnsMeta.length === 0) {
|
|
544
|
+
return relations;
|
|
545
|
+
}
|
|
546
|
+
return relations.map((rel) => {
|
|
547
|
+
if (rel.joinColumn)
|
|
548
|
+
return rel;
|
|
549
|
+
const fkPropertyName = `${rel.propertyKey}Id`;
|
|
550
|
+
const matchingColumn = columnsMeta.find((col) => col.propertyKey === fkPropertyName);
|
|
551
|
+
if (!matchingColumn)
|
|
552
|
+
return rel;
|
|
553
|
+
const resolvedJoinColumn = matchingColumn.name ?? fkPropertyName;
|
|
554
|
+
return {
|
|
555
|
+
...rel,
|
|
556
|
+
joinColumn: resolvedJoinColumn,
|
|
557
|
+
};
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
resolveManyToManyJoinTable(rel) {
|
|
561
|
+
if (rel.joinTable) {
|
|
562
|
+
return {
|
|
563
|
+
joinTableName: rel.joinTable.name,
|
|
564
|
+
joinColumn: rel.joinTable.joinColumn,
|
|
565
|
+
inverseJoinColumn: rel.joinTable.inverseJoinColumn,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
if (rel.mappedBy) {
|
|
569
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
570
|
+
const relatedManyToMany = this.resolveManyToManyMetadata(RelatedEntity);
|
|
571
|
+
const ownerRel = relatedManyToMany.find((r) => r.propertyKey === rel.mappedBy && r.joinTable);
|
|
572
|
+
if (ownerRel?.joinTable) {
|
|
573
|
+
return {
|
|
574
|
+
joinTableName: ownerRel.joinTable.name,
|
|
575
|
+
joinColumn: ownerRel.joinTable.inverseJoinColumn,
|
|
576
|
+
inverseJoinColumn: ownerRel.joinTable.joinColumn,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
async loadManyToManyRelations(entity, parentResults, relations) {
|
|
583
|
+
const manyToManyMeta = this.resolveManyToManyMetadata(entity);
|
|
584
|
+
if (manyToManyMeta.length === 0)
|
|
585
|
+
return;
|
|
586
|
+
const parentMetadata = this.resolveEntityMetadata(entity);
|
|
587
|
+
if (!parentMetadata)
|
|
588
|
+
return;
|
|
589
|
+
const pk = parentMetadata.columns.find((column) => column.options?.primary);
|
|
590
|
+
if (!pk)
|
|
591
|
+
return;
|
|
592
|
+
const parents = Array.isArray(parentResults)
|
|
593
|
+
? parentResults
|
|
594
|
+
: [parentResults];
|
|
595
|
+
for (const rel of manyToManyMeta) {
|
|
596
|
+
if (!relations.includes(rel.propertyKey))
|
|
597
|
+
continue;
|
|
598
|
+
const joinInfo = this.resolveManyToManyJoinTable(rel);
|
|
599
|
+
if (!joinInfo)
|
|
600
|
+
continue;
|
|
601
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
602
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
603
|
+
if (!relatedMetadata)
|
|
604
|
+
continue;
|
|
605
|
+
const relatedPk = relatedMetadata.columns.find((col) => col.options?.primary);
|
|
606
|
+
if (!relatedPk)
|
|
607
|
+
continue;
|
|
608
|
+
const relatedTableName = relatedMetadata.name ?? RelatedEntity.name;
|
|
609
|
+
for (const parent of parents) {
|
|
610
|
+
const parentId = parent[pk.name];
|
|
611
|
+
if (parentId === undefined || parentId === null)
|
|
612
|
+
continue;
|
|
613
|
+
const qb = RawQueryBuilderFactory_1.RawQueryBuilderFactory.create();
|
|
614
|
+
const selectCols = relatedMetadata.columns.map((col) => `${this.wrap(relatedTableName)}.${this.wrap(col.name)}`);
|
|
615
|
+
const joinCondition = (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(relatedTableName))}.${(0, sql_template_tag_1.raw)(this.wrap(relatedPk.name))} = ${(0, sql_template_tag_1.raw)(this.wrap(joinInfo.joinTableName))}.${(0, sql_template_tag_1.raw)(this.wrap(joinInfo.inverseJoinColumn))}`;
|
|
616
|
+
const whereCondition = (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(joinInfo.joinTableName))}.${(0, sql_template_tag_1.raw)(this.wrap(joinInfo.joinColumn))} = ${parentId}`;
|
|
617
|
+
qb.select(selectCols)
|
|
618
|
+
.from(this.wrap(relatedTableName))
|
|
619
|
+
.innerJoin(this.wrap(joinInfo.joinTableName), this.wrap(joinInfo.joinTableName), joinCondition)
|
|
620
|
+
.where([whereCondition]);
|
|
621
|
+
const transactionHolder = new TransactionSessionManager_1.TransactionSessionManager();
|
|
622
|
+
try {
|
|
623
|
+
await transactionHolder.connect();
|
|
624
|
+
await transactionHolder.startTransaction();
|
|
625
|
+
if (this.isMySqlFamily()) {
|
|
626
|
+
await transactionHolder.query("SET autocommit = 0");
|
|
627
|
+
}
|
|
628
|
+
const resultQuery = qb.build();
|
|
629
|
+
const subQueryStart = Date.now();
|
|
630
|
+
this.beginTrackQuery();
|
|
631
|
+
const queryResult = (await transactionHolder.query(resultQuery));
|
|
632
|
+
this.trackQuery(relatedTableName, resultQuery.text ?? String(resultQuery), Date.now() - subQueryStart);
|
|
633
|
+
await transactionHolder.commit();
|
|
634
|
+
const resultTransformer = ResultTransformerFactory_1.ResultTransformerFactory.create();
|
|
635
|
+
const { results } = queryResult;
|
|
636
|
+
if (!results || results.length === 0) {
|
|
637
|
+
parent[rel.propertyKey] = [];
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
parent[rel.propertyKey] = resultTransformer.toEntities(RelatedEntity, queryResult);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
catch (e) {
|
|
644
|
+
try {
|
|
645
|
+
await transactionHolder.rollback();
|
|
646
|
+
}
|
|
647
|
+
catch (rollbackError) {
|
|
648
|
+
this.logger.error(`Failed to rollback ManyToMany transaction: ${rollbackError}`);
|
|
649
|
+
}
|
|
650
|
+
throw e;
|
|
651
|
+
}
|
|
652
|
+
finally {
|
|
653
|
+
try {
|
|
654
|
+
await transactionHolder.close();
|
|
655
|
+
}
|
|
656
|
+
catch (closeError) {
|
|
657
|
+
this.logger.error(`Failed to close ManyToMany transaction: ${closeError}`);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
async loadOneToOneRelations(entity, parentResults, relations) {
|
|
664
|
+
const oneToOneMeta = this.resolveOneToOneMetadata(entity);
|
|
665
|
+
if (oneToOneMeta.length === 0)
|
|
666
|
+
return;
|
|
667
|
+
const parentMetadata = this.resolveEntityMetadata(entity);
|
|
668
|
+
if (!parentMetadata)
|
|
669
|
+
return;
|
|
670
|
+
const pk = parentMetadata.columns.find((column) => column.options?.primary);
|
|
671
|
+
if (!pk)
|
|
672
|
+
return;
|
|
673
|
+
const parents = Array.isArray(parentResults)
|
|
674
|
+
? parentResults
|
|
675
|
+
: [parentResults];
|
|
676
|
+
for (const rel of oneToOneMeta) {
|
|
677
|
+
if (!relations.includes(rel.propertyKey))
|
|
678
|
+
continue;
|
|
679
|
+
if (rel.joinColumn) {
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
683
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
684
|
+
if (!relatedMetadata)
|
|
685
|
+
continue;
|
|
686
|
+
const relatedPk = relatedMetadata.columns.find((col) => col.options?.primary);
|
|
687
|
+
if (!relatedPk)
|
|
688
|
+
continue;
|
|
689
|
+
for (const parent of parents) {
|
|
690
|
+
if (rel.joinColumn) {
|
|
691
|
+
const fkValue = parent[rel.joinColumn];
|
|
692
|
+
if (fkValue === undefined || fkValue === null) {
|
|
693
|
+
parent[rel.propertyKey] = null;
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
const related = await this.findOne(RelatedEntity, {
|
|
697
|
+
where: { [relatedPk.name]: fkValue },
|
|
698
|
+
});
|
|
699
|
+
parent[rel.propertyKey] = related ?? null;
|
|
700
|
+
}
|
|
701
|
+
else if (rel.inverseSide) {
|
|
702
|
+
const parentId = parent[pk.name];
|
|
703
|
+
if (parentId === undefined || parentId === null) {
|
|
704
|
+
parent[rel.propertyKey] = null;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
const relatedOneToOne = this.resolveOneToOneMetadata(RelatedEntity);
|
|
708
|
+
const ownerRel = relatedOneToOne.find((r) => r.propertyKey === rel.inverseSide && r.joinColumn);
|
|
709
|
+
if (ownerRel?.joinColumn) {
|
|
710
|
+
const related = await this.findOne(RelatedEntity, {
|
|
711
|
+
where: { [ownerRel.joinColumn]: parentId },
|
|
712
|
+
});
|
|
713
|
+
parent[rel.propertyKey] = related ?? null;
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
parent[rel.propertyKey] = null;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async registerForeignKeys(TargetEntity, tableName) {
|
|
723
|
+
const entityScanner = typedi_1.default.get(scanner_1.EntityScanner);
|
|
724
|
+
const manyToOneItems = this.resolveManyToOneMetadata(TargetEntity);
|
|
725
|
+
const isValidManyToOne = manyToOneItems && manyToOneItems.length > 0;
|
|
726
|
+
if (isValidManyToOne) {
|
|
727
|
+
for (const manyToOneItem of manyToOneItems) {
|
|
728
|
+
const { joinColumn } = manyToOneItem;
|
|
729
|
+
const mappingEntity = manyToOneItem.getMappingEntity();
|
|
730
|
+
if (!mappingEntity) {
|
|
731
|
+
throw new EntityNotFound_1.EntityNotFound(mappingEntity);
|
|
732
|
+
}
|
|
733
|
+
const mappingTableMetadata = entityScanner.scan(mappingEntity);
|
|
734
|
+
if (!mappingTableMetadata) {
|
|
735
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(mappingEntity.name);
|
|
736
|
+
}
|
|
737
|
+
if (!joinColumn) {
|
|
738
|
+
throw new InvalidQueryError_1.InvalidQueryError("JoinColumn does not exist.");
|
|
739
|
+
}
|
|
740
|
+
const mappingTablePrimaryKey = manyToOneItem.references
|
|
741
|
+
? manyToOneItem.references
|
|
742
|
+
: mappingTableMetadata.columns.find((e) => e.options?.primary)?.name;
|
|
743
|
+
if (!mappingTablePrimaryKey) {
|
|
744
|
+
throw new PrimaryKeyNotFoundError_1.PrimaryKeyNotFoundError(mappingEntity.name);
|
|
745
|
+
}
|
|
746
|
+
const mappingTableName = mappingTableMetadata.name || this.getNameStrategy(mappingEntity);
|
|
747
|
+
if (this.driver) {
|
|
748
|
+
const columnExists = await this.driver.hasColumn(tableName, joinColumn);
|
|
749
|
+
if (!columnExists) {
|
|
750
|
+
const fkColumnType = this.driver.castType("int") + " NULL";
|
|
751
|
+
await this.driver.addColumn(tableName, joinColumn, fkColumnType);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (this.driver) {
|
|
755
|
+
const fkName = this.driver.generateForeignKeyName(tableName, mappingTableName, joinColumn);
|
|
756
|
+
const fkExists = await this.driver.hasForeignKey(tableName, fkName);
|
|
757
|
+
if (fkExists)
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
await this.driver?.addForeignKey(tableName, joinColumn, mappingTableName, mappingTablePrimaryKey);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const oneToOneItems = this.resolveOneToOneMetadata(TargetEntity);
|
|
764
|
+
for (const oneToOneItem of oneToOneItems) {
|
|
765
|
+
const { joinColumn } = oneToOneItem;
|
|
766
|
+
if (!joinColumn)
|
|
767
|
+
continue;
|
|
768
|
+
const RelatedEntity = oneToOneItem.getRelatedEntity();
|
|
769
|
+
if (!RelatedEntity) {
|
|
770
|
+
throw new EntityNotFound_1.EntityNotFound(RelatedEntity);
|
|
771
|
+
}
|
|
772
|
+
const relatedMetadata = entityScanner.scan(RelatedEntity);
|
|
773
|
+
if (!relatedMetadata) {
|
|
774
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(RelatedEntity.name);
|
|
775
|
+
}
|
|
776
|
+
const relatedPrimaryKey = relatedMetadata.columns.find((e) => e.options?.primary)?.name;
|
|
777
|
+
if (!relatedPrimaryKey) {
|
|
778
|
+
throw new PrimaryKeyNotFoundError_1.PrimaryKeyNotFoundError(RelatedEntity.name);
|
|
779
|
+
}
|
|
780
|
+
if (this.driver) {
|
|
781
|
+
const columnExists = await this.driver.hasColumn(tableName, joinColumn);
|
|
782
|
+
if (!columnExists) {
|
|
783
|
+
const fkColumnType = this.driver.castType("int") + " NULL";
|
|
784
|
+
await this.driver.addColumn(tableName, joinColumn, fkColumnType);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
const relatedTableName = relatedMetadata.name || this.getNameStrategy(RelatedEntity);
|
|
788
|
+
if (this.driver) {
|
|
789
|
+
const fkName = this.driver.generateForeignKeyName(tableName, relatedTableName, joinColumn);
|
|
790
|
+
const fkExists = await this.driver.hasForeignKey(tableName, fkName);
|
|
791
|
+
if (fkExists)
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
await this.driver?.addForeignKey(tableName, joinColumn, relatedTableName, relatedPrimaryKey);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
async registerIndex(TargetEntity, tableName) {
|
|
798
|
+
const indexer = Reflect.getMetadata(Indexer_1.INDEX_TOKEN, TargetEntity.prototype);
|
|
799
|
+
if (indexer) {
|
|
800
|
+
for (const index of indexer) {
|
|
801
|
+
const indexName = `INDEX_${tableName}_${index.name}`;
|
|
802
|
+
const indexes = (await this.driver?.getIndexes(tableName));
|
|
803
|
+
let isExist = false;
|
|
804
|
+
for (const idx of indexes || []) {
|
|
805
|
+
const existingIndexName = idx["Key_name"] ?? idx["Field"];
|
|
806
|
+
if (existingIndexName === indexName) {
|
|
807
|
+
isExist = true;
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (!isExist) {
|
|
812
|
+
await this.driver?.addIndex(tableName, index.name, indexName);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
async findOne(entity, findOption) {
|
|
818
|
+
const result = await this.find(entity, { ...findOption, limit: 1 });
|
|
819
|
+
if (result === undefined || result === null) {
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
if (Array.isArray(result)) {
|
|
823
|
+
return result[0] ?? null;
|
|
824
|
+
}
|
|
825
|
+
return result;
|
|
826
|
+
}
|
|
827
|
+
async explain(entity, findOption = {}) {
|
|
828
|
+
if (!this.driver || !this.driver.supportsExplain()) {
|
|
829
|
+
throw new InvalidQueryError_1.InvalidQueryError("EXPLAIN is not supported by the current database driver.");
|
|
830
|
+
}
|
|
831
|
+
const { select, orderBy, where, take } = findOption;
|
|
832
|
+
const { limit } = findOption;
|
|
833
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
834
|
+
if (!metadata) {
|
|
835
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
836
|
+
}
|
|
837
|
+
const qb = RawQueryBuilderFactory_1.RawQueryBuilderFactory.create();
|
|
838
|
+
const selectMap = [];
|
|
839
|
+
const whereMap = [];
|
|
840
|
+
const orderByMap = [];
|
|
841
|
+
const manyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
842
|
+
const eagerRelations = manyToOneRelations.filter((rel) => {
|
|
843
|
+
const isEager = rel.option?.eager === true;
|
|
844
|
+
const isInRelations = findOption.relations?.includes(rel.columnName);
|
|
845
|
+
return isEager || isInRelations;
|
|
846
|
+
});
|
|
847
|
+
const oneToOneRelations = this.resolveOneToOneMetadata(entity);
|
|
848
|
+
const eagerOneToOneRelations = oneToOneRelations.filter((rel) => {
|
|
849
|
+
if (!rel.joinColumn)
|
|
850
|
+
return false;
|
|
851
|
+
const isEager = rel.option?.eager === true;
|
|
852
|
+
const isInRelations = findOption.relations?.includes(rel.propertyKey);
|
|
853
|
+
return isEager || isInRelations;
|
|
854
|
+
});
|
|
855
|
+
const hasEagerJoins = eagerRelations.length > 0 || eagerOneToOneRelations.length > 0;
|
|
856
|
+
const tableName = metadata.name;
|
|
857
|
+
if (select) {
|
|
858
|
+
const selectedColumns = this.resolveSelectColumns(select);
|
|
859
|
+
if (hasEagerJoins) {
|
|
860
|
+
selectMap.push(...selectedColumns.map((col) => `${this.wrap(tableName)}.${this.wrap(col)}`));
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
selectMap.push(...selectedColumns.map((col) => this.wrap(col)));
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
if (hasEagerJoins) {
|
|
868
|
+
selectMap.push(...metadata.columns.map((column) => `${this.wrap(tableName)}.${this.wrap(column.name)}`));
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
selectMap.push(...metadata.columns.map((column) => this.wrap(column.name)));
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
for (const key in where) {
|
|
875
|
+
const value = where[key];
|
|
876
|
+
if (value !== undefined && value !== null) {
|
|
877
|
+
if (hasEagerJoins) {
|
|
878
|
+
whereMap.push(Conditions_1.Conditions.equals(`${this.wrap(tableName)}.${this.wrap(key)}`, value));
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const deletedAtColumn = this.getDeletedAtColumn(entity);
|
|
886
|
+
if (deletedAtColumn && !findOption.withDeleted) {
|
|
887
|
+
if (hasEagerJoins) {
|
|
888
|
+
whereMap.push(Conditions_1.Conditions.isNull(`${this.wrap(tableName)}.${this.wrap(deletedAtColumn)}`));
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
whereMap.push(Conditions_1.Conditions.isNull(this.wrap(deletedAtColumn)));
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
for (const key in orderBy) {
|
|
895
|
+
const value = orderBy[key];
|
|
896
|
+
if (value) {
|
|
897
|
+
orderByMap.push({ column: this.wrap(key), direction: value });
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
qb.select(selectMap).from(this.wrap(tableName));
|
|
901
|
+
qb.where(whereMap).orderBy(orderByMap);
|
|
902
|
+
if (Array.isArray(limit)) {
|
|
903
|
+
let [offset, count] = limit;
|
|
904
|
+
if (offset < 0)
|
|
905
|
+
offset = 0;
|
|
906
|
+
if (count < 0)
|
|
907
|
+
count = 0;
|
|
908
|
+
if (count === 0)
|
|
909
|
+
count = 1;
|
|
910
|
+
if (take && take > 0)
|
|
911
|
+
count = take;
|
|
912
|
+
if (this.isMySqlFamily())
|
|
913
|
+
qb.setDatabaseType("mysql");
|
|
914
|
+
qb.limit([offset, count]);
|
|
915
|
+
}
|
|
916
|
+
else if (limit) {
|
|
917
|
+
qb.limit(limit);
|
|
918
|
+
}
|
|
919
|
+
const selectQuery = qb.build();
|
|
920
|
+
const explainPrefix = this.driver.buildExplainSql("");
|
|
921
|
+
const explainQuery = (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(explainPrefix)}${selectQuery}`;
|
|
922
|
+
const transactionHolder = new TransactionSessionManager_1.TransactionSessionManager();
|
|
923
|
+
try {
|
|
924
|
+
const readNode = this.getReadNode(findOption.useMaster);
|
|
925
|
+
if (readNode) {
|
|
926
|
+
await transactionHolder.connectToNode(readNode);
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
await transactionHolder.connect();
|
|
930
|
+
}
|
|
931
|
+
await transactionHolder.startTransaction();
|
|
932
|
+
if (this.isMySqlFamily()) {
|
|
933
|
+
await transactionHolder.query("SET autocommit = 0");
|
|
934
|
+
}
|
|
935
|
+
const result = await transactionHolder.query(explainQuery);
|
|
936
|
+
await transactionHolder.commit();
|
|
937
|
+
const rawRows = result?.results ?? [];
|
|
938
|
+
return this.parseExplainResult(rawRows);
|
|
939
|
+
}
|
|
940
|
+
catch (e) {
|
|
941
|
+
try {
|
|
942
|
+
await transactionHolder.rollback();
|
|
943
|
+
}
|
|
944
|
+
catch {
|
|
945
|
+
}
|
|
946
|
+
throw e;
|
|
947
|
+
}
|
|
948
|
+
finally {
|
|
949
|
+
try {
|
|
950
|
+
await transactionHolder.close();
|
|
951
|
+
}
|
|
952
|
+
catch {
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
parseExplainResult(rawRows) {
|
|
957
|
+
if (!rawRows || rawRows.length === 0) {
|
|
958
|
+
return {
|
|
959
|
+
raw: [],
|
|
960
|
+
rows: null,
|
|
961
|
+
type: null,
|
|
962
|
+
possibleKeys: null,
|
|
963
|
+
key: null,
|
|
964
|
+
cost: null,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
const firstRow = rawRows[0];
|
|
968
|
+
if (firstRow && "QUERY PLAN" in firstRow) {
|
|
969
|
+
return this.parsePostgresExplain(firstRow["QUERY PLAN"]);
|
|
970
|
+
}
|
|
971
|
+
if ("type" in firstRow || "select_type" in firstRow) {
|
|
972
|
+
return this.parseMysqlExplain(rawRows);
|
|
973
|
+
}
|
|
974
|
+
if ("detail" in firstRow || "notused" in firstRow) {
|
|
975
|
+
return this.parseSqliteExplain(rawRows);
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
raw: rawRows,
|
|
979
|
+
rows: null,
|
|
980
|
+
type: null,
|
|
981
|
+
possibleKeys: null,
|
|
982
|
+
key: null,
|
|
983
|
+
cost: null,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
parseMysqlExplain(rawRows) {
|
|
987
|
+
const first = rawRows[0];
|
|
988
|
+
const rows = first.rows != null ? Number(first.rows) : null;
|
|
989
|
+
const type = first.type != null ? String(first.type) : null;
|
|
990
|
+
const possibleKeysRaw = first.possible_keys;
|
|
991
|
+
const possibleKeys = possibleKeysRaw != null
|
|
992
|
+
? String(possibleKeysRaw)
|
|
993
|
+
.split(",")
|
|
994
|
+
.map((k) => k.trim())
|
|
995
|
+
: null;
|
|
996
|
+
const key = first.key != null ? String(first.key) : null;
|
|
997
|
+
const cost = first.filtered != null ? Number(first.filtered) : null;
|
|
998
|
+
return { raw: rawRows, rows, type, possibleKeys, key, cost };
|
|
999
|
+
}
|
|
1000
|
+
parsePostgresExplain(queryPlan) {
|
|
1001
|
+
const rawArray = Array.isArray(queryPlan) ? queryPlan : [queryPlan];
|
|
1002
|
+
const plan = rawArray[0]?.Plan ?? rawArray[0]?.["Plan"] ?? null;
|
|
1003
|
+
if (!plan) {
|
|
1004
|
+
return {
|
|
1005
|
+
raw: rawArray,
|
|
1006
|
+
rows: null,
|
|
1007
|
+
type: null,
|
|
1008
|
+
possibleKeys: null,
|
|
1009
|
+
key: null,
|
|
1010
|
+
cost: null,
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
const rows = plan["Plan Rows"] != null ? Number(plan["Plan Rows"]) : null;
|
|
1014
|
+
const type = plan["Node Type"] != null ? String(plan["Node Type"]) : null;
|
|
1015
|
+
const key = plan["Index Name"] != null ? String(plan["Index Name"]) : null;
|
|
1016
|
+
const cost = plan["Total Cost"] != null ? Number(plan["Total Cost"]) : null;
|
|
1017
|
+
return { raw: rawArray, rows, type, possibleKeys: null, key, cost };
|
|
1018
|
+
}
|
|
1019
|
+
parseSqliteExplain(rawRows) {
|
|
1020
|
+
const details = rawRows.map((r) => String(r.detail ?? ""));
|
|
1021
|
+
const firstDetail = details[0] ?? "";
|
|
1022
|
+
let type = null;
|
|
1023
|
+
let key = null;
|
|
1024
|
+
if (firstDetail.startsWith("SCAN"))
|
|
1025
|
+
type = "SCAN";
|
|
1026
|
+
else if (firstDetail.startsWith("SEARCH"))
|
|
1027
|
+
type = "SEARCH";
|
|
1028
|
+
const indexMatch = firstDetail.match(/USING (?:COVERING )?INDEX (\S+)/);
|
|
1029
|
+
if (indexMatch)
|
|
1030
|
+
key = indexMatch[1];
|
|
1031
|
+
return {
|
|
1032
|
+
raw: rawRows,
|
|
1033
|
+
rows: null,
|
|
1034
|
+
type,
|
|
1035
|
+
possibleKeys: null,
|
|
1036
|
+
key,
|
|
1037
|
+
cost: null,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
async findWithCursor(entity, option = {}) {
|
|
1041
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1042
|
+
if (!metadata) {
|
|
1043
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1044
|
+
}
|
|
1045
|
+
const pk = metadata.columns.find((column) => column.options?.primary);
|
|
1046
|
+
const orderByColumn = option.orderBy ?? pk?.name;
|
|
1047
|
+
if (!orderByColumn) {
|
|
1048
|
+
throw new InvalidQueryError_1.InvalidQueryError("Cursor pagination requires an orderBy column or a primary key.");
|
|
1049
|
+
}
|
|
1050
|
+
const direction = option.direction ?? "ASC";
|
|
1051
|
+
const pageSize = (0, CursorPagination_1.normalizePageSize)(option.take);
|
|
1052
|
+
let cursorValue = null;
|
|
1053
|
+
if (option.cursor) {
|
|
1054
|
+
cursorValue = (0, CursorPagination_1.decodeCursor)(option.cursor);
|
|
1055
|
+
if (cursorValue === null) {
|
|
1056
|
+
throw new InvalidQueryError_1.InvalidQueryError("Invalid cursor value.");
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const where = { ...(option.where ?? {}) };
|
|
1060
|
+
const transactionHolder = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1061
|
+
const resultTransformer = ResultTransformerFactory_1.ResultTransformerFactory.create();
|
|
1062
|
+
try {
|
|
1063
|
+
const readNode = this.getReadNode(option.useMaster);
|
|
1064
|
+
if (readNode) {
|
|
1065
|
+
await transactionHolder.connectToNode(readNode);
|
|
1066
|
+
}
|
|
1067
|
+
else {
|
|
1068
|
+
await transactionHolder.connect();
|
|
1069
|
+
}
|
|
1070
|
+
await transactionHolder.startTransaction();
|
|
1071
|
+
if (this.isMySqlFamily()) {
|
|
1072
|
+
await transactionHolder.query("SET autocommit = 0");
|
|
1073
|
+
}
|
|
1074
|
+
const tableName = metadata.name;
|
|
1075
|
+
const qb = RawQueryBuilderFactory_1.RawQueryBuilderFactory.create();
|
|
1076
|
+
const selectMap = metadata.columns.map((column) => this.wrap(column.name));
|
|
1077
|
+
const whereMap = [];
|
|
1078
|
+
for (const key in where) {
|
|
1079
|
+
const value = where[key];
|
|
1080
|
+
if (value !== undefined && value !== null) {
|
|
1081
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
const deletedAtColumn = this.getDeletedAtColumn(entity);
|
|
1085
|
+
if (deletedAtColumn) {
|
|
1086
|
+
whereMap.push(Conditions_1.Conditions.isNull(this.wrap(deletedAtColumn)));
|
|
1087
|
+
}
|
|
1088
|
+
if (cursorValue !== null) {
|
|
1089
|
+
if (direction === "ASC") {
|
|
1090
|
+
whereMap.push(Conditions_1.Conditions.gt(this.wrap(orderByColumn), cursorValue));
|
|
1091
|
+
}
|
|
1092
|
+
else {
|
|
1093
|
+
whereMap.push(Conditions_1.Conditions.lt(this.wrap(orderByColumn), cursorValue));
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
qb.select(selectMap)
|
|
1097
|
+
.from(this.wrap(tableName))
|
|
1098
|
+
.where(whereMap)
|
|
1099
|
+
.orderBy([{ column: this.wrap(orderByColumn), direction }]);
|
|
1100
|
+
qb.limit(pageSize + 1);
|
|
1101
|
+
const resultQuery = qb.build();
|
|
1102
|
+
const queryResult = (await transactionHolder.query(resultQuery));
|
|
1103
|
+
await transactionHolder.commit();
|
|
1104
|
+
const { results } = queryResult;
|
|
1105
|
+
if (!results || results.length === 0) {
|
|
1106
|
+
return {
|
|
1107
|
+
data: [],
|
|
1108
|
+
hasNextPage: false,
|
|
1109
|
+
nextCursor: null,
|
|
1110
|
+
count: 0,
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
const hasNextPage = results.length > pageSize;
|
|
1114
|
+
const pageResults = hasNextPage ? results.slice(0, pageSize) : results;
|
|
1115
|
+
const entities = resultTransformer.toEntities(entity, {
|
|
1116
|
+
results: pageResults,
|
|
1117
|
+
fields: queryResult.fields,
|
|
1118
|
+
});
|
|
1119
|
+
let nextCursor = null;
|
|
1120
|
+
if (hasNextPage && pageResults.length > 0) {
|
|
1121
|
+
const lastItem = pageResults[pageResults.length - 1];
|
|
1122
|
+
const lastValue = lastItem[orderByColumn];
|
|
1123
|
+
nextCursor = (0, CursorPagination_1.encodeCursor)(lastValue);
|
|
1124
|
+
}
|
|
1125
|
+
return {
|
|
1126
|
+
data: entities,
|
|
1127
|
+
hasNextPage,
|
|
1128
|
+
nextCursor,
|
|
1129
|
+
count: entities.length,
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
catch (e) {
|
|
1133
|
+
try {
|
|
1134
|
+
await transactionHolder.rollback();
|
|
1135
|
+
}
|
|
1136
|
+
catch (rollbackError) {
|
|
1137
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1138
|
+
}
|
|
1139
|
+
throw e;
|
|
1140
|
+
}
|
|
1141
|
+
finally {
|
|
1142
|
+
try {
|
|
1143
|
+
await transactionHolder.close();
|
|
1144
|
+
}
|
|
1145
|
+
catch (closeError) {
|
|
1146
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
async find(entity, findOption = {}) {
|
|
1151
|
+
const { select, orderBy, where, take, groupBy, having } = findOption;
|
|
1152
|
+
const { limit } = findOption;
|
|
1153
|
+
const transactionHolder = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1154
|
+
const resultTransformer = ResultTransformerFactory_1.ResultTransformerFactory.create();
|
|
1155
|
+
try {
|
|
1156
|
+
const readNode = this.getReadNode(findOption.useMaster);
|
|
1157
|
+
if (readNode) {
|
|
1158
|
+
await transactionHolder.connectToNode(readNode);
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
await transactionHolder.connect();
|
|
1162
|
+
}
|
|
1163
|
+
await transactionHolder.startTransaction();
|
|
1164
|
+
if (this.isMySqlFamily()) {
|
|
1165
|
+
await transactionHolder.query("SET autocommit = 0");
|
|
1166
|
+
}
|
|
1167
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1168
|
+
if (!metadata) {
|
|
1169
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1170
|
+
}
|
|
1171
|
+
const qb = RawQueryBuilderFactory_1.RawQueryBuilderFactory.create();
|
|
1172
|
+
const selectMap = [];
|
|
1173
|
+
const whereMap = [];
|
|
1174
|
+
const orderByMap = [];
|
|
1175
|
+
const manyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
1176
|
+
const eagerRelations = manyToOneRelations.filter((rel) => {
|
|
1177
|
+
const isEager = rel.option?.eager === true;
|
|
1178
|
+
const isInRelations = findOption.relations?.includes(rel.columnName);
|
|
1179
|
+
return isEager || isInRelations;
|
|
1180
|
+
});
|
|
1181
|
+
const oneToOneRelations = this.resolveOneToOneMetadata(entity);
|
|
1182
|
+
const eagerOneToOneRelations = oneToOneRelations.filter((rel) => {
|
|
1183
|
+
if (!rel.joinColumn)
|
|
1184
|
+
return false;
|
|
1185
|
+
const isEager = rel.option?.eager === true;
|
|
1186
|
+
const isInRelations = findOption.relations?.includes(rel.propertyKey);
|
|
1187
|
+
return isEager || isInRelations;
|
|
1188
|
+
});
|
|
1189
|
+
const hasEagerJoins = eagerRelations.length > 0 || eagerOneToOneRelations.length > 0;
|
|
1190
|
+
const tableName = metadata.name;
|
|
1191
|
+
if (select) {
|
|
1192
|
+
const selectedColumns = this.resolveSelectColumns(select);
|
|
1193
|
+
if (hasEagerJoins) {
|
|
1194
|
+
selectMap.push(...selectedColumns.map((col) => `${this.wrap(tableName)}.${this.wrap(col)}`));
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
selectMap.push(...selectedColumns.map((col) => this.wrap(col)));
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
if (hasEagerJoins) {
|
|
1202
|
+
selectMap.push(...metadata.columns.map((column) => `${this.wrap(tableName)}.${this.wrap(column.name)}`));
|
|
1203
|
+
}
|
|
1204
|
+
else {
|
|
1205
|
+
selectMap.push(...metadata.columns.map((column) => this.wrap(column.name)));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
for (const rel of eagerRelations) {
|
|
1209
|
+
const RelatedEntity = rel.getMappingEntity();
|
|
1210
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
1211
|
+
if (!relatedMetadata)
|
|
1212
|
+
continue;
|
|
1213
|
+
const relatedName = relatedMetadata.name || RelatedEntity.name;
|
|
1214
|
+
for (const col of relatedMetadata.columns) {
|
|
1215
|
+
const alias = `${rel.columnName}_${col.name}`;
|
|
1216
|
+
selectMap.push(`${this.wrap(relatedName)}.${this.wrap(col.name)} AS ${this.wrap(alias)}`);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
for (const rel of eagerOneToOneRelations) {
|
|
1220
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
1221
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
1222
|
+
if (!relatedMetadata)
|
|
1223
|
+
continue;
|
|
1224
|
+
const relatedName = relatedMetadata.name || RelatedEntity.name;
|
|
1225
|
+
for (const col of relatedMetadata.columns) {
|
|
1226
|
+
const alias = `${rel.propertyKey}_${col.name}`;
|
|
1227
|
+
selectMap.push(`${this.wrap(relatedName)}.${this.wrap(col.name)} AS ${this.wrap(alias)}`);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
for (const key in where) {
|
|
1231
|
+
const value = where[key];
|
|
1232
|
+
if (value !== undefined && value !== null) {
|
|
1233
|
+
if (hasEagerJoins) {
|
|
1234
|
+
whereMap.push(Conditions_1.Conditions.equals(`${this.wrap(tableName)}.${this.wrap(key)}`, value));
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
const deletedAtColumn = this.getDeletedAtColumn(entity);
|
|
1242
|
+
if (deletedAtColumn && !findOption.withDeleted) {
|
|
1243
|
+
if (hasEagerJoins) {
|
|
1244
|
+
whereMap.push(Conditions_1.Conditions.isNull(`${this.wrap(tableName)}.${this.wrap(deletedAtColumn)}`));
|
|
1245
|
+
}
|
|
1246
|
+
else {
|
|
1247
|
+
whereMap.push(Conditions_1.Conditions.isNull(this.wrap(deletedAtColumn)));
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
for (const key in orderBy) {
|
|
1251
|
+
const value = orderBy[key];
|
|
1252
|
+
if (value) {
|
|
1253
|
+
orderByMap.push({ column: this.wrap(key), direction: value });
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
qb.select(selectMap).from(this.wrap(tableName));
|
|
1257
|
+
for (const rel of eagerRelations) {
|
|
1258
|
+
const RelatedEntity = rel.getMappingEntity();
|
|
1259
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
1260
|
+
if (!relatedMetadata)
|
|
1261
|
+
continue;
|
|
1262
|
+
const relatedTableName = relatedMetadata.name || RelatedEntity.name;
|
|
1263
|
+
const joinColumn = rel.joinColumn ?? rel.columnName;
|
|
1264
|
+
const relatedPk = relatedMetadata.columns.find((col) => col.options?.primary);
|
|
1265
|
+
if (!relatedPk)
|
|
1266
|
+
continue;
|
|
1267
|
+
const joinCondition = (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(tableName))}.${(0, sql_template_tag_1.raw)(this.wrap(joinColumn))} = ${(0, sql_template_tag_1.raw)(this.wrap(relatedTableName))}.${(0, sql_template_tag_1.raw)(this.wrap(relatedPk.name))}`;
|
|
1268
|
+
qb.leftJoin(this.wrap(relatedTableName), this.wrap(relatedTableName), joinCondition);
|
|
1269
|
+
}
|
|
1270
|
+
for (const rel of eagerOneToOneRelations) {
|
|
1271
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
1272
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
1273
|
+
if (!relatedMetadata)
|
|
1274
|
+
continue;
|
|
1275
|
+
const relatedTableName = relatedMetadata.name || RelatedEntity.name;
|
|
1276
|
+
const joinColumn = rel.joinColumn;
|
|
1277
|
+
const relatedPk = relatedMetadata.columns.find((col) => col.options?.primary);
|
|
1278
|
+
if (!relatedPk)
|
|
1279
|
+
continue;
|
|
1280
|
+
const joinCondition = (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(tableName))}.${(0, sql_template_tag_1.raw)(this.wrap(joinColumn))} = ${(0, sql_template_tag_1.raw)(this.wrap(relatedTableName))}.${(0, sql_template_tag_1.raw)(this.wrap(relatedPk.name))}`;
|
|
1281
|
+
qb.leftJoin(this.wrap(relatedTableName), this.wrap(relatedTableName), joinCondition);
|
|
1282
|
+
}
|
|
1283
|
+
qb.where(whereMap);
|
|
1284
|
+
if (groupBy && groupBy.length > 0) {
|
|
1285
|
+
const groupByColumns = groupBy.map((col) => hasEagerJoins
|
|
1286
|
+
? `${this.wrap(tableName)}.${this.wrap(col)}`
|
|
1287
|
+
: this.wrap(col));
|
|
1288
|
+
qb.groupBy(groupByColumns);
|
|
1289
|
+
}
|
|
1290
|
+
if (having && having.length > 0) {
|
|
1291
|
+
qb.having(having);
|
|
1292
|
+
}
|
|
1293
|
+
qb.orderBy(orderByMap);
|
|
1294
|
+
if (Array.isArray(limit)) {
|
|
1295
|
+
let [offset, count] = limit;
|
|
1296
|
+
if (offset < 0) {
|
|
1297
|
+
offset = 0;
|
|
1298
|
+
}
|
|
1299
|
+
if (count < 0) {
|
|
1300
|
+
count = 0;
|
|
1301
|
+
}
|
|
1302
|
+
if (count === 0) {
|
|
1303
|
+
count = 1;
|
|
1304
|
+
}
|
|
1305
|
+
if (take && take > 0) {
|
|
1306
|
+
count = take;
|
|
1307
|
+
}
|
|
1308
|
+
if (this.isMySqlFamily()) {
|
|
1309
|
+
qb.setDatabaseType("mysql");
|
|
1310
|
+
}
|
|
1311
|
+
qb.limit([offset, count]);
|
|
1312
|
+
}
|
|
1313
|
+
else {
|
|
1314
|
+
if (limit) {
|
|
1315
|
+
qb.limit(limit);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
const resultQuery = qb.build();
|
|
1319
|
+
const effectiveTimeout = findOption.timeout ?? this.defaultQueryTimeout;
|
|
1320
|
+
if (effectiveTimeout && effectiveTimeout > 0 && this.driver) {
|
|
1321
|
+
const timeoutSql = this.driver.setQueryTimeout(effectiveTimeout);
|
|
1322
|
+
await transactionHolder.query(timeoutSql);
|
|
1323
|
+
}
|
|
1324
|
+
const queryStartTime = Date.now();
|
|
1325
|
+
this.beginTrackQuery();
|
|
1326
|
+
const queryResult = (await transactionHolder.query(resultQuery));
|
|
1327
|
+
this.trackQuery(entity.name, resultQuery.text ?? String(resultQuery), Date.now() - queryStartTime);
|
|
1328
|
+
await transactionHolder.commit();
|
|
1329
|
+
const { results } = queryResult;
|
|
1330
|
+
if (!results || results.length === 0) {
|
|
1331
|
+
return undefined;
|
|
1332
|
+
}
|
|
1333
|
+
const isEntityArray = results.length > 1;
|
|
1334
|
+
let entityResult;
|
|
1335
|
+
if (hasEagerJoins) {
|
|
1336
|
+
entityResult = resultTransformer.transformNested(entity, queryResult);
|
|
1337
|
+
}
|
|
1338
|
+
else if (isEntityArray) {
|
|
1339
|
+
entityResult = resultTransformer.toEntities(entity, queryResult);
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
entityResult = resultTransformer.toEntity(entity, queryResult);
|
|
1343
|
+
}
|
|
1344
|
+
if (findOption.relations &&
|
|
1345
|
+
findOption.relations.length > 0 &&
|
|
1346
|
+
entityResult) {
|
|
1347
|
+
await this.loadOneToManyRelations(entity, entityResult, findOption.relations);
|
|
1348
|
+
await this.loadManyToManyRelations(entity, entityResult, findOption.relations);
|
|
1349
|
+
await this.loadOneToOneRelations(entity, entityResult, findOption.relations);
|
|
1350
|
+
}
|
|
1351
|
+
const lazyRelations = manyToOneRelations.filter((rel) => {
|
|
1352
|
+
return rel.option?.lazy === true && rel.option?.eager !== true;
|
|
1353
|
+
});
|
|
1354
|
+
if (lazyRelations.length > 0 && entityResult) {
|
|
1355
|
+
const entities = Array.isArray(entityResult)
|
|
1356
|
+
? entityResult
|
|
1357
|
+
: [entityResult];
|
|
1358
|
+
for (const rel of lazyRelations) {
|
|
1359
|
+
const joinColumn = rel.joinColumn ?? rel.columnName;
|
|
1360
|
+
const RelatedEntity = rel.getMappingEntity();
|
|
1361
|
+
for (const item of entities) {
|
|
1362
|
+
const fkValue = item[joinColumn];
|
|
1363
|
+
if (fkValue === undefined || fkValue === null)
|
|
1364
|
+
continue;
|
|
1365
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
1366
|
+
if (!relatedMetadata)
|
|
1367
|
+
continue;
|
|
1368
|
+
const relatedPk = relatedMetadata.columns.find((col) => col.options?.primary);
|
|
1369
|
+
if (!relatedPk)
|
|
1370
|
+
continue;
|
|
1371
|
+
const em = this;
|
|
1372
|
+
(0, LazyLoader_1.injectLazyProxy)(item, rel.columnName, async () => {
|
|
1373
|
+
const result = await em.findOne(RelatedEntity, {
|
|
1374
|
+
where: { [relatedPk.name]: fkValue },
|
|
1375
|
+
});
|
|
1376
|
+
return result;
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
return entityResult;
|
|
1382
|
+
}
|
|
1383
|
+
catch (e) {
|
|
1384
|
+
try {
|
|
1385
|
+
await transactionHolder.rollback();
|
|
1386
|
+
}
|
|
1387
|
+
catch (rollbackError) {
|
|
1388
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1389
|
+
}
|
|
1390
|
+
throw e;
|
|
1391
|
+
}
|
|
1392
|
+
finally {
|
|
1393
|
+
try {
|
|
1394
|
+
await transactionHolder.close();
|
|
1395
|
+
}
|
|
1396
|
+
catch (closeError) {
|
|
1397
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
resolveSelectColumns(select) {
|
|
1402
|
+
if (Array.isArray(select)) {
|
|
1403
|
+
return select.map((col) => String(col));
|
|
1404
|
+
}
|
|
1405
|
+
const columns = [];
|
|
1406
|
+
for (const key in select) {
|
|
1407
|
+
if (select[key] === true) {
|
|
1408
|
+
columns.push(key);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
return columns;
|
|
1412
|
+
}
|
|
1413
|
+
wrap(columnName) {
|
|
1414
|
+
if (this.driver && "wrap" in this.driver) {
|
|
1415
|
+
return this.driver.wrap(columnName);
|
|
1416
|
+
}
|
|
1417
|
+
if (this.isPostgres()) {
|
|
1418
|
+
return `"${columnName.replace(/"/g, '""')}"`;
|
|
1419
|
+
}
|
|
1420
|
+
return `\`${columnName.replace(/`/g, "``")}\``;
|
|
1421
|
+
}
|
|
1422
|
+
isMySqlFamily() {
|
|
1423
|
+
const t = this.dbType ?? this.client.type;
|
|
1424
|
+
return ["mysql", "mariadb"].includes(t);
|
|
1425
|
+
}
|
|
1426
|
+
isPostgres() {
|
|
1427
|
+
const t = this.dbType ?? this.client.type;
|
|
1428
|
+
return t === "postgres";
|
|
1429
|
+
}
|
|
1430
|
+
async save(entity, item) {
|
|
1431
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1432
|
+
if (!metadata) {
|
|
1433
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1434
|
+
}
|
|
1435
|
+
EntityValidator_1.EntityValidator.validate(entity, item);
|
|
1436
|
+
await this.cascadeSaveManyToOne(entity, item);
|
|
1437
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1438
|
+
try {
|
|
1439
|
+
await transactionManager.connect();
|
|
1440
|
+
await transactionManager.startTransaction();
|
|
1441
|
+
if (this.isMySqlFamily()) {
|
|
1442
|
+
await transactionManager.query("SET autocommit = 0");
|
|
1443
|
+
}
|
|
1444
|
+
const pkColumns = metadata.columns.filter((column) => column.options?.primary);
|
|
1445
|
+
const pk = pkColumns[0];
|
|
1446
|
+
const hasAutoIncrementPk = pkColumns.some((col) => col.options?.autoIncrement);
|
|
1447
|
+
const primaryKeyValue = pk ? item[pk.name] : undefined;
|
|
1448
|
+
const isInsert = hasAutoIncrementPk
|
|
1449
|
+
? !primaryKeyValue
|
|
1450
|
+
: true;
|
|
1451
|
+
const buildPkWhere = (pkValues) => {
|
|
1452
|
+
return pkColumns.map((col) => {
|
|
1453
|
+
const value = pkValues
|
|
1454
|
+
? pkValues[col.name]
|
|
1455
|
+
: item[col.name];
|
|
1456
|
+
return (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(col.name))} = ${value}`;
|
|
1457
|
+
});
|
|
1458
|
+
};
|
|
1459
|
+
const buildPkFindWhere = (pkValues) => {
|
|
1460
|
+
const where = {};
|
|
1461
|
+
for (const col of pkColumns) {
|
|
1462
|
+
where[col.name] = pkValues
|
|
1463
|
+
? pkValues[col.name]
|
|
1464
|
+
: item[col.name];
|
|
1465
|
+
}
|
|
1466
|
+
return where;
|
|
1467
|
+
};
|
|
1468
|
+
if (isInsert) {
|
|
1469
|
+
await this.runHooks(entity, item, "beforeInsert");
|
|
1470
|
+
await this.eventEmitter.emit("beforeInsert", { entity, data: item });
|
|
1471
|
+
await this.notifySubscribers(entity, "beforeInsert", {
|
|
1472
|
+
entity: item,
|
|
1473
|
+
manager: this,
|
|
1474
|
+
});
|
|
1475
|
+
const insertableColumns = metadata.columns.filter((column) => {
|
|
1476
|
+
const isAutoIncrement = column.options?.autoIncrement;
|
|
1477
|
+
const value = item[column.name];
|
|
1478
|
+
if (isAutoIncrement && (value === null || value === undefined)) {
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
return true;
|
|
1482
|
+
});
|
|
1483
|
+
const columns = insertableColumns.map((column) => {
|
|
1484
|
+
return (0, sql_template_tag_1.raw)(this.wrap(column.name));
|
|
1485
|
+
});
|
|
1486
|
+
const values = insertableColumns.map((column) => {
|
|
1487
|
+
return item[column.name];
|
|
1488
|
+
});
|
|
1489
|
+
const manyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
1490
|
+
for (const rel of manyToOneRelations) {
|
|
1491
|
+
if (!rel.joinColumn)
|
|
1492
|
+
continue;
|
|
1493
|
+
const relatedValue = item[rel.columnName];
|
|
1494
|
+
const idPropValue = item[`${rel.columnName}Id`];
|
|
1495
|
+
const existingIdx = insertableColumns.findIndex((col) => col.name === rel.joinColumn);
|
|
1496
|
+
let fkValue = undefined;
|
|
1497
|
+
if (relatedValue === null) {
|
|
1498
|
+
fkValue = null;
|
|
1499
|
+
}
|
|
1500
|
+
else if (relatedValue && typeof relatedValue === "object") {
|
|
1501
|
+
const RelatedEntity = rel.getMappingEntity();
|
|
1502
|
+
const relatedMeta = this.resolveEntityMetadata(RelatedEntity);
|
|
1503
|
+
if (relatedMeta) {
|
|
1504
|
+
const relatedPk = relatedMeta.columns.find((col) => col.options?.primary);
|
|
1505
|
+
if (relatedPk) {
|
|
1506
|
+
fkValue = relatedValue[relatedPk.name] ?? undefined;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
else if (idPropValue != null) {
|
|
1511
|
+
fkValue = idPropValue;
|
|
1512
|
+
}
|
|
1513
|
+
if (fkValue !== undefined) {
|
|
1514
|
+
if (existingIdx >= 0) {
|
|
1515
|
+
values[existingIdx] = fkValue;
|
|
1516
|
+
}
|
|
1517
|
+
else {
|
|
1518
|
+
columns.push((0, sql_template_tag_1.raw)(this.wrap(rel.joinColumn)));
|
|
1519
|
+
values.push(fkValue);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
const isPostgres = this.isPostgres();
|
|
1524
|
+
const returningCols = pkColumns
|
|
1525
|
+
.map((col) => this.wrap(col.name))
|
|
1526
|
+
.join(", ");
|
|
1527
|
+
const returningSql = isPostgres
|
|
1528
|
+
? (0, sql_template_tag_1.raw)(` RETURNING ${returningCols}`)
|
|
1529
|
+
: (0, sql_template_tag_1.raw)("");
|
|
1530
|
+
const insertSql = (0, sql_template_tag_1.default) `
|
|
1531
|
+
INSERT INTO ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))}
|
|
1532
|
+
(${(0, sql_template_tag_1.join)(columns, ", ")})
|
|
1533
|
+
VALUES (${(0, sql_template_tag_1.join)(values, ", ")})${returningSql}
|
|
1534
|
+
`;
|
|
1535
|
+
const saveQueryStart = Date.now();
|
|
1536
|
+
this.beginTrackQuery();
|
|
1537
|
+
const queryResult = (await transactionManager.query(insertSql));
|
|
1538
|
+
this.trackQuery(entity.name, insertSql.text ?? String(insertSql), Date.now() - saveQueryStart);
|
|
1539
|
+
await transactionManager.commit();
|
|
1540
|
+
if (this.isMySqlFamily()) {
|
|
1541
|
+
const findWhere = hasAutoIncrementPk
|
|
1542
|
+
? { [pk.name]: queryResult?.results?.insertId }
|
|
1543
|
+
: buildPkFindWhere();
|
|
1544
|
+
const result = await this.findOne(entity, {
|
|
1545
|
+
where: findWhere,
|
|
1546
|
+
});
|
|
1547
|
+
const cascadeId = hasAutoIncrementPk
|
|
1548
|
+
? queryResult?.results?.insertId
|
|
1549
|
+
: primaryKeyValue;
|
|
1550
|
+
await this.cascadeSaveOneToMany(entity, item, cascadeId);
|
|
1551
|
+
await this.runHooks(entity, item, "afterInsert");
|
|
1552
|
+
await this.eventEmitter.emit("afterInsert", { entity, data: item });
|
|
1553
|
+
await this.notifySubscribers(entity, "afterInsert", {
|
|
1554
|
+
entity: item,
|
|
1555
|
+
manager: this,
|
|
1556
|
+
});
|
|
1557
|
+
return result;
|
|
1558
|
+
}
|
|
1559
|
+
if (isPostgres && queryResult?.results?.length > 0) {
|
|
1560
|
+
const returnedRow = queryResult.results[0];
|
|
1561
|
+
const findWhere = buildPkFindWhere(returnedRow);
|
|
1562
|
+
const result = await this.findOne(entity, {
|
|
1563
|
+
where: findWhere,
|
|
1564
|
+
});
|
|
1565
|
+
const cascadeId = returnedRow[pk.name];
|
|
1566
|
+
await this.cascadeSaveOneToMany(entity, item, cascadeId);
|
|
1567
|
+
await this.runHooks(entity, item, "afterInsert");
|
|
1568
|
+
await this.eventEmitter.emit("afterInsert", { entity, data: item });
|
|
1569
|
+
await this.notifySubscribers(entity, "afterInsert", {
|
|
1570
|
+
entity: item,
|
|
1571
|
+
manager: this,
|
|
1572
|
+
});
|
|
1573
|
+
return result;
|
|
1574
|
+
}
|
|
1575
|
+
await this.runHooks(entity, item, "afterInsert");
|
|
1576
|
+
await this.eventEmitter.emit("afterInsert", { entity, data: item });
|
|
1577
|
+
await this.notifySubscribers(entity, "afterInsert", {
|
|
1578
|
+
entity: item,
|
|
1579
|
+
manager: this,
|
|
1580
|
+
});
|
|
1581
|
+
return queryResult;
|
|
1582
|
+
}
|
|
1583
|
+
await this.runHooks(entity, item, "beforeUpdate");
|
|
1584
|
+
await this.eventEmitter.emit("beforeUpdate", { entity, data: item });
|
|
1585
|
+
await this.notifySubscribers(entity, "beforeUpdate", {
|
|
1586
|
+
entity: item,
|
|
1587
|
+
manager: this,
|
|
1588
|
+
});
|
|
1589
|
+
const pkColumnNames = new Set(pkColumns.map((col) => col.name));
|
|
1590
|
+
const updatableColumns = metadata.columns.filter((column) => {
|
|
1591
|
+
if (pkColumnNames.has(column.name))
|
|
1592
|
+
return false;
|
|
1593
|
+
return item[column.name] !== undefined;
|
|
1594
|
+
});
|
|
1595
|
+
const updateMap = updatableColumns.map((column) => {
|
|
1596
|
+
return (0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(column.name))} = ${item[column.name]}`;
|
|
1597
|
+
});
|
|
1598
|
+
const updatedColumnNames = new Set(updatableColumns.map((col) => col.name));
|
|
1599
|
+
const updateManyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
1600
|
+
for (const rel of updateManyToOneRelations) {
|
|
1601
|
+
if (!rel.joinColumn)
|
|
1602
|
+
continue;
|
|
1603
|
+
const relatedValue = item[rel.columnName];
|
|
1604
|
+
if (relatedValue === undefined)
|
|
1605
|
+
continue;
|
|
1606
|
+
const alreadyInSet = updatedColumnNames.has(rel.joinColumn);
|
|
1607
|
+
if (relatedValue === null) {
|
|
1608
|
+
if (alreadyInSet) {
|
|
1609
|
+
const existingIdx = updatableColumns.findIndex((col) => col.name === rel.joinColumn);
|
|
1610
|
+
updateMap[existingIdx] =
|
|
1611
|
+
(0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(rel.joinColumn))} = ${null}`;
|
|
1612
|
+
}
|
|
1613
|
+
else {
|
|
1614
|
+
updateMap.push((0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(rel.joinColumn))} = ${null}`);
|
|
1615
|
+
updatedColumnNames.add(rel.joinColumn);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
else if (typeof relatedValue === "object") {
|
|
1619
|
+
const RelatedEntity = rel.getMappingEntity();
|
|
1620
|
+
const relatedMeta = this.resolveEntityMetadata(RelatedEntity);
|
|
1621
|
+
if (relatedMeta) {
|
|
1622
|
+
const relatedPk = relatedMeta.columns.find((col) => col.options?.primary);
|
|
1623
|
+
if (relatedPk) {
|
|
1624
|
+
const fkValue = relatedValue[relatedPk.name];
|
|
1625
|
+
if (fkValue !== undefined && fkValue !== null) {
|
|
1626
|
+
if (alreadyInSet) {
|
|
1627
|
+
const existingIdx = updatableColumns.findIndex((col) => col.name === rel.joinColumn);
|
|
1628
|
+
updateMap[existingIdx] =
|
|
1629
|
+
(0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(rel.joinColumn))} = ${fkValue}`;
|
|
1630
|
+
}
|
|
1631
|
+
else {
|
|
1632
|
+
updateMap.push((0, sql_template_tag_1.default) `${(0, sql_template_tag_1.raw)(this.wrap(rel.joinColumn))} = ${fkValue}`);
|
|
1633
|
+
updatedColumnNames.add(rel.joinColumn);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
const pkWhereClauses = buildPkWhere();
|
|
1641
|
+
if (updateMap.length > 0) {
|
|
1642
|
+
const updateSql = (0, sql_template_tag_1.default) `
|
|
1643
|
+
UPDATE ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))}
|
|
1644
|
+
SET ${(0, sql_template_tag_1.join)(updateMap, ", ")}
|
|
1645
|
+
WHERE ${(0, sql_template_tag_1.join)(pkWhereClauses, " AND ")}
|
|
1646
|
+
`;
|
|
1647
|
+
const updateStart = Date.now();
|
|
1648
|
+
this.beginTrackQuery();
|
|
1649
|
+
await transactionManager.query(updateSql);
|
|
1650
|
+
this.trackQuery(entity.name, updateSql.text ?? String(updateSql), Date.now() - updateStart);
|
|
1651
|
+
}
|
|
1652
|
+
await transactionManager.commit();
|
|
1653
|
+
await this.cascadeSaveOneToMany(entity, item, primaryKeyValue);
|
|
1654
|
+
await this.runHooks(entity, item, "afterUpdate");
|
|
1655
|
+
await this.eventEmitter.emit("afterUpdate", { entity, data: item });
|
|
1656
|
+
await this.notifySubscribers(entity, "afterUpdate", {
|
|
1657
|
+
entity: item,
|
|
1658
|
+
manager: this,
|
|
1659
|
+
});
|
|
1660
|
+
const result = await this.findOne(entity, {
|
|
1661
|
+
where: buildPkFindWhere(),
|
|
1662
|
+
});
|
|
1663
|
+
return result;
|
|
1664
|
+
}
|
|
1665
|
+
catch (e) {
|
|
1666
|
+
try {
|
|
1667
|
+
await transactionManager.rollback();
|
|
1668
|
+
}
|
|
1669
|
+
catch (rollbackError) {
|
|
1670
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1671
|
+
}
|
|
1672
|
+
throw e;
|
|
1673
|
+
}
|
|
1674
|
+
finally {
|
|
1675
|
+
try {
|
|
1676
|
+
await transactionManager.close();
|
|
1677
|
+
}
|
|
1678
|
+
catch (closeError) {
|
|
1679
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
async deleteMany(entity, ids) {
|
|
1684
|
+
if (ids.length === 0) {
|
|
1685
|
+
return { affected: 0 };
|
|
1686
|
+
}
|
|
1687
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1688
|
+
if (!metadata) {
|
|
1689
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1690
|
+
}
|
|
1691
|
+
const pk = metadata.columns.find((column) => column.options?.primary);
|
|
1692
|
+
if (!pk) {
|
|
1693
|
+
throw new PrimaryKeyNotFoundError_1.PrimaryKeyNotFoundError(entity.name);
|
|
1694
|
+
}
|
|
1695
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1696
|
+
try {
|
|
1697
|
+
await transactionManager.connect();
|
|
1698
|
+
await transactionManager.startTransaction();
|
|
1699
|
+
if (this.isMySqlFamily()) {
|
|
1700
|
+
await transactionManager.query("SET autocommit = 0");
|
|
1701
|
+
}
|
|
1702
|
+
const placeholders = (0, sql_template_tag_1.join)(ids.map((id) => (0, sql_template_tag_1.default) `${id}`), ", ");
|
|
1703
|
+
const deleteQuery = (0, sql_template_tag_1.default) `DELETE FROM ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))} WHERE ${(0, sql_template_tag_1.raw)(this.wrap(pk.name))} IN (${placeholders})`;
|
|
1704
|
+
const queryResult = (await transactionManager.query(deleteQuery));
|
|
1705
|
+
await transactionManager.commit();
|
|
1706
|
+
let affected = 0;
|
|
1707
|
+
if (this.isMySqlFamily()) {
|
|
1708
|
+
affected = queryResult?.results?.affectedRows ?? 0;
|
|
1709
|
+
}
|
|
1710
|
+
else {
|
|
1711
|
+
affected = queryResult?.rowCount ?? 0;
|
|
1712
|
+
}
|
|
1713
|
+
return { affected };
|
|
1714
|
+
}
|
|
1715
|
+
catch (e) {
|
|
1716
|
+
try {
|
|
1717
|
+
await transactionManager.rollback();
|
|
1718
|
+
}
|
|
1719
|
+
catch (rollbackError) {
|
|
1720
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1721
|
+
}
|
|
1722
|
+
throw e;
|
|
1723
|
+
}
|
|
1724
|
+
finally {
|
|
1725
|
+
try {
|
|
1726
|
+
await transactionManager.close();
|
|
1727
|
+
}
|
|
1728
|
+
catch (closeError) {
|
|
1729
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
async saveMany(entity, items) {
|
|
1734
|
+
if (items.length === 0) {
|
|
1735
|
+
return [];
|
|
1736
|
+
}
|
|
1737
|
+
const results = [];
|
|
1738
|
+
for (const item of items) {
|
|
1739
|
+
const saved = await this.save(entity, item);
|
|
1740
|
+
results.push(saved);
|
|
1741
|
+
}
|
|
1742
|
+
return results;
|
|
1743
|
+
}
|
|
1744
|
+
async insertMany(entity, items) {
|
|
1745
|
+
if (items.length === 0) {
|
|
1746
|
+
return { affected: 0 };
|
|
1747
|
+
}
|
|
1748
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1749
|
+
if (!metadata) {
|
|
1750
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1751
|
+
}
|
|
1752
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1753
|
+
try {
|
|
1754
|
+
await transactionManager.connect();
|
|
1755
|
+
await transactionManager.startTransaction();
|
|
1756
|
+
if (this.isMySqlFamily()) {
|
|
1757
|
+
await transactionManager.query("SET autocommit = 0");
|
|
1758
|
+
}
|
|
1759
|
+
const deletedAtColumn = this.getDeletedAtColumn(entity);
|
|
1760
|
+
const timestampTypes = new Set(["datetime", "timestamp", "date"]);
|
|
1761
|
+
const timestampColumns = metadata.columns.filter((col) => col.options?.type &&
|
|
1762
|
+
timestampTypes.has(col.options.type) &&
|
|
1763
|
+
col.name !== deletedAtColumn);
|
|
1764
|
+
if (timestampColumns.length > 0) {
|
|
1765
|
+
const now = new Date();
|
|
1766
|
+
for (const item of items) {
|
|
1767
|
+
for (const col of timestampColumns) {
|
|
1768
|
+
if (item[col.name] == null) {
|
|
1769
|
+
item[col.name] = now;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
const insertableColumns = metadata.columns.filter((column) => {
|
|
1775
|
+
const isAutoIncrement = column.options?.autoIncrement;
|
|
1776
|
+
if (!isAutoIncrement)
|
|
1777
|
+
return true;
|
|
1778
|
+
return items.every((item) => item[column.name] !== null &&
|
|
1779
|
+
item[column.name] !== undefined);
|
|
1780
|
+
});
|
|
1781
|
+
const columns = insertableColumns.map((column) => (0, sql_template_tag_1.raw)(this.wrap(column.name)));
|
|
1782
|
+
const manyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
1783
|
+
const fkColumns = [];
|
|
1784
|
+
for (const rel of manyToOneRelations) {
|
|
1785
|
+
if (!rel.joinColumn)
|
|
1786
|
+
continue;
|
|
1787
|
+
const alreadyIncluded = insertableColumns.some((col) => col.name === rel.joinColumn);
|
|
1788
|
+
if (!alreadyIncluded) {
|
|
1789
|
+
columns.push((0, sql_template_tag_1.raw)(this.wrap(rel.joinColumn)));
|
|
1790
|
+
fkColumns.push({
|
|
1791
|
+
joinColumn: rel.joinColumn,
|
|
1792
|
+
propertyName: rel.columnName,
|
|
1793
|
+
relMeta: rel,
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
const valueRows = items.map((item) => {
|
|
1798
|
+
const rowValues = insertableColumns.map((column) => item[column.name]);
|
|
1799
|
+
for (const fk of fkColumns) {
|
|
1800
|
+
const relatedValue = item[fk.propertyName];
|
|
1801
|
+
const idPropValue = item[`${fk.propertyName}Id`];
|
|
1802
|
+
if (relatedValue != null) {
|
|
1803
|
+
if (typeof relatedValue === "object") {
|
|
1804
|
+
const RelatedEntity = fk.relMeta.getMappingEntity();
|
|
1805
|
+
const relatedMeta = this.resolveEntityMetadata(RelatedEntity);
|
|
1806
|
+
const relatedPk = relatedMeta?.columns.find((col) => col.options?.primary);
|
|
1807
|
+
rowValues.push(relatedPk ? relatedValue[relatedPk.name] ?? null : null);
|
|
1808
|
+
}
|
|
1809
|
+
else {
|
|
1810
|
+
rowValues.push(relatedValue);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
else if (idPropValue != null) {
|
|
1814
|
+
rowValues.push(idPropValue);
|
|
1815
|
+
}
|
|
1816
|
+
else {
|
|
1817
|
+
rowValues.push(null);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
return (0, sql_template_tag_1.default) `(${(0, sql_template_tag_1.join)(rowValues, ", ")})`;
|
|
1821
|
+
});
|
|
1822
|
+
const queryStr = (0, sql_template_tag_1.default) `INSERT INTO ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))} (${(0, sql_template_tag_1.join)(columns, ", ")}) VALUES ${(0, sql_template_tag_1.join)(valueRows, ", ")}`;
|
|
1823
|
+
const queryResult = (await transactionManager.query(queryStr));
|
|
1824
|
+
await transactionManager.commit();
|
|
1825
|
+
let affected = items.length;
|
|
1826
|
+
if (this.isMySqlFamily()) {
|
|
1827
|
+
affected = queryResult?.results?.affectedRows ?? items.length;
|
|
1828
|
+
}
|
|
1829
|
+
else if (queryResult?.rowCount !== undefined) {
|
|
1830
|
+
affected = queryResult.rowCount;
|
|
1831
|
+
}
|
|
1832
|
+
return { affected };
|
|
1833
|
+
}
|
|
1834
|
+
catch (e) {
|
|
1835
|
+
try {
|
|
1836
|
+
await transactionManager.rollback();
|
|
1837
|
+
}
|
|
1838
|
+
catch (rollbackError) {
|
|
1839
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1840
|
+
}
|
|
1841
|
+
throw e;
|
|
1842
|
+
}
|
|
1843
|
+
finally {
|
|
1844
|
+
try {
|
|
1845
|
+
await transactionManager.close();
|
|
1846
|
+
}
|
|
1847
|
+
catch (closeError) {
|
|
1848
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
async upsert(entity, data, conflictColumns) {
|
|
1853
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1854
|
+
if (!metadata) {
|
|
1855
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1856
|
+
}
|
|
1857
|
+
if (!this.driver) {
|
|
1858
|
+
throw new Error("Driver is not initialized.");
|
|
1859
|
+
}
|
|
1860
|
+
const pkColumns = metadata.columns
|
|
1861
|
+
.filter((col) => col.options?.primary)
|
|
1862
|
+
.map((col) => col.name);
|
|
1863
|
+
const resolvedConflictColumns = conflictColumns ?? pkColumns;
|
|
1864
|
+
if (resolvedConflictColumns.length === 0) {
|
|
1865
|
+
throw new PrimaryKeyNotFoundError_1.PrimaryKeyNotFoundError(entity.name);
|
|
1866
|
+
}
|
|
1867
|
+
const insertableColumns = metadata.columns.filter((col) => {
|
|
1868
|
+
const value = data[col.name];
|
|
1869
|
+
if (col.options?.autoIncrement &&
|
|
1870
|
+
(value === null || value === undefined)) {
|
|
1871
|
+
return false;
|
|
1872
|
+
}
|
|
1873
|
+
return value !== undefined;
|
|
1874
|
+
});
|
|
1875
|
+
if (insertableColumns.length === 0) {
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
const conflictSet = new Set(resolvedConflictColumns);
|
|
1879
|
+
const updateColumnNames = insertableColumns
|
|
1880
|
+
.map((col) => col.name)
|
|
1881
|
+
.filter((name) => !conflictSet.has(name));
|
|
1882
|
+
const wrappedColumns = insertableColumns.map((col) => this.wrap(col.name));
|
|
1883
|
+
const wrappedConflict = resolvedConflictColumns.map((name) => this.wrap(name));
|
|
1884
|
+
const wrappedUpdate = updateColumnNames.map((name) => this.wrap(name));
|
|
1885
|
+
const tableName = this.wrap(metadata.name);
|
|
1886
|
+
if (wrappedUpdate.length === 0) {
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1890
|
+
try {
|
|
1891
|
+
await transactionManager.connect();
|
|
1892
|
+
await transactionManager.startTransaction();
|
|
1893
|
+
if (this.isMySqlFamily()) {
|
|
1894
|
+
await transactionManager.query("SET autocommit = 0");
|
|
1895
|
+
}
|
|
1896
|
+
const columnValues = insertableColumns.map((col) => data[col.name]);
|
|
1897
|
+
const upsertSql = this.buildUpsertQuery(tableName, wrappedColumns, columnValues, wrappedConflict, wrappedUpdate);
|
|
1898
|
+
await transactionManager.query(upsertSql);
|
|
1899
|
+
await transactionManager.commit();
|
|
1900
|
+
}
|
|
1901
|
+
catch (e) {
|
|
1902
|
+
try {
|
|
1903
|
+
await transactionManager.rollback();
|
|
1904
|
+
}
|
|
1905
|
+
catch (rollbackError) {
|
|
1906
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1907
|
+
}
|
|
1908
|
+
throw e;
|
|
1909
|
+
}
|
|
1910
|
+
finally {
|
|
1911
|
+
try {
|
|
1912
|
+
await transactionManager.close();
|
|
1913
|
+
}
|
|
1914
|
+
catch (closeError) {
|
|
1915
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
buildUpsertQuery(tableName, columns, values, conflictColumns, updateColumns) {
|
|
1920
|
+
const columnList = (0, sql_template_tag_1.join)(columns.map((c) => (0, sql_template_tag_1.raw)(c)), ", ");
|
|
1921
|
+
const valueList = (0, sql_template_tag_1.join)(values, ", ");
|
|
1922
|
+
if (this.isMySqlFamily()) {
|
|
1923
|
+
const updateSet = (0, sql_template_tag_1.join)(updateColumns.map((col) => (0, sql_template_tag_1.raw)(`${col} = VALUES(${col})`)), ", ");
|
|
1924
|
+
return (0, sql_template_tag_1.default) `INSERT INTO ${(0, sql_template_tag_1.raw)(tableName)} (${columnList}) VALUES (${valueList}) ON DUPLICATE KEY UPDATE ${updateSet}`;
|
|
1925
|
+
}
|
|
1926
|
+
const conflictList = (0, sql_template_tag_1.join)(conflictColumns.map((c) => (0, sql_template_tag_1.raw)(c)), ", ");
|
|
1927
|
+
if (this.isPostgres()) {
|
|
1928
|
+
const updateSet = (0, sql_template_tag_1.join)(updateColumns.map((col) => (0, sql_template_tag_1.raw)(`${col} = EXCLUDED.${col}`)), ", ");
|
|
1929
|
+
return (0, sql_template_tag_1.default) `INSERT INTO ${(0, sql_template_tag_1.raw)(tableName)} (${columnList}) VALUES (${valueList}) ON CONFLICT (${conflictList}) DO UPDATE SET ${updateSet}`;
|
|
1930
|
+
}
|
|
1931
|
+
if ((this.dbType ?? this.client.type) === "sqlite") {
|
|
1932
|
+
const updateSet = (0, sql_template_tag_1.join)(updateColumns.map((col) => (0, sql_template_tag_1.raw)(`${col} = excluded.${col}`)), ", ");
|
|
1933
|
+
return (0, sql_template_tag_1.default) `INSERT INTO ${(0, sql_template_tag_1.raw)(tableName)} (${columnList}) VALUES (${valueList}) ON CONFLICT (${conflictList}) DO UPDATE SET ${updateSet}`;
|
|
1934
|
+
}
|
|
1935
|
+
throw new Error(`Unsupported database type for upsert: ${this.dbType}`);
|
|
1936
|
+
}
|
|
1937
|
+
async delete(entity, criteria) {
|
|
1938
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
1939
|
+
if (!metadata) {
|
|
1940
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
1941
|
+
}
|
|
1942
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
1943
|
+
try {
|
|
1944
|
+
await transactionManager.connect();
|
|
1945
|
+
await transactionManager.startTransaction();
|
|
1946
|
+
if (this.isMySqlFamily()) {
|
|
1947
|
+
await transactionManager.query("SET autocommit = 0");
|
|
1948
|
+
}
|
|
1949
|
+
await this.runHooks(entity, criteria, "beforeDelete");
|
|
1950
|
+
await this.eventEmitter.emit("beforeDelete", { entity, data: criteria });
|
|
1951
|
+
await this.notifySubscribers(entity, "beforeDelete", {
|
|
1952
|
+
entityClass: entity,
|
|
1953
|
+
criteria,
|
|
1954
|
+
manager: this,
|
|
1955
|
+
});
|
|
1956
|
+
await this.cascadeDeleteOneToMany(entity, criteria);
|
|
1957
|
+
const whereMap = [];
|
|
1958
|
+
for (const key in criteria) {
|
|
1959
|
+
const value = criteria[key];
|
|
1960
|
+
if (value !== undefined && value !== null) {
|
|
1961
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
if (whereMap.length === 0) {
|
|
1965
|
+
throw new DeleteWithoutConditionsError_1.DeleteWithoutConditionsError("Delete");
|
|
1966
|
+
}
|
|
1967
|
+
const whereSql = (0, sql_template_tag_1.join)(whereMap, " AND ");
|
|
1968
|
+
const deleteQuery = (0, sql_template_tag_1.default) `DELETE FROM ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))} WHERE ${whereSql}`;
|
|
1969
|
+
const deleteStart = Date.now();
|
|
1970
|
+
this.beginTrackQuery();
|
|
1971
|
+
const queryResult = (await transactionManager.query(deleteQuery));
|
|
1972
|
+
this.trackQuery(entity.name, deleteQuery.text ?? String(deleteQuery), Date.now() - deleteStart);
|
|
1973
|
+
await transactionManager.commit();
|
|
1974
|
+
let affected = 0;
|
|
1975
|
+
if (this.isMySqlFamily()) {
|
|
1976
|
+
affected = queryResult?.results?.affectedRows ?? 0;
|
|
1977
|
+
}
|
|
1978
|
+
else {
|
|
1979
|
+
affected = queryResult?.rowCount ?? 0;
|
|
1980
|
+
}
|
|
1981
|
+
await this.runHooks(entity, criteria, "afterDelete");
|
|
1982
|
+
await this.eventEmitter.emit("afterDelete", { entity, data: criteria });
|
|
1983
|
+
await this.notifySubscribers(entity, "afterDelete", {
|
|
1984
|
+
entityClass: entity,
|
|
1985
|
+
criteria,
|
|
1986
|
+
manager: this,
|
|
1987
|
+
});
|
|
1988
|
+
return { affected };
|
|
1989
|
+
}
|
|
1990
|
+
catch (e) {
|
|
1991
|
+
try {
|
|
1992
|
+
await transactionManager.rollback();
|
|
1993
|
+
}
|
|
1994
|
+
catch (rollbackError) {
|
|
1995
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
1996
|
+
}
|
|
1997
|
+
throw e;
|
|
1998
|
+
}
|
|
1999
|
+
finally {
|
|
2000
|
+
try {
|
|
2001
|
+
await transactionManager.close();
|
|
2002
|
+
}
|
|
2003
|
+
catch (closeError) {
|
|
2004
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
async clear(entity) {
|
|
2009
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
2010
|
+
if (!metadata) {
|
|
2011
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
2012
|
+
}
|
|
2013
|
+
if (!this.driver) {
|
|
2014
|
+
throw new Error("Driver is not initialized. Call connect() first.");
|
|
2015
|
+
}
|
|
2016
|
+
await this.driver.clear(metadata.name);
|
|
2017
|
+
}
|
|
2018
|
+
async softDelete(entity, criteria) {
|
|
2019
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
2020
|
+
if (!metadata) {
|
|
2021
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
2022
|
+
}
|
|
2023
|
+
const deletedAtColumn = this.getDeletedAtColumn(entity);
|
|
2024
|
+
if (!deletedAtColumn) {
|
|
2025
|
+
throw new InvalidQueryError_1.InvalidQueryError(`Entity "${entity.name}" does not have a @DeletedAt column. Use delete() instead.`);
|
|
2026
|
+
}
|
|
2027
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
2028
|
+
try {
|
|
2029
|
+
await transactionManager.connect();
|
|
2030
|
+
await transactionManager.startTransaction();
|
|
2031
|
+
if (this.isMySqlFamily()) {
|
|
2032
|
+
await transactionManager.query("SET autocommit = 0");
|
|
2033
|
+
}
|
|
2034
|
+
const whereMap = [];
|
|
2035
|
+
for (const key in criteria) {
|
|
2036
|
+
const value = criteria[key];
|
|
2037
|
+
if (value !== undefined && value !== null) {
|
|
2038
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
if (whereMap.length === 0) {
|
|
2042
|
+
throw new DeleteWithoutConditionsError_1.DeleteWithoutConditionsError("Soft delete");
|
|
2043
|
+
}
|
|
2044
|
+
const whereSql = (0, sql_template_tag_1.join)(whereMap, " AND ");
|
|
2045
|
+
const nowExpr = this.isPostgres() ? (0, sql_template_tag_1.raw)("NOW()") : (0, sql_template_tag_1.raw)("NOW()");
|
|
2046
|
+
const updateQuery = (0, sql_template_tag_1.default) `UPDATE ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))} SET ${(0, sql_template_tag_1.raw)(this.wrap(deletedAtColumn))} = ${nowExpr} WHERE ${whereSql}`;
|
|
2047
|
+
const queryResult = (await transactionManager.query(updateQuery));
|
|
2048
|
+
await transactionManager.commit();
|
|
2049
|
+
let affected = 0;
|
|
2050
|
+
if (this.isMySqlFamily()) {
|
|
2051
|
+
affected = queryResult?.results?.affectedRows ?? 0;
|
|
2052
|
+
}
|
|
2053
|
+
else {
|
|
2054
|
+
affected = queryResult?.rowCount ?? 0;
|
|
2055
|
+
}
|
|
2056
|
+
return { affected };
|
|
2057
|
+
}
|
|
2058
|
+
catch (e) {
|
|
2059
|
+
try {
|
|
2060
|
+
await transactionManager.rollback();
|
|
2061
|
+
}
|
|
2062
|
+
catch (rollbackError) {
|
|
2063
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
2064
|
+
}
|
|
2065
|
+
throw e;
|
|
2066
|
+
}
|
|
2067
|
+
finally {
|
|
2068
|
+
try {
|
|
2069
|
+
await transactionManager.close();
|
|
2070
|
+
}
|
|
2071
|
+
catch (closeError) {
|
|
2072
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
async restore(entity, criteria) {
|
|
2077
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
2078
|
+
if (!metadata) {
|
|
2079
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
2080
|
+
}
|
|
2081
|
+
const deletedAtColumn = this.getDeletedAtColumn(entity);
|
|
2082
|
+
if (!deletedAtColumn) {
|
|
2083
|
+
throw new InvalidQueryError_1.InvalidQueryError(`Entity "${entity.name}" does not have a @DeletedAt column. Cannot restore.`);
|
|
2084
|
+
}
|
|
2085
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
2086
|
+
try {
|
|
2087
|
+
await transactionManager.connect();
|
|
2088
|
+
await transactionManager.startTransaction();
|
|
2089
|
+
if (this.isMySqlFamily()) {
|
|
2090
|
+
await transactionManager.query("SET autocommit = 0");
|
|
2091
|
+
}
|
|
2092
|
+
const whereMap = [];
|
|
2093
|
+
for (const key in criteria) {
|
|
2094
|
+
const value = criteria[key];
|
|
2095
|
+
if (value !== undefined && value !== null) {
|
|
2096
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
if (whereMap.length === 0) {
|
|
2100
|
+
throw new DeleteWithoutConditionsError_1.DeleteWithoutConditionsError("Restore");
|
|
2101
|
+
}
|
|
2102
|
+
const whereSql = (0, sql_template_tag_1.join)(whereMap, " AND ");
|
|
2103
|
+
const restoreQuery = (0, sql_template_tag_1.default) `UPDATE ${(0, sql_template_tag_1.raw)(this.wrap(metadata.name))} SET ${(0, sql_template_tag_1.raw)(this.wrap(deletedAtColumn))} = NULL WHERE ${whereSql}`;
|
|
2104
|
+
const queryResult = (await transactionManager.query(restoreQuery));
|
|
2105
|
+
await transactionManager.commit();
|
|
2106
|
+
let affected = 0;
|
|
2107
|
+
if (this.isMySqlFamily()) {
|
|
2108
|
+
affected = queryResult?.results?.affectedRows ?? 0;
|
|
2109
|
+
}
|
|
2110
|
+
else {
|
|
2111
|
+
affected = queryResult?.rowCount ?? 0;
|
|
2112
|
+
}
|
|
2113
|
+
return { affected };
|
|
2114
|
+
}
|
|
2115
|
+
catch (e) {
|
|
2116
|
+
try {
|
|
2117
|
+
await transactionManager.rollback();
|
|
2118
|
+
}
|
|
2119
|
+
catch (rollbackError) {
|
|
2120
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
2121
|
+
}
|
|
2122
|
+
throw e;
|
|
2123
|
+
}
|
|
2124
|
+
finally {
|
|
2125
|
+
try {
|
|
2126
|
+
await transactionManager.close();
|
|
2127
|
+
}
|
|
2128
|
+
catch (closeError) {
|
|
2129
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
async cascadeSaveOneToMany(entity, item, savedParentId) {
|
|
2134
|
+
const oneToManyMeta = this.resolveOneToManyMetadata(entity);
|
|
2135
|
+
for (const rel of oneToManyMeta) {
|
|
2136
|
+
const children = item[rel.propertyKey];
|
|
2137
|
+
if (!children || !Array.isArray(children) || children.length === 0)
|
|
2138
|
+
continue;
|
|
2139
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
2140
|
+
if (!(0, CascadeType_1.hasCascade)(rel.cascade, "insert") &&
|
|
2141
|
+
!(0, CascadeType_1.hasCascade)(rel.cascade, "update"))
|
|
2142
|
+
continue;
|
|
2143
|
+
const manyToOneItems = this.resolveManyToOneMetadata(RelatedEntity);
|
|
2144
|
+
const matchingRelation = manyToOneItems.find((m) => m.columnName === rel.mappedBy);
|
|
2145
|
+
const fkColumn = matchingRelation?.joinColumn ?? rel.mappedBy;
|
|
2146
|
+
for (const child of children) {
|
|
2147
|
+
child[fkColumn] = savedParentId;
|
|
2148
|
+
await this.save(RelatedEntity, child);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
async cascadeSaveManyToOne(entity, item) {
|
|
2153
|
+
const manyToOneRelations = this.resolveManyToOneMetadata(entity);
|
|
2154
|
+
for (const rel of manyToOneRelations) {
|
|
2155
|
+
const relatedValue = item[rel.columnName];
|
|
2156
|
+
if (!relatedValue || typeof relatedValue !== "object")
|
|
2157
|
+
continue;
|
|
2158
|
+
if (!(0, CascadeType_1.hasCascade)(rel.option?.cascade, "insert") &&
|
|
2159
|
+
!(0, CascadeType_1.hasCascade)(rel.option?.cascade, "update"))
|
|
2160
|
+
continue;
|
|
2161
|
+
const RelatedEntity = rel.getMappingEntity();
|
|
2162
|
+
const saved = await this.save(RelatedEntity, relatedValue);
|
|
2163
|
+
const relatedMetadata = this.resolveEntityMetadata(RelatedEntity);
|
|
2164
|
+
if (relatedMetadata) {
|
|
2165
|
+
const relatedPk = relatedMetadata.columns.find((col) => col.options?.primary);
|
|
2166
|
+
if (relatedPk && rel.joinColumn) {
|
|
2167
|
+
item[rel.joinColumn] = saved[relatedPk.name];
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
async cascadeDeleteOneToMany(entity, criteria) {
|
|
2173
|
+
const oneToManyMeta = this.resolveOneToManyMetadata(entity);
|
|
2174
|
+
for (const rel of oneToManyMeta) {
|
|
2175
|
+
if (!(0, CascadeType_1.hasCascade)(rel.cascade, "delete"))
|
|
2176
|
+
continue;
|
|
2177
|
+
const RelatedEntity = rel.getRelatedEntity();
|
|
2178
|
+
const parentMetadata = this.resolveEntityMetadata(entity);
|
|
2179
|
+
if (!parentMetadata)
|
|
2180
|
+
continue;
|
|
2181
|
+
const pk = parentMetadata.columns.find((col) => col.options?.primary);
|
|
2182
|
+
if (!pk)
|
|
2183
|
+
continue;
|
|
2184
|
+
const parents = await this.find(entity, {
|
|
2185
|
+
where: criteria,
|
|
2186
|
+
});
|
|
2187
|
+
if (!parents)
|
|
2188
|
+
continue;
|
|
2189
|
+
const parentArray = Array.isArray(parents) ? parents : [parents];
|
|
2190
|
+
const manyToOneItems = this.resolveManyToOneMetadata(RelatedEntity);
|
|
2191
|
+
const matchingRelation = manyToOneItems.find((m) => m.columnName === rel.mappedBy);
|
|
2192
|
+
const fkColumn = matchingRelation?.joinColumn ?? rel.mappedBy;
|
|
2193
|
+
for (const parent of parentArray) {
|
|
2194
|
+
const parentId = parent[pk.name];
|
|
2195
|
+
if (parentId === undefined || parentId === null)
|
|
2196
|
+
continue;
|
|
2197
|
+
await this.delete(RelatedEntity, {
|
|
2198
|
+
[fkColumn]: parentId,
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
async aggregate(entity, fn, field, where) {
|
|
2204
|
+
const metadata = this.resolveEntityMetadata(entity);
|
|
2205
|
+
if (!metadata) {
|
|
2206
|
+
throw new EntityMetadataNotFoundError_1.EntityMetadataNotFoundError(entity.name);
|
|
2207
|
+
}
|
|
2208
|
+
const transactionManager = new TransactionSessionManager_1.TransactionSessionManager();
|
|
2209
|
+
try {
|
|
2210
|
+
await transactionManager.connect();
|
|
2211
|
+
await transactionManager.startTransaction();
|
|
2212
|
+
if (this.isMySqlFamily()) {
|
|
2213
|
+
await transactionManager.query("SET autocommit = 0");
|
|
2214
|
+
}
|
|
2215
|
+
const tableName = metadata.name;
|
|
2216
|
+
const selectExpr = (0, sql_template_tag_1.raw)(`${fn}(${field === "*" ? "*" : this.wrap(field)})`);
|
|
2217
|
+
const whereMap = [];
|
|
2218
|
+
if (where) {
|
|
2219
|
+
for (const key in where) {
|
|
2220
|
+
const value = where[key];
|
|
2221
|
+
if (value !== undefined && value !== null) {
|
|
2222
|
+
whereMap.push(Conditions_1.Conditions.equals(this.wrap(key), value));
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
let queryStr;
|
|
2227
|
+
if (whereMap.length > 0) {
|
|
2228
|
+
const whereSql = (0, sql_template_tag_1.join)(whereMap, " AND ");
|
|
2229
|
+
queryStr = (0, sql_template_tag_1.default) `SELECT ${selectExpr} AS ${(0, sql_template_tag_1.raw)(this.wrap("result"))} FROM ${(0, sql_template_tag_1.raw)(this.wrap(tableName))} WHERE ${whereSql}`;
|
|
2230
|
+
}
|
|
2231
|
+
else {
|
|
2232
|
+
queryStr = (0, sql_template_tag_1.default) `SELECT ${selectExpr} AS ${(0, sql_template_tag_1.raw)(this.wrap("result"))} FROM ${(0, sql_template_tag_1.raw)(this.wrap(tableName))}`;
|
|
2233
|
+
}
|
|
2234
|
+
const queryResult = (await transactionManager.query(queryStr));
|
|
2235
|
+
await transactionManager.commit();
|
|
2236
|
+
const { results } = queryResult;
|
|
2237
|
+
if (!results || results.length === 0)
|
|
2238
|
+
return 0;
|
|
2239
|
+
const row = results[0];
|
|
2240
|
+
const value = row.result ?? row["result"];
|
|
2241
|
+
return value === null || value === undefined ? 0 : Number(value);
|
|
2242
|
+
}
|
|
2243
|
+
catch (e) {
|
|
2244
|
+
try {
|
|
2245
|
+
await transactionManager.rollback();
|
|
2246
|
+
}
|
|
2247
|
+
catch (rollbackError) {
|
|
2248
|
+
this.logger.error(`Failed to rollback transaction: ${rollbackError}`);
|
|
2249
|
+
}
|
|
2250
|
+
throw e;
|
|
2251
|
+
}
|
|
2252
|
+
finally {
|
|
2253
|
+
try {
|
|
2254
|
+
await transactionManager.close();
|
|
2255
|
+
}
|
|
2256
|
+
catch (closeError) {
|
|
2257
|
+
this.logger.error(`Failed to close transaction: ${closeError}`);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
async count(entity, where) {
|
|
2262
|
+
return this.aggregate(entity, "COUNT", "*", where);
|
|
2263
|
+
}
|
|
2264
|
+
async findAndCount(entity, findOption = {}) {
|
|
2265
|
+
const [entities, totalCount] = await Promise.all([
|
|
2266
|
+
this.find(entity, findOption),
|
|
2267
|
+
this.count(entity, findOption.where),
|
|
2268
|
+
]);
|
|
2269
|
+
return [entities, totalCount];
|
|
2270
|
+
}
|
|
2271
|
+
async sum(entity, field, where) {
|
|
2272
|
+
return this.aggregate(entity, "SUM", field, where);
|
|
2273
|
+
}
|
|
2274
|
+
async avg(entity, field, where) {
|
|
2275
|
+
return this.aggregate(entity, "AVG", field, where);
|
|
2276
|
+
}
|
|
2277
|
+
async min(entity, field, where) {
|
|
2278
|
+
return this.aggregate(entity, "MIN", field, where);
|
|
2279
|
+
}
|
|
2280
|
+
async max(entity, field, where) {
|
|
2281
|
+
return this.aggregate(entity, "MAX", field, where);
|
|
2282
|
+
}
|
|
2283
|
+
async query(sqlQuery, params) {
|
|
2284
|
+
const transactionHolder = new TransactionSessionManager_1.TransactionSessionManager();
|
|
2285
|
+
try {
|
|
2286
|
+
await transactionHolder.connect();
|
|
2287
|
+
await transactionHolder.startTransaction();
|
|
2288
|
+
if (this.isMySqlFamily()) {
|
|
2289
|
+
await transactionHolder.query("SET autocommit = 0");
|
|
2290
|
+
}
|
|
2291
|
+
let queryResult;
|
|
2292
|
+
if (typeof sqlQuery === "string") {
|
|
2293
|
+
if (params && params.length > 0) {
|
|
2294
|
+
const parameterizedSql = {
|
|
2295
|
+
text: sqlQuery,
|
|
2296
|
+
sql: sqlQuery,
|
|
2297
|
+
values: params,
|
|
2298
|
+
strings: [sqlQuery],
|
|
2299
|
+
};
|
|
2300
|
+
queryResult = await transactionHolder.query(parameterizedSql);
|
|
2301
|
+
}
|
|
2302
|
+
else {
|
|
2303
|
+
queryResult = await transactionHolder.query(sqlQuery);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
else {
|
|
2307
|
+
queryResult = await transactionHolder.query(sqlQuery);
|
|
2308
|
+
}
|
|
2309
|
+
await transactionHolder.commit();
|
|
2310
|
+
if (queryResult?.results) {
|
|
2311
|
+
return queryResult.results ?? [];
|
|
2312
|
+
}
|
|
2313
|
+
if (Array.isArray(queryResult)) {
|
|
2314
|
+
return queryResult;
|
|
2315
|
+
}
|
|
2316
|
+
return [];
|
|
2317
|
+
}
|
|
2318
|
+
catch (e) {
|
|
2319
|
+
try {
|
|
2320
|
+
await transactionHolder.rollback();
|
|
2321
|
+
}
|
|
2322
|
+
catch (rollbackError) {
|
|
2323
|
+
this.logger.error(`Failed to rollback raw query transaction: ${rollbackError}`);
|
|
2324
|
+
}
|
|
2325
|
+
throw e;
|
|
2326
|
+
}
|
|
2327
|
+
finally {
|
|
2328
|
+
try {
|
|
2329
|
+
await transactionHolder.close();
|
|
2330
|
+
}
|
|
2331
|
+
catch (closeError) {
|
|
2332
|
+
this.logger.error(`Failed to close raw query transaction: ${closeError}`);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
getRepository(entity) {
|
|
2337
|
+
return BaseRepository_1.BaseRepository.of(entity, this);
|
|
2338
|
+
}
|
|
2339
|
+
async withTenant(tenantId, callback) {
|
|
2340
|
+
return MetadataContext_1.MetadataContext.run(tenantId, () => callback(this));
|
|
2341
|
+
}
|
|
2342
|
+
getDriver() {
|
|
2343
|
+
return this.driver;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
exports.EntityManager = EntityManager;
|
|
2347
|
+
//# sourceMappingURL=EntityManager.js.map
|