mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
added todos, extracted entity persister from repository; clean up repository
This commit is contained in:
parent
7a2fe7b143
commit
02e419aeb2
20
README.md
20
README.md
@ -14,4 +14,22 @@ usages.
|
||||
## Todos
|
||||
|
||||
* add partial selection support
|
||||
* in query builder should we use property names or table names? (right now its mixed)
|
||||
* in query builder should we use property names or table names? (right now its mixed)
|
||||
* should all entities have a primary column?
|
||||
* check if inheritance and abstract table works fine
|
||||
* think about indices
|
||||
* make subscribers and listeners to work correctly
|
||||
* think more about cascades
|
||||
* add cascadePersist to cascades?
|
||||
* naming strategy need to be done correctly
|
||||
* fix all propertyName/tableName problems and make sure everything work correctly
|
||||
* check column types, make validation there
|
||||
* foreign keys for relations
|
||||
* what happens if owner one-to-one on both sides
|
||||
* check self referencing
|
||||
* class lifecycle callbacks?
|
||||
* query builder limit offset
|
||||
* query with count? (Paginator)
|
||||
* wrap persistment in transaction
|
||||
* array / json / date column types
|
||||
* exceptions everywhere!
|
||||
@ -7,9 +7,12 @@ import {EntityMetadataBuilder} from "../metadata-builder/EntityMetadataBuilder";
|
||||
import {importClassesFromDirectories} from "../util/DirectoryExportedClassesLoader";
|
||||
|
||||
/**
|
||||
* Connection manager holds all connections made to the databases.
|
||||
* Connection manager holds all connections made to the databases and providers helper management functions
|
||||
* for all exist connections.
|
||||
*/
|
||||
export class ConnectionManager {
|
||||
|
||||
// todo: inject naming strategy, make it configurable
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Properties
|
||||
|
||||
@ -13,7 +13,7 @@ export interface Join {
|
||||
export class QueryBuilder<Entity> {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Pirvate properties
|
||||
// Private properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private _aliasMap: AliasMap;
|
||||
@ -221,7 +221,6 @@ export class QueryBuilder<Entity> {
|
||||
}
|
||||
|
||||
getSql(): string {
|
||||
// joins are before because their many-to-many relations can add aliases
|
||||
let sql = this.createSelectExpression();
|
||||
sql += this.createJoinExpression();
|
||||
sql += this.createWhereExpression();
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
|
||||
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
|
||||
import {RelationMetadata} from "../metadata-builder/metadata/RelationMetadata";
|
||||
import metadata = Reflect.metadata;
|
||||
import {Connection} from "../connection/Connection";
|
||||
|
||||
export interface EntityWithId {
|
||||
|
||||
173
src/repository/EntityPersister.ts
Normal file
173
src/repository/EntityPersister.ts
Normal file
@ -0,0 +1,173 @@
|
||||
import {
|
||||
JunctionRemoveOperation,
|
||||
InsertOperation,
|
||||
JunctionInsertOperation,
|
||||
PersistOperation, UpdateByRelationOperation, UpdateOperation, RemoveOperation
|
||||
} from "./EntityPersistOperationsBuilder";
|
||||
import {Connection} from "../connection/Connection";
|
||||
|
||||
export class EntityPersister {
|
||||
|
||||
constructor(private connection: Connection) {
|
||||
}
|
||||
|
||||
executePersistOperation(persistOperation: PersistOperation) {
|
||||
return Promise.resolve()
|
||||
.then(() => { // insert new relations
|
||||
return Promise.all(persistOperation.inserts.map(operation => {
|
||||
return this.insert(operation.entity).then((result: any) => {
|
||||
operation.entityId = result.insertId;
|
||||
});
|
||||
}));
|
||||
|
||||
}).then(() => { // insert junction table insertions
|
||||
|
||||
return Promise.all(persistOperation.junctionInserts.map(junctionOperation => {
|
||||
return this.insertJunctions(junctionOperation, persistOperation.inserts);
|
||||
}));
|
||||
}).then(() => { // remove junction table insertions
|
||||
|
||||
return Promise.all(persistOperation.junctionRemoves.map(junctionOperation => {
|
||||
return this.removeJunctions(junctionOperation);
|
||||
}));
|
||||
|
||||
}).then(() => {
|
||||
|
||||
return Promise.all(persistOperation.updatesByRelations.map(updateByRelation => {
|
||||
this.updateByRelation(updateByRelation, persistOperation.inserts);
|
||||
}));
|
||||
|
||||
}).then(() => { // perform updates
|
||||
|
||||
return Promise.all(persistOperation.updates.map(updateOperation => {
|
||||
return this.update(updateOperation);
|
||||
}));
|
||||
|
||||
}).then(() => { // remove removed relations
|
||||
return Promise.all(persistOperation.removes.map(operation => {
|
||||
return this.updateDeletedRelations(operation);
|
||||
}));
|
||||
|
||||
}).then(() => { // remove removed entities
|
||||
return Promise.all(persistOperation.removes.map(operation => {
|
||||
return this.delete(operation.entity);
|
||||
}));
|
||||
|
||||
}).then(() => { // update ids
|
||||
|
||||
persistOperation.inserts.forEach(insertOperation => {
|
||||
const metadata = this.connection.getMetadata(insertOperation.entity.constructor);
|
||||
insertOperation.entity[metadata.primaryColumn.name] = insertOperation.entityId;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private updateByRelation(operation: UpdateByRelationOperation, insertOperations: InsertOperation[]) {
|
||||
let tableName: string, relationName: string, relationId: any, idColumn: string, id: any;
|
||||
const idInInserts = insertOperations.find(o => o.entity === operation.targetEntity).entityId;
|
||||
if (operation.updatedRelation.isOneToMany) {
|
||||
const metadata = this.connection.getMetadata(operation.insertOperation.entity.constructor);
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.inverseRelation.name;
|
||||
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
idColumn = metadata.primaryColumn.name;
|
||||
id = operation.insertOperation.entityId;
|
||||
|
||||
} else {
|
||||
const metadata = this.connection.getMetadata(operation.targetEntity.constructor);
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.name;
|
||||
relationId = operation.insertOperation.entityId;
|
||||
idColumn = metadata.primaryColumn.name;
|
||||
id = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
}
|
||||
const query = `UPDATE ${tableName} SET ${relationName}='${relationId}' WHERE ${idColumn}='${id}'`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private update(updateOperation: UpdateOperation) {
|
||||
const entity = updateOperation.entity;
|
||||
const metadata = this.connection.getMetadata(entity.constructor);
|
||||
const values = updateOperation.columns.map(column => {
|
||||
return column.name + "='" + entity[column.propertyName] + "'";
|
||||
});
|
||||
|
||||
const query = `UPDATE ${metadata.table.name} SET ${values} WHERE ${metadata.primaryColumn.name}='${metadata.getEntityId(entity)}'` ;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private updateDeletedRelations(removeOperation: RemoveOperation) { // todo: check if both many-to-one deletions work too
|
||||
if (removeOperation.relation.isManyToMany || removeOperation.relation.isOneToMany) return;
|
||||
const value = removeOperation.relation.name + "=NULL";
|
||||
const query = `UPDATE ${removeOperation.metadata.table.name} SET ${value} WHERE ${removeOperation.metadata.primaryColumn.name}='${removeOperation.fromEntityId}'` ;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private delete(entity: any) {
|
||||
const metadata = this.connection.getMetadata(entity.constructor);
|
||||
const query = `DELETE FROM ${metadata.table.name} WHERE ${metadata.primaryColumn.name}='${entity[metadata.primaryColumn.propertyName]}'`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private insert(entity: any) {
|
||||
const metadata = this.connection.getMetadata(entity.constructor);
|
||||
const columns = metadata.columns
|
||||
.filter(column => !column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => column.name);
|
||||
/*const virtualColumns = metadata.columns
|
||||
.filter(column => column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => column.name);*/
|
||||
const values = metadata.columns
|
||||
.filter(column => !column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => "'" + entity[column.propertyName] + "'");
|
||||
/*const virtualValues = metadata.columns
|
||||
.filter(column => column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => "'" + entity[column.propertyName] + "'");
|
||||
const allColumns = columns.concat(virtualColumns);
|
||||
const allVolumes = values.concat(virtualValues);*/
|
||||
const relationColumns = metadata.relations
|
||||
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
|
||||
.filter(relation => entity.hasOwnProperty(relation.propertyName))
|
||||
.filter(relation => entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name])
|
||||
.map(relation => relation.name);
|
||||
|
||||
const relationValues = metadata.relations
|
||||
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
|
||||
.filter(relation => entity.hasOwnProperty(relation.propertyName))
|
||||
.filter(relation => entity[relation.propertyName].hasOwnProperty(relation.relatedEntityMetadata.primaryColumn.name))
|
||||
.map(relation => "'" + entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name] + "'");
|
||||
|
||||
const query = `INSERT INTO ${metadata.table.name}(${columns.concat(relationColumns).join(",")}) VALUES (${values.concat(relationValues).join(",")})`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private insertJunctions(junctionOperation: JunctionInsertOperation, insertOperations: InsertOperation[]) {
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.connection.getMetadata(junctionOperation.entity1.constructor);
|
||||
const metadata2 = this.connection.getMetadata(junctionOperation.entity2.constructor);
|
||||
const columns = junctionMetadata.columns.map(column => column.name);
|
||||
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name] || insertOperations.find(o => o.entity === junctionOperation.entity1).entityId;
|
||||
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name] || insertOperations.find(o => o.entity === junctionOperation.entity2).entityId;
|
||||
const values = [id1, id2]; // todo: order may differ, find solution (column.table to compare with entity metadata table?)
|
||||
|
||||
const query = `INSERT INTO ${junctionMetadata.table.name}(${columns.join(",")}) VALUES (${values.join(",")})`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private removeJunctions(junctionOperation: JunctionRemoveOperation) {
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.connection.getMetadata(junctionOperation.entity1.constructor);
|
||||
const metadata2 = this.connection.getMetadata(junctionOperation.entity2.constructor);
|
||||
const columns = junctionMetadata.columns.map(column => column.name);
|
||||
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name];
|
||||
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name];
|
||||
const query = `DELETE FROM ${junctionMetadata.table.name} WHERE ${columns[0]}='${id1}' AND ${columns[1]}='${id2}'`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,10 +4,8 @@ import {OrmBroadcaster} from "../subscriber/OrmBroadcaster";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {PlainObjectToNewEntityTransformer} from "../query-builder/transformer/PlainObjectToNewEntityTransformer";
|
||||
import {PlainObjectToDatabaseEntityTransformer} from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer";
|
||||
import {
|
||||
EntityPersistOperationsBuilder, PersistOperation, JunctionInsertOperation,
|
||||
InsertOperation, JunctionRemoveOperation, UpdateOperation, UpdateByRelationOperation, RemoveOperation
|
||||
} from "./EntityPersistOperationsBuilder";
|
||||
import {EntityPersistOperationsBuilder, PersistOperation} from "./EntityPersistOperationsBuilder";
|
||||
import {EntityPersister} from "./EntityPersister";
|
||||
|
||||
// todo: think how we can implement queryCount, queryManyAndCount
|
||||
// todo: extract non safe methods from repository (removeById, removeByConditions)
|
||||
@ -53,216 +51,6 @@ export class Repository<Entity> {
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new entity.
|
||||
*/
|
||||
create(copyFrom?: any): Entity {
|
||||
if (copyFrom) {
|
||||
const transformer = new PlainObjectToNewEntityTransformer();
|
||||
return transformer.transform(copyFrom, this.metadata);
|
||||
}
|
||||
return <Entity> this.metadata.create();
|
||||
}
|
||||
|
||||
initialize(object: any): Promise<Entity> {
|
||||
const transformer = new PlainObjectToDatabaseEntityTransformer();
|
||||
const queryBuilder = this.createQueryBuilder(this.metadata.table.name);
|
||||
return transformer.transform(object, this.metadata, queryBuilder);
|
||||
}
|
||||
|
||||
merge(entity1: Entity, entity2: Entity): Entity {
|
||||
return Object.assign(this.metadata.create(), entity1, entity2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds columns and relations from entity2 which does not exist or does not match in entity1.
|
||||
*/
|
||||
difference(entity1: Entity, entity2: Entity): PersistOperation {
|
||||
const builder = new EntityPersistOperationsBuilder(this.connection);
|
||||
return builder.difference(this.metadata, entity1, entity2);
|
||||
}
|
||||
|
||||
persist(entity: Entity) {
|
||||
const promise = !this.hasId(entity) ? Promise.resolve(null) : this.initialize(entity);
|
||||
//if (!this.hasId(entity)) { // do insert
|
||||
return promise.then(dbEntity => {
|
||||
const persistOperations = this.difference(dbEntity, entity);
|
||||
// create update queries based on diff map
|
||||
// return Promise.resolve()
|
||||
return Promise.resolve()
|
||||
|
||||
.then(() => { // insert new relations
|
||||
return Promise.all(persistOperations.inserts.map(operation => {
|
||||
return this.insert(operation.entity).then((result: any) => {
|
||||
operation.entityId = result.insertId;
|
||||
});
|
||||
}));
|
||||
|
||||
}).then(() => { // insert junction table insertions
|
||||
|
||||
return Promise.all(persistOperations.junctionInserts.map(junctionOperation => {
|
||||
return this.insertJunctions(junctionOperation, persistOperations.inserts);
|
||||
}));
|
||||
}).then(() => { // remove junction table insertions
|
||||
|
||||
return Promise.all(persistOperations.junctionRemoves.map(junctionOperation => {
|
||||
return this.removeJunctions(junctionOperation);
|
||||
}));
|
||||
|
||||
}).then(() => {
|
||||
|
||||
return Promise.all(persistOperations.updatesByRelations.map(updateByRelation => {
|
||||
this.updateByRelation(updateByRelation, persistOperations.inserts);
|
||||
}));
|
||||
|
||||
}).then(() => { // perform updates
|
||||
|
||||
return Promise.all(persistOperations.updates.map(updateOperation => {
|
||||
return this.update(updateOperation);
|
||||
}));
|
||||
|
||||
}).then(() => { // remove removed relations
|
||||
return Promise.all(persistOperations.removes.map(operation => {
|
||||
return this.updateDeletedRelations(operation);
|
||||
}));
|
||||
|
||||
}).then(() => { // remove removed entities
|
||||
return Promise.all(persistOperations.removes.map(operation => {
|
||||
return this.delete(operation.entity);
|
||||
}));
|
||||
|
||||
}).then(() => { // update ids
|
||||
|
||||
persistOperations.inserts.forEach(insertOperation => {
|
||||
const metadata = this.connection.getMetadata(insertOperation.entity.constructor);
|
||||
insertOperation.entity[metadata.primaryColumn.name] = insertOperation.entityId;
|
||||
});
|
||||
|
||||
return entity;
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private updateByRelation(operation: UpdateByRelationOperation, insertOperations: InsertOperation[]) {
|
||||
let tableName: string, relationName: string, relationId: any, idColumn: string, id: any;
|
||||
const idInInserts = insertOperations.find(o => o.entity === operation.targetEntity).entityId;
|
||||
if (operation.updatedRelation.isOneToMany) {
|
||||
const metadata = this.connection.getMetadata(operation.insertOperation.entity.constructor);
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.inverseRelation.name;
|
||||
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
idColumn = metadata.primaryColumn.name;
|
||||
id = operation.insertOperation.entityId;
|
||||
|
||||
} else {
|
||||
const metadata = this.connection.getMetadata(operation.targetEntity.constructor);
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.name;
|
||||
relationId = operation.insertOperation.entityId;
|
||||
idColumn = metadata.primaryColumn.name;
|
||||
id = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
}
|
||||
const query = `UPDATE ${tableName} SET ${relationName}='${relationId}' WHERE ${idColumn}='${id}'`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private update(updateOperation: UpdateOperation) {
|
||||
const entity = updateOperation.entity;
|
||||
const metadata = this.connection.getMetadata(entity.constructor);
|
||||
const values = updateOperation.columns.map(column => {
|
||||
return column.name + "='" + entity[column.propertyName] + "'";
|
||||
});
|
||||
|
||||
const query = `UPDATE ${metadata.table.name} SET ${values} WHERE ${metadata.primaryColumn.name}='${metadata.getEntityId(entity)}'` ;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private updateDeletedRelations(removeOperation: RemoveOperation) { // todo: check if both many-to-one deletions work too
|
||||
if (removeOperation.relation.isManyToMany || removeOperation.relation.isOneToMany) return;
|
||||
const value = removeOperation.relation.name + "=NULL";
|
||||
const query = `UPDATE ${removeOperation.metadata.table.name} SET ${value} WHERE ${removeOperation.metadata.primaryColumn.name}='${removeOperation.fromEntityId}'` ;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private delete(entity: any) {
|
||||
const metadata = this.connection.getMetadata(entity.constructor);
|
||||
const query = `DELETE FROM ${metadata.table.name} WHERE ${metadata.primaryColumn.name}='${entity[metadata.primaryColumn.propertyName]}'`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private insert(entity: any) {
|
||||
const metadata = this.connection.getMetadata(entity.constructor);
|
||||
const columns = metadata.columns
|
||||
.filter(column => !column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => column.name);
|
||||
/*const virtualColumns = metadata.columns
|
||||
.filter(column => column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => column.name);*/
|
||||
const values = metadata.columns
|
||||
.filter(column => !column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => "'" + entity[column.propertyName] + "'");
|
||||
/*const virtualValues = metadata.columns
|
||||
.filter(column => column.isVirtual)
|
||||
.filter(column => entity.hasOwnProperty(column.propertyName))
|
||||
.map(column => "'" + entity[column.propertyName] + "'");
|
||||
const allColumns = columns.concat(virtualColumns);
|
||||
const allVolumes = values.concat(virtualValues);*/
|
||||
const relationColumns = metadata.relations
|
||||
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
|
||||
.filter(relation => entity.hasOwnProperty(relation.propertyName))
|
||||
.filter(relation => entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name])
|
||||
.map(relation => relation.name);
|
||||
|
||||
const relationValues = metadata.relations
|
||||
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
|
||||
.filter(relation => entity.hasOwnProperty(relation.propertyName))
|
||||
.filter(relation => entity[relation.propertyName].hasOwnProperty(relation.relatedEntityMetadata.primaryColumn.name))
|
||||
.map(relation => "'" + entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name] + "'");
|
||||
|
||||
const query = `INSERT INTO ${metadata.table.name}(${columns.concat(relationColumns).join(",")}) VALUES (${values.concat(relationValues).join(",")})`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private insertJunctions(junctionOperation: JunctionInsertOperation, insertOperations: InsertOperation[]) {
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.connection.getMetadata(junctionOperation.entity1.constructor);
|
||||
const metadata2 = this.connection.getMetadata(junctionOperation.entity2.constructor);
|
||||
const columns = junctionMetadata.columns.map(column => column.name);
|
||||
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name] || insertOperations.find(o => o.entity === junctionOperation.entity1).entityId;
|
||||
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name] || insertOperations.find(o => o.entity === junctionOperation.entity2).entityId;
|
||||
const values = [id1, id2]; // todo: order may differ, find solution (column.table to compare with entity metadata table?)
|
||||
|
||||
const query = `INSERT INTO ${junctionMetadata.table.name}(${columns.join(",")}) VALUES (${values.join(",")})`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
private removeJunctions(junctionOperation: JunctionRemoveOperation) {
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.connection.getMetadata(junctionOperation.entity1.constructor);
|
||||
const metadata2 = this.connection.getMetadata(junctionOperation.entity2.constructor);
|
||||
const columns = junctionMetadata.columns.map(column => column.name);
|
||||
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name];
|
||||
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name];
|
||||
const query = `DELETE FROM ${junctionMetadata.table.name} WHERE ${columns[0]}='${id1}' AND ${columns[1]}='${id2}'`;
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
/*copyEntity(entity1: Entity, entity2: Entity) {
|
||||
this.metadata.columns
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Creates a entities from the given array of plain javascript objects. If fetchAllData param is specified then
|
||||
* entities data will be loaded from the database first, then filled with given json data.
|
||||
*/
|
||||
createMany(copyFromObjects: any[]): Entity[] {
|
||||
return copyFromObjects.map(object => this.create(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has an id.
|
||||
*/
|
||||
@ -281,12 +69,67 @@ export class Repository<Entity> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns raw database results.
|
||||
* Creates a new entity. If fromRawEntity is given then it creates a new entity and copies all entity properties
|
||||
* from this object into a new entity (copies only properties that should be in a new entity).
|
||||
*/
|
||||
query(query: string): Promise<any> {
|
||||
return this.connection.driver.query(query);
|
||||
create(fromRawEntity?: Object): Entity {
|
||||
if (fromRawEntity) {
|
||||
const transformer = new PlainObjectToNewEntityTransformer();
|
||||
return transformer.transform(fromRawEntity, this.metadata);
|
||||
}
|
||||
return <Entity> this.metadata.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a entities from the given array of plain javascript objects.
|
||||
*/
|
||||
createMany(copyFromObjects: any[]): Entity[] {
|
||||
return copyFromObjects.map(object => this.create(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity from the given plan javascript object. If entity already exist in the database, then
|
||||
* it loads it (and everything related to it), replaces all values with the new ones from the given object
|
||||
* and returns this new entity. This new entity is actually a loaded from the db entity with all properties
|
||||
* replaced from the new object.
|
||||
*/
|
||||
initialize(object: Object): Promise<Entity> {
|
||||
const transformer = new PlainObjectToDatabaseEntityTransformer();
|
||||
const queryBuilder = this.createQueryBuilder(this.metadata.table.name);
|
||||
return transformer.transform(object, this.metadata, queryBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two entities into one new entity.
|
||||
*/
|
||||
merge(entity1: Entity, entity2: Entity): Entity {
|
||||
return Object.assign(this.metadata.create(), entity1, entity2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists (saves) a given entity in the database.
|
||||
*/
|
||||
persist(entity: Entity) {
|
||||
const persister = new EntityPersister(this.connection);
|
||||
const promise = !this.hasId(entity) ? Promise.resolve(null) : this.initialize(entity);
|
||||
return promise.then(dbEntity => {
|
||||
const persistOperation = this.difference(dbEntity, entity);
|
||||
return persister.executePersistOperation(persistOperation);
|
||||
}).then(() => entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given entity from the database.
|
||||
*/
|
||||
remove(entity: Entity) {
|
||||
const persister = new EntityPersister(this.connection);
|
||||
return this.initialize(entity).then(dbEntity => {
|
||||
// make this only to remove
|
||||
const persistOperation = this.difference(dbEntity, entity);
|
||||
return persister.executePersistOperation(persistOperation);
|
||||
}).then(() => entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
@ -318,89 +161,24 @@ export class Repository<Entity> {
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Persist starts
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves a given entity. If entity is not inserted yet then it inserts a new entity.
|
||||
* If entity already inserted then performs its update.
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
//persist(entity: Entity/*, dynamicCascadeOptions?: DynamicCascadeOptions<Entity>*/): Promise<Entity> {
|
||||
// todo
|
||||
// return Promise.resolve<Entity>(null);
|
||||
|
||||
// if (!this.schema.isEntityTypeCorrect(entity))
|
||||
// throw new BadEntityInstanceException(entity, this.schema.entityClass);
|
||||
|
||||
// const remover = new EntityRemover<Entity>(this.connection);
|
||||
// const persister = new EntityPersister<Entity>(this.connection);
|
||||
|
||||
/* return remover.computeRemovedRelations(this.metadata, entity, dynamicCascadeOptions)
|
||||
.then(result => persister.persist(this.metadata, entity, dynamicCascadeOptions))
|
||||
.then(result => remover.executeRemoveOperations())
|
||||
.then(result => remover.executeUpdateInverseSideRelationRemoveIds())
|
||||
.then(result => entity);*/
|
||||
// }
|
||||
|
||||
/*computeChangeSet(entity: Entity) {
|
||||
// if there is no primary key - there is no way to determine if object needs to be updated or insert
|
||||
// since we cannot get the target object without identifier, that's why we always do insert for such objects
|
||||
if (!this.metadata.primaryColumn)
|
||||
return this.insert(entity);
|
||||
|
||||
// load entity from the db
|
||||
this.findById(this.metadata.getEntityId(entity)).then(dbEntity => {
|
||||
|
||||
});
|
||||
}*/
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Persist ends
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Removes a given entity.
|
||||
*/
|
||||
remove(entity: Entity/*, dynamicCascadeOptions?: DynamicCascadeOptions<Entity>*/): Promise<void> {
|
||||
// todo
|
||||
return Promise.resolve();
|
||||
// if (!this.schema.isEntityTypeCorrect(entity))
|
||||
// throw new BadEntityInstanceException(entity, this.schema.entityClass);
|
||||
/*const remover = new EntityRemover<Entity>(this.connection);
|
||||
return remover.registerEntityRemoveOperation(this.metadata, this.metadata.getEntityId(entity), dynamicCascadeOptions)
|
||||
.then(results => remover.executeRemoveOperations())
|
||||
.then(results => remover.executeUpdateInverseSideRelationRemoveIds());*/
|
||||
query(query: string): Promise<any> {
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity by a given id. Does not take care about cascade remove operations.
|
||||
*/
|
||||
removeById(id: string): Promise<void> {
|
||||
const alias = this.metadata.table.name;
|
||||
return this.createQueryBuilder(alias)
|
||||
.delete()
|
||||
.where(alias + "." + this.metadata.primaryColumn.name + "=:id")
|
||||
.setParameter("id", id)
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entities by a given simple conditions. Does not take care about cascade remove operations.
|
||||
*/
|
||||
removeByConditions(conditions: Object): Promise<void> {
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias).delete();
|
||||
Object.keys(conditions).forEach(key => builder.where(alias + "." + key + "=:" + key));
|
||||
return builder
|
||||
.setParameters(conditions)
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Finds columns and relations from entity2 which does not exist or does not match in entity1. Returns an object
|
||||
* that contains all information about what needs to be persisted.
|
||||
*/
|
||||
private difference(entity1: Entity, entity2: Entity): PersistOperation {
|
||||
const builder = new EntityPersistOperationsBuilder(this.connection);
|
||||
return builder.difference(this.metadata, entity1, entity2);
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,7 +4,6 @@
|
||||
export interface RemoveEvent<Entity> {
|
||||
|
||||
entity?: Entity;
|
||||
conditions?: any;
|
||||
entityId?: string;
|
||||
|
||||
}
|
||||
@ -2,9 +2,9 @@
|
||||
* This event is used on update events.
|
||||
*/
|
||||
export interface UpdateEvent<Entity> {
|
||||
|
||||
// todo: will we send an entity changeset ?
|
||||
|
||||
entity?: Entity;
|
||||
options?: any;
|
||||
conditions?: any;
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user