import { EntityData, IEntityType, IPrimaryKey } from '../decorators'; import { DatabaseDriver } from './DatabaseDriver'; import { Connection, QueryResult } from '../connections'; import { ReferenceType } from '../entity'; import { FilterQuery } from './IDatabaseDriver'; import { QueryBuilder, QueryOrder } from '../query'; import { Utils } from '../utils'; export abstract class AbstractSqlDriver extends DatabaseDriver { async find>(entityName: string, where: FilterQuery, populate: string[] = [], orderBy: Record = {}, limit?: number, offset?: number): Promise { const qb = this.createQueryBuilder(entityName); qb.select('*').populate(populate).where(where).orderBy(orderBy); if (limit !== undefined) { qb.limit(limit, offset); } return qb.execute('all'); } async findOne>(entityName: string, where: FilterQuery | string, populate: string[] = [], orderBy: Record = {}): Promise { if (Utils.isPrimaryKey(where)) { const pk = this.metadata[entityName].primaryKey; where = { [pk]: where }; } const qb = this.createQueryBuilder(entityName); return qb.select('*').populate(populate).where(where).orderBy(orderBy).limit(1).execute('get'); } async count(entityName: string, where: any): Promise { const qb = this.createQueryBuilder(entityName); const pk = this.metadata[entityName].primaryKey; const res = await qb.count(pk, true).where(where).execute('get', false); return +res.count; } async nativeInsert>(entityName: string, data: EntityData): Promise { const collections = this.extractManyToMany(entityName, data); const pk = this.getPrimaryKeyField(entityName); if (Object.keys(data).length === 0) { data[pk] = null; } const qb = this.createQueryBuilder(entityName); const res = await qb.insert(data).execute('run', false); res.insertId = res.insertId || data[pk]; await this.processManyToMany(entityName, res.insertId, collections); return res; } async nativeUpdate>(entityName: string, where: FilterQuery, data: EntityData): Promise { const pk = this.metadata[entityName] ? this.metadata[entityName].primaryKey : this.config.getNamingStrategy().referenceColumnName(); if (Utils.isPrimaryKey(where)) { where = { [pk]: where }; } const collections = this.extractManyToMany(entityName, data); let res: QueryResult = { affectedRows: 0, insertId: 0 }; if (Object.keys(data).length) { const qb = this.createQueryBuilder(entityName); res = await qb.update(data).where(where).execute('run', false); } await this.processManyToMany(entityName, Utils.extractPK(data[pk] || where, this.metadata[entityName])!, collections); return res; } async nativeDelete>(entityName: string, where: FilterQuery | string | any): Promise { if (Utils.isPrimaryKey(where)) { const pk = this.metadata[entityName] ? this.metadata[entityName].primaryKey : this.config.getNamingStrategy().referenceColumnName(); where = { [pk]: where }; } return this.createQueryBuilder(entityName).delete(where).execute('run', false); } protected createQueryBuilder(entityName: string): QueryBuilder { return new QueryBuilder(entityName, this.metadata, this); } protected extractManyToMany>(entityName: string, data: EntityData): EntityData { if (!this.metadata[entityName]) { return {}; } const props = this.metadata[entityName].properties; const ret: EntityData = {}; for (const k of Object.keys(data)) { const prop = props[k]; if (prop && prop.reference === ReferenceType.MANY_TO_MANY) { ret[k] = data[k]; delete data[k]; } } return ret; } protected async processManyToMany>(entityName: string, pk: IPrimaryKey, collections: EntityData) { if (!this.metadata[entityName]) { return; } const props = this.metadata[entityName].properties; const owners = Object.keys(collections).filter(k => props[k].owner); for (const k of owners) { const prop = props[k]; const fk1 = prop.joinColumn; const fk2 = prop.inverseJoinColumn; const qb1 = this.createQueryBuilder(prop.pivotTable); await qb1.delete({ [fk1]: pk }).execute('run', false); for (const item of collections[k]) { const qb2 = this.createQueryBuilder(prop.pivotTable); await qb2.insert({ [fk1]: pk, [fk2]: item }).execute('run', false); } } } }