import * as knex from 'knex' ; import { ArrayCollection } from './ArrayCollection' ; import { Store } from './Store' ; import { EntityInterface , ProxyInterface } from './EntityInterface' ; import { Mapping } from './Mapping' ; import { Scope } from './Scope' ; import { QueryBuilder } from './QueryBuilder' ; import { MetaData } from './MetaData' ; import { EntityProxy } from './EntityProxy' ; export class export class UnitOfWork { type { string } public static STATE_UNKNOWN : string = 'unknown' ; type { string } public static STATE_CLEAN : string = 'clean' ; type { string } public static STATE_DIRTY : string = 'dirty' ; type { string } public static STATE_NEW : string = 'new' ; type { string } public static STATE_DELETED : string = 'deleted' ; type { string } public static RELATIONSHIP_ADDED : string = 'relationship_new' ; type { string } public static RELATIONSHIP_REMOVED : string = 'relationship_removed' ; type { ArrayCollection } private dirtyObjects : ArrayCollection < EntityProxy > = new ArrayCollection ; type { ArrayCollection } private newObjects : ArrayCollection < EntityProxy > = new ArrayCollection ; type { ArrayCollection } private deletedObjects : ArrayCollection < EntityProxy > = new ArrayCollection ; type { ArrayCollection } private cleanObjects : ArrayCollection < EntityProxy > = new ArrayCollection ; type { ArrayCollection } private relationshipsChangedObjects : ArrayCollection < EntityProxy > = new ArrayCollection ; type { Scope } private entityManager : Scope ; type { { } | null } private transactions : Object = { } ; type { Array } private afterCommit : Array < { target : EntityInterface ; method : string ; parameters : Array < any > ; } > = [ ] ; param { Scope } entityManager public constructor ( entityManager ) { this . entityManager = entityManager ; } param { ProxyInterface } entity returns { string } public static getObjectState ( entity ) { return MetaData . forInstance ( entity ) . fetch ( 'entityState.state' , UnitOfWork . STATE_UNKNOWN ) ; } param { EntityInterface } entity returns { boolean } public static hasRelationChanges ( entity ) { return ! ! MetaData . forInstance ( entity ) . fetch ( 'entityState.relations' ) ; } param { EntityInterface } entity returns { boolean } public static isClean ( entity ) { return UnitOfWork . getObjectState ( entity ) === UnitOfWork . STATE_CLEAN && ! UnitOfWork . hasRelationChanges ( entity ) ; } param { EntityInterface } entity returns { boolean } public static isDirty ( entity ) { return ! UnitOfWork . isClean ( entity ) ; } param { knex . QueryBuilder } query static logQuery ( query ) { if ( process . env . LOG_QUERIES ) { console . log ( 'Executing query:' , query . toString ( ) ) ; } } returns { ArrayCollection < EntityProxy > } public getDirtyObjects ( ) { return this . dirtyObjects ; } returns { ArrayCollection < EntityProxy > } public getNewObjects ( ) { return this . newObjects ; } returns { ArrayCollection < EntityProxy > } public getDeletedObjects ( ) { return this . deletedObjects ; } returns { ArrayCollection < EntityProxy > } public getCleanObjects ( ) { return this . cleanObjects ; } returns { ArrayCollection < EntityProxy > } public getRelationshipsChangedObjects ( ) { return this . relationshipsChangedObjects ; } returns { Scope } public getEntityManager ( ) { return this . entityManager ; } param { string } change param { Object } targetEntity param { string } property param { Object } relationEntity returns { UnitOfWork } public registerCollectionChange ( change , targetEntity , property , relationEntity ) { const addTo = change === UnitOfWork . RELATIONSHIP_ADDED ? 'added' : 'removed' ; const removeFrom = change === UnitOfWork . RELATIONSHIP_ADDED ? 'removed' : 'added' ; const targetMeta = MetaData . forInstance ( targetEntity ) ; const relationChanges = targetMeta . fetchOrPut ( 'entityState.relations' , { added : { } , removed : { } } ) ; const removeFromList = relationChanges [ removeFrom ] ; if ( removeFromList [ property ] && removeFromList [ property ] . includes ( relationEntity ) ) { removeFromList [ property ] . remove ( relationEntity ) ; if ( removeFromList [ property ] . length === 0 ) { delete removeFromList [ property ] ; } if ( Object . keys ( relationChanges . added ) . length === 0 && Object . keys ( relationChanges . removed ) . length === 0 ) { this . relationshipsChangedObjects . remove ( targetEntity ) ; targetMeta . remove ( 'entityState.relations' ) ; } return this ; } const addToList = relationChanges [ addTo ] ; const addToCollection = addToList [ property ] ? addToList [ property ] : addToList [ property ] = new ArrayCollection ; addToCollection . add ( relationEntity ) ; this . relationshipsChangedObjects . add ( targetEntity ) ; return this ; } param { string } change param { Object } targetEntity param { string } property param { Object } relationEntity returns { UnitOfWork } public registerRelationChange ( change , targetEntity , property , relationEntity ) { const addTo = change === UnitOfWork . RELATIONSHIP_ADDED ? 'added' : 'removed' ; const removeFrom = change === UnitOfWork . RELATIONSHIP_ADDED ? 'removed' : 'added' ; const targetMeta = MetaData . forInstance ( targetEntity ) ; const relationChanges = targetMeta . fetchOrPut ( 'entityState.relations' , { added : { } , removed : { } } ) ; const removeFromList = relationChanges [ removeFrom ] ; if ( removeFromList [ property ] === relationEntity ) { delete removeFromList [ property ] ; if ( Object . keys ( relationChanges . added ) . length === 0 && Object . keys ( relationChanges . removed ) . length === 0 ) { this . relationshipsChangedObjects . remove ( targetEntity ) ; targetMeta . remove ( 'entityState.relations' ) ; } return this ; } relationChanges [ addTo ] [ property ] = relationEntity ; this . relationshipsChangedObjects . add ( targetEntity ) ; return this ; } param { ProxyInterface } entity param { string } state returns { UnitOfWork } public setEntityState ( entity , state ) { const target = entity . isEntityProxy ? entity . getTarget ( ) : entity ; const metaData = MetaData . forInstance ( target ) ; const previousState = metaData . fetch ( 'entityState.state' , UnitOfWork . STATE_UNKNOWN ) ; if ( previousState === state ) { return this ; } if ( previousState === UnitOfWork . STATE_CLEAN && state === UnitOfWork . STATE_NEW ) { return this ; } if ( previousState !== UnitOfWork . STATE_UNKNOWN ) { this [ `${ previousState }Objects` ] . remove ( target ) ; } this [ `${ state }Objects` ] . add ( target ) ; metaData . put ( 'entityState.state' , state ) ; return this ; } param { Object } newObject returns { UnitOfWork } public registerNew ( newObject ) { const objectState = UnitOfWork . getObjectState ( newObject ) ; if ( objectState === UnitOfWork . STATE_NEW ) { return this ; } if ( objectState !== UnitOfWork . STATE_UNKNOWN ) { throw new Error ( `Only unregistered entities can be marked as new. Entity '${ newObject . constructor . name }' has state '${ objectState }'.` ) ; } this . setEntityState ( newObject , UnitOfWork . STATE_NEW ) ; return this ; } param { Object } dirtyObject param { String [ ] } property returns { UnitOfWork } public registerDirty ( dirtyObject , ... property ) { if ( ! property . length ) { throw new Error ( `Can't mark instance of '${ dirtyObject . constructor . name }' as dirty without supplying properties.` ) ; } const metaData = MetaData . forInstance ( dirtyObject ) ; const entityState = metaData . fetchOrPut ( 'entityState' , { state : UnitOfWork . STATE_UNKNOWN } ) ; if ( entityState . state === UnitOfWork . STATE_NEW || entityState . state === UnitOfWork . STATE_UNKNOWN ) { return this ; } if ( entityState . state === UnitOfWork . STATE_DELETED ) { throw new Error ( 'Trying to mark entity staged for deletion as dirty.' ) ; } this . setEntityState ( dirtyObject , UnitOfWork . STATE_DIRTY ) ; metaData . fetchOrPut ( 'entityState.dirty' , [ ] ) . push ( ... property ) ; return this ; } param { Object } deletedObject returns { UnitOfWork } public registerDeleted ( deletedObject ) { return this . setEntityState ( deletedObject , UnitOfWork . STATE_DELETED ) ; } param { Object } cleanObject param { boolean } fresh returns { UnitOfWork } public registerClean ( cleanObject , fresh = false ) { if ( ! fresh ) { this . revertRelationshipChanges ( cleanObject ) ; this . markDirtyAsCleaned ( cleanObject ) ; } this . setEntityState ( cleanObject , UnitOfWork . STATE_CLEAN ) ; return this ; } param { EntityInterface } entity param { EntityInterface } cascadingParent returns { UnitOfWork } public prepareCascadesFor ( entity , cascadingParent = null ) { const mapping = Mapping . forEntity ( entity ) ; const relations = mapping . getRelations ( ) ; if ( null === relations ) { return this ; } Object . getOwnPropertyNames ( relations ) . forEach ( property => { if ( typeof entity [ property ] !== 'object' || entity [ property ] === null ) { return ; } if ( entity [ property ] === cascadingParent ) { return ; } if ( ! ( entity [ property ] instanceof Array ) ) { this . cascadeSingle ( entity , property , entity [ property ] , mapping ) ; this . registerRelationChange ( UnitOfWork . RELATIONSHIP_ADDED , entity , property , entity [ property ] ) ; return ; } entity [ property ] . forEach ( relation => { this . cascadeSingle ( entity , property , relation , mapping ) ; this . registerCollectionChange ( UnitOfWork . RELATIONSHIP_ADDED , entity , property , relation ) ; } ) ; } ) ; return this ; } returns { UnitOfWork } public prepareCascades ( ) { if ( this . newObjects . length ) { this . newObjects . forEach ( entity => this . prepareCascadesFor ( entity ) ) ; } if ( ! this . relationshipsChangedObjects . length ) { return this ; } this . relationshipsChangedObjects . forEach ( changed => { const relationChanges = MetaData . forInstance ( changed ) . fetch ( 'entityState.relations' ) ; const mapping = Mapping . forEntity ( changed ) ; const processChanged = changedType => { Object . getOwnPropertyNames ( relationChanges [ changedType ] ) . forEach ( property => { const changes = relationChanges [ changedType ] ; if ( ! ( changes [ property ] instanceof Array ) ) { this . cascadeSingle ( changed , property , changes [ property ] , mapping ) ; return ; } changes [ property ] . forEach ( target => { this . cascadeSingle ( changed , property , target , mapping ) ; } ) ; } ) ; } ; processChanged ( 'added' ) ; processChanged ( 'removed' ) ; } ) ; return this ; } param { boolean } skipClean param { boolean } skipLifecycleHooks param { refreshCreated returns { Promise < UnitOfWork > } public commit ( skipClean = false , skipLifecycleHooks = false , config = { refreshCreated : null , refreshUpdated : null } ) { this . prepareCascades ( ) ; const defaultConfig = this . entityManager . getConfig ( ) ; const refreshCreated = defaultConfig . fetch ( 'entityManager.refreshCreated' ) ; const refreshUpdated = defaultConfig . fetch ( 'entityManager.refreshUpdated' ) ; const shouldRefreshUpdate = typeof config . refreshUpdated === 'boolean' ? config . refreshUpdated : refreshUpdated ; const shouldRefreshCreate = typeof config . refreshCreated === 'boolean' ? config . refreshCreated : refreshCreated ; return this . insertNew ( skipLifecycleHooks ) . then ( ( ) => this . updateDirty ( skipLifecycleHooks ) ) . then ( ( ) => this . deleteDeleted ( skipLifecycleHooks ) ) . then ( ( ) => this . updateRelationships ( ) ) . then ( ( ) => this . commitOrRollback ( true ) ) . then ( ( ) => shouldRefreshUpdate && this . refreshDirty ( ) ) . then ( ( ) => shouldRefreshCreate && this . refreshNew ( ) ) . then ( ( ) => ! skipClean && this . cleanObjectsAndTransactions ( ) ) . then ( ( ) => ! skipClean && this . processAfterCommit ( ) ) . then ( ( ) => ! skipClean && this . cleanAfterCommit ( ) ) . catch ( error => this . commitOrRollback ( false , error ) ) ; } param { EntityInterface } entity returns { UnitOfWork } public clearEntityState ( entity ) { MetaData . forInstance ( entity ) . remove ( 'entityState' ) ; return this ; } param { EntityInterface [ ] } entities returns { UnitOfWork } public clear ( ... entities ) { ( < Array < EntityInterface | ProxyInterface > > ( entities . length ? entities : this . newObjects ) ) . forEach ( ( created ) => this . clearEntityState ( created ) ) ; ( < Array < EntityInterface | ProxyInterface > > ( entities . length ? entities : this . deletedObjects ) ) . forEach ( deleted => this . clearEntityState ( deleted ) ) ; ( < Array < EntityInterface | ProxyInterface > > ( entities . length ? entities : this . cleanObjects ) ) . forEach ( clean => this . clearEntityState ( clean ) ) ; ( < Array < EntityInterface | ProxyInterface > > ( entities . length ? entities : this . relationshipsChangedObjects ) ) . forEach ( changed => this . clearEntityState ( changed ) ) ; if ( entities . length ) { this . relationshipsChangedObjects . remove ( ... entities ) ; this . dirtyObjects . remove ( ... entities ) ; this . deletedObjects . remove ( ... entities ) ; this . newObjects . remove ( ... entities ) ; this . cleanObjects . remove ( ... entities ) ; } else { this . relationshipsChangedObjects = new ArrayCollection ; this . dirtyObjects = new ArrayCollection ; this . deletedObjects = new ArrayCollection ; this . newObjects = new ArrayCollection ; this . cleanObjects = new ArrayCollection ; } this . transactions = { } ; this . afterCommit = [ ] ; return this ; } returns { UnitOfWork } public clean ( ) { return this . cleanObjectsAndTransactions ( ) . then ( ( ) => this . cleanAfterCommit ( ) ) ; } param { EntityInterface } entity param { string } property param { EntityInterface } relation param { Mapping } mapping returns { UnitOfWork } private cascadeSingle < T > ( entity , property , relation , mapping ) { const relationState = UnitOfWork . getObjectState ( relation ) ; if ( relationState === UnitOfWork . STATE_DELETED ) { throw new Error ( `Trying to add relation with entity on "${ mapping . getEntityName ( ) }.${ property }" that has been staged for removal.` ) ; } if ( relationState === UnitOfWork . STATE_UNKNOWN ) { const cascades = mapping . getField ( property ) . cascades ; if ( ! Array . isArray ( cascades ) || ! cascades . includes ( Mapping . CASCADE_PERSIST ) ) { throw new Error ( `Un-persisted relation found on "${ mapping . getEntityName ( ) }.${ property }". Either persist the entity, or use the cascade persist option.` ) ; } this . prepareCascadesFor ( relation , entity ) ; this . registerNew ( relation ) ; } return this ; } return { Promise < Array < Function > > } private processAfterCommit ( ) { const methods = [ ] ; this . afterCommit . forEach ( action => { methods . push ( action . target [ action . method ] ( ... action . parameters , this . entityManager ) ) ; } ) ; return Promise . all ( methods ) ; } param { string } method param { EntityInterface } entity param { * [ ] } parameters returns { Promise < any > } private lifecycleCallback ( method , entity , ... parameters ) { const beforeMethod = 'before' + method [ 0 ] . toUpperCase ( ) + method . substr ( 1 ) ; const afterMethod = 'after' + method [ 0 ] . toUpperCase ( ) + method . substr ( 1 ) ; if ( typeof entity [ afterMethod ] === 'function' ) { this . afterCommit . push ( { target : entity , method : afterMethod , parameters } ) ; } const callbackResult = typeof entity [ beforeMethod ] === 'function' ? entity [ beforeMethod ] ( ... parameters , this . entityManager ) : null ; return Promise . resolve ( callbackResult ) ; } param { boolean } commit param { Error } error returns { Promise } private commitOrRollback ( commit = true , error ? ) { const resolves = [ ] ; const method = commit ? 'commit' : 'rollback' ; Object . getOwnPropertyNames ( this . transactions ) . forEach ( store => { resolves . push ( this . transactions [ store ] . transaction [ method ] ( ) ) ; } ) ; if ( ! commit ) { this . rollbackIds ( ) ; } return Promise . all ( resolves ) . then ( ( ) => { this . transactions = { } ; if ( error ) { throw error ; } } ) ; } returns { UnitOfWork } private rollbackIds ( ) { this . newObjects . forEach ( newObject => { if ( newObject . isEntityProxy ) { newObject . deactivateProxying ( ) ; } delete newObject [ Mapping . forEntity ( newObject ) . getPrimaryKey ( ) ] ; } ) ; return this ; } param { EntityInterface } target returns { UnitOfWork } private markDirtyAsCleaned ( target ? ) { if ( target ) { MetaData . forInstance ( target ) . remove ( 'entityState.dirty' ) ; this . dirtyObjects . remove ( target ) ; } else if ( this . dirtyObjects && this . dirtyObjects . length > 0 ) { this . dirtyObjects . forEach ( dirty => this . markDirtyAsCleaned ( dirty ) ) ; } else { this . dirtyObjects = new ArrayCollection ; } return this ; } param { EntityInterface } target returns { UnitOfWork } private revertRelationshipChanges ( target ? ) { if ( target ) { MetaData . forInstance ( target ) . remove ( 'entityState.relations' ) ; this . relationshipsChangedObjects . remove ( target ) ; } else if ( this . relationshipsChangedObjects && this . relationshipsChangedObjects . length > 0 ) { this . relationshipsChangedObjects . forEach ( changed => this . revertRelationshipChanges ( changed ) ) ; } else { this . relationshipsChangedObjects = new ArrayCollection ; } return this ; } returns { Promise < any > } private refreshDirty ( ) { return this . entityManager . refresh ( ... this . dirtyObjects ) ; } returns { Promise < any > } private refreshNew ( ) { return this . entityManager . refresh ( ... this . newObjects ) ; } param { EntityInterface } target returns { Promise } private getTransaction ( target ) { const store = this . entityManager . getStore ( target ) ; const storeName = store . getName ( ) ; if ( ! this . transactions [ storeName ] ) { this . transactions [ storeName ] = new Promise ( ( resolve , reject ) => { const connection = store . getConnection ( Store . ROLE_MASTER ) ; connection . transaction ( transaction => { this . transactions [ storeName ] = { connection : connection , transaction : transaction } ; resolve ( this . transactions [ storeName ] ) ; } ) . catch ( error => reject ( error ) ) ; } ) ; } if ( this . transactions [ storeName ] instanceof Promise ) { return this . transactions [ storeName ] ; } return Promise . resolve ( this . transactions [ storeName ] ) ; } param { EntityInterface } targets param { Function } handler returns { Promise < any > } private persist ( targets , handler ) { const statementHandlers = [ ] ; targets . forEach ( target => statementHandlers . push ( this . persistTarget ( target , handler ) ) ) ; return Promise . all ( statementHandlers ) ; } param { EntityInterface } target param { Function } handler returns { Promise < any > } private persistTarget ( target , handler ) { return this . getTransaction ( target ) . then ( transaction => { const tableName = Mapping . forEntity ( target ) . getTableName ( ) ; const queryBuilder = this . entityManager . getRepository ( this . entityManager . resolveEntityReference ( target ) ) . getQueryBuilder ( null , transaction . connection ( tableName ) ) ; queryBuilder . getQuery ( ) . getStatement ( ) . transacting ( transaction . transaction ) ; return handler ( queryBuilder , target ) ; } ) ; } param { boolean } skipLifecyclehooks returns { Promise < any > } private insertNew ( skipLifecyclehooks = false ) { return this . persist ( this . newObjects , < T > ( queryBuilder , target ) => { const mapping = Mapping . forEntity ( target ) ; const primaryKey = mapping . getPrimaryKey ( ) ; const executeInsertion = ( ) => { return queryBuilder . insert ( target , primaryKey ) . getQuery ( ) . execute ( ) . then ( result => { if ( ! primaryKey ) { return ; } if ( target . isEntityProxy ) { target [ primaryKey ] = { _skipDirty : result [ 0 ] } ; target . activateProxying ( ) ; } else { target [ primaryKey ] = result [ 0 ] ; } } ) ; } ; if ( skipLifecyclehooks ) { return executeInsertion ( ) ; } return this . lifecycleCallback ( 'create' , target ) . then ( ( ) => executeInsertion ( ) ) ; } ) ; } param { boolean } skipLifecyclehooks returns { Promise < any > } private updateDirty ( skipLifecyclehooks = false ) { return this . persist ( this . dirtyObjects , < T > ( queryBuilder , target ) => { const dirtyProperties = MetaData . forInstance ( target ) . fetch ( `entityState.dirty` , [ ] ) ; const targetMapping = Mapping . forEntity ( target ) ; const primaryKey = targetMapping . getPrimaryKeyField ( ) ; const newValues = { } ; if ( dirtyProperties . length > 0 ) { dirtyProperties . forEach ( dirtyProperty => { newValues [ dirtyProperty ] = target [ dirtyProperty ] ; } ) ; } const executeUpdate = ( ) => { return queryBuilder . update ( newValues ) . where ( { [ primaryKey ] : target [ primaryKey ] } ) . getQuery ( ) . execute ( ) ; } ; if ( skipLifecyclehooks ) { return executeUpdate ( ) ; } return this . lifecycleCallback ( 'update' , target , newValues ) . then ( ( ) => executeUpdate ( ) ) ; } ) ; } param { boolean } skipLifecyclehooks returns { Promise < any > } private deleteDeleted ( skipLifecyclehooks = false ) { return this . persist ( this . deletedObjects , < T > ( queryBuilder , target ) => { const primaryKey = Mapping . forEntity ( target ) . getPrimaryKeyField ( ) ; const executeDelete = ( ) => { return queryBuilder . remove ( ) . where ( { [ primaryKey ] : target [ primaryKey ] } ) . getQuery ( ) . execute ( ) ; } ; if ( skipLifecyclehooks ) { return executeDelete ( ) ; } return this . lifecycleCallback ( 'remove' , target , this . deletedObjects ) . then ( ( ) => executeDelete ( ) ) ; } ) ; } returns { Promise < { } > } private updateRelationships ( ) { const relationshipUpdates = [ ] ; this . relationshipsChangedObjects . forEach ( changed => { const changedMapping = Mapping . forEntity ( changed ) ; const changedMeta = MetaData . forInstance ( changed ) . fetch ( 'entityState.relations' ) ; const relations = changedMapping . getRelations ( ) ; const applyChanges = ( from , action ) => { Object . getOwnPropertyNames ( changedMeta [ from ] ) . forEach ( property => { const newRelations = changedMeta [ from ] [ property ] ; if ( ! ( newRelations instanceof ArrayCollection ) ) { return relationshipUpdates . push ( persistRelationChange ( action , changed , property , newRelations ) ) ; } newRelations . forEach ( newRelation => { relationshipUpdates . push ( persistRelationChange ( action , changed , property , newRelation ) ) ; } ) ; } ) ; } ; const persistRelationChange = ( action , owning , property , other ) => { const relation = relations [ property ] ; if ( relation . type !== Mapping . RELATION_MANY_TO_MANY ) { const mapping = relation . mappedBy ? Mapping . forEntity ( other ) : changedMapping ; const owningSide = relation . mappedBy ? other : changed ; const otherSide = relation . mappedBy ? changed : other ; const joinColumn = mapping . getJoinColumn ( relation . mappedBy ? relation . mappedBy : property ) ; const primaryKey = mapping . getPrimaryKey ( ) ; return this . persistTarget ( owningSide , < T > ( queryBuilder , target ) => { const query = queryBuilder . where ( { [ primaryKey ] : target [ primaryKey ] } ) ; let newValue = otherSide [ joinColumn . referencedColumnName ] ; if ( action === UnitOfWork . RELATIONSHIP_REMOVED ) { query . where ( { [ joinColumn . name ] : newValue } ) ; newValue = null ; } return query . update ( { [ joinColumn . name ] : newValue } ) . getQuery ( ) . execute ( ) ; } ) ; } const owningSide = relation . mappedBy ? other : owning ; const otherSide = relation . mappedBy ? owning : other ; const joinTable = relation . mappedBy ? Mapping . forEntity ( other ) . getJoinTable ( relation . mappedBy ) : changedMapping . getJoinTable ( property ) ; return this . getTransaction ( owningSide ) . then ( transaction => { const queryBuilder = transaction . connection ( joinTable . name ) ; const values = { } ; joinTable . joinColumns . forEach ( column => { values [ column . name ] = owningSide [ column . referencedColumnName ] ; } ) ; joinTable . inverseJoinColumns . forEach ( column => { values [ column . name ] = otherSide [ column . referencedColumnName ] ; } ) ; if ( action === UnitOfWork . RELATIONSHIP_ADDED ) { const query = queryBuilder . insert ( values ) . transacting ( transaction . transaction ) ; UnitOfWork . logQuery ( query ) ; return query . then ( ) ; } const query = queryBuilder . where ( values ) . del ( ) . transacting ( transaction . transaction ) . then ( ) ; UnitOfWork . logQuery ( query ) ; return query ; } ) ; } ; applyChanges ( 'added' , UnitOfWork . RELATIONSHIP_ADDED ) ; applyChanges ( 'removed' , UnitOfWork . RELATIONSHIP_REMOVED ) ; } ) ; return Promise . all ( relationshipUpdates ) ; } return { Promise < void > } private cleanAfterCommit ( ) { this . afterCommit = [ ] ; return Promise . resolve ( ) ; } return { Promise < void > } private cleanObjectsAndTransactions ( ) { this . newObjects . each ( created => this . registerClean ( created ) ) ; this . dirtyObjects . each ( updated => this . registerClean ( updated ) ) ; this . relationshipsChangedObjects . each ( changed => this . registerClean ( changed ) ) ; this . deletedObjects . each ( deleted => this . clearEntityState ( deleted ) ) ; this . deletedObjects = new ArrayCollection ; this . transactions = { } ; return Promise . resolve ( ) ; } }