# MikroORM - [TypeScript ORM built on proven patterns](https://mikro-orm.io/index.md) ## blog - [Blog](https://mikro-orm.io/blog.md) - [Archive](https://mikro-orm.io/blog/archive.md) - [Authors](https://mikro-orm.io/blog/authors.md) - [Handling Transactions and Concurrency in MikroORM](https://mikro-orm.io/blog/handling-transactions-and-concurrency-in-mikroorm.md) - [Introducing MikroORM, TypeScript data-mapper ORM with Identity Map](https://mikro-orm.io/blog/introducing-mikroorm-typescript-data-mapper-orm-with-identity-map.md) - [MikroORM 3: Knex.js, CLI, Schema Updates, Entity Generator and more…](https://mikro-orm.io/blog/mikro-orm-3-released.md) - [MikroORM 4.1: Let’s talk about performance](https://mikro-orm.io/blog/mikro-orm-4-1-released.md) - [MikroORM 4: Filling the Gaps](https://mikro-orm.io/blog/mikro-orm-4-released.md) - [MikroORM 5.8 released](https://mikro-orm.io/blog/mikro-orm-5-8-released.md) - [MikroORM 5: Stricter, Safer, Smarter](https://mikro-orm.io/blog/mikro-orm-5-released.md) - [MikroORM 6.2: Say hello to SQL Server (and libSQL)](https://mikro-orm.io/blog/mikro-orm-6-2-released.md) - [MikroORM 6.3: Schema first?](https://mikro-orm.io/blog/mikro-orm-6-3-released.md) - [MikroORM 6.4](https://mikro-orm.io/blog/mikro-orm-6-4-released.md) - [MikroORM 6.5](https://mikro-orm.io/blog/mikro-orm-6-5-released.md) - [MikroORM 6.6](https://mikro-orm.io/blog/mikro-orm-6-6-released.md) - [MikroORM 6: Polished](https://mikro-orm.io/blog/mikro-orm-6-released.md) - [MikroORM 7: Unchained](https://mikro-orm.io/blog/mikro-orm-7-released.md) - [Blog](https://mikro-orm.io/blog/page/2.md) - [Tags](https://mikro-orm.io/blog/tags.md) - [14 posts tagged with "javascript"](https://mikro-orm.io/blog/tags/javascript.md) - [14 posts tagged with "javascript"](https://mikro-orm.io/blog/tags/javascript/page/2.md) - [14 posts tagged with "node"](https://mikro-orm.io/blog/tags/node.md) - [14 posts tagged with "node"](https://mikro-orm.io/blog/tags/node/page/2.md) - [One post tagged with "oop"](https://mikro-orm.io/blog/tags/oop.md) - [13 posts tagged with "sql"](https://mikro-orm.io/blog/tags/sql.md) - [13 posts tagged with "sql"](https://mikro-orm.io/blog/tags/sql/page/2.md) - [14 posts tagged with "typescript"](https://mikro-orm.io/blog/tags/typescript.md) - [14 posts tagged with "typescript"](https://mikro-orm.io/blog/tags/typescript/page/2.md) ## search - [Search the documentation](https://mikro-orm.io/search.md) ## versions - [MikroORM documentation versions](https://mikro-orm.io/versions.md) ## api - [API](https://mikro-orm.io/api.md) - [@mikro-orm/cli](https://mikro-orm.io/api/cli.md) - [Changelog](https://mikro-orm.io/api/cli/changelog.md) - [configure](https://mikro-orm.io/api/cli/function/configure.md) - [Settings](https://mikro-orm.io/api/cli/interface/Settings.md) - [@mikro-orm/core](https://mikro-orm.io/api/core.md) - [Changelog](https://mikro-orm.io/api/core/changelog.md) - [abstractAbstractNamingStrategy](https://mikro-orm.io/api/core/class/AbstractNamingStrategy.md) - [ArrayType ](https://mikro-orm.io/api/core/class/ArrayType.md) - [abstractBaseEntity](https://mikro-orm.io/api/core/class/BaseEntity.md) - [BigIntType ](https://mikro-orm.io/api/core/class/BigIntType.md) - [BlobType](https://mikro-orm.io/api/core/class/BlobType.md) - [BooleanType](https://mikro-orm.io/api/core/class/BooleanType.md) - [ChangeSet ](https://mikro-orm.io/api/core/class/ChangeSet.md) - [CharacterType](https://mikro-orm.io/api/core/class/CharacterType.md) - [CheckConstraintViolationException](https://mikro-orm.io/api/core/class/CheckConstraintViolationException.md) - [Collection ](https://mikro-orm.io/api/core/class/Collection.md) - [Configuration ](https://mikro-orm.io/api/core/class/Configuration.md) - [abstractConnection](https://mikro-orm.io/api/core/class/Connection.md) - [ConnectionException](https://mikro-orm.io/api/core/class/ConnectionException.md) - [ConstraintViolationException](https://mikro-orm.io/api/core/class/ConstraintViolationException.md) - [Cursor ](https://mikro-orm.io/api/core/class/Cursor.md) - [CursorError ](https://mikro-orm.io/api/core/class/CursorError.md) - [abstractDatabaseDriver ](https://mikro-orm.io/api/core/class/DatabaseDriver.md) - [DatabaseObjectExistsException](https://mikro-orm.io/api/core/class/DatabaseObjectExistsException.md) - [DatabaseObjectNotFoundException](https://mikro-orm.io/api/core/class/DatabaseObjectNotFoundException.md) - [DateTimeType](https://mikro-orm.io/api/core/class/DateTimeType.md) - [DateType](https://mikro-orm.io/api/core/class/DateType.md) - [DeadlockException](https://mikro-orm.io/api/core/class/DeadlockException.md) - [DecimalType ](https://mikro-orm.io/api/core/class/DecimalType.md) - [DefaultLogger](https://mikro-orm.io/api/core/class/DefaultLogger.md) - [DoubleType](https://mikro-orm.io/api/core/class/DoubleType.md) - [DriverException](https://mikro-orm.io/api/core/class/DriverException.md) - [EntityAssigner](https://mikro-orm.io/api/core/class/EntityAssigner.md) - [EntityCaseNamingStrategy](https://mikro-orm.io/api/core/class/EntityCaseNamingStrategy.md) - [EntityLoader](https://mikro-orm.io/api/core/class/EntityLoader.md) - [EntityManager ](https://mikro-orm.io/api/core/class/EntityManager.md) - [EntityMetadata ](https://mikro-orm.io/api/core/class/EntityMetadata.md) - [EntityRepository ](https://mikro-orm.io/api/core/class/EntityRepository.md) - [EntitySchema ](https://mikro-orm.io/api/core/class/EntitySchema.md) - [EntitySerializer](https://mikro-orm.io/api/core/class/EntitySerializer.md) - [EntityTransformer](https://mikro-orm.io/api/core/class/EntityTransformer.md) - [EnumArrayType ](https://mikro-orm.io/api/core/class/EnumArrayType.md) - [EnumType](https://mikro-orm.io/api/core/class/EnumType.md) - [EventManager](https://mikro-orm.io/api/core/class/EventManager.md) - [ExceptionConverter](https://mikro-orm.io/api/core/class/ExceptionConverter.md) - [FloatType](https://mikro-orm.io/api/core/class/FloatType.md) - [ForeignKeyConstraintViolationException](https://mikro-orm.io/api/core/class/ForeignKeyConstraintViolationException.md) - [GeneratedCacheAdapter](https://mikro-orm.io/api/core/class/GeneratedCacheAdapter.md) - [abstractHydrator](https://mikro-orm.io/api/core/class/Hydrator.md) - [IntegerType](https://mikro-orm.io/api/core/class/IntegerType.md) - [IntervalType](https://mikro-orm.io/api/core/class/IntervalType.md) - [InvalidFieldNameException](https://mikro-orm.io/api/core/class/InvalidFieldNameException.md) - [JsonType](https://mikro-orm.io/api/core/class/JsonType.md) - [LockWaitTimeoutException](https://mikro-orm.io/api/core/class/LockWaitTimeoutException.md) - [MediumIntType](https://mikro-orm.io/api/core/class/MediumIntType.md) - [MemoryCacheAdapter](https://mikro-orm.io/api/core/class/MemoryCacheAdapter.md) - [MetadataDiscovery](https://mikro-orm.io/api/core/class/MetadataDiscovery.md) - [MetadataError ](https://mikro-orm.io/api/core/class/MetadataError.md) - [MetadataProvider](https://mikro-orm.io/api/core/class/MetadataProvider.md) - [MetadataStorage](https://mikro-orm.io/api/core/class/MetadataStorage.md) - [MikroORM ](https://mikro-orm.io/api/core/class/MikroORM.md) - [MongoNamingStrategy](https://mikro-orm.io/api/core/class/MongoNamingStrategy.md) - [NonUniqueFieldNameException](https://mikro-orm.io/api/core/class/NonUniqueFieldNameException.md) - [NotFoundError ](https://mikro-orm.io/api/core/class/NotFoundError.md) - [NotNullConstraintViolationException](https://mikro-orm.io/api/core/class/NotNullConstraintViolationException.md) - [NullCacheAdapter](https://mikro-orm.io/api/core/class/NullCacheAdapter.md) - [NullHighlighter](https://mikro-orm.io/api/core/class/NullHighlighter.md) - [OptimisticLockError ](https://mikro-orm.io/api/core/class/OptimisticLockError.md) - [abstractPlainObject](https://mikro-orm.io/api/core/class/PlainObject.md) - [abstractPlatform](https://mikro-orm.io/api/core/class/Platform.md) - [PolymorphicRef](https://mikro-orm.io/api/core/class/PolymorphicRef.md) - [RawQueryFragment ](https://mikro-orm.io/api/core/class/RawQueryFragment.md) - [ReadOnlyException](https://mikro-orm.io/api/core/class/ReadOnlyException.md) - [Reference ](https://mikro-orm.io/api/core/class/Reference.md) - [RequestContext](https://mikro-orm.io/api/core/class/RequestContext.md) - [ScalarReference ](https://mikro-orm.io/api/core/class/ScalarReference.md) - [SerializationContext ](https://mikro-orm.io/api/core/class/SerializationContext.md) - [ServerException](https://mikro-orm.io/api/core/class/ServerException.md) - [SimpleLogger](https://mikro-orm.io/api/core/class/SimpleLogger.md) - [SmallIntType](https://mikro-orm.io/api/core/class/SmallIntType.md) - [StringType](https://mikro-orm.io/api/core/class/StringType.md) - [SyntaxErrorException](https://mikro-orm.io/api/core/class/SyntaxErrorException.md) - [TableExistsException](https://mikro-orm.io/api/core/class/TableExistsException.md) - [TableNotFoundException](https://mikro-orm.io/api/core/class/TableNotFoundException.md) - [TextType](https://mikro-orm.io/api/core/class/TextType.md) - [TimeType](https://mikro-orm.io/api/core/class/TimeType.md) - [TinyIntType](https://mikro-orm.io/api/core/class/TinyIntType.md) - [TransactionContext](https://mikro-orm.io/api/core/class/TransactionContext.md) - [TransactionEventBroadcaster](https://mikro-orm.io/api/core/class/TransactionEventBroadcaster.md) - [TransactionManager](https://mikro-orm.io/api/core/class/TransactionManager.md) - [TransactionStateError](https://mikro-orm.io/api/core/class/TransactionStateError.md) - [abstractType ](https://mikro-orm.io/api/core/class/Type.md) - [Uint8ArrayType](https://mikro-orm.io/api/core/class/Uint8ArrayType.md) - [UnderscoreNamingStrategy](https://mikro-orm.io/api/core/class/UnderscoreNamingStrategy.md) - [UniqueConstraintViolationException](https://mikro-orm.io/api/core/class/UniqueConstraintViolationException.md) - [UnitOfWork](https://mikro-orm.io/api/core/class/UnitOfWork.md) - [UnknownType](https://mikro-orm.io/api/core/class/UnknownType.md) - [Utils](https://mikro-orm.io/api/core/class/Utils.md) - [UuidType](https://mikro-orm.io/api/core/class/UuidType.md) - [ValidationError ](https://mikro-orm.io/api/core/class/ValidationError.md) - [Cascade](https://mikro-orm.io/api/core/enum/Cascade.md) - [ChangeSetType](https://mikro-orm.io/api/core/enum/ChangeSetType.md) - [DataloaderType](https://mikro-orm.io/api/core/enum/DataloaderType.md) - [DeferMode](https://mikro-orm.io/api/core/enum/DeferMode.md) - [EventType](https://mikro-orm.io/api/core/enum/EventType.md) - [FlushMode](https://mikro-orm.io/api/core/enum/FlushMode.md) - [GroupOperator](https://mikro-orm.io/api/core/enum/GroupOperator.md) - [IsolationLevel](https://mikro-orm.io/api/core/enum/IsolationLevel.md) - [LoadStrategy](https://mikro-orm.io/api/core/enum/LoadStrategy.md) - [LockMode](https://mikro-orm.io/api/core/enum/LockMode.md) - [constNodeState](https://mikro-orm.io/api/core/enum/NodeState.md) - [PopulateHint](https://mikro-orm.io/api/core/enum/PopulateHint.md) - [PopulatePath](https://mikro-orm.io/api/core/enum/PopulatePath.md) - [QueryFlag](https://mikro-orm.io/api/core/enum/QueryFlag.md) - [QueryOperator](https://mikro-orm.io/api/core/enum/QueryOperator.md) - [QueryOrder](https://mikro-orm.io/api/core/enum/QueryOrder.md) - [QueryOrderNumeric](https://mikro-orm.io/api/core/enum/QueryOrderNumeric.md) - [ReferenceKind](https://mikro-orm.io/api/core/enum/ReferenceKind.md) - [TransactionPropagation](https://mikro-orm.io/api/core/enum/TransactionPropagation.md) - [assign](https://mikro-orm.io/api/core/function/assign.md) - [compareArrays](https://mikro-orm.io/api/core/function/compareArrays.md) - [compareBooleans](https://mikro-orm.io/api/core/function/compareBooleans.md) - [compareBuffers](https://mikro-orm.io/api/core/function/compareBuffers.md) - [compareObjects](https://mikro-orm.io/api/core/function/compareObjects.md) - [createSqlFunction](https://mikro-orm.io/api/core/function/createSqlFunction.md) - [defineConfig](https://mikro-orm.io/api/core/function/defineConfig.md) - [defineEntity](https://mikro-orm.io/api/core/function/defineEntity.md) - [equals](https://mikro-orm.io/api/core/function/equals.md) - [isRaw](https://mikro-orm.io/api/core/function/isRaw.md) - [parseJsonSafe](https://mikro-orm.io/api/core/function/parseJsonSafe.md) - [quote](https://mikro-orm.io/api/core/function/quote.md) - [raw](https://mikro-orm.io/api/core/function/raw.md) - [ref](https://mikro-orm.io/api/core/function/ref.md) - [rel](https://mikro-orm.io/api/core/function/rel.md) - [serialize](https://mikro-orm.io/api/core/function/serialize.md) - [sql](https://mikro-orm.io/api/core/function/sql.md) - [wrap](https://mikro-orm.io/api/core/function/wrap.md) - [AssignOptions ](https://mikro-orm.io/api/core/interface/AssignOptions.md) - [CacheAdapter](https://mikro-orm.io/api/core/interface/CacheAdapter.md) - [CheckConstraint ](https://mikro-orm.io/api/core/interface/CheckConstraint.md) - [ClearDatabaseOptions](https://mikro-orm.io/api/core/interface/ClearDatabaseOptions.md) - [CollationOptions](https://mikro-orm.io/api/core/interface/CollationOptions.md) - [ConnectionConfig](https://mikro-orm.io/api/core/interface/ConnectionConfig.md) - [ConnectionOptions](https://mikro-orm.io/api/core/interface/ConnectionOptions.md) - [CountOptions ](https://mikro-orm.io/api/core/interface/CountOptions.md) - [CreateContextOptions](https://mikro-orm.io/api/core/interface/CreateContextOptions.md) - [CreateOptions ](https://mikro-orm.io/api/core/interface/CreateOptions.md) - [CreateSchemaOptions](https://mikro-orm.io/api/core/interface/CreateSchemaOptions.md) - [DefineEntityHooks ](https://mikro-orm.io/api/core/interface/DefineEntityHooks.md) - [DeleteOptions ](https://mikro-orm.io/api/core/interface/DeleteOptions.md) - [DriverMethodOptions](https://mikro-orm.io/api/core/interface/DriverMethodOptions.md) - [DropSchemaOptions](https://mikro-orm.io/api/core/interface/DropSchemaOptions.md) - [Edge](https://mikro-orm.io/api/core/interface/Edge.md) - [EmbeddableOptions ](https://mikro-orm.io/api/core/interface/EmbeddableOptions.md) - [EmbeddedOptions ](https://mikro-orm.io/api/core/interface/EmbeddedOptions.md) - [EmptyOptions](https://mikro-orm.io/api/core/interface/EmptyOptions.md) - [EnsureDatabaseOptions](https://mikro-orm.io/api/core/interface/EnsureDatabaseOptions.md) - [EntityLoaderOptions ](https://mikro-orm.io/api/core/interface/EntityLoaderOptions.md) - [EntityMetadataWithProperties ](https://mikro-orm.io/api/core/interface/EntityMetadataWithProperties.md) - [EntityProperty ](https://mikro-orm.io/api/core/interface/EntityProperty.md) - [EntitySchemaWithMeta ](https://mikro-orm.io/api/core/interface/EntitySchemaWithMeta.md) - [EnumOptions ](https://mikro-orm.io/api/core/interface/EnumOptions.md) - [EventArgs ](https://mikro-orm.io/api/core/interface/EventArgs.md) - [EventSubscriber ](https://mikro-orm.io/api/core/interface/EventSubscriber.md) - [FindAllOptions ](https://mikro-orm.io/api/core/interface/FindAllOptions.md) - [FindByCursorOptions ](https://mikro-orm.io/api/core/interface/FindByCursorOptions.md) - [FindOneOptions ](https://mikro-orm.io/api/core/interface/FindOneOptions.md) - [FindOneOrFailOptions ](https://mikro-orm.io/api/core/interface/FindOneOrFailOptions.md) - [FindOptions ](https://mikro-orm.io/api/core/interface/FindOptions.md) - [FlatQueryOrderMap](https://mikro-orm.io/api/core/interface/FlatQueryOrderMap.md) - [FlushEventArgs](https://mikro-orm.io/api/core/interface/FlushEventArgs.md) - [ForkOptions](https://mikro-orm.io/api/core/interface/ForkOptions.md) - [GenerateOptions](https://mikro-orm.io/api/core/interface/GenerateOptions.md) - [GetReferenceOptions](https://mikro-orm.io/api/core/interface/GetReferenceOptions.md) - [Highlighter](https://mikro-orm.io/api/core/interface/Highlighter.md) - [IConfiguration](https://mikro-orm.io/api/core/interface/IConfiguration.md) - [IDatabaseDriver ](https://mikro-orm.io/api/core/interface/IDatabaseDriver.md) - [IEntityGenerator](https://mikro-orm.io/api/core/interface/IEntityGenerator.md) - [IMigrationGenerator](https://mikro-orm.io/api/core/interface/IMigrationGenerator.md) - [IMigrator](https://mikro-orm.io/api/core/interface/IMigrator.md) - [IndexColumnOptions](https://mikro-orm.io/api/core/interface/IndexColumnOptions.md) - [IndexOptions ](https://mikro-orm.io/api/core/interface/IndexOptions.md) - [InitCollectionOptions ](https://mikro-orm.io/api/core/interface/InitCollectionOptions.md) - [ISchemaGenerator](https://mikro-orm.io/api/core/interface/ISchemaGenerator.md) - [ISeedManager](https://mikro-orm.io/api/core/interface/ISeedManager.md) - [IWrappedEntity ](https://mikro-orm.io/api/core/interface/IWrappedEntity.md) - [LoadCountOptions ](https://mikro-orm.io/api/core/interface/LoadCountOptions.md) - [LoadedCollection ](https://mikro-orm.io/api/core/interface/LoadedCollection.md) - [LoadedReference ](https://mikro-orm.io/api/core/interface/LoadedReference.md) - [LoadHint ](https://mikro-orm.io/api/core/interface/LoadHint.md) - [LoadReferenceOptions ](https://mikro-orm.io/api/core/interface/LoadReferenceOptions.md) - [LoadReferenceOrFailOptions ](https://mikro-orm.io/api/core/interface/LoadReferenceOrFailOptions.md) - [LockOptions](https://mikro-orm.io/api/core/interface/LockOptions.md) - [LogContext](https://mikro-orm.io/api/core/interface/LogContext.md) - [Logger](https://mikro-orm.io/api/core/interface/Logger.md) - [LoggerOptions](https://mikro-orm.io/api/core/interface/LoggerOptions.md) - [ManyToManyOptions ](https://mikro-orm.io/api/core/interface/ManyToManyOptions.md) - [ManyToOneOptions ](https://mikro-orm.io/api/core/interface/ManyToOneOptions.md) - [MatchingOptions ](https://mikro-orm.io/api/core/interface/MatchingOptions.md) - [MergeOptions](https://mikro-orm.io/api/core/interface/MergeOptions.md) - [MetadataDiscoveryOptions](https://mikro-orm.io/api/core/interface/MetadataDiscoveryOptions.md) - [MigrationDiff](https://mikro-orm.io/api/core/interface/MigrationDiff.md) - [MigrationObject](https://mikro-orm.io/api/core/interface/MigrationObject.md) - [NamingStrategy](https://mikro-orm.io/api/core/interface/NamingStrategy.md) - [NativeDeleteOptions ](https://mikro-orm.io/api/core/interface/NativeDeleteOptions.md) - [NativeInsertUpdateManyOptions ](https://mikro-orm.io/api/core/interface/NativeInsertUpdateManyOptions.md) - [NativeInsertUpdateOptions ](https://mikro-orm.io/api/core/interface/NativeInsertUpdateOptions.md) - [Node](https://mikro-orm.io/api/core/interface/Node.md) - [OneToManyOptions ](https://mikro-orm.io/api/core/interface/OneToManyOptions.md) - [OneToOneOptions ](https://mikro-orm.io/api/core/interface/OneToOneOptions.md) - [Options ](https://mikro-orm.io/api/core/interface/Options.md) - [PoolConfig](https://mikro-orm.io/api/core/interface/PoolConfig.md) - [PrimaryKeyOptions ](https://mikro-orm.io/api/core/interface/PrimaryKeyOptions.md) - [PropertyChain ](https://mikro-orm.io/api/core/interface/PropertyChain.md) - [PropertyOptions ](https://mikro-orm.io/api/core/interface/PropertyOptions.md) - [QueryResult ](https://mikro-orm.io/api/core/interface/QueryResult.md) - [ReferenceOptions ](https://mikro-orm.io/api/core/interface/ReferenceOptions.md) - [RefreshDatabaseOptions](https://mikro-orm.io/api/core/interface/RefreshDatabaseOptions.md) - [RegisterOptions](https://mikro-orm.io/api/core/interface/RegisterOptions.md) - [SeederObject](https://mikro-orm.io/api/core/interface/SeederObject.md) - [SeederOptions](https://mikro-orm.io/api/core/interface/SeederOptions.md) - [SerializedPrimaryKeyOptions ](https://mikro-orm.io/api/core/interface/SerializedPrimaryKeyOptions.md) - [SerializeOptions ](https://mikro-orm.io/api/core/interface/SerializeOptions.md) - [SimpleColumnMeta](https://mikro-orm.io/api/core/interface/SimpleColumnMeta.md) - [StreamOptions ](https://mikro-orm.io/api/core/interface/StreamOptions.md) - [Subquery](https://mikro-orm.io/api/core/interface/Subquery.md) - [SyncCacheAdapter](https://mikro-orm.io/api/core/interface/SyncCacheAdapter.md) - [TransactionEventArgs](https://mikro-orm.io/api/core/interface/TransactionEventArgs.md) - [TransactionOptions](https://mikro-orm.io/api/core/interface/TransactionOptions.md) - [TransformContext](https://mikro-orm.io/api/core/interface/TransformContext.md) - [TypeConfig](https://mikro-orm.io/api/core/interface/TypeConfig.md) - [UniqueOptions ](https://mikro-orm.io/api/core/interface/UniqueOptions.md) - [UpdateOptions ](https://mikro-orm.io/api/core/interface/UpdateOptions.md) - [UpdateSchemaOptions ](https://mikro-orm.io/api/core/interface/UpdateSchemaOptions.md) - [UpsertManyOptions ](https://mikro-orm.io/api/core/interface/UpsertManyOptions.md) - [UpsertOptions ](https://mikro-orm.io/api/core/interface/UpsertOptions.md) - [DefineConfig](https://mikro-orm.io/api/core/namespace/DefineConfig.md) - [Hidden](https://mikro-orm.io/api/core/namespace/Hidden.md) - [Opt](https://mikro-orm.io/api/core/namespace/Opt.md) - [RequiredNullable](https://mikro-orm.io/api/core/namespace/RequiredNullable.md) - [@mikro-orm/entity-generator](https://mikro-orm.io/api/entity-generator.md) - [Changelog](https://mikro-orm.io/api/entity-generator/changelog.md) - [EntityGenerator](https://mikro-orm.io/api/entity-generator/class/EntityGenerator.md) - [@mikro-orm/libsql](https://mikro-orm.io/api/libsql.md) - [Changelog](https://mikro-orm.io/api/libsql/changelog.md) - [LibSqlConnection](https://mikro-orm.io/api/libsql/class/LibSqlConnection.md) - [LibSqlDriver](https://mikro-orm.io/api/libsql/class/LibSqlDriver.md) - [MikroORM ](https://mikro-orm.io/api/libsql/class/MikroORM.md) - [defineConfig](https://mikro-orm.io/api/libsql/function/defineConfig.md) - [@mikro-orm/mariadb](https://mikro-orm.io/api/mariadb.md) - [Changelog](https://mikro-orm.io/api/mariadb/changelog.md) - [MariaDbDriver](https://mikro-orm.io/api/mariadb/class/MariaDbDriver.md) - [MariaDbPlatform](https://mikro-orm.io/api/mariadb/class/MariaDbPlatform.md) - [MariaDbSchemaHelper](https://mikro-orm.io/api/mariadb/class/MariaDbSchemaHelper.md) - [MikroORM ](https://mikro-orm.io/api/mariadb/class/MikroORM.md) - [defineConfig](https://mikro-orm.io/api/mariadb/function/defineConfig.md) - [@mikro-orm/migrations](https://mikro-orm.io/api/migrations.md) - [Changelog](https://mikro-orm.io/api/migrations/changelog.md) - [JSMigrationGenerator](https://mikro-orm.io/api/migrations/class/JSMigrationGenerator.md) - [abstractMigration](https://mikro-orm.io/api/migrations/class/Migration.md) - [abstractMigrationGenerator](https://mikro-orm.io/api/migrations/class/MigrationGenerator.md) - [MigrationRunner](https://mikro-orm.io/api/migrations/class/MigrationRunner.md) - [MigrationStorage](https://mikro-orm.io/api/migrations/class/MigrationStorage.md) - [Migrator](https://mikro-orm.io/api/migrations/class/Migrator.md) - [TSMigrationGenerator](https://mikro-orm.io/api/migrations/class/TSMigrationGenerator.md) - [@mikro-orm/mongodb](https://mikro-orm.io/api/mongodb.md) - [Changelog](https://mikro-orm.io/api/mongodb/changelog.md) - [EntityManager ](https://mikro-orm.io/api/mongodb/class/EntityManager.md) - [EntityRepository ](https://mikro-orm.io/api/mongodb/class/EntityRepository.md) - [MikroORM ](https://mikro-orm.io/api/mongodb/class/MikroORM.md) - [MongoConnection](https://mikro-orm.io/api/mongodb/class/MongoConnection.md) - [MongoDriver](https://mikro-orm.io/api/mongodb/class/MongoDriver.md) - [MongoPlatform](https://mikro-orm.io/api/mongodb/class/MongoPlatform.md) - [MongoSchemaGenerator](https://mikro-orm.io/api/mongodb/class/MongoSchemaGenerator.md) - [publicObjectId](https://mikro-orm.io/api/mongodb/class/ObjectId.md) - [defineConfig](https://mikro-orm.io/api/mongodb/function/defineConfig.md) - [EnsureIndexesOptions](https://mikro-orm.io/api/mongodb/interface/EnsureIndexesOptions.md) - [MongoCountOptions](https://mikro-orm.io/api/mongodb/interface/MongoCountOptions.md) - [MongoCreateSchemaOptions](https://mikro-orm.io/api/mongodb/interface/MongoCreateSchemaOptions.md) - [MongoFindOptions ](https://mikro-orm.io/api/mongodb/interface/MongoFindOptions.md) - [MongoQueryOptions](https://mikro-orm.io/api/mongodb/interface/MongoQueryOptions.md) - [@mikro-orm/mssql](https://mikro-orm.io/api/mssql.md) - [Changelog](https://mikro-orm.io/api/mssql/changelog.md) - [MikroORM ](https://mikro-orm.io/api/mssql/class/MikroORM.md) - [MsSqlConnection](https://mikro-orm.io/api/mssql/class/MsSqlConnection.md) - [MsSqlDriver](https://mikro-orm.io/api/mssql/class/MsSqlDriver.md) - [MsSqlPlatform](https://mikro-orm.io/api/mssql/class/MsSqlPlatform.md) - [MsSqlSchemaHelper](https://mikro-orm.io/api/mssql/class/MsSqlSchemaHelper.md) - [UnicodeString](https://mikro-orm.io/api/mssql/class/UnicodeString.md) - [UnicodeStringType](https://mikro-orm.io/api/mssql/class/UnicodeStringType.md) - [defineConfig](https://mikro-orm.io/api/mssql/function/defineConfig.md) - [@mikro-orm/mysql](https://mikro-orm.io/api/mysql.md) - [Changelog](https://mikro-orm.io/api/mysql/changelog.md) - [MikroORM ](https://mikro-orm.io/api/mysql/class/MikroORM.md) - [MySqlConnection](https://mikro-orm.io/api/mysql/class/MySqlConnection.md) - [MySqlDriver](https://mikro-orm.io/api/mysql/class/MySqlDriver.md) - [MySqlPlatform](https://mikro-orm.io/api/mysql/class/MySqlPlatform.md) - [defineConfig](https://mikro-orm.io/api/mysql/function/defineConfig.md) - [@mikro-orm/oracledb](https://mikro-orm.io/api/oracledb.md) - [Changelog](https://mikro-orm.io/api/oracledb/changelog.md) - [MikroORM ](https://mikro-orm.io/api/oracledb/class/MikroORM.md) - [OracleConnection](https://mikro-orm.io/api/oracledb/class/OracleConnection.md) - [OracleDriver](https://mikro-orm.io/api/oracledb/class/OracleDriver.md) - [OracleExceptionConverter](https://mikro-orm.io/api/oracledb/class/OracleExceptionConverter.md) - [OraclePlatform](https://mikro-orm.io/api/oracledb/class/OraclePlatform.md) - [OracleQueryBuilder ](https://mikro-orm.io/api/oracledb/class/OracleQueryBuilder.md) - [OracleSchemaGenerator](https://mikro-orm.io/api/oracledb/class/OracleSchemaGenerator.md) - [OracleSchemaHelper](https://mikro-orm.io/api/oracledb/class/OracleSchemaHelper.md) - [defineConfig](https://mikro-orm.io/api/oracledb/function/defineConfig.md) - [@mikro-orm/postgresql](https://mikro-orm.io/api/postgresql.md) - [Changelog](https://mikro-orm.io/api/postgresql/changelog.md) - [EntityManager ](https://mikro-orm.io/api/postgresql/class/EntityManager.md) - [MikroORM ](https://mikro-orm.io/api/postgresql/class/MikroORM.md) - [PostgreSqlConnection](https://mikro-orm.io/api/postgresql/class/PostgreSqlConnection.md) - [PostgreSqlDriver](https://mikro-orm.io/api/postgresql/class/PostgreSqlDriver.md) - [PostgreSqlPlatform](https://mikro-orm.io/api/postgresql/class/PostgreSqlPlatform.md) - [defineConfig](https://mikro-orm.io/api/postgresql/function/defineConfig.md) - [raw](https://mikro-orm.io/api/postgresql/function/raw.md) - [@mikro-orm/reflection](https://mikro-orm.io/api/reflection.md) - [Changelog](https://mikro-orm.io/api/reflection/changelog.md) - [TsMorphMetadataProvider](https://mikro-orm.io/api/reflection/class/TsMorphMetadataProvider.md) - [@mikro-orm/seeder](https://mikro-orm.io/api/seeder.md) - [Changelog](https://mikro-orm.io/api/seeder/changelog.md) - [abstractFactory ](https://mikro-orm.io/api/seeder/class/Factory.md) - [abstractSeeder ](https://mikro-orm.io/api/seeder/class/Seeder.md) - [SeedManager](https://mikro-orm.io/api/seeder/class/SeedManager.md) - [@mikro-orm/sql](https://mikro-orm.io/api/sql.md) - [Changelog](https://mikro-orm.io/api/sql/changelog.md) - [abstractAbstractSqlConnection](https://mikro-orm.io/api/sql/class/AbstractSqlConnection.md) - [abstractAbstractSqlDriver ](https://mikro-orm.io/api/sql/class/AbstractSqlDriver.md) - [abstractAbstractSqlPlatform](https://mikro-orm.io/api/sql/class/AbstractSqlPlatform.md) - [BaseMySqlPlatform](https://mikro-orm.io/api/sql/class/BaseMySqlPlatform.md) - [BasePostgreSqlPlatform](https://mikro-orm.io/api/sql/class/BasePostgreSqlPlatform.md) - [BaseSqliteConnection](https://mikro-orm.io/api/sql/class/BaseSqliteConnection.md) - [EntityManager ](https://mikro-orm.io/api/sql/class/EntityManager.md) - [EntityRepository ](https://mikro-orm.io/api/sql/class/EntityRepository.md) - [FullTextType](https://mikro-orm.io/api/sql/class/FullTextType.md) - [Kysely ](https://mikro-orm.io/api/sql/class/Kysely.md) - [MikroKyselyPlugin](https://mikro-orm.io/api/sql/class/MikroKyselyPlugin.md) - [MySqlSchemaHelper](https://mikro-orm.io/api/sql/class/MySqlSchemaHelper.md) - [NodeSqliteDialect](https://mikro-orm.io/api/sql/class/NodeSqliteDialect.md) - [OracleDialect](https://mikro-orm.io/api/sql/class/OracleDialect.md) - [PostgreSqlSchemaHelper](https://mikro-orm.io/api/sql/class/PostgreSqlSchemaHelper.md) - [QueryBuilder ](https://mikro-orm.io/api/sql/class/QueryBuilder.md) - [SchemaComparator](https://mikro-orm.io/api/sql/class/SchemaComparator.md) - [abstractSchemaHelper](https://mikro-orm.io/api/sql/class/SchemaHelper.md) - [SqliteDriver](https://mikro-orm.io/api/sql/class/SqliteDriver.md) - [SqlitePlatform](https://mikro-orm.io/api/sql/class/SqlitePlatform.md) - [SqliteSchemaHelper](https://mikro-orm.io/api/sql/class/SqliteSchemaHelper.md) - [SqlSchemaGenerator](https://mikro-orm.io/api/sql/class/SqlSchemaGenerator.md) - [JoinType](https://mikro-orm.io/api/sql/enum/JoinType.md) - [QueryType](https://mikro-orm.io/api/sql/enum/QueryType.md) - [raw](https://mikro-orm.io/api/sql/function/raw.md) - [Alias ](https://mikro-orm.io/api/sql/interface/Alias.md) - [CheckDef ](https://mikro-orm.io/api/sql/interface/CheckDef.md) - [Column](https://mikro-orm.io/api/sql/interface/Column.md) - [ColumnDifference](https://mikro-orm.io/api/sql/interface/ColumnDifference.md) - [CountQueryBuilder ](https://mikro-orm.io/api/sql/interface/CountQueryBuilder.md) - [CteOptions](https://mikro-orm.io/api/sql/interface/CteOptions.md) - [DatabaseView](https://mikro-orm.io/api/sql/interface/DatabaseView.md) - [DeleteQueryBuilder ](https://mikro-orm.io/api/sql/interface/DeleteQueryBuilder.md) - [ExecuteOptions](https://mikro-orm.io/api/sql/interface/ExecuteOptions.md) - [ForeignKey](https://mikro-orm.io/api/sql/interface/ForeignKey.md) - [GetKyselyOptions](https://mikro-orm.io/api/sql/interface/GetKyselyOptions.md) - [ICriteriaNode ](https://mikro-orm.io/api/sql/interface/ICriteriaNode.md) - [ICriteriaNodeProcessOptions](https://mikro-orm.io/api/sql/interface/ICriteriaNodeProcessOptions.md) - [IndexDef](https://mikro-orm.io/api/sql/interface/IndexDef.md) - [InsertQueryBuilder ](https://mikro-orm.io/api/sql/interface/InsertQueryBuilder.md) - [IQueryBuilder ](https://mikro-orm.io/api/sql/interface/IQueryBuilder.md) - [JoinOptions](https://mikro-orm.io/api/sql/interface/JoinOptions.md) - [MikroKyselyPluginOptions](https://mikro-orm.io/api/sql/interface/MikroKyselyPluginOptions.md) - [OnConflictClause ](https://mikro-orm.io/api/sql/interface/OnConflictClause.md) - [OracleDialectConfig](https://mikro-orm.io/api/sql/interface/OracleDialectConfig.md) - [OraclePool](https://mikro-orm.io/api/sql/interface/OraclePool.md) - [OraclePoolConnection](https://mikro-orm.io/api/sql/interface/OraclePoolConnection.md) - [QBStreamOptions](https://mikro-orm.io/api/sql/interface/QBStreamOptions.md) - [RunQueryBuilder ](https://mikro-orm.io/api/sql/interface/RunQueryBuilder.md) - [SchemaDifference](https://mikro-orm.io/api/sql/interface/SchemaDifference.md) - [SelectQueryBuilder ](https://mikro-orm.io/api/sql/interface/SelectQueryBuilder.md) - [Table](https://mikro-orm.io/api/sql/interface/Table.md) - [TableDifference](https://mikro-orm.io/api/sql/interface/TableDifference.md) - [TableOptions](https://mikro-orm.io/api/sql/interface/TableOptions.md) - [TruncateQueryBuilder ](https://mikro-orm.io/api/sql/interface/TruncateQueryBuilder.md) - [UpdateQueryBuilder ](https://mikro-orm.io/api/sql/interface/UpdateQueryBuilder.md) - [@mikro-orm/sqlite](https://mikro-orm.io/api/sqlite.md) - [Changelog](https://mikro-orm.io/api/sqlite/changelog.md) - [MikroORM ](https://mikro-orm.io/api/sqlite/class/MikroORM.md) - [SqliteConnection](https://mikro-orm.io/api/sqlite/class/SqliteConnection.md) - [SqliteDriver](https://mikro-orm.io/api/sqlite/class/SqliteDriver.md) - [defineConfig](https://mikro-orm.io/api/sqlite/function/defineConfig.md) ## docs - [Advanced](https://mikro-orm.io/docs/advanced.md) - [Architecture Overview](https://mikro-orm.io/docs/architecture.md) - [Result cache](https://mikro-orm.io/docs/caching.md) - [Cascading persist, merge and remove](https://mikro-orm.io/docs/cascading.md) - [Collections](https://mikro-orm.io/docs/collections.md) - [Composite and Foreign Keys as Primary Key](https://mikro-orm.io/docs/composite-keys.md) - [Configuration](https://mikro-orm.io/docs/configuration.md) - [Core Concepts](https://mikro-orm.io/docs/core-concepts.md) - [Creating Custom Driver](https://mikro-orm.io/docs/custom-driver.md) - [Custom Types](https://mikro-orm.io/docs/custom-types.md) - [Dataloaders](https://mikro-orm.io/docs/dataloaders.md) - [Decorators Reference](https://mikro-orm.io/docs/decorators.md) - [Defining Entities via defineEntity](https://mikro-orm.io/docs/define-entity.md) - [Defining Entities](https://mikro-orm.io/docs/defining-entities.md) - [Deployment](https://mikro-orm.io/docs/deployment.md) - [Separating Concerns using Embeddables](https://mikro-orm.io/docs/embeddables.md) - [Using Entity Constructors](https://mikro-orm.io/docs/entity-constructors.md) - [Entity Generator](https://mikro-orm.io/docs/entity-generator.md) - [Working with Entity Manager](https://mikro-orm.io/docs/entity-manager.md) - [Events and Lifecycle Hooks](https://mikro-orm.io/docs/events.md) - [Example Integrations](https://mikro-orm.io/docs/examples.md) - [Filters](https://mikro-orm.io/docs/filters.md) - [Folder-based Discovery](https://mikro-orm.io/docs/folder-based-discovery.md) - [Getting Started Guide](https://mikro-orm.io/docs/guide.md) - [Chapter 4: Advanced](https://mikro-orm.io/docs/guide/advanced.md) - [Chapter 1: First Entity](https://mikro-orm.io/docs/guide/first-entity.md) - [Chapter 3: Project Setup](https://mikro-orm.io/docs/guide/project-setup.md) - [Chapter 2: Relationships](https://mikro-orm.io/docs/guide/relationships.md) - [Chapter 5: Type-safety](https://mikro-orm.io/docs/guide/type-safety.md) - [Identity Map and Request Context](https://mikro-orm.io/docs/identity-map.md) - [Indexes and Unique Constraints](https://mikro-orm.io/docs/indexes.md) - [Inheritance Mapping](https://mikro-orm.io/docs/inheritance-mapping.md) - [Integrations](https://mikro-orm.io/docs/integrations.md) - [JSON Properties](https://mikro-orm.io/docs/json-properties.md) - [Using Kysely](https://mikro-orm.io/docs/kysely.md) - [Relationship Loading Strategies](https://mikro-orm.io/docs/loading-strategies.md) - [Logging](https://mikro-orm.io/docs/logging.md) - [Materialized Views](https://mikro-orm.io/docs/materialized-views.md) - [Metadata Cache](https://mikro-orm.io/docs/metadata-cache.md) - [Metadata Providers](https://mikro-orm.io/docs/metadata-providers.md) - [Migrations](https://mikro-orm.io/docs/migrations.md) - [Modeling](https://mikro-orm.io/docs/modeling.md) - [Using Multiple Schemas](https://mikro-orm.io/docs/multiple-schemas.md) - [Naming Strategy](https://mikro-orm.io/docs/naming-strategy.md) - [Populating relations](https://mikro-orm.io/docs/populating-relations.md) - [Propagation](https://mikro-orm.io/docs/propagation.md) - [Property Validation](https://mikro-orm.io/docs/property-validation.md) - [Using Query Builder](https://mikro-orm.io/docs/query-builder.md) - [Query Conditions](https://mikro-orm.io/docs/query-conditions.md) - [Querying](https://mikro-orm.io/docs/querying.md) - [Quick Start](https://mikro-orm.io/docs/quick-start.md) - [Using raw SQL query fragments](https://mikro-orm.io/docs/raw-queries.md) - [Read Replica Connections](https://mikro-orm.io/docs/read-connections.md) - [Recipes](https://mikro-orm.io/docs/recipes.md) - [Reference](https://mikro-orm.io/docs/reference.md) - [Modeling Entity Relationships](https://mikro-orm.io/docs/relationships.md) - [Entity Repository](https://mikro-orm.io/docs/repositories.md) - [Schema & Database](https://mikro-orm.io/docs/schema-database.md) - [Schema First Guide](https://mikro-orm.io/docs/schema-first-guide.md) - [Schema Generator](https://mikro-orm.io/docs/schema-generator.md) - [Seeding](https://mikro-orm.io/docs/seeding.md) - [Serializing](https://mikro-orm.io/docs/serializing.md) - [Streaming](https://mikro-orm.io/docs/streaming.md) - [Transactional Outbox Pattern](https://mikro-orm.io/docs/transactional-outbox.md) - [Transactions and Concurrency](https://mikro-orm.io/docs/transactions.md) - [Type-Safe Relations](https://mikro-orm.io/docs/type-safe-relations.md) - [Unit of Work and Transactions](https://mikro-orm.io/docs/unit-of-work.md) - [Upgrading](https://mikro-orm.io/docs/upgrading.md) - [Upgrading from v2 to v3](https://mikro-orm.io/docs/upgrading-v2-to-v3.md) - [Upgrading from v3 to v4](https://mikro-orm.io/docs/upgrading-v3-to-v4.md) - [Upgrading from v4 to v5](https://mikro-orm.io/docs/upgrading-v4-to-v5.md) - [Upgrading from v5 to v6](https://mikro-orm.io/docs/upgrading-v5-to-v6.md) - [Upgrading from v6 to v7](https://mikro-orm.io/docs/upgrading-v6-to-v7.md) - [Using MikroORM with AdminJS](https://mikro-orm.io/docs/usage-with-adminjs.md) - [Using MikroORM with AdonisJS](https://mikro-orm.io/docs/usage-with-adonis.md) - [Usage with CockroachDB](https://mikro-orm.io/docs/usage-with-cockroachdb.md) - [Usage with Jest](https://mikro-orm.io/docs/usage-with-jest.md) - [Usage with JavaScript](https://mikro-orm.io/docs/usage-with-js.md) - [Usage with MongoDB](https://mikro-orm.io/docs/usage-with-mongo.md) - [Using MikroORM with NestJS framework](https://mikro-orm.io/docs/usage-with-nestjs.md) - [Using MikroORM with Next.js](https://mikro-orm.io/docs/usage-with-nextjs.md) - [Usage with SQL Drivers](https://mikro-orm.io/docs/usage-with-sql.md) - [Usage with SQLite](https://mikro-orm.io/docs/usage-with-sqlite.md) - [Usage with transpilers](https://mikro-orm.io/docs/usage-with-transpilers.md) - [Using native BigInt PKs (MySQL and PostgreSQL)](https://mikro-orm.io/docs/using-bigint-pks.md) - [Using Decorators](https://mikro-orm.io/docs/using-decorators.md) - [View Entities](https://mikro-orm.io/docs/view-entities.md) - [Virtual Entities](https://mikro-orm.io/docs/virtual-entities.md) - [The wrap() Helper](https://mikro-orm.io/docs/wrap-helper.md) --- # Full Documentation Content ## [MikroORM 7: Unchained](https://mikro-orm.io/blog/mikro-orm-7-released.md) · 36 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM After a year and a half of active development, I am thrilled to announce that MikroORM v7 is finally stable. This is the biggest release yet, and the subtitle says it all - Unchained. We broke free from knex, dropped all core dependencies to zero, shipped native ESM, removed the hard coupling to Node.js, and added a bunch of new features on top. Let's dive in! ![](/assets/images/unchained-34f8dd620a9f884d8bf9199d46b3e5a9.jpg) **Tags:** * [typescript](https://mikro-orm.io/blog/tags/typescript.md) * [javascript](https://mikro-orm.io/blog/tags/javascript.md) * [node](https://mikro-orm.io/blog/tags/node.md) * [sql](https://mikro-orm.io/blog/tags/sql.md) [**Read More**](https://mikro-orm.io/blog/mikro-orm-7-released.md) --- ### 2026[​](#2026 "Direct link to 2026") * [March 11](https://mikro-orm.io/blog/mikro-orm-7-released.md) [ - ](https://mikro-orm.io/blog/mikro-orm-7-released.md) [MikroORM 7: Unchained](https://mikro-orm.io/blog/mikro-orm-7-released.md) --- # Authors * [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) ## [Martin Adámek](https://github.com/B4nan) 14 Author of MikroORM [](https://x.com/B4nan "X")[](https://github.com/B4nan "GitHub") --- # Handling Transactions and Concurrency in MikroORM · 10 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM How to handle transactions and concurrency with ease. ## Note about persisting[​](#note-about-persisting "Direct link to Note about persisting") There are 2 methods we should first describe to understand how persisting works in MikroORM: `em.persist()` and `em.flush()`. `em.persist(entity, flush?: boolean)` is used to mark new entities for future persisting. It will make the entity managed by given `EntityManager` and once `flush` will be called, it will be written to the database. Second boolean parameter can be used to invoke `flush` immediately. Its default value is configurable via `autoFlush` option. > Default value of `autoFlush` is currently set to `true`, which will change in upcoming major release. Users are encouraged to either set `autoFlush` to `false` or use `em.persistLater()` (equal to `em.persist(entity, false)`) and `em.persistAndFlush()` methods instead. Every time persisting is mentioned in this article, it is with `autoFlush` set to `false` in mind. To understand `flush`, lets first define what managed entity is: An entity is managed if it’s fetched from the database (via `em.find()`, `em.findOne()` or via other managed entity) or registered as new through `em.persist()`. `em.flush()` will go through all managed entities, compute appropriate change sets and perform according database queries. As an entity loaded from database becomes managed automatically, you do not have to call `persist` on those, and `flush` is enough to update them. ![](https://cdn-images-1.medium.com/max/1600/0*Eo5JP9abOfPV24Uf.jpg) ## Transaction demarcation[​](#transaction-demarcation "Direct link to Transaction demarcation") Transaction demarcation is the task of defining your transaction boundaries. For the most part, MikroORM already takes care of proper transaction demarcation for you: All the write operations (INSERT/UPDATE/DELETE) are queued until `em.flush()` is invoked which wraps all of these changes in a single transaction. However, MikroORM also allows (and encourages) you to take over and control transaction demarcation yourself. ### Approach 1: Implicitly[​](#approach-1-implicitly "Direct link to Approach 1: Implicitly") The first approach is to use the implicit transaction handling provided by the MikroORM `EntityManager`. Given the following code snippet, without any explicit transaction demarcation: ``` const user = new User(); user.name = 'George'; await orm.em.persistAndFlush(user); ``` Since we do not do any custom transaction demarcation in the above code, `em.flush()` will begin and commit/rollback a transaction. This is sufficient if all the data manipulation that is part of a unit of work happens through the domain model and thus the ORM — in other words, unless you run some write queries manually, via `QueryBuilder`, or use one of `em.nativeInsert/Update/Delete` helpers. Here is a bit more complex example where multiple entities are involved: ``` const author = await orm.em.findOne(Author, id, ['books.tags', 'books.publisher']); author.books[0].title = 'New book name'; author.books[0].tags[0].name = 'old'; author.books[0].tags.add(new BookTag('sale')); author.books[0].publisher.name = 'New publisher name'; await orm.em.flush(); ``` We load one author by id, all his books and their tags as well as their publisher. For simplicity, let’s assume the author has one book associated, which has one book tag and one publisher. Then we update multiple things on book of that author, editing name of the tag, adding new one, and changing publisher’s name. As we are working with already managed entities (retrieved from `EntityManager`), we can simply `flush` without needing to `persist` those entities. The `flush` call here will compute all differences and run database queries accordingly. They will all be encapsulated in a transaction, as you can see from following list of fired queries: ``` START TRANSACTION; INSERT INTO `book_tag` (`name`) VALUES (?); UPDATE `book` SET `title` = ? WHERE `id` = ?; DELETE FROM `book_to_book_tag` WHERE `book_id` = ?; INSERT INTO `book_to_book_tag` (`book_id`, `book_tag_id`) VALUES (?, ?); INSERT INTO `book_to_book_tag` (`book_id`, `book_tag_id`) VALUES (?, ?); UPDATE `publisher` SET `name` = ? WHERE `id` = ?; UPDATE `book_tag` SET `name` = ? WHERE `id` = ?; COMMIT; ``` ### Approach 2: Explicitly[​](#approach-2-explicitly "Direct link to Approach 2: Explicitly") The explicit alternative is to use the transactions API directly to control the boundaries. The code then looks like this: ``` await orm.em.beginTransaction(); try { //... do some work const user = new User(...); user.name = 'George'; await orm.em.persistAndFlush(user); await orm.em.commit(); } catch (e) { await orm.em.rollback(); throw e; } ``` Explicit transaction demarcation is required when you want to include custom DBAL operations in a unit of work (e.g. when firing native SQL UPDATE queries) or when you want to make use of some methods of the `EntityManager` API that require an active transaction (e.g. locking) — such methods will throw a `ValidationError` to inform you of that requirement. A more convenient alternative for explicit transaction demarcation is to use `em.transactional(cb)`. It will automatically start the transaction, execute your asynchronous callback and commit it. In case of an exception during those operations, the transaction will be automatically rolled back and the exception will be re-thrown. An example that is functionally equivalent to the previously shown code looks as follows: ``` await orm.em.transactional(async _em => { //... do some work const user = new User(...); user.name = 'George'; _em.persistLater(user); }); ``` In the callback parameter, you will get forked `EntityManager` that will contain a copy of the current Identity Map. You should use this copy instead of the parent one for all queries inside the transaction. It will be flushed prior to transaction commit. ### Exception Handling[​](#exception-handling "Direct link to Exception Handling") When using *implicit* transaction demarcation and an exception occurs during `em.flush()`, the transaction is automatically rolled back. When using *explicit* transaction demarcation and an exception occurs, the transaction should be rolled back immediately as demonstrated in the example above. Users are encouraged to use `em.transactional(cb)` which will handle that automatically. As a result of this procedure, all previously managed or removed instances of the `EntityManager` become detached. The state of the detached objects will be the state at the point at which the transaction was rolled back. The state of the objects is in no way rolled back and thus the objects are now out of sync with the database. The application can continue to use the detached objects, knowing that their state is potentially no longer accurate. If you intend to start another unit of work after an exception has occurred you should do that with a new `EntityManager`. Simply use `em.fork()` to obtain fresh copy with cleared identity map. ![](https://cdn-images-1.medium.com/max/1600/0*D4B7hf_Up9bc9wzg.jpg) ## Concurrency and locking[​](#concurrency-andlocking "Direct link to Concurrency and locking") ### Why we need concurrency control?[​](#why-we-need-concurrency-control "Direct link to Why we need concurrency control?") If transactions are executed *serially* (one at a time), no transaction concurrency exists. However, if concurrent transactions with interleaving operations are allowed, you may easily run into one of those problems: 1. The lost update problem 2. The dirty read problem 3. The incorrect summary problem Take a look at [this article](https://www.includehelp.com/dbms/concurrency-and-problem-due-to-concurrency.aspx) for in-depth explanation of those. To mitigate those problems, MikroORM offers support for Pessimistic and Optimistic locking strategies natively. This allows you to take very fine-grained control over what kind of locking is required for your entities in your application. ### Optimistic Locking[​](#optimistic-locking "Direct link to Optimistic Locking") Database transactions are fine for concurrency control during a single request. However, a database transaction should not span across requests, the so-called “user think time”. Therefore a long-running “business transaction” that spans multiple requests needs to involve several database transactions. Thus, database transactions alone can no longer control concurrency during such a long-running business transaction. Concurrency control becomes the partial responsibility of the application itself. MikroORM has integrated support for automatic optimistic locking via a version field. In this approach any entity that should be protected against concurrent modifications during long-running business transactions gets a version field that is either a simple number or a Date (timestamp). When changes to such an entity are persisted at the end of a long-running conversation the version of the entity is compared to the version in the database and if they don’t match, a `ValidationError` is thrown, indicating that the entity has been modified by someone else already. To define a version field, simply use `@Property` decorator with `version` flag set to `true`. Only `Date` and `number` types are allowed. ``` export class User { // ... @Property({ version: true }) version: number; // ... } ``` ``` export class Book { // ... @Property({ version: true }) version: Date; // ... } ``` > Version numbers (not timestamps) should be preferred as they can not potentially conflict in a highly concurrent environment, unlike timestamps where this is a possibility, depending on the resolution of the timestamp on the particular database platform. When a version conflict is encountered during `em.flush()`, a `ValidationError` is thrown and the active transaction rolled back (or marked for rollback). This exception can be caught and handled. Potential responses to a `ValidationError` are to present the conflict to the user or to refresh or reload objects in a new transaction and then retrying the transaction. The time between showing an update form and actually modifying the entity can in the worst scenario be as long as your applications session timeout. If changes happen to the entity in that time frame you want to know directly when retrieving the entity that you will hit an optimistic locking exception. You can always verify the version of an entity during a request either when calling `em.findOne()`: ``` const theEntityId = 1; const expectedVersion = 184; try { const entity = await orm.em.findOne(User, theEntityId, { lockMode: LockMode.OPTIMISTIC, lockVersion: expectedVersion }); // do the work await orm.em.flush(); } catch (e) { console.log('Sorry, but someone else has already changed this entity. Please apply the changes again!'); } ``` Or you can use `em.lock()` to find out: ``` const theEntityId = 1; const expectedVersion = 184; const entity = await orm.em.findOne(User, theEntityId); try { // assert version await orm.em.lock(entity, LockMode.OPTIMISTIC, expectedVersion); } catch (e) { console.log('Sorry, but someone else has already changed this entity. Please apply the changes again!'); } ``` Using optimistic locking correctly, you **have** to pass the version as an additional parameter when updating entity. See following example: ``` const res = await fetch('api.example.com/book/123'); const book = res.json(); console.log(book.version); // prints the current version // user does some changes and calls the PUT handler const changes = { title: 'new title' }; await fetch('api.example.com/book/123', { method: 'PUT', body: { ...changes, version: book.version, }, }); ``` ``` // GET /book/:id async findOne(req, res) { const book = await this.em.findOne(Book, +req.query.id); res.json(book); } // PUT /book/:id async update(req, res) { const book = await this.em.findOne(Book, +req.query.id, { lockMode: LockMode.OPTIMISTIC, lockVersion: req.body.version }); book.assign(req.body); await this.em.flush(); res.json(book); } ``` Your frontend app loads an entity from API, the response includes the version property. User makes some changes and fires PUT request back to the API, with version field included in the payload. The PUT handler of the API then reads the version and passes it to the `em.findOne()` call. ## Pessimistic Locking[​](#pessimistic-locking "Direct link to Pessimistic Locking") MikroORM supports Pessimistic Locking at the database level. Every Entity can be part of a pessimistic lock, there is no special metadata required to use this feature. Pessimistic Locking requires active transaction, so you will have to use explicit transaction demarcation. MikroORM currently supports two pessimistic lock modes: * Pessimistic Write (`LockMode.PESSIMISTIC_WRITE`), locks the underlying database rows for concurrent Read and Write Operations. * Pessimistic Read (`LockMode.PESSIMISTIC_READ`), locks other concurrent requests that attempt to update or lock rows in write mode. You can use pessimistic locks in three different scenarios: 1. Using `em.findOne(className, id, { lockMode })` 2. Using `em.lock(entity, lockMode)` 3. Using `QueryBuilder.setLockMode(lockMode)` This is how it looks like in action: ``` await em.transactional(async _em => { await _em.findOne(Author, id, { lockMode: LockMode.PESSIMISTIC_WRITE }); }); // START TRANSACTION // SELECT `e0`.* FROM `author` AS `e0` WHERE `e0`.`id` = ? FOR UPDATE // COMMIT ``` ``` const author = orm.em.findOne(Author, id); // ... await orm.em.transactional(async em => { await em.lock(author, LockMode.PESSIMISTIC_READ); }); // SELECT `e0`.* FROM `author` AS `e0` WHERE `e0`.`id` = ? // START TRANSACTION // SELECT 1 FROM `author` AS `e0` WHERE `e0`.`id` = ? LOCK IN SHARE MODE // COMMIT ``` > Like [MikroORM](https://b4nan.github.io/mikro-orm/)? ⭐️ [Star it](https://github.com/mikro-orm/mikro-orm) on GitHub and share this article with your friends. **Tags:** * [typescript](https://mikro-orm.io/blog/tags/typescript.md) * [javascript](https://mikro-orm.io/blog/tags/javascript.md) * [node](https://mikro-orm.io/blog/tags/node.md) * [sql](https://mikro-orm.io/blog/tags/sql.md) --- # Introducing MikroORM, TypeScript data-mapper ORM with Identity Map · 11 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM This might be the ORM you’ve been looking for… ## Motivation[​](#motivation "Direct link to Motivation") During my early days at university, I remember how quickly I fell in love with object oriented programming and the concepts of [Object-relational mapping](http://hibernate.org/orm/what-is-an-orm/) and [Domain Driven Design](https://stackoverflow.com/questions/1222392/can-someone-explain-domain-driven-design-ddd-in-plain-english-please/1222488#1222488). Back then, I was mainly a PHP programmer (*while we did a lot of Java/Hibernate at school*), so a natural choice for me was to start using [Doctrine](https://www.doctrine-project.org/). A few years ago, when I switched from PHP to Node.js (*and later to TypeScript*), I was really confused. How come there is nothing similar to Hibernate or Doctrine in the JavaScript world? About a year ago, I finally came across [TypeORM](https://typeorm.io/), and when I read this line in the readme I thought I found what I was looking for: > TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate.org/orm/), [Doctrine](http://www.doctrine-project.org/) and [Entity Framework](https://www.asp.net/entity-framework). ![](https://cdn-images-1.medium.com/max/800/1*gWvTBke0c8BFLGR8u_5zSg.jpeg) I started playing with it immediately, but I got disappointed very quickly. No Identity Map that would keep track of all loaded entities. No Unit of Work that would handle transaction isolation. No unified API for references with very strange support for [accessing just the identifier without populating the entity](https://typeorm.io/#/relations-faq/how-to-use-relation-id-without-joining-relation), MongoDB driver (*which I was aiming to use*) was experimental and I had a lot problems setting it up. After a few days of struggle, I went away from it. By that time, I started to think about writing something myself. And that is how [**MikroORM**](https://github.com/mikro-orm/mikro-orm) started! ![](https://cdn-images-1.medium.com/max/800/1*f8phoYPnVRkwuV1wynXz_A.png) > [MikroORM](https://github.com/mikro-orm/mikro-orm) is TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Currently it supports **MongoDB**, **MySQL, PostgreSQL** and **SQLite** databases, but more can be supported via [custom drivers right now](https://b4nan.github.io/mikro-orm/custom-driver/). It has first class TypeScript support, while staying back compatible with [Vanilla JavaScript](https://b4nan.github.io/mikro-orm/usage-with-js/). ## Installation[​](#installation "Direct link to Installation") First install the module via `yarn` or `npm` and do not forget to install the database driver as well. Next you will need to enable support for [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
in `tsconfig.json` via `experimentalDecorators` flag. Then call `MikroORM.init` as part of bootstrapping your application. Last step is to provide forked `EntityManager` for each request, so it will have its own unique [Identity Map](https://b4nan.github.io/mikro-orm/identity-map/). To do so, you can use `EntityManager.fork()` method. Another way, that is more [DI](https://medium.freecodecamp.org/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f) friendly, is to create new [request context](https://b4nan.github.io/mikro-orm/identity-map/#request-context) for each request, which will use some [dark magic](https://github.com/nodejs/node/blob/master/doc/api/async_hooks.md) in the background to always pick the right `EntityManager` for you. ``` # using yarn $ yarn add mikro-orm mongodb # for mongo $ yarn add mikro-orm mysql2 # for mysql $ yarn add mikro-orm pg # for postgresql $ yarn add mikro-orm sqlite # for sqlite # or npm $ npm i -s mikro-orm mongodb # for mongo $ npm i -s mikro-orm mysql2 # for mysql $ npm i -s mikro-orm pg # for postgresql $ npm i -s mikro-orm sqlite # for sqlite ``` ``` { "compilerOptions": { "module": "commonjs", "target": "es2017", "moduleResolution": "node", "declaration": true, "strict": true, "strictPropertyInitialization": false, "experimentalDecorators": true } } ``` ``` const orm = await MikroORM.init({ entities: [Author, Book, BookTag], dbName: 'my-db-name', clientUrl: '...', // defaults to 'mongodb://127.0.0.1:27017' for mongodb driver type: 'mongo', // one of 'mysql', 'postgresql', 'sqlite', defaults to 'mongo' autoFlush: false, // read more here: https://b4nan.github.io/mikro-orm/unit-of-work/ }); console.log(orm.em); // access EntityManager via `em` property ``` ``` const app = express(); app.use((req, res, next) => { req.em = orm.em.fork(); // save the fork to `req` object }); app.get('/books', async (req, res) => { const books = await req.em.find(Book); // use the fork via `req.em` }); ``` ``` const app = express(); // by providing request context, creating forked EntityManager will be handled automatically app.use((req, res, next) => { RequestContext.create(orm.em, next); }); ``` ## Defining entities[​](#defining-entities "Direct link to Defining entities") To [define an entity](https://b4nan.github.io/mikro-orm/defining-entities/), simply create a class and decorate it. Here is an example of `Book` entity defined for MongoDB driver: ``` import { ObjectID } from 'mongodb'; import { Collection, Entity, IEntity, ManyToMany, ManyToOne, PrimaryKey, Property } from 'mikro-orm'; import { Author, BookTag, Publisher } from '.'; @Entity() export class Book { @PrimaryKey() _id: ObjectID; @Property() createdAt = new Date(); @Property({ onUpdate: () => new Date() }) updatedAt = new Date(); @Property() title: string; @ManyToOne() author: Author; @ManyToOne() publisher: Publisher; @ManyToMany({ entity: () => BookTag, inversedBy: 'books' }) tags = new Collection(this); constructor(title: string, author: Author) { this.title = title; this.author = author; } } export interface Book extends IEntity { } ``` As you can see, it’s pretty simple and straightforward. Entities are simple JavaScript objects (*so called POJO*), decorated with `@Entity` decorator (*for TypeScript*), or accompanied with [schema definition object](https://b4nan.github.io/mikro-orm/usage-with-js/) (*for vanilla JavaScript*). No real restrictions are made, you do not have to extend any base class, you are more than welcome to [use entity constructors](https://b4nan.github.io/mikro-orm/entity-constructors/) for specifying required parameters to always keep the entity in valid state. The only requirement is to define the primary key property. ![](https://cdn-images-1.medium.com/max/800/1*NlsF497deWAYi5FSijW9NQ.jpeg) You might be curious about the last line with `Book` as an interface. This is called [interface merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) and it is there to let TypeScript know the entity will have some extra API methods (like `init()` or `isInitialized()`) available as it will be monkey-patched during discovery process. More about this can be found [in the docs](https://b4nan.github.io/mikro-orm/defining-entities/). ## Persisting entities with EntityManager[​](#persisting-entities-with-entitymanager "Direct link to Persisting entities with EntityManager") To save entity state to database, you need to [persist it](https://b4nan.github.io/mikro-orm/entity-manager/). Persist determines whether to use `insert` or `update` and computes appropriate change-set. As a result, only changed fields will be updated in database. [MikroORM](https://b4nan.github.io/mikro-orm/) comes with support for [cascading persist and remove operations](https://b4nan.github.io/mikro-orm/cascading/). Cascade persist is enabled by default, which means that by persisting an entity, all referenced entities will be automatically persisted too. ``` const author = new Author('Jon Snow', 'snow@wall.st'); author.born = new Date(); const publisher = new Publisher('7K publisher'); const book1 = new Book('My Life on The Wall, part 1', author); book1.publisher = publisher; const book2 = new Book('My Life on The Wall, part 2', author); book2.publisher = publisher; const book3 = new Book('My Life on The Wall, part 3', author); book3.publisher = publisher; // just persist books, author and publisher will be automatically cascade persisted await orm.em.persistAndFlush([book1, book2, book3]); // or one by one orm.em.persistLater(book1); orm.em.persistLater(book2); orm.em.persistLater(book3); await orm.em.flush(); // flush everything to database at once ``` ![](https://cdn-images-1.medium.com/max/800/1*x6Oqsg8I4y4Z3FiWtn1ORA.gif) ## Fetching entities[​](#fetching-entities "Direct link to Fetching entities") To fetch entities from database you can use `find()` and `findOne()` methods of `EntityManager`: ``` // find all authors with name matching 'Jon', and populate all of their books const authors = await orm.em.find(Author, { name: /Jon/ }, ['books']); for (const author of authors) { console.log(author.name); // Jon Snow for (const book of author.books) { console.log(book.title); // initialized console.log(book.author.isInitialized()); // true console.log(book.author.id); console.log(book.author.name); // Jon Snow console.log(book.publisher); // just reference console.log(book.publisher.isInitialized()); // false console.log(book.publisher.id); console.log(book.publisher.name); // undefined } } ``` More convenient way of fetching entities from database is by using `EntityRepository`, that carries the entity name so you do not have to pass it to every `find` and `findOne` calls: ``` import { QueryOrder } from 'mikro-orm'; const booksRepository = orm.em.getRepository(Book); // with sorting, limit and offset parameters, populating author references const books = await booksRepository.find({ author: '...' }, ['author'], { title: QueryOrder.DESC }, 2, 1); // or with options object const books = await booksRepository.find({ author: '...' }, { populate: ['author'], limit: 1, offset: 2, sort: { title: QueryOrder.DESC }, }); console.log(books); // Book[] ``` ## Working with references[​](#working-with-references "Direct link to Working with references") Entity associations are mapped to entity references. Reference is an entity that has at least the identifier (*primary key*). This reference is stored in the Identity Map so you will get the same object reference when fetching the same document from database. Thanks to this concept, MikroORM offers unified API for accessing entity references, regardless of whether the entity is initialized or not. Even if you do not populate an association, there will be its reference with primary key set. You can call `await entity.init()` to initialize the entity. This will trigger database call and populate itself, keeping the same reference to entity object in identity map. ``` const book = orm.em.findOne(Book, '...'); console.log(book.author); // reference with ID only, instance of Author entity // this will get the same reference as we already have in `book.author` const author = orm.em.getReference(Author, book.author.id); console.log(author.id); // accessing the id will not trigger any db call console.log(author.isInitialized()); // false console.log(author.name); // undefined console.log(author === book.author); // true // this will trigger db call, we could also use `orm.em.findOne(Author, author.id)` to do the same await author.init(); console.log(author.isInitialized()); // true console.log(author.name); // defined ``` ![](https://cdn-images-1.medium.com/max/800/1*PY1hb2ufRhbevdIFt9jR1g.jpeg) ## Identity Map and Unit of Work[​](#identity-map-and-unit-ofwork "Direct link to Identity Map and Unit of Work") [MikroORM](https://b4nan.github.io/mikro-orm/) uses the Identity Map in background to track objects. This means that whenever you fetch entity via `EntityManager`, MikroORM will keep a reference to it inside its `UnitOfWork`, and will always return the same instance of it, even if you query one entity via different properties. This also means you can compare entities via strict equality operators (`===` and `!==`): ``` const authorRepository = orm.em.getRepository(Author); const jon = await authorRepository.findOne({ name: 'Jon Snow' }, ['books']); const jon2 = await authorRepository.findOne({ email: 'snow@wall.st' }); const authors = await authorRepository.findAll(['books']); // identity map in action console.log(jon === authors[0]); // true console.log(jon === jon2); // true // as we always have one instance, books will be populated also here console.log(jon2.books); ``` Another benefit of Identity Map is that this allows us to skip some database calls. When you try to load an already managed entity by its identifier, the one from Identity Map will be returned, without querying the database. The power of Unit of Work is in running all queries inside a batch and wrapped inside a transaction (*if supported by given driver*). This approach is usually more performant as opposed to firing queries from various places. ## Collections[​](#collections "Direct link to Collections") `OneToMany` and `ManyToMany` collections are stored in a `Collection` wrapper. It implements iterator so you can use `for of` loop to iterate through it. Another way to access collection items is to use bracket syntax like when you access array items. Keep in mind that this approach will not check if the collection is initialized, while using `get` method will throw error in this case. ``` // find author and populate his books collection const author = orm.em.findOne(Author, '...', ['books']); for (const book of author.books) { console.log(book); // instance of Book } author.books.add(book); console.log(author.books.contains(book)); // true author.books.remove(book); console.log(author.books.contains(book)); // false author.books.add(book); console.log(author.books.count()); // 1 console.log(author.books.getItems()); // Book[] console.log(author.books.getIdentifiers()); // array of primary keys of all items author.books.removeAll(); ``` More information about collections can be found [in the docs](https://b4nan.github.io/mikro-orm/collections/). ## What’s next?[​](#whats-next "Direct link to What’s next?") So you read through the whole article, got here and still not satisfied? There are more articles to come (beginning with integration manual for popular frameworks like [Express](https://expressjs.com/) or [NestJS](https://nestjs.com/)), but you can take a look at some advanced features covered in docs right now: * [Smart nested populate](https://b4nan.github.io/mikro-orm/nested-populate/) * [Smart query conditions](https://b4nan.github.io/mikro-orm/query-conditions/) * [Updating entity values with `IEntity.assign()`](https://b4nan.github.io/mikro-orm/entity-helper/) * [Property validation](https://b4nan.github.io/mikro-orm/property-validation/) * [Lifecycle hooks](https://b4nan.github.io/mikro-orm/lifecycle-hooks/) * [Naming strategy](https://b4nan.github.io/mikro-orm/naming-strategy/) * [Usage with NestJS](https://b4nan.github.io/mikro-orm/usage-with-nestjs/) * [Usage with JavaScript](https://b4nan.github.io/mikro-orm/usage-with-js/) ![](https://cdn-images-1.medium.com/max/800/1*4877k4Hq9dPdtmvg9hnGFA.jpeg) To start playing with [MikroORM](https://github.com/mikro-orm/mikro-orm), go through [quick start](https://github.com/mikro-orm/mikro-orm#quick-start) and [read the docs](https://b4nan.github.io/mikro-orm/). You can also take a look at [example integrations with some popular frameworks](http://github.com/mikro-orm/mikro-orm-examples). > *Like* [*MikroORM*](https://b4nan.github.io/mikro-orm/)*? ⭐️* [*Star it*](https://github.com/mikro-orm/mikro-orm) *on GitHub and share this article with your friends.* *This article was originally published on Medium: * **Tags:** * [typescript](https://mikro-orm.io/blog/tags/typescript.md) * [javascript](https://mikro-orm.io/blog/tags/javascript.md) * [node](https://mikro-orm.io/blog/tags/node.md) * [oop](https://mikro-orm.io/blog/tags/oop.md) --- # MikroORM 3: Knex.js, CLI, Schema Updates, Entity Generator and more… · 11 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM New major version of the TypeScript ORM has been released, read about its new features and breaking changes. ### Integrated Knex.js[​](#integrated-knexjs "Direct link to Integrated Knex.js") You probably know Knex.js already, but if you don’t, it is a “batteries included” SQL query builder for **Postgres** , **MSSQL** , **MySQL** , **MariaDB** , **SQLite3** , **Oracle** , and **Amazon Redshift** designed to be flexible, portable, and fun to use. ![](https://cdn-images-1.medium.com/max/649/0*FHWIwC9WTwl2hkQ7.png) Knex.js is now used as both a query builder and a query runner for all SQL drivers. This allows to simplify SQL driver implementations as well as brings some new possibilities. #### Using Knex.js[​](#using-knexjs "Direct link to Using Knex.js") You can access configured knex instance via qb.getKnexQuery() method. Then you can execute it via the Connection.execute() and map the results via EntityManager.map(). ``` const qb = orm.em.createQueryBuilder(Author); qb.update({ name: 'test 123', type: PublisherType.GLOBAL }).where({ id: 123, type: PublisherType.LOCAL }); const knex = qb.getKnexQuery(); // instance of Knex' QueryBuilder // do what ever you need with `knex` const res = await orm.em.getConnection().execute(knex); const entities = res.map(a => orm.em.map(Author, a)); console.log(entities); // Author[] ``` You can also get clear and configured knex instance from the connection via getKnex() method. As this method is not available on the base Connection class, you will need to either manually type cast the connection to AbstractSqlConnection (or the actual implementation you are using, e.g. MySqlConnection), or provide correct driver type hint to your EntityManager instance, which will be then automatically inferred in em.getConnection() method. > Driver and connection implementations are not directly exported from mikro-orm module. You can import them from mikro-orm/dist (e.g. `import { PostgreSqlDriver } from 'mikro-orm/dist/drivers/PostgreSqlDriver'`). ``` const conn = orm.em.getConnection() as AbstractSqlConnection; // you can make sure the `em` is correctly typed to `EntityManager` // or one of its implementations: // const em: EntityManager = orm.em; const knex = conn.getKnex(); // do what ever you need with `knex` const res = await knex; ``` #### Connection Pooling[​](#connection-pooling "Direct link to Connection Pooling") With Knex.js used as a query runner, support for connection pooling is finally available. [Tarn.js](https://github.com/vincit/tarn.js) is used for this internally, using connection pool with min: 2, max: 10 for the MySQL and PG libraries, and a single connection for sqlite3 by default. Use pool option to change this when initializing the ORM. ``` const orm = await MikroORM.init({ entities: [Author, Book], dbName: 'my-db-name', pool: { min: 10, max: 20 }, // see https://github.com/vincit/tarn.js#usage for other pool options }); ``` #### More SQL Drivers?[​](#more-sql-drivers "Direct link to More SQL Drivers?") One of the strongest reasons to integrate Knex.js was that it allows to simplify and unify SQL drivers and opens doors for implementing new SQL drivers. Knex.js currently supports (apart from those currently supported by MikroORM): MSSQL, Oracle and Amazon Redshift. Thanks to AbstractSqlDriver and AbstractSqlConnection classes it should be fairly simple to implement them. I am open for PRs for those drivers, as I would like to focus on developing new ORM features mainly, instead of learning new SQL dialects I have never used. I will be happy to assist to anybody interested — feel free to reach me out either via Slack, email or GitHub issues. ### Simplified Entity Definition[​](#simplified-entity-definition "Direct link to Simplified Entity Definition") Now it is no longer needed to merge entities with IEntity interface, that was polluting entity's interface with internal methods. New interfaces IdentifiedEntity\, UuidEntity\ and MongoEntity\ are introduced, that should be implemented by entities. They are not adding any new properties or methods, keeping the entity's interface clean. IEntity interface has been renamed to AnyEntity\ and it no longer has public methods like toJSON(), toObject() or init(). One can use wrap() method provided by ORM that will enhance property type when needed with those methods (e.g. await wrap(book.author).init()). To keep all methods available on the entity, you can still use interface merging with WrappedEntity\ that both extends AnyEntity\ and defines all those methods. You will need to mark the entity by implementing one of \*Entity interfaces: * IdEntity\ for numeric/string PK on id property (id: number) * UuidEntity\ for string PK on uuid property (uuid: string) * MongoEntity\ for mongo, where id: string and \_id: ObjectId are required * AnyEntity\ for other possible properties (fill the PK property name to PK parameter, e.g.: AnyEntity\') ``` @Entity() export class User implements IdEntity { @PrimaryKey() id!: number; @Property() name!: string; @OneToOne() address?: Address; @ManyToMany() cars = new Collection(this); constructor(name: string) { this.name = name; } } ``` To keep all public methods that were part of IEntity interface in v2, you can use [WrappedEntity\ via interface merging](https://mikro-orm.io/docs/defining-entities#using-wrappedentity-interface). ### Nested Queries[​](#nested-queries "Direct link to Nested Queries") SQL driver now support nested where and orderBy conditions. This means that you can query by properties of a relationship and the relation will be automatically joined for you. They are available both in EntityManager and QueryBuilder APIs. ``` const book = await orm.em.findOne(Book, { author: { name: 'Jon Snow', address: { street: 'Downing Street', }, }, }, ['author.address']); console.log(book.author.name); // 'Jon Snow' console.log(book.author.address.street); // 'Downing Street' ``` ### Strict Typing of Queries[​](#strict-typing-of-queries "Direct link to Strict Typing of Queries") Previously the where parameter of EntityManager’s find methods (find(), findOne(), count()) was weakly typed. It allowed users to pass pretty much anything there. Now the query is strictly typed, only entity properties and operators can be used and the type of property value is also checked. ``` // correct query em.find(Author, { favouriteBook: { author: { name: '...' }, }, age: { $gte: 40 }, // operators are also supported }); // 2 errors will be reported here em.find(Author, { favouriteBook: { author: { born: 'test' }, // string instead of Date }, age: { $lte: 'nan' }, // string instead of number }); ``` ### Improved Schema Generator[​](#improved-schema-generator "Direct link to Improved Schema Generator") SchemaGenerator now supports creating, updating and dropping the schema. You can either get the SQL queries as array of strings or directly run them on the database. > Always check the generated SQL first before running it. There is also new columnType property attribute you can use to specify the database specific column type explicitly. ### Migrations[​](#migrations "Direct link to Migrations") ![](https://cdn-images-1.medium.com/max/628/0*b3RWZY_ROCrJs3RE.jpeg) Better way to handle schema updates than using the SchemaGenerator directly is to use Migrations. MikroORM 3 has [integrated support for migrations](https://mikro-orm.io/docs/migrations) via [umzug](https://github.com/sequelize/umzug). It allows you to generate migrations with current schema differences. By default, each migration will be all executed inside a transaction, and all of them will be wrapped in one master transaction, so if one of them fails, everything will be rolled back. ### Generating Entities from Current Database[​](#generating-entities-from-current-database "Direct link to Generating Entities from Current Database") As a counterpart to the SchemaGenerator that propagates changes in your entities to the database schema, there is now [EntityGenerator](https://mikro-orm.io/docs/entity-generator) to help you with reverse engineering current database schema and creating entities based on it. It supports basic entity definition including ManyToOne and OneToOne relationships. Currently ManyToMany will be generated as additional entity with two ManyToOne relations and you will need to refactor this yourself. While it can help a lot, there is quite a lot of room for improvement. In future I would like to implement proper support for ManyToMany relations as well for enums and indexes. Another possible extension would be to allow editing existing entities (syncing them with current schema). ### CLI[​](#cli "Direct link to CLI") While you can use SchemaGenerator and EntityGenerator manually, much easier way is to use [new CLI tool](https://mikro-orm.io/docs/installation#setting-up-the-commandline-tool). Simply create configuration file in root directory or add its path to package.json. TypeScript files are also supported via ts-node: ``` { "name": "your-app", "dependencies": { ... }, "mikro-orm": { "useTsNode": true, "configPaths": [ "./src/mikro-orm.config.ts", "./dist/mikro-orm.config.js" ] } } ``` Now you can use the CLI with help of [npx](https://github.com/npm/npx): ``` $ npx mikro-orm Usage: mikro-orm [options] Commands: mikro-orm cache:clear Clear metadata cache mikro-orm cache:generate Generate metadata cache for production mikro-orm generate-entities Generate entities based on current database schema mikro-orm database:import Imports the SQL file to the database mikro-orm schema:create Create database schema based on current metadata mikro-orm schema:drop Drop database schema based on current metadata mikro-orm schema:update Update database schema based on current metadata mikro-orm migration:create Create new migration with current schema diff mikro-orm migration:up Migrate up to the latest version mikro-orm migration:down Migrate one step down mikro-orm migration:list List all executed migrations mikro-orm migration:pending List all pending migrations mikro-orm debug Debug CLI configuration Options: -v, --version Show version number [boolean] -h, --help Show help [boolean] Examples: mikro-orm schema:update --run Runs schema synchronization ``` To verify your setup, you can use the mikro-orm debug command. Once you have it configured properly, you can also re-use it when initializing the ORM: ``` // when no options parameter is provided, CLI config will be used const orm = await MikroORM.init(); ``` ### Custom Mapping Types[​](#custom-mapping-types "Direct link to Custom Mapping Types") ![](https://cdn-images-1.medium.com/max/500/0*zAn0BtH_iz7b8Ywj.jpg) With [Custom Types](https://mikro-orm.io/docs/custom-types/) we can now enhance how the database value will be represented in the ORM. You can define custom types by extending Type abstract class, it has 4 optional methods: * convertToDatabaseValue(value: any, platform: Platform): any Converts a value from its JS representation to its database representation of this type. By default returns unchanged value. * convertToJSValue(value: any, platform: Platform): any Converts a value from its database representation to its JS representation of this type. By default returns unchanged value. * toJSON(value: any, platform: Platform): any Converts a value from its JS representation to its serialized JSON form of this type. By default converts to the database value. * getColumnType(prop: EntityProperty, platform: Platform): string Gets the SQL declaration snippet for a field of this type. By default returns columnType of given property. Here is a simplified version of DateType that is already present in the ORM: ``` import { Type, Platform, EntityProperty, ValidationError } from 'mikro-orm'; export class DateType extends Type { convertToDatabaseValue(value: any, platform: Platform): any { return value.toISOString().substr(0, 10); } convertToJSValue(value: any, platform: Platform): any { return new Date(value); } getColumnType(): string { return 'date'; } } ``` ``` @Entity() export class FooBar implements IdEntity { @PrimaryKey() id!: number; @Property({ type: DateType }) born?: Date; } ``` ### And Many More…[​](#and-many-more "Direct link to And Many More…") There are many more new features, see the [changelog](https://github.com/mikro-orm/mikro-orm/blob/master/CHANGELOG.md) to read the full list. Here are few of them worth mentioning: * [Improved support for References](https://mikro-orm.io/docs/entity-references/) * [Native Enum support](https://mikro-orm.io/docs/defining-entities/#enums) * [em.findAndCount()](https://mikro-orm.io/docs/entity-manager#fetching-paginated-results) and [em.findOneOrFail()](https://mikro-orm.io/docs/entity-manager#handling-not-found-entities) methods * [ReflectMetadataProvider](https://mikro-orm.io/docs/metadata-providers/#reflectmetadataprovider) as a fast alternative to ts-morph reflection * Improved logging with query highlighting * [Support for bundling via Webpack](https://mikro-orm.io/docs/deployment/#deploy-a-bundle-of-entities-and-dependencies-with-webpack) * Eager loading * [Read Connections](https://mikro-orm.io/docs/read-connections) * More strict entity definition validation ### Notable Breaking Changes[​](#notable-breaking-changes "Direct link to Notable Breaking Changes") Here is a short list of breaking changes. You can see the full list in the docs: . #### Auto-flushing Disabled by Default[​](#auto-flushing-disabled-by-default "Direct link to Auto-flushing Disabled by Default") > *If you had* *autoFlush: false in your ORM configuration before, you can now remove this line, no changes are needed in your app.* Default value for autoFlush is now false. That means you need to call em.flush() yourself to persist changes into database. You can still change this via ORM's options to ease the transition but generally it is not recommended as it can cause unwanted small transactions being created around each persist. ``` orm.em.persist(new Entity()); // no auto-flushing by default await orm.em.flush(); await orm.em.persist(new Entity(), true); // you can still use second parameter to auto-flush ``` #### Transactions API[​](#transactions-api "Direct link to Transactions API") Transactions now require using em.transactional() method, previous methods beginTransaction/commit/rollback are now removed. ``` await orm.em.transactional(async _em => { //... do some work const user = new User(...); user.name = 'George'; _em.persistLater(user); }); ``` ### Making it a bit more Professional…[​](#making-it-a-bit-more-professional "Direct link to Making it a bit more Professional…") Not a big deal, but probably worth mentioning — MikroORM’s repository has been transferred to new [MikroORM GitHub Organization](https://github.com/mikro-orm) and the website is now moved to [mikro-orm.io](https://mikro-orm.io). Old links should be properly redirected, if you find some 404, please let me know thru GitHub issues! Website has also been redesigned — now it is built with Docusaurus (v2) and provides fulltext search by Algolia. The docs are now also [versioned](https://mikro-orm.io/versions). [Check it out!](https://mikro-orm.io) ![](https://cdn-images-1.medium.com/max/1024/1*2pdwLgyPZNltJQ_2j8poSQ.png) ### What’s next?[​](#whats-next "Direct link to What’s next?") Here are some features I am planning to work in the near future: * Composite primary keys * Transactions in MongoDB * Complex hydration of joined result sets * Slow query log * M :N support in entity generator There are also some interesting suggestion in the Github issues, like [Dataloader integration](https://github.com/mikro-orm/mikro-orm/issues/266). #### WDYT?[​](#wdyt "Direct link to WDYT?") So that is MikroORM 3, what do you think about it? What features or changes would you like to see next? Or what part of the documentation should be improved and how? > *Like* [*MikroORM*](https://mikro-orm.io)*? ⭐️* [*Star it*](https://github.com/mikro-orm/mikro-orm) *on GitHub and share this article with your friends.* **Tags:** * [typescript](https://mikro-orm.io/blog/tags/typescript.md) * [javascript](https://mikro-orm.io/blog/tags/javascript.md) * [node](https://mikro-orm.io/blog/tags/node.md) * [sql](https://mikro-orm.io/blog/tags/sql.md) --- # MikroORM 4.1: Let’s talk about performance · 3 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM I just shipped version 4.1 of [MikroORM](https://github.com/mikro-orm/mikro-orm), the TypeScript ORM for Node.js, and I feel like this particular release deserves a bit more attention than a regular feature release. ![](https://cdn-images-1.medium.com/max/725/0*R2CETMgg1344gf0V.jpg) ### So what changed?[​](#so-what-changed "Direct link to So what changed?") This release had only one clear goal in mind — the performance. It all started with [an issue](https://github.com/mikro-orm/mikro-orm/issues/732) pointing out that flushing 10k entities in a single unit of work is very slow. While this kind of use case was never a target for me, I started to see all the possibilities the Unit of Work pattern offers. ### Batch inserts, updates and deletes[​](#batch-inserts-updates-and-deletes "Direct link to Batch inserts, updates and deletes") The biggest performance killer was the amount of queries — even if the query is as simple and optimised as possible, firing 10k of those will be always quite slow. For inserts and deletes, it was quite trivial to group all the queries. A bit more challenging were the updates — to batch those, MikroORM now uses case statements. As a result, when you now flush changes made to one entity type, only one query per given operation (create/update/delete) will be executed. This brings significant difference, as we are now executing fixed number of queries (in fact the changes are batched in chunks of 300 items). ``` for (let i = 1; i <= 5; i++) { const u = new User(`Peter ${i}`, `peter+${i}@foo.bar`); em.persist(u); } await em.flush(); // insert into `user` (`name`, `email`) values // ('Peter 1', 'peter+1@foo.bar'), // ('Peter 2', 'peter+2@foo.bar'), // ('Peter 3', 'peter+3@foo.bar'), // ('Peter 4', 'peter+4@foo.bar'), // ('Peter 5', 'peter+5@foo.bar'); ``` ``` for (const user of users) { user.name += ' changed!'; } await em.flush(); // update `user` set // `name` = case // when (`id` = 1) then 'Peter 1 changed!' // when (`id` = 2) then 'Peter 2 changed!' // when (`id` = 3) then 'Peter 3 changed!' // when (`id` = 4) then 'Peter 4 changed!' // when (`id` = 5) then 'Peter 5 changed!' // else `priority` end // where `id` in (1, 2, 3, 4, 5) ``` ``` em.remove(users); await em.flush(); // delete from `user` where `id` in (1, 2, 3, 4, 5) ``` ### JIT compilation[​](#jit-compilation "Direct link to JIT compilation") Second important change in 4.1 is JIT compilation. Under the hood, MikroORM now first generates simple functions for comparing and hydrating entities, that are tailored to their metadata definition. The main difference is that those generated functions are accessing the object properties directly (e.g. o.name), instead of dynamically (e.g. o\[prop.name]), as all the information from metadata are inlined there. This allows V8 to better understand the code so it is able to run it faster. ### Results[​](#results "Direct link to Results") Here are the results for a simple 10k entities benchmark: ![](https://cdn-images-1.medium.com/max/1024/1*aROevToSrzcQdPsPzXYnSQ.png) In average, inserting 10k entities takes around 70ms with sqlite, updates are a tiny bit slower. You can see results for other drivers here: . ![](https://cdn-images-1.medium.com/max/400/0*2WaopAkejC3T6213.jpg) ### Acknowledgement[​](#acknowledgement "Direct link to Acknowledgement") Kudos to [Marc J. Schmidt](https://github.com/marcj), the author of the initial issue, as without his help this would probably never happen, or at least not in near future. Thanks a lot! > *Like* [*MikroORM*](https://mikro-orm.io/)*? ⭐️* [*Star it*](https://github.com/mikro-orm/mikro-orm) *on GitHub and share this article with your friends. If you want to support the project financially, you can do so via* [*GitHub Sponsors*](https://github.com/sponsors/B4nan)*.* **Tags:** * [typescript](https://mikro-orm.io/blog/tags/typescript.md) * [javascript](https://mikro-orm.io/blog/tags/javascript.md) * [node](https://mikro-orm.io/blog/tags/node.md) * [sql](https://mikro-orm.io/blog/tags/sql.md) --- # MikroORM 4: Filling the Gaps · 13 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM After 4 months of active development, I am thrilled to announce the release of [MikroORM 4](http://github.com/mikro-orm/mikro-orm). When I started to work on v4, the goal was to make it relatively small release, mainly to drop support for TypeScript 3.6 and Node.js 8, and to split the project into multiple packages, so we can have more fine grained control over the dependencies (mainly because of ts-morph having TS as a runtime dependency). > But what a major release would that be, without having a bunch of new features as well, right? ![Photo by Ryoji Iwata on Unsplash](https://cdn-images-1.medium.com/max/1024/0*JU7VN0bgkL57RnZJ) ### Quick summary of 3.x releases[​](#quick-summary-of-3x-releases "Direct link to Quick summary of 3.x releases") Before I dive into all the things v4, let’s recap the major features that landed in 3.x releases: * [Defining entities via EntitySchema](https://mikro-orm.io/docs/define-entity#entityschema-low-level-api) * [Propagation of changes to m:1/1:1 to inverse sides](https://mikro-orm.io/docs/propagation) * [Transactions in MongoDB](https://mikro-orm.io/docs/usage-with-mongo#transactions) * [Composite primary keys](https://mikro-orm.io/docs/composite-keys) ### Monorepo[​](#monorepo "Direct link to Monorepo") The first major change I want to talk about is the split into multiple packages. As mentioned above, the biggest motivation for this change was to get rid of TS as a runtime dependency, when it is not needed. Another nice example is knex, which is used as a base layer for SQL driver, but has no meaning for mongodb users. Lastly, it turned out Highlight.js, that was used for query highlighting, is also quite fat and slow, so I ended up writing custom highlighters that are built for CLI and are (almost) dependency free. In v4, there are 12 packages and 2 highlighters, you install only what you use, and you have control over what is needed in production and what is just a dev dependency. This is especially useful for serverless users, where cold start speeds matter. It felt natural to offer some shortcuts on the EntityManager and EntityRepository level, so we now have flavours of those classes in place, that offer things like em.execute(sql) or em.aggregate(). To access those driver specific methods, be sure to use the classes from driver packages: ``` import { EntityManager } from '@mikro-orm/mysql'; // or any other SQL driver package const em: EntityManager; const qb = await em.createQueryBuilder(…); // or `em.execute()`, `em.getKnex()`, ... ``` > Database connectors like pg or sqlite3 are now dependencies of the driver packages (e. g. @mikro-orm/sqlite). ### Filters[​](#filters "Direct link to Filters") Probably the most interesting feature of v4 are [filters](https://mikro-orm.io/docs/filters/), also known as association scopes. They allow you to define data visibility rules, both global and bound to entity. One common application of filters are soft deletes, or automatic tenant conditions. ``` @Entity() @Filter({ name: 'expensive', cond: { price: { $gt: 1000 } } }) @Filter({ name: 'long', cond: { 'length(text)': { $gt: 10000 } } }) @Filter({ name: 'hasAuthor', cond: { author: { $ne: null } }, default: true }) @Filter({ name: 'writtenBy', cond: args => ({ author: { name: args.name } }) }) export class Book { ... } const books1 = await orm.em.find(Book, {}, { filters: ['long', 'expensive'], }); const books2 = await orm.em.find(Book, {}, { filters: { hasAuthor: false, long: true, writtenBy: { name: 'God' } }, }); ``` Filters are applied to those methods of EntityManager: find(), findOne(), findAndCount(), findOneOrFail(), count(), nativeUpdate() and nativeDelete(). Filters can be parametric, the parameter can be also in form of callback (possibly async). You can also make the filter enabled by default. > Filter can be defined at the entity level, dynamically via EM (global filters) or in the ORM configuration. #### Global Filters[​](#global-filters "Direct link to Global Filters") We can also register filters dynamically via EntityManager API. We call such filters global. They are enabled by default (unless disabled via last parameter in addFilter() method), and applied to all entities. You can limit the global filter to only specified entities. > *Filters as well as filter params set on the EM will be copied to all its forks.* ``` // bound to entity, enabled by default em.addFilter('writtenBy', args => ({ author: args.id }), Book); // global, enabled by default, for all entities em.addFilter('tenant', args => { ... }); // global, enabled by default, for only specified entities em.addFilter('tenant', args => { ... }, [Author, Book]); ... // set params (probably in some middleware) em.setFilterParams('tenant', { tenantId: 123 }); em.setFilterParams('writtenBy', { id: 321 }); ``` ### EventSubscribers and flush events[​](#eventsubscribers-and-flush-events "Direct link to EventSubscribers and flush events") As opposed to regular lifecycle hooks, we can now use [EventSubscriber](https://mikro-orm.io/docs/lifecycle-hooks/#eventsubscriber) to hook to multiple entities or if you do not want to pollute the entity prototype. All methods are optional, if you omit the getSubscribedEntities() method, it means you are subscribing to all entities. ``` import { EntityName, EventArgs, EventSubscriber, Subscriber } from '@mikro-orm/core'; @Subscriber() export class AuthorSubscriber implements EventSubscriber { getSubscribedEntities(): EntityName[] { return [Author]; } async afterCreate(args: EventArgs): Promise { // ... } async afterUpdate(args: EventArgs): Promise { // ... } } ``` #### Flush events[​](#flush-events "Direct link to Flush events") There is a [special kind of events](https://mikro-orm.io/docs/lifecycle-hooks/#flush-events) executed during the commit phase (flush operation). They are executed before, during and after the flush, and they are not bound to any entity in particular. * beforeFlush is executed before change sets are computed, this is the only event where it is safe to persist new entities. * onFlush is executed after the change sets are computed. * afterFlush is executed as the last step just before the flush call resolves. it will be executed even if there are no changes to be flushed. Flush event args will not contain any entity instance, as they are entity agnostic. They do contain additional reference to the UnitOfWork instance. Following example demonstrates the hidden power of flush events — they allow to hook into the change set tracking, adjusting what will be persisted and how. Here we try to find a CREATE change set for entity FooBar, and if there is any, we automatically create a new FooBaz entity, connecting it to the FooBar one. This kind of operations was previously impossible, as in regular lifecycle hooks we can only adjust the entity that triggers the event. ``` @Subscriber() export class FooBarSubscriber implements EventSubscriber { async onFlush(args: FlushEventArgs): Promise { const changeSets = args.uow.getChangeSets(); const cs = changeSets.find(cs => cs.type === ChangeSetType.CREATE && cs.entity instanceof FooBar); if (cs) { const baz = new FooBaz(); baz.name = 'dynamic'; cs.entity.baz = baz; args.uow.computeChangeSet(baz); args.uow.recomputeSingleChangeSet(cs.entity); } } } const bar = new FooBar(); bar.name = 'bar'; await em.persistAndFlush(bar); ``` ### Joined loading strategy[​](#joined-loading-strategy "Direct link to Joined loading strategy") Loading of complex relations now support so called [JOINED strategy](https://mikro-orm.io/docs/loading-strategies/). Its name is quite self-explanatory — instead of the default (SELECT\_IN) strategy, it uses single SQL query and maps the result to multiple entities. ``` // with the default SELECT_IN strategy, following will issue 2 queries const author = await orm.em.findOne(Author, 1, { populate: ['books'] }); // select * from author where id = 1; // select * from book where author_id in (1); // we can now use JOINED strategy to use a single query const author = await orm.em.findOne(Author, 1, { populate: ['books'], strategy: LoadStrategy.JOINED }); // select a.*, b.* from author a left join book b on b.author_id = a.id where a.id = 1; ``` ### Single Table Inheritance[​](#single-table-inheritance "Direct link to Single Table Inheritance") [STI is an inheritance mapping strategy](https://mikro-orm.io/docs/inheritance-mapping/#single-table-inheritance) where all classes of a hierarchy are mapped to a single database table. In order to distinguish which row represents which type in the hierarchy a so-called discriminator column is used. > If no discriminator map is provided, it will be generated automatically. Following example defines 3 entities — they will all be stored in a single database table called person, with a special column named type, that will be used behind the scenes to know what class should be used to represent given row/entity. ``` @Entity({ discriminatorColumn: 'type', discriminatorMap: { person: 'Person', employee: 'Employee', owner: 'Owner' }, }) export class Person { // … } @Entity() export class Employee extends Person { // … } @Entity() export class Owner extends Person { // … } ``` ### Embeddables[​](#embeddables "Direct link to Embeddables") [Embeddables](https://mikro-orm.io/docs/embeddables/) are classes which are not entities themselves, but are embedded in entities and can also be queried. You’ll mostly want to use them to reduce duplication or separating concerns. Value objects such as date range or address are the primary use case for this feature. > Embeddables can only contain properties with basic @Property() mapping. Following example will result in a single database table, where the address fields will be inlined (with prefix) to the user table. ``` @Entity() export class User { @Embedded() address!: Address; } @Embeddable() export class Address { @Property() street!: string; @Property() postalCode!: string; @Property() city!: string; @Property() country!: string; } ``` ### Lazy scalar properties[​](#lazy-scalar-properties "Direct link to Lazy scalar properties") In MikroORM 4, we can mark any property as [lazy: true](https://mikro-orm.io/docs/defining-entities#lazy-scalar-properties) to omit it from the select clause. This can be handy for properties that are too large and you want to have them available only some times, like a full text of an article. When we need such value, we can use populate parameter to load it as if it was a reference. > If the entity is already loaded and you need to populate a lazy scalar property, you might need to pass refresh: true in the FindOptions. ``` @Entity() export class Book { @Property({ columnType: 'text', lazy: true }) text: string; } const b1 = await em.find(Book, 1); // this will omit the `text` property const b2 = await em.find(Book, 1, { populate: ['text'] }); // this will load the `text` property ``` ### Computed Properties[​](#computed-properties "Direct link to Computed Properties") Another small enhancement in entity definition is the [@Formula() decorator](https://mikro-orm.io/docs/defining-entities/#formulas). It can be used to map some SQL snippet to your entity. The SQL fragment can be as complex as you want and even include subselects. ``` @Entity() export class Box { @Formula('obj_length * obj_height * obj_width') objectVolume?: number; } ``` > Formulas will be added to the select clause automatically. In case you are facing problems with NonUniqueFieldNameException, you can define the formula as a callback that will receive the entity alias in the parameter. ### Type-safe references[​](#type-safe-references "Direct link to Type-safe references") Next feature I would like to mention is rather hidden, and is a bit experimental. In MikroORM 4, all EntityManager and EntityRepository methods for querying entities (e.g. find()) will now return special Loaded type, where we automatically infer what relations are populated. It dynamically adds special get() method to both Reference and Collection instances, that you can use to ensure the relation is loaded on the type level. ``` @Entity() export class Book { @PrimaryKey() id: number; @ManyToOne(() => Author, { wrappedReference: true }) author: IdentifiedReference; @ManyToOne(() => Publisher, { wrappedReference: true }) publisher: IdentifiedReference; @ManyToMany(() => BookTag) tags = new Collection(this); } ``` ``` // will return `Loaded` const book = await orm.em.findOne(Book, 1, { populate: ['publisher', 'tags'] }); // the `Book.publisher.get()` will be available as we explicitly populated that relation console.log(book.publisher.get().name); console.log(book.publisher.$.name); // we can also use the `$` alias // on the other hand, `Book.author` was not populated, so trying to access `get()` will fail to compile console.log(book.author.get().name); // fails with `TS2339: Property 'get' does not exist on type 'IdentifiedReference'` // we can also use it on collections console.log(book.tags.get().map(t => t.name)); // `get()` will return array of items ``` ### QueryBuilder improvements[​](#querybuilder-improvements "Direct link to QueryBuilder improvements") There have been quite a lot of small adjustments in QueryBuilder, to name a few things: * support for subqueries and qb.ref() * using sql snippets with qb.raw() * pagination support via subselects (QueryFlag.PAGINATE) * update & delete queries with auto-joining Here are few examples of those features in action: ``` const qb = em.createQueryBuilder(Book); qb.update({ price: qb.raw('price + 1') }).where({ uuid: '123' }); // update `book` set `price` = price + 1 where `uuid_pk` = ?' ``` ``` // following example assumes that there is a virtual (persist: false) property // on `Author` entity named `booksTotal` const qb1 = em.createQueryBuilder(Book, 'b'); qb1.count('b.uuid', true).where({ author: qb1.ref('a.id') }); const qb2 = em.createQueryBuilder(Author, 'a'); qb2.select(['*', qb1.as('Author.booksTotal')]).orderBy({ booksTotal: 'desc' }); // select `a`.*, (select count(distinct `b`.`uuid_pk`) as `count` from `book` as `b` where `b`.`author_id` = `a`.`id`) as `books_total` // from `author` as `a` // order by `books_total` desc ``` ``` // following example assumes that there is a virtual (persist: false) property // on `Author` entity named `booksTotal` const knex = em.getKnex(); const qb1 = em.createQueryBuilder(Book, 'b').count('b.uuid', true).where({ author: knex.ref('a.id') }).getKnexQuery(); const qb2 = em.createQueryBuilder(Author, 'a'); qb2.select('*').withSubQuery(qb1, 'a.booksTotal').where({ 'a.booksTotal': { $in: [1, 2, 3] } }); // select `a`.* // from `author` as `a` // where (select count(distinct `b`.`uuid_pk`) as `count` from `book` as `b` where `b`.`author_id` = `a`.`id`) in (?, ?, ?) ``` ``` const qb = em.createQueryBuilder(Publisher); qb.update({ name: 'test 123' }).where({ $or: [{ books: { author: 123 } }, { books: { title: 'book' } }] }); // update `publisher` set `name` = ? // where `id` in (select `e0`.`id` from ( // select distinct `e0`.`id` from `publisher` as `e0` left join `book` as `e1` on `e0`.`id` = `e1`.`publisher_id` where (`e1`.`author_id` = ? or `e1`.`title` = ?) // ) as `e0`) ``` ### And many many more…[​](#and-many-many-more "Direct link to And many many more…") * em.begin/commit/rollback() methods are back * using file globs for discovery (\*\*/\*.entity.ts) * custom driver exceptions (UniqueConstraintViolationException, …) * adding items to not-initialized collections * bulk deletes and other performance improvements * inference of custom repository type (EntityRepositoryType) * [property serializers](https://mikro-orm.io/docs/serializing#property-serializers) See the [changelog](https://github.com/mikro-orm/mikro-orm/blob/master/CHANGELOG.md) for full list of new features and fixes. #### More example integrations[​](#more-example-integrations "Direct link to More example integrations") * Koa: * GraphQL: * Serverless: ### Upgrading[​](#upgrading "Direct link to Upgrading") For smooth upgrading, read the full [upgrading guide](https://mikro-orm.io/docs/upgrading-v3-to-v4). Here are few notable breaking changes: * Default metadata provider is ReflectMetadataProvider, to use ts-morph, you need to install it from @mikro-orm/reflection and explicitly provide it in the ORM configuration. If you want to use ReflectMetadataProvider, be sure to see the [list of its limitations](https://mikro-orm.io/docs/metadata-providers/#limitations-and-requirements). * TsMorphMetadataProvider now uses \*.d.ts files in production mode, so be sure to enable them in your tsconfig.json. * @mikro-orm/core package is not dependent on knex, and therefore cannot provide methods like createQueryBuilder() — instead, those methods exist on SqlEntityManager. You can import it from the driver package, e.g. `import { EntityManager } from '@mikro-orm/mysql;`. * To use CLI, you need to install @mikro-orm/cli package. * When using folder based discovery, the options entitiesDirs and entitiesDirsTs are now removed in favour of entities and entitiesTs. You can now mix entity references with folders and file globs, negative globs are also supported. * For Nest.js users, there is a new [@mikro-orm/nestjs](https://github.com/mikro-orm/nestjs) package, which is a fork of the [nestjs-mikro-orm](https://github.com/dario1985/nestjs-mikro-orm) module with changes needed for
MikroORM 4. ### What’s next?[​](#whats-next "Direct link to What’s next?") Here are some features I’d like to work on in the near future: * Improved schema diffing * ts-morph reflection via custom TS compiler plugin * Query caching * MS SQL Server support ### WDYT?[​](#wdyt "Direct link to WDYT?") So this is MikroORM 4, what do you think about it? What features or changes would you like to see next? Or what part of the documentation should be improved and how? > *Like* [*MikroORM*](https://mikro-orm.io/)*? ⭐️* [*Star it*](https://github.com/mikro-orm/mikro-orm) *on GitHub and share this article with your friends. If you want to support the project financially, you can do so via* [*GitHub Sponsors*](https://github.com/sponsors/B4nan)*.* **Tags:** * [typescript](https://mikro-orm.io/blog/tags/typescript.md) * [javascript](https://mikro-orm.io/blog/tags/javascript.md) * [node](https://mikro-orm.io/blog/tags/node.md) * [sql](https://mikro-orm.io/blog/tags/sql.md) --- # MikroORM 5.8 released · 6 min read [![Martin Adámek](https://avatars1.githubusercontent.com/u/615580?s=460\&v=4)](https://github.com/B4nan) [Martin Adámek](https://github.com/B4nan) Author of MikroORM After a longer pause, I am pleased to announce next feature release - [MikroORM v5.8](https://github.com/mikro-orm/mikro-orm/releases/tag/v5.8.0), probably the last one before [v6](https://github.com/mikro-orm/mikro-orm/discussions/3593). While I don't blog about the feature releases very often, I feel like this one deserves more attention. Why? ## Fine-grained control over upserting[​](#fine-grained-control-over-upserting "Direct link to Fine-grained control over upserting") Upserting, so `em.upsert` and `em.upsertMany`, is relatively new addition to the [EntityManager toolkit](https://mikro-orm.io/docs/entity-manager#upsert). It allows you to run `insert on conflict` queries (and works in MongoDB too!) to ensure a record exists in the database, updating its state when necessary. The default behavior was to always prefer using the primary key for the `on conflict` condition, and fallback to the first unique property with a value. While this was a sane default, it quickly appeared it won't work very well for some use cases - the most common one being entities with UUID primary keys. In this case, we cannot use the primary key for the conflict resolution - but we often generate this value via property initializers, so the default logic would prefer it (and fail to find a match). A way around this was using the POJO signature of `em.upsert`, and not providing the UUID value in the data - but that leads to counter issues with drivers that don't have an easy way to generate UUID on server side (via database default). And it is not just about drivers like SQLite, where we don't have a UUID function on database level - the very same problem comes with PostgreSQL too, when you want to generate a UUID v7 key - those are not yet supported out of box. MikroORM v5.8 now offers a fine-grained control over how the upserting logic works. You can use the following advanced options now: * `onConflictFields?: (keyof T)[]` to control the conflict clause * `onConflictAction?: 'ignore' | 'merge'` used ignore and merge as that is how the QB methods are called * `onConflictMergeFields?: (keyof T)[]` to control the merge clause * `onConflictExcludeFields?: (keyof T)[]` to omit fields from the merge clause Here is a more complex example, note that it would work with both signatures the same way - we can pass in an array of entities too, and leverage the runtime defaults we set via property initializers. ``` const [author1, author2, author3] = await em.upsertMany(Author, [{ ... }, { ... }, { ... }], { onConflictFields: ['email'], // specify a manual set of fields pass to the on conflict clause onConflictAction: 'merge', onConflictExcludeFields: ['id'], }); ``` This will generate query similar to the following: ``` insert into "author" ("id", "current_age", "email", "foo") values (1, 41, 'a1', true), (2, 42, 'a2', true), (5, 43, 'a3', true) on conflict ("email") do update set "current_age" = excluded."current_age", "foo" = excluded."foo" returning "_id", "current_age", "foo", "bar" ``` It might not be obvious on the first sight, but since we specified `onConflictExcludeFields: ['id']` in this example, the primary key value will be narrowed via the `returning` clause - in the third item, we specify a wrong primary key (`5`) which is not matching the database, and as a result, we get a property hydrated entity with the correct primary key we get from the database. There were other hidden improvements made to the upserting, namely the `qb.onConflict()` now maps property names to column names properly, the `em.upsertMany` runs in batches now (respecting the global `batchSize` option), and the result of `em.upsert/Many` now contains all the values with database defaults, so it truly represents the current database state and not just the data provided by you in the payload. ## Filters with Joined loading strategy[​](#filters-with-joined-loading-strategy "Direct link to Filters with Joined loading strategy") Another important change is about [filters](https://mikro-orm.io/docs/filters). Previously, the filters were applied only when using the `select-in` strategy, as part of the where condition. Since v5.8, when using the `joined` strategy, filters will be also applied to the query, via join on conditions. This means the strategies should now behave the same finally. I am currently considering whether we should change the default strategy in v6, as this was the last big feature missing in the `joined` strategy implementation. A connected change to this one is how the `populateWhere` option works - those conditions are now also applied as `join on` conditions when using the `joined` strategy. ## New `Collection` helpers[​](#new-collection-helpers "Direct link to new-collection-helpers") Lastly, there are several new `Collection` methods, so its interface is more in-line with the `Array` prototype: * `find()` to find the first matching element * `map()` to map elements * `filter()` to filter elements * `reduce()` to map the array to a dictionary * `slice()` to extract a portion of the elements And few more convenience methods: * `exists()` to check if a matching element exists * `isEmpty()` to check if the collection is empty * `indexBy()` to map the items to a dictionary, if there are more items with the same key, only the first one will be present. The `indexBy()` can be pretty helpful, let's have a closer look at what it is capable of. If we specify only the first parameter, we get a map of the elements indexed by a given key: ``` // user.config is `Collection