mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
implementing entity schemas - step 3
This commit is contained in:
parent
efaf61b9d0
commit
20afae93ef
@ -15,15 +15,15 @@ const options: CreateConnectionOptions = {
|
||||
},
|
||||
// entitySchemaDirectories: [__dirname + "/schemas"],
|
||||
entitySchemas: [
|
||||
require("./schemas/post.json"),
|
||||
require("./schemas/post-details.json"),
|
||||
require("./schemas/category.json"),
|
||||
require("./schemas/image.json")
|
||||
require(__dirname + "/../../../../sample/sample24-schemas/schemas/post.json"),
|
||||
require(__dirname + "/../../../../sample/sample24-schemas/schemas/post-details.json"),
|
||||
require(__dirname + "/../../../../sample/sample24-schemas/schemas/category.json"),
|
||||
require(__dirname + "/../../../../sample/sample24-schemas/schemas/image.json")
|
||||
]
|
||||
};
|
||||
|
||||
createConnection(options).then(connection => {
|
||||
let postRepository = connection.getRepository<Post>("post");
|
||||
let postRepository = connection.getRepository<Post>("Post");
|
||||
|
||||
let post: Post = {
|
||||
title: "Hello post",
|
||||
|
||||
@ -22,8 +22,7 @@
|
||||
"post": {
|
||||
"target": "Post",
|
||||
"type": "one-to-one",
|
||||
"owner": false,
|
||||
"inverseSide": "postDetails"
|
||||
"inverseSide": "details"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,7 @@
|
||||
"details": {
|
||||
"target": "PostDetails",
|
||||
"type": "one-to-one",
|
||||
"owner": true,
|
||||
"joinColumn": true,
|
||||
"inverseSide": "post",
|
||||
"cascadeInsert": true,
|
||||
"cascadeUpdate": true,
|
||||
@ -44,7 +44,7 @@
|
||||
"categories": {
|
||||
"target": "Category",
|
||||
"type": "many-to-many",
|
||||
"owner": true,
|
||||
"joinTable": true,
|
||||
"cascadeInsert": true,
|
||||
"cascadeUpdate": true,
|
||||
"cascadeRemove": true,
|
||||
|
||||
@ -33,7 +33,7 @@ export class ConnectionManager {
|
||||
connection.importEntitySchemaFromDirectories(options.entitySchemaDirectories);
|
||||
|
||||
if (options.entitySchemas)
|
||||
connection.importEntities(options.entitySchemas);
|
||||
connection.importSchemas(options.entitySchemas);
|
||||
|
||||
if (options.entityDirectories && options.entityDirectories.length > 0)
|
||||
connection.importEntitiesFromDirectories(options.entityDirectories);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {ConnectionOptions} from "../connection/ConnectionOptions";
|
||||
import {EntitySchema} from "../metadata/entity-schema/EntitySchema";
|
||||
|
||||
/**
|
||||
* All options to help to create a new connection.
|
||||
@ -38,7 +39,7 @@ export interface CreateConnectionOptions {
|
||||
/**
|
||||
* Entity schemas to be loaded for the new connection.
|
||||
*/
|
||||
entitySchemas?: any[];
|
||||
entitySchemas?: EntitySchema[];
|
||||
|
||||
/**
|
||||
* List of directories from where entities will be loaded.
|
||||
|
||||
@ -280,12 +280,15 @@ export class Connection {
|
||||
* Gets repository for the given entity class.
|
||||
*/
|
||||
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): Repository<Entity>;
|
||||
// getRepository<Entity>(entityClass: Function): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity name.
|
||||
*/
|
||||
getRepository<Entity>(entityClass: string): Repository<Entity>;
|
||||
/**
|
||||
* Gets repository for the given entity name.
|
||||
*/
|
||||
getRepository<Entity>(entityClassOrName: ConstructorFunction<Entity>|Function|string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity class or name.
|
||||
@ -294,7 +297,7 @@ export class Connection {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClassOrName);
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClassOrName);
|
||||
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
@ -320,7 +323,7 @@ export class Connection {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClassOrName);
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClassOrName);
|
||||
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
@ -337,7 +340,7 @@ export class Connection {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClass);
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClass);
|
||||
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new ReactiveRepositoryNotFoundError(this.name, entityClass);
|
||||
@ -352,7 +355,7 @@ export class Connection {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClass);
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClass);
|
||||
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClass);
|
||||
|
||||
@ -34,6 +34,11 @@ export class EntityManager {
|
||||
*/
|
||||
getRepository<Entity>(entityClass: string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity class or name.
|
||||
*/
|
||||
getRepository<Entity>(entityClassOrName: ConstructorFunction<Entity>|Function|string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity class or name.
|
||||
*/
|
||||
@ -77,8 +82,12 @@ export class EntityManager {
|
||||
/**
|
||||
* Checks if entity has an id.
|
||||
*/
|
||||
hasId(entity: Function): boolean {
|
||||
return this.getRepository(entity.constructor).hasId(entity);
|
||||
hasId(entity: Function): boolean;
|
||||
hasId(target: Function|string, entity: Function): boolean;
|
||||
hasId(targetOrEntity: Function|string, maybeEntity?: Function): boolean {
|
||||
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
|
||||
const entity = arguments.length === 2 ? <Function> maybeEntity : <Function> targetOrEntity;
|
||||
return this.getRepository(target).hasId(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,17 +132,26 @@ export class EntityManager {
|
||||
/**
|
||||
* Persists (saves) a given entity in the database.
|
||||
*/
|
||||
persist<Entity>(entity: Entity): Promise<Entity> {
|
||||
return this.getRepository(<any> entity.constructor).persist(entity);
|
||||
persist<Entity>(entity: Entity): Promise<Entity>;
|
||||
persist<Entity>(targetOrEntity: Function|string, entity: Entity): Promise<Entity>;
|
||||
persist<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Promise<Entity> {
|
||||
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
|
||||
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
|
||||
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
|
||||
return <any> this.getRepository(<any> target).persist(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given entity from the database.
|
||||
*/
|
||||
remove<Entity>(entity: Entity) {
|
||||
return this.getRepository(<any> entity.constructor).remove(entity);
|
||||
remove<Entity>(entity: Entity): Promise<Entity>;
|
||||
remove<Entity>(targetOrEntity: Function|string, entity: Entity): Promise<Entity>;
|
||||
remove<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Promise<Entity> {
|
||||
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
|
||||
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
|
||||
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
|
||||
return <any> this.getRepository(<any> target).remove(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
|
||||
@ -27,29 +27,33 @@ export class ReactiveEntityManager {
|
||||
/**
|
||||
* Gets repository of the given entity.
|
||||
*/
|
||||
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): Repository<Entity> {
|
||||
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): Repository<Entity> {
|
||||
return this.connection.getRepository(entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reactive repository of the given entity.
|
||||
*/
|
||||
getReactiveRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): ReactiveRepository<Entity> {
|
||||
getReactiveRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): ReactiveRepository<Entity> {
|
||||
return this.connection.getReactiveRepository(entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reactive tree repository of the given entity.
|
||||
*/
|
||||
getReactiveTreeRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): ReactiveTreeRepository<Entity> {
|
||||
getReactiveTreeRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): ReactiveTreeRepository<Entity> {
|
||||
return this.connection.getReactiveTreeRepository(entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has an id.
|
||||
*/
|
||||
hasId(entity: Function): boolean {
|
||||
return this.getReactiveRepository(entity.constructor).hasId(entity);
|
||||
hasId(entity: Function): boolean;
|
||||
hasId(target: Function|string, entity: Function): boolean;
|
||||
hasId(targetOrEntity: Function|string, maybeEntity?: Function): boolean {
|
||||
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
|
||||
const entity = arguments.length === 2 ? <Function> maybeEntity : <Function> targetOrEntity;
|
||||
return this.getReactiveRepository(target).hasId(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,17 +98,25 @@ export class ReactiveEntityManager {
|
||||
/**
|
||||
* Persists (saves) a given entity in the database.
|
||||
*/
|
||||
persist<Entity>(entity: Entity): Rx.Observable<Entity> {
|
||||
persist<Entity>(entity: Entity): Rx.Observable<Entity>;
|
||||
persist<Entity>(targetOrEntity: Function|string, entity: Entity): Rx.Observable<Entity>;
|
||||
persist<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Rx.Observable<Entity> {
|
||||
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
|
||||
return <any> this.getReactiveRepository(<any> entity.constructor).persist(entity);
|
||||
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
|
||||
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
|
||||
return <any> this.getReactiveRepository(<any> target).persist(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given entity from the database.
|
||||
*/
|
||||
remove<Entity>(entity: Entity): Rx.Observable<Entity[]> {
|
||||
remove<Entity>(entity: Entity): Rx.Observable<Entity>;
|
||||
remove<Entity>(targetOrEntity: Function|string, entity: Entity): Rx.Observable<Entity>;
|
||||
remove<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Rx.Observable<Entity> {
|
||||
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
|
||||
return <any> this.getReactiveRepository(<any> entity.constructor).remove(entity);
|
||||
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
|
||||
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
|
||||
return <any> this.getReactiveRepository(<any> target).remove(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@ export interface JoinTableMetadataArgs {
|
||||
/**
|
||||
* Class to which this column is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
readonly target: Function|string;
|
||||
|
||||
/**
|
||||
* Class's property name to which this column is applied.
|
||||
|
||||
@ -46,9 +46,9 @@ export class MetadataArgsStorage {
|
||||
/**
|
||||
* Gets merged (with all abstract classes) table metadatas for the given classes.
|
||||
*/
|
||||
getMergedTableMetadatas(classes: Function[]) {
|
||||
const allTableMetadataArgs = this.tables.filterByTargets(classes);
|
||||
const tableMetadatas = this.tables.filterByTargets(classes).filter(table => table.type === "regular" || table.type === "closure");
|
||||
getMergedTableMetadatas(classes?: Function[]) {
|
||||
const allTableMetadataArgs = classes ? this.tables.filterByTargets(classes) : this.tables;
|
||||
const tableMetadatas = allTableMetadataArgs.filter(table => table.type === "regular" || table.type === "closure");
|
||||
|
||||
return tableMetadatas.map(tableMetadata => {
|
||||
return this.mergeWithAbstract(allTableMetadataArgs, tableMetadata);
|
||||
@ -58,8 +58,9 @@ export class MetadataArgsStorage {
|
||||
/**
|
||||
* Gets merged (with all abstract classes) embeddable table metadatas for the given classes.
|
||||
*/
|
||||
getMergedEmbeddableTableMetadatas(classes: Function[]) {
|
||||
const embeddableTableMetadatas = this.tables.filterByTargets(classes).filter(table => table.type === "embeddable");
|
||||
getMergedEmbeddableTableMetadatas(classes?: Function[]) {
|
||||
const tables = classes ? this.tables.filterByTargets(classes) : this.tables;
|
||||
const embeddableTableMetadatas = tables.filter(table => table.type === "embeddable");
|
||||
|
||||
return embeddableTableMetadatas.map(embeddableTableMetadata => {
|
||||
return this.mergeWithEmbeddable(embeddableTableMetadatas, embeddableTableMetadata);
|
||||
|
||||
@ -21,14 +21,6 @@ export class EntityMetadataCollection extends Array<EntityMetadata> {
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
findByNameOrTarget(nameOrTarget: Function|string) {
|
||||
if (typeof nameOrTarget === "string") {
|
||||
return this.findByName(nameOrTarget);
|
||||
} else {
|
||||
return this.findByTarget(nameOrTarget);
|
||||
}
|
||||
}
|
||||
|
||||
findByName(name: string) {
|
||||
const metadata = this.find(metadata => metadata.name === name);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {ColumnMetadata, ColumnMode} from "../metadata/ColumnMetadata";
|
||||
import {ColumnOptions} from "../decorator/options/ColumnOptions";
|
||||
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
|
||||
import {EntityMetadataValidator} from "./EntityMetadataValidator";
|
||||
@ -18,6 +18,9 @@ import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
|
||||
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
|
||||
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
|
||||
import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
|
||||
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
|
||||
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
|
||||
import {JoinColumnOptions} from "../decorator/options/JoinColumnOptions";
|
||||
|
||||
/**
|
||||
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
|
||||
@ -57,10 +60,24 @@ export class EntityMetadataBuilder {
|
||||
// add columns metadata args from the schema
|
||||
Object.keys(schema.columns).forEach(columnName => {
|
||||
const columnSchema = schema.columns[columnName];
|
||||
let mode: ColumnMode = "regular";
|
||||
if (columnSchema.primary)
|
||||
mode = "primary";
|
||||
if (columnSchema.createDate)
|
||||
mode = "createDate";
|
||||
if (columnSchema.updateDate)
|
||||
mode = "updateDate";
|
||||
if (columnSchema.version)
|
||||
mode = "version";
|
||||
if (columnSchema.treeChildrenCount)
|
||||
mode = "treeChildrenCount";
|
||||
if (columnSchema.treeLevel)
|
||||
mode = "treeLevel";
|
||||
|
||||
const column: ColumnMetadataArgs = {
|
||||
target: schema.target || schema.name,
|
||||
// targetId: schema.name,
|
||||
mode: columnSchema.mode,
|
||||
mode: mode,
|
||||
propertyName: columnName,
|
||||
// todo: what to do with it?: propertyType:
|
||||
options: {
|
||||
@ -90,8 +107,8 @@ export class EntityMetadataBuilder {
|
||||
// targetId: schema.name,
|
||||
propertyName: relationName,
|
||||
// todo: what to do with it?: propertyType:
|
||||
relationType: relationSchema.relationType,
|
||||
type: relationSchema.type,
|
||||
relationType: relationSchema.type,
|
||||
type: relationSchema.target,
|
||||
inverseSideProperty: relationSchema.inverseSide,
|
||||
isTreeParent: relationSchema.isTreeParent,
|
||||
isTreeChildren: relationSchema.isTreeChildren,
|
||||
@ -107,20 +124,63 @@ export class EntityMetadataBuilder {
|
||||
};
|
||||
|
||||
metadataArgsStorage.relations.add(relation);
|
||||
|
||||
// add join column
|
||||
if (relationSchema.joinColumn) {
|
||||
if (typeof relationSchema.joinColumn === "boolean") {
|
||||
const joinColumn: JoinColumnMetadataArgs = {
|
||||
target: schema.target || schema.name,
|
||||
// targetId: schema.name,
|
||||
propertyName: relationName
|
||||
};
|
||||
metadataArgsStorage.joinColumns.push(joinColumn);
|
||||
} else {
|
||||
const joinColumn: JoinColumnMetadataArgs = {
|
||||
target: schema.target || schema.name,
|
||||
// targetId: schema.name,
|
||||
propertyName: relationName,
|
||||
name: relationSchema.joinColumn.name,
|
||||
referencedColumnName: relationSchema.joinColumn.referencedColumnName
|
||||
};
|
||||
metadataArgsStorage.joinColumns.push(joinColumn);
|
||||
}
|
||||
}
|
||||
|
||||
// add join table
|
||||
if (relationSchema.joinTable) {
|
||||
if (typeof relationSchema.joinTable === "boolean") {
|
||||
const joinTable: JoinTableMetadataArgs = {
|
||||
target: schema.target || schema.name,
|
||||
// targetId: schema.name,
|
||||
propertyName: relationName
|
||||
};
|
||||
metadataArgsStorage.joinTables.push(joinTable);
|
||||
} else {
|
||||
const joinTable: JoinTableMetadataArgs = {
|
||||
target: schema.target || schema.name,
|
||||
// targetId: schema.name,
|
||||
propertyName: relationName,
|
||||
name: relationSchema.joinTable.name,
|
||||
joinColumn: relationSchema.joinTable.joinColumn,
|
||||
inverseJoinColumn: relationSchema.joinTable.inverseJoinColumn
|
||||
};
|
||||
metadataArgsStorage.joinTables.push(joinTable);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return this.buildFromAnyMetadataArgsStorage(metadataArgsStorage, namingStrategy, []);
|
||||
return this.buildFromAnyMetadataArgsStorage(metadataArgsStorage, namingStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a complete metadata aggregations for the given entity classes.
|
||||
*/
|
||||
buildFromMetadataArgsStorage(namingStrategy: NamingStrategyInterface, entityClasses: Function[]): EntityMetadata[] {
|
||||
buildFromMetadataArgsStorage(namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
|
||||
return this.buildFromAnyMetadataArgsStorage(getMetadataArgsStorage(), namingStrategy, entityClasses);
|
||||
}
|
||||
|
||||
buildFromAnyMetadataArgsStorage(metadataArgsStorage: MetadataArgsStorage, namingStrategy: NamingStrategyInterface, entityClasses: Function[]): EntityMetadata[] {
|
||||
buildFromAnyMetadataArgsStorage(metadataArgsStorage: MetadataArgsStorage, namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
|
||||
const embeddableMergedArgs = metadataArgsStorage.getMergedEmbeddableTableMetadatas(entityClasses);
|
||||
const entityMetadatas = metadataArgsStorage.getMergedTableMetadatas(entityClasses).map(mergedArgs => {
|
||||
|
||||
|
||||
@ -329,7 +329,7 @@ export class EntityMetadata {
|
||||
* Returns entity id of the given entity.
|
||||
*/
|
||||
getEntityId(entity: any) {
|
||||
return entity[this.primaryColumn.propertyName];
|
||||
return entity ? entity[this.primaryColumn.propertyName] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -9,7 +9,7 @@ import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
|
||||
/**
|
||||
* Function that returns a type of the field. Returned value must be a class used on the relation.
|
||||
*/
|
||||
export type RelationTypeInFunction = ((type?: any) => Function)|string; // todo: |string ?
|
||||
export type RelationTypeInFunction = ((type?: any) => Function)|Function|string; // todo: |string ?
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -3,6 +3,7 @@ import {ColumnType} from "../types/ColumnTypes";
|
||||
import {TableType} from "../TableMetadata";
|
||||
import {RelationType} from "../types/RelationTypes";
|
||||
import {OnDeleteType} from "../ForeignKeyMetadata";
|
||||
import {JoinColumnOptions} from "../../decorator/options/JoinColumnOptions";
|
||||
|
||||
export interface EntitySchema {
|
||||
|
||||
@ -54,11 +55,34 @@ export interface EntitySchema {
|
||||
[columnName: string]: {
|
||||
|
||||
/**
|
||||
* Column mode in which column will work.
|
||||
* For example, "primary" means that it will be a primary column, or "createDate" means that it will create a create
|
||||
* date column.
|
||||
* Indicates if this column is a primary column.
|
||||
*/
|
||||
mode: ColumnMode;
|
||||
primary: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this column is a created date column.
|
||||
*/
|
||||
createDate: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this column is an update date column.
|
||||
*/
|
||||
updateDate: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this column is a version column.
|
||||
*/
|
||||
version: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this column is a treeChildrenCount column.
|
||||
*/
|
||||
treeChildrenCount: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this column is a treeLevel column.
|
||||
*/
|
||||
treeLevel: boolean;
|
||||
|
||||
/**
|
||||
* Column type. Must be one of the value from the ColumnTypes class.
|
||||
@ -135,15 +159,14 @@ export interface EntitySchema {
|
||||
[relationName: string]: {
|
||||
|
||||
/**
|
||||
* Type of relation. Can be one of the value of the RelationTypes class.
|
||||
* Indicates with which entity this relation is made.
|
||||
*/
|
||||
relationType: RelationType;
|
||||
target: Function|string;
|
||||
|
||||
/**
|
||||
* Type of the relation. This type is in function because of language specifics and problems with recursive
|
||||
* referenced classes.
|
||||
* Type of relation. Can be one of the value of the RelationTypes class.
|
||||
*/
|
||||
type: string;
|
||||
type: RelationType;
|
||||
|
||||
/**
|
||||
* Inverse side of the relation.
|
||||
@ -154,6 +177,22 @@ export interface EntitySchema {
|
||||
* Join table options of this column. If set to true then it simply means that it has a join table.
|
||||
*/
|
||||
joinTable?: boolean|{
|
||||
|
||||
/**
|
||||
* Name of the table that will be created to store values of the both tables (join table).
|
||||
* By default is auto generated.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* First column of the join table.
|
||||
*/
|
||||
joinColumn?: JoinColumnOptions;
|
||||
|
||||
/**
|
||||
* Second (inverse) column of the join table.
|
||||
*/
|
||||
inverseJoinColumn?: JoinColumnOptions;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import {CascadesNotAllowedError} from "./error/CascadesNotAllowedError";
|
||||
import {RemoveOperation} from "./operation/RemoveOperation";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {UpdateByInverseSideOperation} from "./operation/UpdateByInverseSideOperation";
|
||||
import {JunctionRemoveOperation} from "./operation/JunctionRemoveOperation";
|
||||
|
||||
/**
|
||||
* 1. collect all exist objects from the db entity
|
||||
@ -57,8 +58,8 @@ export class EntityPersistOperationBuilder {
|
||||
* Finds columns and relations from entity2 which does not exist or does not match in entity1.
|
||||
*/
|
||||
buildFullPersistment(metadata: EntityMetadata,
|
||||
dbEntity: any,
|
||||
persistedEntity: any,
|
||||
dbEntity: EntityWithId,
|
||||
persistedEntity: EntityWithId,
|
||||
dbEntities: EntityWithId[],
|
||||
allPersistedEntities: EntityWithId[]): PersistOperation {
|
||||
|
||||
@ -87,8 +88,8 @@ export class EntityPersistOperationBuilder {
|
||||
* Finds columns and relations from entity2 which does not exist or does not match in entity1.
|
||||
*/
|
||||
buildOnlyRemovement(metadata: EntityMetadata,
|
||||
dbEntity: any,
|
||||
persistedEntity: any,
|
||||
dbEntity: EntityWithId,
|
||||
persistedEntity: EntityWithId,
|
||||
dbEntities: EntityWithId[],
|
||||
allPersistedEntities: EntityWithId[]): PersistOperation {
|
||||
// const dbEntities = this.extractObjectsById(dbEntity, metadata);
|
||||
@ -109,12 +110,12 @@ export class EntityPersistOperationBuilder {
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private findCascadeInsertedEntities(newEntity: any,
|
||||
private findCascadeInsertedEntities(newEntityWithId: EntityWithId,
|
||||
dbEntities: EntityWithId[],
|
||||
fromRelation?: RelationMetadata,
|
||||
operations: InsertOperation[] = []): InsertOperation[] {
|
||||
// const metadata = this.connection.getEntityMetadata(newEntity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(newEntity.constructor);
|
||||
const newEntity = newEntityWithId.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(newEntityWithId.entityTarget);
|
||||
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.propertyName]);
|
||||
|
||||
// if object is new and should be inserted, we check if cascades are allowed before add it to operations list
|
||||
@ -122,17 +123,30 @@ export class EntityPersistOperationBuilder {
|
||||
return operations; // looks like object is new here, but cascades are not allowed - then we should stop iteration
|
||||
|
||||
} else if (isObjectNew && !operations.find(o => o.entity === newEntity)) { // object is new and cascades are allow here
|
||||
operations.push(new InsertOperation(newEntity));
|
||||
operations.push(new InsertOperation(newEntityWithId.entityTarget, newEntity));
|
||||
}
|
||||
|
||||
metadata.relations.forEach(relation => {
|
||||
const value = this.getEntityRelationValue(relation, newEntity);
|
||||
const inverseMetadata = relation.inverseEntityMetadata;
|
||||
if (!value) return;
|
||||
|
||||
if (value instanceof Array) {
|
||||
value.map((subValue: any) => this.findCascadeInsertedEntities(subValue, dbEntities, relation, operations));
|
||||
value.forEach((subValue: any) => {
|
||||
const subValueWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(subValue),
|
||||
entity: subValue,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
this.findCascadeInsertedEntities(subValueWithId, dbEntities, relation, operations);
|
||||
});
|
||||
} else {
|
||||
this.findCascadeInsertedEntities(value, dbEntities, relation, operations);
|
||||
const valueWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
this.findCascadeInsertedEntities(valueWithId, dbEntities, relation, operations);
|
||||
}
|
||||
});
|
||||
|
||||
@ -141,11 +155,14 @@ export class EntityPersistOperationBuilder {
|
||||
|
||||
private findCascadeUpdateEntities(updatesByRelations: UpdateByRelationOperation[],
|
||||
metadata: EntityMetadata,
|
||||
dbEntity: any,
|
||||
newEntity: any,
|
||||
dbEntityWithId: EntityWithId,
|
||||
newEntityWithId: EntityWithId,
|
||||
dbEntities: EntityWithId[],
|
||||
fromRelation?: RelationMetadata,
|
||||
operations: UpdateOperation[] = []): UpdateOperation[] {
|
||||
const dbEntity = dbEntityWithId.entity;
|
||||
const newEntity = newEntityWithId.entity;
|
||||
|
||||
if (!dbEntity)
|
||||
return operations;
|
||||
|
||||
@ -157,7 +174,7 @@ export class EntityPersistOperationBuilder {
|
||||
} else if (diffColumns.length || diffRelations.length) {
|
||||
const entityId = newEntity[metadata.primaryColumn.propertyName];
|
||||
if (entityId)
|
||||
operations.push(new UpdateOperation(newEntity, entityId, diffColumns, diffRelations));
|
||||
operations.push(new UpdateOperation(newEntityWithId.entityTarget, newEntity, entityId, diffColumns, diffRelations));
|
||||
}
|
||||
|
||||
metadata.relations.forEach(relation => {
|
||||
@ -179,16 +196,40 @@ export class EntityPersistOperationBuilder {
|
||||
const dbValue = dbEntities.find(dbValue => {
|
||||
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === subEntity[relationIdColumnName];
|
||||
});
|
||||
if (dbValue)
|
||||
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, subEntity, dbEntities, relation, operations);
|
||||
if (dbValue) {
|
||||
const dbValueWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(dbValue.entity),
|
||||
entity: dbValue.entity,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
const subEntityWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(subEntity),
|
||||
entity: subEntity,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValueWithId, subEntityWithId, dbEntities, relation, operations);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
const dbValue = dbEntities.find(dbValue => {
|
||||
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === value[relationIdColumnName];
|
||||
});
|
||||
if (dbValue)
|
||||
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, value, dbEntities, relation, operations);
|
||||
if (dbValue) {
|
||||
|
||||
const dbValueWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(dbValue.entity),
|
||||
entity: dbValue.entity,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
const valueWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
|
||||
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValueWithId, valueWithId, dbEntities, relation, operations);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -196,12 +237,14 @@ export class EntityPersistOperationBuilder {
|
||||
}
|
||||
|
||||
private findCascadeRemovedEntities(metadata: EntityMetadata,
|
||||
dbEntity: any,
|
||||
dbEntityWithId: EntityWithId,
|
||||
allPersistedEntities: EntityWithId[],
|
||||
fromRelation: RelationMetadata|undefined,
|
||||
fromMetadata: EntityMetadata|undefined,
|
||||
fromEntityId: any,
|
||||
parentAlreadyRemoved: boolean = false): RemoveOperation[] {
|
||||
const dbEntity = dbEntityWithId.entity;
|
||||
|
||||
let operations: RemoveOperation[] = [];
|
||||
if (!dbEntity)
|
||||
return operations;
|
||||
@ -214,7 +257,7 @@ export class EntityPersistOperationBuilder {
|
||||
return operations; // looks like object is removed here, but cascades are not allowed - then we should stop iteration
|
||||
|
||||
} else if (isObjectRemoved) { // object is remove and cascades are allow here
|
||||
operations.push(new RemoveOperation(dbEntity, entityId, <EntityMetadata> fromMetadata, fromRelation, fromEntityId));
|
||||
operations.push(new RemoveOperation(dbEntityWithId.entityTarget, dbEntity, entityId, <EntityMetadata> fromMetadata, fromRelation, fromEntityId));
|
||||
}
|
||||
|
||||
metadata.relations.forEach(relation => {
|
||||
@ -224,11 +267,23 @@ export class EntityPersistOperationBuilder {
|
||||
|
||||
if (dbValue instanceof Array) {
|
||||
dbValue.forEach((subDbEntity: any) => {
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntity, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
|
||||
const subDbEntityWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(subDbEntity),
|
||||
entity: subDbEntity,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntityWithId, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
|
||||
relationOperations.forEach(o => operations.push(o));
|
||||
});
|
||||
} else {
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValue, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
|
||||
const dbValueWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(dbValue),
|
||||
entity: dbValue,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValueWithId, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
|
||||
relationOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
}, []);
|
||||
@ -237,13 +292,16 @@ export class EntityPersistOperationBuilder {
|
||||
}
|
||||
|
||||
private updateInverseRelations(metadata: EntityMetadata,
|
||||
dbEntity: any,
|
||||
newEntity: any,
|
||||
dbEntityWithId: EntityWithId,
|
||||
newEntityWithId: EntityWithId,
|
||||
operations: UpdateByInverseSideOperation[] = []): UpdateByInverseSideOperation[] {
|
||||
const dbEntity = dbEntityWithId.entity;
|
||||
const newEntity = newEntityWithId.entity;
|
||||
metadata.relations
|
||||
.filter(relation => relation.isOneToMany) // todo: maybe need to check isOneToOne and not owner
|
||||
// .filter(relation => newEntity[relation.propertyName] instanceof Array) // todo: what to do with empty relations? need to set to NULL from inverse side?
|
||||
.forEach(relation => {
|
||||
const relationMetadata = relation.inverseEntityMetadata;
|
||||
|
||||
// to find new objects in relation go throw all objects in newEntity and check if they don't exist in dbEntity
|
||||
if (newEntity && newEntity[relation.propertyName] instanceof Array) {
|
||||
@ -255,7 +313,7 @@ export class EntityPersistOperationBuilder {
|
||||
return relation.inverseEntityMetadata.getEntityId(newSubEntity) === relation.inverseEntityMetadata.getEntityId(dbSubEntity);
|
||||
});
|
||||
}).forEach((subEntity: any) => {
|
||||
operations.push(new UpdateByInverseSideOperation("update", subEntity, newEntity, relation));
|
||||
operations.push(new UpdateByInverseSideOperation(relationMetadata.target, newEntityWithId.entityTarget, "update", subEntity, newEntity, relation));
|
||||
});
|
||||
}
|
||||
|
||||
@ -269,7 +327,7 @@ export class EntityPersistOperationBuilder {
|
||||
return relation.inverseEntityMetadata.getEntityId(dbSubEntity) === relation.inverseEntityMetadata.getEntityId(newSubEntity);
|
||||
});
|
||||
}).forEach((subEntity: any) => {
|
||||
operations.push(new UpdateByInverseSideOperation("remove", subEntity, newEntity, relation));
|
||||
operations.push(new UpdateByInverseSideOperation(relationMetadata.target, newEntityWithId.entityTarget, "remove", subEntity, newEntity, relation));
|
||||
});
|
||||
}
|
||||
|
||||
@ -286,37 +344,49 @@ export class EntityPersistOperationBuilder {
|
||||
*
|
||||
*/
|
||||
|
||||
private updateRelations(insertOperations: InsertOperation[], newEntity: any): UpdateByRelationOperation[] {
|
||||
private updateRelations(insertOperations: InsertOperation[], newEntity: EntityWithId): UpdateByRelationOperation[] {
|
||||
return insertOperations.reduce((operations, insertOperation) => {
|
||||
return operations.concat(this.findRelationsWithEntityInside(insertOperation, newEntity));
|
||||
}, <UpdateByRelationOperation[]> []);
|
||||
}
|
||||
|
||||
private findRelationsWithEntityInside(insertOperation: InsertOperation, entityToSearchIn: any): UpdateByRelationOperation[] {
|
||||
// updateByt metadata = this.connection.getEntityMetadata(entityToSearchIn.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(entityToSearchIn.constructor);
|
||||
private findRelationsWithEntityInside(insertOperation: InsertOperation, entityToSearchInWithId: EntityWithId): UpdateByRelationOperation[] {
|
||||
const entityToSearchIn = entityToSearchInWithId.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(entityToSearchInWithId.entityTarget);
|
||||
|
||||
const operations: UpdateByRelationOperation[] = [];
|
||||
metadata.relations.forEach(relation => {
|
||||
const value = this.getEntityRelationValue(relation, entityToSearchIn);
|
||||
const inverseMetadata = relation.inverseEntityMetadata;
|
||||
if (!value) return;
|
||||
|
||||
if (value instanceof Array) {
|
||||
value.forEach((sub: any) => {
|
||||
|
||||
if (!relation.isManyToMany && sub === insertOperation.entity)
|
||||
operations.push(new UpdateByRelationOperation(entityToSearchIn, insertOperation, relation));
|
||||
operations.push(new UpdateByRelationOperation(entityToSearchInWithId.entityTarget, entityToSearchIn, insertOperation, relation));
|
||||
|
||||
const subOperations = this.findRelationsWithEntityInside(insertOperation, sub);
|
||||
const subWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(sub),
|
||||
entity: sub,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
const subOperations = this.findRelationsWithEntityInside(insertOperation, subWithId);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
});
|
||||
|
||||
} else if (value) {
|
||||
|
||||
if (value === insertOperation.entity) {
|
||||
operations.push(new UpdateByRelationOperation(entityToSearchIn, insertOperation, relation));
|
||||
operations.push(new UpdateByRelationOperation(entityToSearchInWithId.entityTarget, entityToSearchIn, insertOperation, relation));
|
||||
}
|
||||
const subOperations = this.findRelationsWithEntityInside(insertOperation, value);
|
||||
|
||||
const valueWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
const subOperations = this.findRelationsWithEntityInside(insertOperation, valueWithId);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
|
||||
}
|
||||
@ -325,7 +395,8 @@ export class EntityPersistOperationBuilder {
|
||||
return operations;
|
||||
}
|
||||
|
||||
private findJunctionInsertOperations(metadata: EntityMetadata, newEntity: any, dbEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
|
||||
private findJunctionInsertOperations(metadata: EntityMetadata, newEntityWithId: EntityWithId, dbEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
|
||||
const newEntity = newEntityWithId.entity;
|
||||
const dbEntity = dbEntities.find(dbEntity => {
|
||||
return dbEntity.id === newEntity[metadata.primaryColumn.propertyName] && dbEntity.entityTarget === metadata.target;
|
||||
});
|
||||
@ -347,19 +418,31 @@ export class EntityPersistOperationBuilder {
|
||||
operations.push({
|
||||
metadata: relation.junctionEntityMetadata,
|
||||
entity1: newEntity,
|
||||
entity2: subEntity
|
||||
entity2: subEntity,
|
||||
entity1Target: newEntityWithId.entityTarget,
|
||||
entity2Target: relationMetadata.target
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const subOperations = this.findJunctionInsertOperations(relationMetadata, subEntity, dbEntities, false);
|
||||
const subEntityWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(subEntity),
|
||||
entity: subEntity,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
const subOperations = this.findJunctionInsertOperations(relationMetadata, subEntityWithId, dbEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const subOperations = this.findJunctionInsertOperations(relationMetadata, value, dbEntities, false);
|
||||
const valueWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
const subOperations = this.findJunctionInsertOperations(relationMetadata, valueWithId, dbEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
}
|
||||
@ -368,7 +451,8 @@ export class EntityPersistOperationBuilder {
|
||||
}, <JunctionInsertOperation[]> []);
|
||||
}
|
||||
|
||||
private findJunctionRemoveOperations(metadata: EntityMetadata, dbEntity: any, newEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
|
||||
private findJunctionRemoveOperations(metadata: EntityMetadata, dbEntityWithId: EntityWithId, newEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
|
||||
const dbEntity = dbEntityWithId.entity;
|
||||
if (!dbEntity) // if new entity is persisted then it does not have anything to be deleted
|
||||
return [];
|
||||
|
||||
@ -393,25 +477,39 @@ export class EntityPersistOperationBuilder {
|
||||
operations.push({
|
||||
metadata: relation.junctionEntityMetadata,
|
||||
entity1: dbEntity,
|
||||
entity2: subEntity
|
||||
entity2: subEntity,
|
||||
entity1Target: dbEntityWithId.entityTarget,
|
||||
entity2Target: relationMetadata.target
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const subOperations = this.findJunctionRemoveOperations(relationMetadata, subEntity, newEntities, false);
|
||||
const subEntityWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(subEntity),
|
||||
entity: subEntity,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
|
||||
const subOperations = this.findJunctionRemoveOperations(relationMetadata, subEntityWithId, newEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const subOperations = this.findJunctionRemoveOperations(relationMetadata, dbValue, newEntities, false);
|
||||
const dbValueWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(dbValue),
|
||||
entity: dbValue,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
|
||||
const subOperations = this.findJunctionRemoveOperations(relationMetadata, dbValueWithId, newEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
}
|
||||
|
||||
return operations;
|
||||
}, <JunctionInsertOperation[]> []);
|
||||
}, <JunctionRemoveOperation[]> []);
|
||||
}
|
||||
|
||||
private diffColumns(metadata: EntityMetadata, newEntity: any, dbEntity: any) {
|
||||
@ -441,14 +539,12 @@ export class EntityPersistOperationBuilder {
|
||||
const entityTarget = relation.target;
|
||||
|
||||
const newEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
|
||||
const dbEntityRelationMetadata = this.entityMetadatas.findByTarget(dbEntity[relation.propertyName].constructor);
|
||||
const dbEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
|
||||
return newEntityRelationMetadata.getEntityId(newEntity[relation.propertyName]) !== dbEntityRelationMetadata.getEntityId(dbEntity[relation.propertyName]);
|
||||
});
|
||||
}
|
||||
|
||||
private findEntityWithId(entityWithIds: EntityWithId[], entityTarget: Function|string, id: any) {
|
||||
// console.log(entityWithId.entityTarget);
|
||||
// console.log(entityWithId.entity.constructor);
|
||||
return entityWithIds.find(entityWithId => entityWithId.id === id && entityWithId.entityTarget === entityTarget);
|
||||
}
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ export class PersistOperationExecutor {
|
||||
private executeInsertClosureTableOperations(persistOperation: PersistOperation) {
|
||||
const promises = persistOperation.inserts
|
||||
.filter(operation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
return metadata.table.isClosure;
|
||||
})
|
||||
.map(operation => {
|
||||
@ -221,7 +221,7 @@ export class PersistOperationExecutor {
|
||||
*/
|
||||
private executeRemoveOperations(persistOperation: PersistOperation) {
|
||||
return Promise.all(persistOperation.removes.map(operation => {
|
||||
return this.delete(operation.entity);
|
||||
return this.delete(operation.target, operation.entity);
|
||||
}));
|
||||
}
|
||||
|
||||
@ -230,7 +230,7 @@ export class PersistOperationExecutor {
|
||||
*/
|
||||
private updateIdsOfInsertedEntities(persistOperation: PersistOperation) {
|
||||
persistOperation.inserts.forEach(insertOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(insertOperation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(insertOperation.target);
|
||||
insertOperation.entity[metadata.primaryColumn.propertyName] = insertOperation.entityId;
|
||||
});
|
||||
}
|
||||
@ -240,7 +240,7 @@ export class PersistOperationExecutor {
|
||||
*/
|
||||
private updateSpecialColumnsInEntities(persistOperation: PersistOperation) {
|
||||
persistOperation.inserts.forEach(insertOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(insertOperation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(insertOperation.target);
|
||||
if (metadata.hasUpdateDateColumn)
|
||||
insertOperation.entity[metadata.updateDateColumn.propertyName] = insertOperation.date;
|
||||
if (metadata.hasCreateDateColumn)
|
||||
@ -257,7 +257,7 @@ export class PersistOperationExecutor {
|
||||
}*/
|
||||
});
|
||||
persistOperation.updates.forEach(updateOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(updateOperation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(updateOperation.target);
|
||||
if (metadata.hasUpdateDateColumn)
|
||||
updateOperation.entity[metadata.updateDateColumn.propertyName] = updateOperation.date;
|
||||
if (metadata.hasCreateDateColumn)
|
||||
@ -272,9 +272,9 @@ export class PersistOperationExecutor {
|
||||
*/
|
||||
private updateIdsOfRemovedEntities(persistOperation: PersistOperation) {
|
||||
persistOperation.removes.forEach(removeOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(removeOperation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(removeOperation.target);
|
||||
const removedEntity = persistOperation.allPersistedEntities.find(allNewEntity => {
|
||||
return allNewEntity.entity.constructor === removeOperation.entity.constructor && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.propertyName];
|
||||
return allNewEntity.entityTarget === removeOperation.target && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.propertyName];
|
||||
});
|
||||
if (removedEntity)
|
||||
removedEntity.entity[metadata.primaryColumn.propertyName] = undefined;
|
||||
@ -289,7 +289,7 @@ export class PersistOperationExecutor {
|
||||
const relatedInsertOperation = insertOperations.find(o => o.entity === operation.targetEntity);
|
||||
const idInInserts = relatedInsertOperation ? relatedInsertOperation.entityId : null;
|
||||
if (operation.updatedRelation.isOneToMany) {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.target);
|
||||
if (operation.insertOperation.entity === target)
|
||||
updateMap[operation.updatedRelation.inverseRelation.propertyName] = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
|
||||
@ -307,7 +307,7 @@ export class PersistOperationExecutor {
|
||||
const relatedInsertOperation = insertOperations.find(o => o.entity === operation.targetEntity);
|
||||
const idInInserts = relatedInsertOperation ? relatedInsertOperation.entityId : null;
|
||||
if (operation.updatedRelation.isOneToMany || operation.updatedRelation.isOneToOneNotOwner) {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.target);
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.inverseRelation.name;
|
||||
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
@ -315,7 +315,7 @@ export class PersistOperationExecutor {
|
||||
id = operation.insertOperation.entityId;
|
||||
|
||||
} else {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.targetEntity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.entityTarget);
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.name;
|
||||
relationId = operation.insertOperation.entityId;
|
||||
@ -326,8 +326,8 @@ export class PersistOperationExecutor {
|
||||
}
|
||||
|
||||
private updateInverseRelation(operation: UpdateByInverseSideOperation, insertOperations: InsertOperation[]) {
|
||||
const targetEntityMetadata = this.entityMetadatas.findByTarget(operation.targetEntity.constructor);
|
||||
const fromEntityMetadata = this.entityMetadatas.findByTarget(operation.fromEntity.constructor);
|
||||
const targetEntityMetadata = this.entityMetadatas.findByTarget(operation.entityTarget);
|
||||
const fromEntityMetadata = this.entityMetadatas.findByTarget(operation.fromEntityTarget);
|
||||
const tableName = targetEntityMetadata.table.name;
|
||||
const targetRelation = operation.fromRelation.inverseRelation;
|
||||
const idColumn = targetEntityMetadata.primaryColumn.name;
|
||||
@ -350,7 +350,7 @@ export class PersistOperationExecutor {
|
||||
|
||||
private update(updateOperation: UpdateOperation) {
|
||||
const entity = updateOperation.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(updateOperation.target);
|
||||
const values: { [key: string]: any } = {};
|
||||
|
||||
updateOperation.columns.forEach(column => {
|
||||
@ -386,14 +386,14 @@ export class PersistOperationExecutor {
|
||||
throw new Error("Remove operation relation is not set"); // todo: find out how its possible
|
||||
}
|
||||
|
||||
private delete(entity: any) {
|
||||
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
|
||||
private delete(target: Function|string, entity: any) {
|
||||
const metadata = this.entityMetadatas.findByTarget(target);
|
||||
return this.driver.delete(metadata.table.name, { [metadata.primaryColumn.name]: entity[metadata.primaryColumn.propertyName] });
|
||||
}
|
||||
|
||||
private insert(operation: InsertOperation) {
|
||||
const entity = operation.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
|
||||
const columns = metadata.columns
|
||||
.filter(column => !column.isVirtual && column.hasEntityValue(entity));
|
||||
@ -449,7 +449,7 @@ export class PersistOperationExecutor {
|
||||
|
||||
private insertIntoClosureTable(operation: InsertOperation, updateMap: { [key: string]: any }) {
|
||||
const entity = operation.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
const parentEntity = entity[metadata.treeParentRelation.propertyName];
|
||||
|
||||
let parentEntityId: any = 0;
|
||||
@ -472,7 +472,7 @@ export class PersistOperationExecutor {
|
||||
}
|
||||
|
||||
private updateTreeLevel(operation: InsertOperation) {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
|
||||
if (metadata.hasTreeLevelColumn && operation.treeLevel) {
|
||||
const values = { [metadata.treeLevelColumn.name]: operation.treeLevel };
|
||||
@ -485,8 +485,8 @@ export class PersistOperationExecutor {
|
||||
|
||||
private insertJunctions(junctionOperation: JunctionInsertOperation, insertOperations: InsertOperation[]) {
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1.constructor);
|
||||
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2.constructor);
|
||||
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1Target);
|
||||
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2Target);
|
||||
const columns = junctionMetadata.columns.map(column => column.name);
|
||||
const insertOperation1 = insertOperations.find(o => o.entity === junctionOperation.entity1);
|
||||
const insertOperation2 = insertOperations.find(o => o.entity === junctionOperation.entity2);
|
||||
@ -523,8 +523,8 @@ export class PersistOperationExecutor {
|
||||
|
||||
private removeJunctions(junctionOperation: JunctionRemoveOperation) {
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1.constructor);
|
||||
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2.constructor);
|
||||
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1Target);
|
||||
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2Target);
|
||||
const columns = junctionMetadata.columns.map(column => column.name);
|
||||
const id1 = junctionOperation.entity1[metadata1.primaryColumn.propertyName];
|
||||
const id2 = junctionOperation.entity2[metadata2.primaryColumn.propertyName];
|
||||
|
||||
@ -5,7 +5,8 @@ export class InsertOperation {
|
||||
|
||||
public treeLevel: number;
|
||||
|
||||
constructor(public entity: any,
|
||||
constructor(public target: Function|string, // todo: probably should be metadata here
|
||||
public entity: any,
|
||||
public entityId?: number,
|
||||
public date = new Date()) {
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
export class JunctionInsertOperation {
|
||||
constructor(public metadata: EntityMetadata,
|
||||
public entity1: any,
|
||||
public entity2: any) {
|
||||
public entity2: any,
|
||||
public entity1Target: Function|string,
|
||||
public entity2Target: Function|string) {
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,8 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
export class JunctionRemoveOperation {
|
||||
constructor(public metadata: EntityMetadata,
|
||||
public entity1: any,
|
||||
public entity2: any) {
|
||||
public entity2: any,
|
||||
public entity1Target: any,
|
||||
public entity2Target: any) {
|
||||
}
|
||||
}
|
||||
@ -22,8 +22,8 @@ export class PersistOperation {
|
||||
|
||||
// todo: what if we have two same entities in the insert operations?
|
||||
|
||||
dbEntity: any;
|
||||
persistedEntity: any;
|
||||
dbEntity: EntityWithId;
|
||||
persistedEntity: EntityWithId;
|
||||
allDbEntities: EntityWithId[];
|
||||
allPersistedEntities: EntityWithId[];
|
||||
inserts: InsertOperation[] = [];
|
||||
|
||||
@ -5,7 +5,8 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
* @internal
|
||||
*/
|
||||
export class RemoveOperation {
|
||||
constructor(public entity: any,
|
||||
constructor(public target: Function|string, // todo: probably should be metadata here
|
||||
public entity: any,
|
||||
public entityId: any,
|
||||
public fromMetadata: EntityMetadata, // todo: use relation.metadata instead?
|
||||
public relation: RelationMetadata|undefined,
|
||||
|
||||
@ -4,7 +4,9 @@ import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
* @internal
|
||||
*/
|
||||
export class UpdateByInverseSideOperation {
|
||||
constructor(public operationType: "update"|"remove",
|
||||
constructor(public entityTarget: Function|string, // todo: probably must be entity metadata here?
|
||||
public fromEntityTarget: Function|string,
|
||||
public operationType: "update"|"remove",
|
||||
public targetEntity: any,
|
||||
public fromEntity: any,
|
||||
public fromRelation: RelationMetadata) {
|
||||
|
||||
@ -5,7 +5,8 @@ import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
* @internal
|
||||
*/
|
||||
export class UpdateByRelationOperation {
|
||||
constructor(public targetEntity: any,
|
||||
constructor(public entityTarget: Function|string, // todo: probably must be entity metadata here?
|
||||
public targetEntity: any,
|
||||
public insertOperation: InsertOperation,
|
||||
public updatedRelation: RelationMetadata) {
|
||||
}
|
||||
|
||||
@ -5,7 +5,8 @@ import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
* @internal
|
||||
*/
|
||||
export class UpdateOperation {
|
||||
constructor(public entity: any,
|
||||
constructor(public target: Function|string,
|
||||
public entity: any,
|
||||
public entityId: any,
|
||||
public columns: ColumnMetadata[],
|
||||
public relations: RelationMetadata[],
|
||||
|
||||
@ -366,7 +366,7 @@ export class QueryBuilder<Entity> {
|
||||
})
|
||||
.then(results => this.rawResultsToEntities(results))
|
||||
.then(results => this.addLazyProperties(results))
|
||||
.then(results => this.broadcaster.broadcastLoadEventsForAll(results).then(() => results))
|
||||
.then(results => this.broadcaster.broadcastLoadEventsForAll(this.aliasMap.mainAlias.target, results).then(() => results))
|
||||
.then(results => {
|
||||
return {
|
||||
entities: results,
|
||||
@ -384,7 +384,7 @@ export class QueryBuilder<Entity> {
|
||||
.then(results => this.addLazyProperties(results))
|
||||
.then(results => {
|
||||
return this.broadcaster
|
||||
.broadcastLoadEventsForAll(results)
|
||||
.broadcastLoadEventsForAll(this.aliasMap.mainAlias.target, results)
|
||||
.then(() => results);
|
||||
})
|
||||
.then(results => {
|
||||
|
||||
@ -126,7 +126,17 @@ export class Repository<Entity> {
|
||||
return this.findNotLoadedIds(entityWithIds, allPersistedEntities);
|
||||
}) // need to find db entities that were not loaded by initialize method
|
||||
.then(allDbEntities => {
|
||||
return this.entityPersistOperationBuilder.buildFullPersistment(this.metadata, loadedDbEntity, entity, allDbEntities, allPersistedEntities);
|
||||
const persistedEntity: EntityWithId = {
|
||||
id: this.metadata.getEntityId(entity),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: entity
|
||||
};
|
||||
const dbEntity: EntityWithId = {
|
||||
id: this.metadata.getEntityId(loadedDbEntity),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: loadedDbEntity
|
||||
};
|
||||
return this.entityPersistOperationBuilder.buildFullPersistment(this.metadata, dbEntity, persistedEntity, allDbEntities, allPersistedEntities);
|
||||
})
|
||||
.then(persistOperation => {
|
||||
return this.persistOperationExecutor.executePersistOperation(persistOperation);
|
||||
@ -151,7 +161,18 @@ export class Repository<Entity> {
|
||||
})
|
||||
// .then(([dbEntities, allPersistedEntities]: [EntityWithId[], EntityWithId[]]) => {
|
||||
.then(results => {
|
||||
const persistOperation = this.entityPersistOperationBuilder.buildOnlyRemovement(this.metadata, dbEntity, entity, results[0], results[1]);
|
||||
const entityWithId: EntityWithId = {
|
||||
id: this.metadata.getEntityId(entity),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: entity
|
||||
};
|
||||
const dbEntityWithId: EntityWithId = {
|
||||
id: this.metadata.getEntityId(dbEntity),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: dbEntity
|
||||
};
|
||||
|
||||
const persistOperation = this.entityPersistOperationBuilder.buildOnlyRemovement(this.metadata, dbEntityWithId, entityWithId, results[0], results[1]);
|
||||
return this.persistOperationExecutor.executePersistOperation(persistOperation);
|
||||
}).then(() => entity);
|
||||
}
|
||||
@ -438,10 +459,10 @@ export class Repository<Entity> {
|
||||
private findNotLoadedIds(dbEntities: EntityWithId[], persistedEntities: EntityWithId[]): Promise<EntityWithId[]> {
|
||||
const missingDbEntitiesLoad = persistedEntities
|
||||
.filter(entityWithId => entityWithId.id !== null && entityWithId.id !== undefined)
|
||||
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entity.constructor === entityWithId.entity.constructor && dbEntity.id === entityWithId.id))
|
||||
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entityTarget === entityWithId.entityTarget && dbEntity.id === entityWithId.id))
|
||||
.map(entityWithId => {
|
||||
const metadata = this.entityMetadatas.findByTarget(entityWithId.entity.constructor);
|
||||
const repository = this.connection.getRepository(entityWithId.entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(entityWithId.entityTarget);
|
||||
const repository = this.connection.getRepository(entityWithId.entityTarget as any); // todo: fix type
|
||||
return repository.findOneById(entityWithId.id).then(loadedEntity => {
|
||||
if (!loadedEntity) return undefined;
|
||||
|
||||
|
||||
@ -139,22 +139,22 @@ export class Broadcaster {
|
||||
return Promise.all(subscribers.concat(listeners)).then(() => {});
|
||||
}
|
||||
|
||||
broadcastLoadEvents(entity: any): Promise<void> {
|
||||
broadcastLoadEvents(target: Function|string, entity: any): Promise<void> {
|
||||
if (entity instanceof Promise)
|
||||
return Promise.resolve();
|
||||
|
||||
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
|
||||
const metadata = this.entityMetadatas.findByTarget(target);
|
||||
let promises: Promise<any>[] = [];
|
||||
|
||||
metadata
|
||||
.relations
|
||||
.filter(relation => entity.hasOwnProperty(relation.propertyName))
|
||||
.map(relation => entity[relation.propertyName])
|
||||
.map(value => {
|
||||
.map(relation => {
|
||||
const value = entity[relation.propertyName];
|
||||
if (value instanceof Array) {
|
||||
promises = promises.concat(this.broadcastLoadEventsForAll(value));
|
||||
promises = promises.concat(this.broadcastLoadEventsForAll(relation.inverseEntityMetadata.target, value));
|
||||
} else {
|
||||
promises.push(this.broadcastLoadEvents(value));
|
||||
promises.push(this.broadcastLoadEvents(relation.inverseEntityMetadata.target, value));
|
||||
}
|
||||
});
|
||||
|
||||
@ -173,8 +173,8 @@ export class Broadcaster {
|
||||
return Promise.all(promises).then(() => {});
|
||||
}
|
||||
|
||||
broadcastLoadEventsForAll(entities: any[]): Promise<void> {
|
||||
const promises = entities.map(entity => this.broadcastLoadEvents(entity));
|
||||
broadcastLoadEventsForAll(target: Function|string, entities: any[]): Promise<void> {
|
||||
const promises = entities.map(entity => this.broadcastLoadEvents(target, entity));
|
||||
return Promise.all(promises).then(() => {});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user