mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
Merge pull request #7 from pleerock/async-await
refactored connection class
This commit is contained in:
commit
1f716002c3
23
package.json
23
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "typeorm",
|
||||
"private": true,
|
||||
"version": "0.0.2-alpha.31",
|
||||
"version": "0.0.2-alpha.32",
|
||||
"description": "Data-mapper ORM for Typescript",
|
||||
"license": "Apache-2.0",
|
||||
"readmeFilename": "README.md",
|
||||
@ -31,30 +31,31 @@
|
||||
"gulp-mocha": "^2.2.0",
|
||||
"gulp-replace": "^0.5.4",
|
||||
"gulp-shell": "^0.5.1",
|
||||
"gulp-tslint": "^4.3.5",
|
||||
"gulpclass": "0.1.0",
|
||||
"mocha": "^2.3.2",
|
||||
"sinon": "^1.17.2",
|
||||
"gulp-tslint": "^5.0.0",
|
||||
"gulpclass": "0.1.1",
|
||||
"mocha": "^2.5.3",
|
||||
"mysql": "^2.10.2",
|
||||
"sinon": "^1.17.4",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"tslint": "^3.7.4",
|
||||
"tslint": "next",
|
||||
"tslint-stylish": "^2.1.0-beta",
|
||||
"typescript": "next",
|
||||
"typings": "^0.7.12"
|
||||
"typings": "^1.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs": "^0.0.2",
|
||||
"glob": "^7.0.3",
|
||||
"lodash": "^4.11.1",
|
||||
"lodash": "^4.13.1",
|
||||
"moment": "^2.13.0",
|
||||
"mysql": "^2.10.2",
|
||||
"path": "^0.12.7",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
"require-all": "^2.0.0",
|
||||
"rxjs": "^5.0.0-beta.7",
|
||||
"rxjs": "^5.0.0-beta.8",
|
||||
"sha1": "^1.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "./node_modules/.bin/gulp package",
|
||||
"test": "node_modules/.bin/mocha -w"
|
||||
"postinstall": "./node_modules/.bin/typings install",
|
||||
"test": "node_modules/.bin/gulp tests"
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,10 +43,10 @@ export class Post {
|
||||
secondaryImages: Image[];
|
||||
|
||||
@ManyToOne(type => Cover, cover => cover.posts, {
|
||||
name: "coverId",
|
||||
cascadeInsert: true,
|
||||
cascadeRemove: true
|
||||
})
|
||||
@JoinColumn({ name: "coverId" })
|
||||
cover: Cover;
|
||||
|
||||
@Column("int", {
|
||||
|
||||
@ -1,59 +1,29 @@
|
||||
import * as _ from "lodash";
|
||||
import {NamingStrategyInterface} from "../../../src/naming-strategy/NamingStrategyInterface";
|
||||
import {NamingStrategy} from "../../../src/decorator/NamingStrategy";
|
||||
import {DefaultNamingStrategy} from "../../../src/naming-strategy/DefaultNamingStrategy";
|
||||
|
||||
@NamingStrategy("custom_strategy")
|
||||
export class CustomNamingStrategy implements NamingStrategyInterface {
|
||||
export class CustomNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
|
||||
tableName(className: string): string {
|
||||
return _.snakeCase(className);
|
||||
}
|
||||
|
||||
tableNameCustomized(customName: string): string {
|
||||
return customName;
|
||||
}
|
||||
|
||||
columnName(propertyName: string): string {
|
||||
return _.snakeCase(propertyName);
|
||||
}
|
||||
|
||||
columnNameCustomized(customName: string): string {
|
||||
return customName;
|
||||
}
|
||||
|
||||
relationName(propertyName: string): string {
|
||||
return _.snakeCase(propertyName);
|
||||
}
|
||||
|
||||
indexName(target: Function, name: string, columns: string[]): string {
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
return "index" + columns.join("_");
|
||||
}
|
||||
|
||||
joinColumnInverseSideName(joinColumnName: string, propertyName: string): string {
|
||||
if (joinColumnName)
|
||||
return joinColumnName;
|
||||
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
joinTableName(firstTableName: string,
|
||||
secondTableName: string,
|
||||
firstColumnName: string,
|
||||
secondColumnName: string,
|
||||
firstPropertyName: string,
|
||||
secondPropertyName: string): string {
|
||||
return _.snakeCase(firstTableName + "_" + firstColumnName + "_" + secondTableName + "_" + secondPropertyName);
|
||||
}
|
||||
|
||||
joinTableColumnName(tableName: string, columnName: string, secondTableName: string, secondColumnName: string): string {
|
||||
const column1 = tableName + "_" + columnName;
|
||||
const column2 = secondTableName + "_" + secondColumnName;
|
||||
return column1 === column2 ? column1 + "_1" : column1;
|
||||
}
|
||||
|
||||
joinTableInverseColumnName(tableName: string, columnName: string, secondTableName: string, secondColumnName: string): string {
|
||||
const column1 = tableName + "_" + columnName;
|
||||
const column2 = secondTableName + "_" + secondColumnName;
|
||||
return column1 === column2 ? column1 + "_2" : column1;
|
||||
}
|
||||
|
||||
closureJunctionTableName(tableName: string): string {
|
||||
return tableName + "_closure";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,15 +4,14 @@ import {Repository} from "../repository/Repository";
|
||||
import {EventSubscriberInterface} from "../subscriber/EventSubscriberInterface";
|
||||
import {RepositoryNotFoundError} from "./error/RepositoryNotFoundError";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {SchemaCreator} from "../schema-creator/SchemaCreator";
|
||||
import {ConstructorFunction} from "../common/ConstructorFunction";
|
||||
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
|
||||
import {EntityManager} from "../entity-manager/EntityManager";
|
||||
import {importClassesFromDirectories} from "../util/DirectoryExportedClassesLoader";
|
||||
import {defaultMetadataStorage, getContainer} from "../index";
|
||||
import {EntityMetadataBuilder} from "../metadata-storage/EntityMetadataBuilder";
|
||||
import {getMetadataArgsStorage, getFromContainer} from "../index";
|
||||
import {EntityMetadataBuilder} from "../metadata-builder/EntityMetadataBuilder";
|
||||
import {DefaultNamingStrategy} from "../naming-strategy/DefaultNamingStrategy";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {NamingStrategyMetadata} from "../metadata/NamingStrategyMetadata";
|
||||
import {NoConnectionForRepositoryError} from "./error/NoConnectionForRepositoryError";
|
||||
import {CannotImportAlreadyConnectedError} from "./error/CannotImportAlreadyConnectedError";
|
||||
@ -22,11 +21,13 @@ import {ReactiveRepository} from "../repository/ReactiveRepository";
|
||||
import {ReactiveEntityManager} from "../entity-manager/ReactiveEntityManager";
|
||||
import {TreeRepository} from "../repository/TreeRepository";
|
||||
import {ReactiveTreeRepository} from "../repository/ReactiveTreeRepository";
|
||||
|
||||
/**
|
||||
* Temporary type to store and link both repository and its metadata.
|
||||
*/
|
||||
type RepositoryAndMetadata = { metadata: EntityMetadata, repository: Repository<any>, reactiveRepository: ReactiveRepository<any> };
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {NamingStrategyNotFoundError} from "./error/NamingStrategyNotFoundError";
|
||||
import {EntityManagerFactory} from "../entity-manager/EntityManagerFactory";
|
||||
import {RepositoryFactory} from "../repository/RepositoryFactory";
|
||||
import {SchemaCreatorFactory} from "../schema-creator/SchemaCreatorFactory";
|
||||
import {ReactiveRepositoryNotFoundError} from "./error/ReactiveRepositoryNotFoundError";
|
||||
import {RepositoryNotTreeError} from "./error/RepositoryNotTreeError";
|
||||
|
||||
/**
|
||||
* A single connection instance to the database. Each connection has its own repositories, subscribers and metadatas.
|
||||
@ -37,7 +38,15 @@ export class Connection {
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private repositoryAndMetadatas: RepositoryAndMetadata[] = [];
|
||||
/**
|
||||
* All connection's repositories.
|
||||
*/
|
||||
private repositories: Repository<any>[] = [];
|
||||
|
||||
/**
|
||||
* All connection's reactive repositories.
|
||||
*/
|
||||
private reactiveRepositories: ReactiveRepository<any>[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Readonly properties
|
||||
@ -121,8 +130,8 @@ export class Connection {
|
||||
this.driver = driver;
|
||||
this.driver.connectionOptions = options;
|
||||
this.options = options;
|
||||
this.entityManager = new EntityManager(this);
|
||||
this.reactiveEntityManager = new ReactiveEntityManager(this);
|
||||
this.entityManager = getFromContainer(EntityManagerFactory).createEntityManager(this);
|
||||
this.reactiveEntityManager = getFromContainer(EntityManagerFactory).createReactiveEntityManager(this);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -143,48 +152,50 @@ export class Connection {
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
*/
|
||||
connect(): Promise<this> {
|
||||
async connect(): Promise<this> {
|
||||
if (this.isConnected)
|
||||
throw new CannotConnectAlreadyConnectedError(this.name);
|
||||
|
||||
return this.driver.connect().then(() => {
|
||||
|
||||
// first build all metadata
|
||||
this.buildMetadatas();
|
||||
|
||||
// second build schema
|
||||
if (this.options.autoSchemaCreate === true)
|
||||
return this.syncSchema();
|
||||
|
||||
return Promise.resolve();
|
||||
// connect to the database via its driver
|
||||
await this.driver.connect();
|
||||
|
||||
}).then(() => {
|
||||
this._isConnected = true;
|
||||
return this;
|
||||
});
|
||||
// build all metadatas registered in the current connection
|
||||
this.buildMetadatas();
|
||||
|
||||
// second build schema
|
||||
if (this.options.autoSchemaCreate === true)
|
||||
await this.syncSchema();
|
||||
|
||||
// set connected status for the current connection
|
||||
this._isConnected = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this connection.
|
||||
*/
|
||||
close(): Promise<void> {
|
||||
async close(): Promise<void> {
|
||||
if (!this.isConnected)
|
||||
throw new CannotCloseNotConnectedError(this.name);
|
||||
|
||||
|
||||
return this.driver.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates database schema for all entities registered in this connection.
|
||||
*/
|
||||
syncSchema() {
|
||||
async syncSchema(dropBeforeSync: boolean = false): Promise<void> {
|
||||
if (dropBeforeSync)
|
||||
await this.driver.clearDatabase();
|
||||
|
||||
const schemaBuilder = this.driver.createSchemaBuilder();
|
||||
const schemaCreator = new SchemaCreator(schemaBuilder, this.entityMetadatas);
|
||||
const schemaCreatorFactory = getFromContainer(SchemaCreatorFactory);
|
||||
const schemaCreator = schemaCreatorFactory.create(schemaBuilder, this.entityMetadatas);
|
||||
return schemaCreator.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entities from the given paths (directories) for the current connection.
|
||||
* Imports entities from the given paths (directories) for the current connection.
|
||||
*/
|
||||
importEntitiesFromDirectories(paths: string[]): this {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
@ -206,7 +217,7 @@ export class Connection {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Imports entities for the current connection.
|
||||
*/
|
||||
@ -235,7 +246,7 @@ export class Connection {
|
||||
importNamingStrategies(strategies: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("naming strategies", this.name);
|
||||
|
||||
|
||||
strategies.forEach(cls => this.namingStrategyClasses.push(cls));
|
||||
return this;
|
||||
}
|
||||
@ -258,18 +269,12 @@ export class Connection {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
let metadata: EntityMetadata;
|
||||
if (typeof entityClassOrName === "string") {
|
||||
metadata = this.entityMetadatas.findByName(entityClassOrName);
|
||||
} else {
|
||||
metadata = this.entityMetadatas.findByTarget(entityClassOrName);
|
||||
}
|
||||
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClassOrName);
|
||||
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
|
||||
return repoMeta.repository;
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -290,52 +295,46 @@ export class Connection {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
let metadata: EntityMetadata;
|
||||
if (typeof entityClassOrName === "string") {
|
||||
metadata = this.entityMetadatas.findByName(entityClassOrName);
|
||||
} else {
|
||||
metadata = this.entityMetadatas.findByTarget(entityClassOrName);
|
||||
}
|
||||
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClassOrName);
|
||||
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
if (!repoMeta.metadata.table.isClosure)
|
||||
throw new Error(`Cannot get a tree repository. ${repoMeta.metadata.name} is not a tree table. Try to use @ClosureTable decorator instead of @Table.`);
|
||||
if (!metadata.table.isClosure)
|
||||
throw new RepositoryNotTreeError(entityClassOrName);
|
||||
|
||||
return <TreeRepository<Entity>> repoMeta.repository;
|
||||
return <TreeRepository<Entity>> repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reactive repository for the given entity class.
|
||||
*/
|
||||
getReactiveRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): ReactiveRepository<Entity> {
|
||||
getReactiveRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): ReactiveRepository<Entity> {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClass);
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
throw new RepositoryNotFoundError(this.name, entityClass);
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClass);
|
||||
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new ReactiveRepositoryNotFoundError(this.name, entityClass);
|
||||
|
||||
return repoMeta.reactiveRepository;
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reactive tree repository for the given entity class.
|
||||
*/
|
||||
getReactiveTreeRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): ReactiveTreeRepository<Entity> {
|
||||
getReactiveTreeRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): ReactiveTreeRepository<Entity> {
|
||||
if (!this.isConnected)
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClass);
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
const metadata = this.entityMetadatas.findByNameOrTarget(entityClass);
|
||||
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClass);
|
||||
if (!repoMeta.metadata.table.isClosure)
|
||||
throw new Error(`Cannot get a tree repository. ${repoMeta.metadata.name} is not a tree table. Try to use @ClosureTable decorator instead of @Table.`);
|
||||
if (!metadata.table.isClosure)
|
||||
throw new RepositoryNotTreeError(entityClass);
|
||||
|
||||
return <ReactiveTreeRepository<Entity>> repoMeta.reactiveRepository;
|
||||
return <ReactiveTreeRepository<Entity>> repository;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -346,68 +345,64 @@ export class Connection {
|
||||
* Builds all registered metadatas.
|
||||
*/
|
||||
private buildMetadatas() {
|
||||
|
||||
// first register naming strategies
|
||||
const metadatas = defaultMetadataStorage().namingStrategyMetadatas.filterByClasses(this.namingStrategyClasses);
|
||||
metadatas.forEach(cls => this.namingStrategyMetadatas.push(cls));
|
||||
|
||||
// second register subscriber metadatas
|
||||
const subscribers = defaultMetadataStorage()
|
||||
.eventSubscriberMetadatas
|
||||
// take imported naming strategy metadatas
|
||||
getMetadataArgsStorage()
|
||||
.namingStrategies
|
||||
.filterByClasses(this.namingStrategyClasses)
|
||||
.forEach(metadata => this.namingStrategyMetadatas.push(new NamingStrategyMetadata(metadata)));
|
||||
|
||||
// take imported event subscribers
|
||||
getMetadataArgsStorage()
|
||||
.eventSubscribers
|
||||
.filterByClasses(this.subscriberClasses)
|
||||
.map(metadata => this.createContainerInstance(metadata.target));
|
||||
this.eventSubscribers.push(...subscribers);
|
||||
.map(metadata => getFromContainer(metadata.target))
|
||||
.forEach(subscriber => this.eventSubscribers.push(subscriber));
|
||||
|
||||
// third register entity and entity listener metadatas
|
||||
const entityMetadataBuilder = new EntityMetadataBuilder(this.createNamingStrategy());
|
||||
const entityMetadatas = entityMetadataBuilder.build(this.entityClasses);
|
||||
const entityListenerMetadatas = defaultMetadataStorage().entityListenerMetadatas.filterByClasses(this.entityClasses);
|
||||
// take imported entity listeners
|
||||
getMetadataArgsStorage()
|
||||
.entityListeners
|
||||
.filterByClasses(this.entityClasses)
|
||||
.forEach(metadata => this.entityListeners.push(new EntityListenerMetadata(metadata)));
|
||||
|
||||
entityMetadatas.forEach(cls => this.entityMetadatas.push(cls));
|
||||
entityListenerMetadatas.forEach(cls => this.entityListeners.push(cls));
|
||||
entityMetadatas.map(metadata => this.createRepoMeta(metadata)).forEach(cls => this.repositoryAndMetadatas.push(cls));
|
||||
// build entity metadatas for the current connection
|
||||
getFromContainer(EntityMetadataBuilder)
|
||||
.build(this.createNamingStrategy(), this.entityClasses)
|
||||
.forEach(layout => {
|
||||
this.entityMetadatas.push(layout);
|
||||
this.createRepository(layout);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the naming strategy to be used for this connection.
|
||||
* Creates a naming strategy to be used for this connection.
|
||||
*/
|
||||
private createNamingStrategy() {
|
||||
private createNamingStrategy(): NamingStrategyInterface {
|
||||
if (!this.options.namingStrategy)
|
||||
return new DefaultNamingStrategy();
|
||||
return getFromContainer(DefaultNamingStrategy);
|
||||
|
||||
const namingMetadata = this.namingStrategyMetadatas.find(strategy => strategy.name === this.options.namingStrategy);
|
||||
if (!namingMetadata)
|
||||
throw new Error(`Naming strategy called "${this.options.namingStrategy}" was not found.`);
|
||||
|
||||
return this.createContainerInstance(namingMetadata.target);
|
||||
throw new NamingStrategyNotFoundError(this.options.namingStrategy, this.name);
|
||||
|
||||
return getFromContainer<NamingStrategyInterface>(namingMetadata.target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given constructor. If service container is registered in the ORM, then it will
|
||||
* be used, otherwise new instance of naming strategy will be created.
|
||||
* Creates repository and reactive repository for the given entity metadata.
|
||||
*/
|
||||
private createContainerInstance(constructor: Function) {
|
||||
return getContainer() ? getContainer().get(constructor) : new (<any> constructor)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary object RepositoryAndMetadata to store relation between repository and metadata.
|
||||
*/
|
||||
private createRepoMeta(metadata: EntityMetadata): RepositoryAndMetadata {
|
||||
if (metadata.table.isClosure) {
|
||||
const repository = new TreeRepository<any>(this, this.entityMetadatas, metadata);
|
||||
return {
|
||||
metadata: metadata,
|
||||
repository: repository,
|
||||
reactiveRepository: new ReactiveTreeRepository(repository)
|
||||
};
|
||||
private createRepository(entityLayout: EntityMetadata) {
|
||||
const repositoryFactory = getFromContainer(RepositoryFactory);
|
||||
if (entityLayout.table.isClosure) {
|
||||
const repository = repositoryFactory.createRepository(this, this.entityMetadatas, entityLayout);
|
||||
const reactiveRepository = repositoryFactory.createReactiveRepository(repository);
|
||||
this.repositories.push(repository);
|
||||
this.reactiveRepositories.push(reactiveRepository);
|
||||
} else {
|
||||
const repository = new Repository<any>(this, this.entityMetadatas, metadata);
|
||||
return {
|
||||
metadata: metadata,
|
||||
repository: repository,
|
||||
reactiveRepository: new ReactiveRepository(repository)
|
||||
};
|
||||
const repository = repositoryFactory.createTreeRepository(this, this.entityMetadatas, entityLayout);
|
||||
const reactiveRepository = repositoryFactory.createReactiveTreeRepository(repository);
|
||||
this.repositories.push(repository);
|
||||
this.reactiveRepositories.push(reactiveRepository);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Connection options passed to the document.
|
||||
* Connection options passed to the connection.
|
||||
*/
|
||||
export interface ConnectionOptions {
|
||||
|
||||
|
||||
13
src/connection/error/NamingStrategyNotFoundError.ts
Normal file
13
src/connection/error/NamingStrategyNotFoundError.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class NamingStrategyNotFoundError extends Error {
|
||||
name = "NamingStrategyNotFoundError";
|
||||
|
||||
constructor(strategyName: string, connectionName: string) {
|
||||
super();
|
||||
this.message = `Naming strategy named "${strategyName}" was not found. Looks like this naming strategy does not ` +
|
||||
`exist or it was not registered in current "${connectionName}" connection?`;
|
||||
}
|
||||
|
||||
}
|
||||
14
src/connection/error/ReactiveRepositoryNotFoundError.ts
Normal file
14
src/connection/error/ReactiveRepositoryNotFoundError.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class ReactiveRepositoryNotFoundError extends Error {
|
||||
name = "ReactiveRepositoryNotFoundError";
|
||||
|
||||
constructor(connectionName: string, entityClass: Function|string) {
|
||||
super();
|
||||
const targetName = typeof entityClass === "function" && (<any> entityClass).name ? (<any> entityClass).name : entityClass;
|
||||
this.message = `No reactive repository for "${targetName}" was found. Looks like this entity is not registered in ` +
|
||||
`current "${connectionName}" connection?`;
|
||||
}
|
||||
|
||||
}
|
||||
13
src/connection/error/RepositoryNotTreeError.ts
Normal file
13
src/connection/error/RepositoryNotTreeError.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class RepositoryNotTreeError extends Error {
|
||||
name = "RepositoryNotTreeError";
|
||||
|
||||
constructor(entityClass: Function|string) {
|
||||
super();
|
||||
const targetName = typeof entityClass === "function" && (<any> entityClass).name ? (<any> entityClass).name : entityClass;
|
||||
this.message = `Repository of the "${targetName}" class is not a TreeRepository. Try to use @ClosureTable decorator instead of @Table.`;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import {NamingStrategyMetadata} from "../metadata/NamingStrategyMetadata";
|
||||
import {defaultMetadataStorage} from "../index";
|
||||
import {getMetadataArgsStorage} from "../index";
|
||||
import {NamingStrategyMetadataArgs} from "../metadata-args/NamingStrategyMetadataArgs";
|
||||
|
||||
/**
|
||||
* Decorator registers a new naming strategy to be used in naming things.
|
||||
@ -7,6 +7,10 @@ import {defaultMetadataStorage} from "../index";
|
||||
export function NamingStrategy(name?: string): Function {
|
||||
return function (target: Function) {
|
||||
const strategyName = name ? name : (<any> target).name;
|
||||
defaultMetadataStorage().namingStrategyMetadatas.add(new NamingStrategyMetadata(target, strategyName));
|
||||
const args: NamingStrategyMetadataArgs = {
|
||||
target: target,
|
||||
name: strategyName
|
||||
};
|
||||
getMetadataArgsStorage().namingStrategies.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {ColumnOptions} from "../../metadata/options/ColumnOptions";
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnTypeUndefinedError} from "../error/ColumnTypeUndefinedError";
|
||||
import {AutoIncrementOnlyForPrimaryError} from "../error/AutoIncrementOnlyForPrimaryError";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnType, ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column. Only properties decorated with this
|
||||
@ -31,11 +31,11 @@ export function Column(typeOrOptions?: ColumnType|ColumnOptions, options?: Colum
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
// todo: need to store not string type, but original type instead? (like in relation metadata)
|
||||
const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if type is not given implicitly then try to guess it
|
||||
if (!type)
|
||||
type = ColumnTypes.determineTypeFromFunction((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
type = ColumnTypes.determineTypeFromFunction((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
@ -53,11 +53,13 @@ export function Column(typeOrOptions?: ColumnType|ColumnOptions, options?: Colum
|
||||
throw new AutoIncrementOnlyForPrimaryError(object, propertyName);
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
mode: "regular",
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import {ColumnOptions} from "../../metadata/options/ColumnOptions";
|
||||
import {ColumnType, ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* This column will store a creation date of the inserted object. Creation date is generated and inserted only once,
|
||||
@ -10,7 +11,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
export function CreateDateColumn(options?: ColumnOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
@ -19,13 +20,14 @@ export function CreateDateColumn(options?: ColumnOptions): Function {
|
||||
options.type = ColumnTypes.DATETIME;
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
mode: "createDate",
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {ColumnOptions} from "../../metadata/options/ColumnOptions";
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnType, ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {ColumnTypeUndefinedError} from "../error/ColumnTypeUndefinedError";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {PrimaryColumnCannotBeNullableError} from "../error/PrimaryColumnCannotBeNullableError";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column. Only properties decorated with this
|
||||
@ -33,11 +33,11 @@ export function PrimaryColumn(typeOrOptions?: ColumnType|ColumnOptions, options?
|
||||
}
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if type is not given implicitly then try to guess it
|
||||
if (!type)
|
||||
type = ColumnTypes.determineTypeFromFunction((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
type = ColumnTypes.determineTypeFromFunction((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
@ -55,13 +55,14 @@ export function PrimaryColumn(typeOrOptions?: ColumnType|ColumnOptions, options?
|
||||
throw new PrimaryColumnCannotBeNullableError(object, propertyName);
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
isPrimaryKey: true,
|
||||
mode: "primary",
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {RelationsCountMetadata} from "../../metadata/RelationsCountMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {RelationsCountMetadataArgs} from "../../metadata-args/RelationsCountMetadataArgs";
|
||||
|
||||
/**
|
||||
* Holds a number of children in the closure table of the column.
|
||||
@ -8,10 +8,15 @@ export function RelationsCountColumn<T>(relation: string|((object: T) => any)):
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
// todo: need to check if property type is number?
|
||||
// const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
// const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().relationCountMetadatas.add(new RelationsCountMetadata(object.constructor, propertyName, relation));
|
||||
const args: RelationsCountMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
relation: relation
|
||||
};
|
||||
getMetadataArgsStorage().relationCounts.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {ColumnOptions} from "../../metadata/options/ColumnOptions";
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* This column will store an update date of the updated object. This date is being updated each time you persist the
|
||||
@ -10,7 +10,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
export function UpdateDateColumn(options?: ColumnOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
@ -19,13 +19,14 @@ export function UpdateDateColumn(options?: ColumnOptions): Function {
|
||||
options.type = ColumnTypes.DATETIME;
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
mode: "updateDate",
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {ColumnOptions} from "../../metadata/options/ColumnOptions";
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* This column will store a number - version of the entity. Every time your entity will be persisted, this number will
|
||||
@ -10,7 +10,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
export function VersionColumn(options?: ColumnOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
@ -21,13 +21,14 @@ export function VersionColumn(options?: ColumnOptions): Function {
|
||||
// todo: check if reflectedType is number too
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
mode: "version",
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,30 @@
|
||||
import {CompositeIndexMetadata} from "../../metadata/CompositeIndexMetadata";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {CompositeIndexOptions} from "../../metadata/options/CompositeIndexOptions";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {CompositeIndexOptions} from "../options/CompositeIndexOptions";
|
||||
import {IndexMetadataArgs} from "../../metadata-args/IndexMetadataArgs";
|
||||
|
||||
/**
|
||||
* Composite indexes must be set on entity classes and must specify fields to be indexed.
|
||||
* Composite index must be set on entity classes and must specify entity's fields to be indexed.
|
||||
*/
|
||||
export function CompositeIndex(name: string, fields: string[], options?: CompositeIndexOptions): Function;
|
||||
|
||||
/**
|
||||
* Composite index must be set on entity classes and must specify entity's fields to be indexed.
|
||||
*/
|
||||
export function CompositeIndex(fields: string[], options?: CompositeIndexOptions): Function;
|
||||
|
||||
/**
|
||||
* Composite index must be set on entity classes and must specify entity's fields to be indexed.
|
||||
*/
|
||||
export function CompositeIndex(fields: (object: any) => any[], options?: CompositeIndexOptions): Function;
|
||||
|
||||
/**
|
||||
* Composite index must be set on entity classes and must specify entity's fields to be indexed.
|
||||
*/
|
||||
export function CompositeIndex(name: string, fields: (object: any) => any[], options?: CompositeIndexOptions): Function;
|
||||
|
||||
/**
|
||||
* Composite index must be set on entity classes and must specify entity's fields to be indexed.
|
||||
*/
|
||||
export function CompositeIndex(nameOrFields: string|string[]|((object: any) => any[]),
|
||||
maybeFieldsOrOptions?: ((object: any) => any[])|CompositeIndexOptions|string[],
|
||||
maybeOptions?: CompositeIndexOptions): Function {
|
||||
@ -17,6 +33,12 @@ export function CompositeIndex(nameOrFields: string|string[]|((object: any) => a
|
||||
const options = typeof maybeFieldsOrOptions === "object" ? <CompositeIndexOptions> maybeFieldsOrOptions : maybeOptions;
|
||||
|
||||
return function (cls: Function) {
|
||||
defaultMetadataStorage().compositeIndexMetadatas.add(new CompositeIndexMetadata(cls, name, fields, options));
|
||||
const args: IndexMetadataArgs = {
|
||||
target: cls,
|
||||
name: name,
|
||||
columns: fields,
|
||||
unique: options && options.unique ? true : false
|
||||
};
|
||||
getMetadataArgsStorage().indices.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,11 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {IndexMetadata} from "../../metadata/IndexMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {IndexMetadataArgs} from "../../metadata-args/IndexMetadataArgs";
|
||||
|
||||
/**
|
||||
* Fields that needs to be indexed must be marked with this decorator.
|
||||
*/
|
||||
export function Index(name?: string) {
|
||||
export function Index(name?: string, options?: { unique: boolean }) {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().indexMetadatas.add(new IndexMetadata(object.constructor, propertyName, name));
|
||||
const args: IndexMetadataArgs = {
|
||||
name: name,
|
||||
target: object.constructor,
|
||||
columns: [propertyName],
|
||||
unique: options && options.unique ? true : false
|
||||
};
|
||||
getMetadataArgsStorage().indices.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after this entity insertion.
|
||||
*/
|
||||
export function AfterInsert() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.AFTER_INSERT
|
||||
));
|
||||
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_INSERT
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after entity is loaded.
|
||||
*/
|
||||
export function AfterLoad() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.AFTER_LOAD
|
||||
));
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_LOAD
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after this entity removal.
|
||||
*/
|
||||
export function AfterRemove() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.AFTER_REMOVE
|
||||
));
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_REMOVE
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied after this entity update.
|
||||
*/
|
||||
export function AfterUpdate() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.AFTER_UPDATE
|
||||
));
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.AFTER_UPDATE
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity insertion.
|
||||
*/
|
||||
export function BeforeInsert() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.BEFORE_INSERT
|
||||
));
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_INSERT
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity removal.
|
||||
*/
|
||||
export function BeforeRemove() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.BEFORE_REMOVE
|
||||
));
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_REMOVE
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventListenerTypes} from "../../metadata/types/EventListenerTypes";
|
||||
import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
|
||||
import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* Calls a method on which this decorator is applied before this entity update.
|
||||
*/
|
||||
export function BeforeUpdate() {
|
||||
return function (object: Object, propertyName: string) {
|
||||
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
|
||||
object.constructor,
|
||||
propertyName,
|
||||
EventListenerTypes.BEFORE_UPDATE
|
||||
));
|
||||
const args: EntityListenerMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
type: EventListenerTypes.BEFORE_UPDATE
|
||||
};
|
||||
getMetadataArgsStorage().entityListeners.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {EventSubscriberMetadata} from "../../metadata/EventSubscriberMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {EventSubscriberMetadataArgs} from "../../metadata-args/EventSubscriberMetadataArgs";
|
||||
|
||||
/**
|
||||
* Classes decorated with this decorator will listen to ORM events and their methods will be triggered when event
|
||||
@ -7,6 +7,9 @@ import {EventSubscriberMetadata} from "../../metadata/EventSubscriberMetadata";
|
||||
*/
|
||||
export function EventSubscriber() {
|
||||
return function (target: Function) {
|
||||
defaultMetadataStorage().eventSubscriberMetadatas.add(new EventSubscriberMetadata(target));
|
||||
const args: EventSubscriberMetadataArgs = {
|
||||
target: target
|
||||
};
|
||||
getMetadataArgsStorage().eventSubscribers.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,72 +1,78 @@
|
||||
import {ColumnType} from "../types/ColumnTypes";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
|
||||
/**
|
||||
* Describes all column's options.
|
||||
*/
|
||||
export interface ColumnOptions {
|
||||
|
||||
/**
|
||||
* Column name.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Column type. Must be one of the value from the ColumnTypes class.
|
||||
*/
|
||||
type?: ColumnType;
|
||||
|
||||
/**
|
||||
* Column name.
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Column type's length. For example type = "string" and length = 100 means that ORM will create a column with
|
||||
* type varchar(100).
|
||||
*/
|
||||
length?: string;
|
||||
readonly length?: string;
|
||||
|
||||
/**
|
||||
* Specifies if this column will use AUTO_INCREMENT or not (e.g. generated number).
|
||||
*/
|
||||
generated?: boolean;
|
||||
readonly generated?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies if column's value must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
readonly unique?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if column's value can be set to NULL.
|
||||
*/
|
||||
nullable?: boolean;
|
||||
readonly nullable?: boolean;
|
||||
|
||||
/**
|
||||
* Extra column definition. Should be used only in emergency situations. Note that if you'll use this property
|
||||
* auto schema generation will not work properly anymore.
|
||||
*/
|
||||
columnDefinition?: string;
|
||||
readonly columnDefinition?: string;
|
||||
|
||||
/**
|
||||
* Column comment.
|
||||
*/
|
||||
comment?: string;
|
||||
readonly comment?: string;
|
||||
|
||||
/**
|
||||
* Column name used previously for this column. Used to make safe schema updates. Experimental and most probably
|
||||
* will be removed in the future. Avoid using it.
|
||||
*/
|
||||
oldColumnName?: string;
|
||||
readonly oldColumnName?: string;
|
||||
|
||||
/**
|
||||
* The precision for a decimal (exact numeric) column (applies only for decimal column), which is the maximum
|
||||
* number of digits that are stored for the values.
|
||||
*/
|
||||
precision?: number;
|
||||
readonly precision?: number;
|
||||
|
||||
/**
|
||||
* The scale for a decimal (exact numeric) column (applies only for decimal column), which represents the number
|
||||
* of digits to the right of the decimal point and must not be greater than precision.
|
||||
*/
|
||||
scale?: number;
|
||||
readonly scale?: number;
|
||||
|
||||
/**
|
||||
* Column collation. Note that not all databases support it.
|
||||
*/
|
||||
collation?: string;
|
||||
readonly collation?: string; // todo: looks like this is not used
|
||||
|
||||
/**
|
||||
* Indicates if this column is order id column.
|
||||
*/
|
||||
readonly isOrderId?: boolean; // todo: looks like this is not implemented yet
|
||||
|
||||
}
|
||||
@ -6,6 +6,6 @@ export interface CompositeIndexOptions {
|
||||
/**
|
||||
* Indicates if this composite index must be unique or not.
|
||||
*/
|
||||
unique?: boolean;
|
||||
readonly unique?: boolean;
|
||||
|
||||
}
|
||||
@ -6,11 +6,11 @@ export interface JoinColumnOptions {
|
||||
/**
|
||||
* Name of the column.
|
||||
*/
|
||||
name?: string;
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Name of the column in the entity to which this column is referenced.
|
||||
*/
|
||||
referencedColumnName?: string;
|
||||
readonly referencedColumnName?: string;
|
||||
|
||||
}
|
||||
@ -9,16 +9,16 @@ export interface JoinTableOptions {
|
||||
* Name of the table that will be created to store values of the both tables (join table).
|
||||
* By default is auto generated.
|
||||
*/
|
||||
name?: string;
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* First column of the join table.
|
||||
*/
|
||||
joinColumn?: JoinColumnOptions;
|
||||
readonly joinColumn?: JoinColumnOptions;
|
||||
|
||||
/**
|
||||
* Second (inverse) column of the join table.
|
||||
*/
|
||||
inverseJoinColumn?: JoinColumnOptions;
|
||||
readonly inverseJoinColumn?: JoinColumnOptions;
|
||||
|
||||
}
|
||||
@ -5,46 +5,41 @@ import {OnDeleteType} from "../../metadata/ForeignKeyMetadata";
|
||||
*/
|
||||
export interface RelationOptions {
|
||||
|
||||
/**
|
||||
* Field name to be used in the database.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be inserted / updated / removed to the db.
|
||||
* This is option a shortcut if you would like to set cascadeInsert, cascadeUpdate and cascadeRemove to true.
|
||||
*/
|
||||
cascadeAll?: boolean;
|
||||
readonly cascadeAll?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be inserted to the db.
|
||||
*/
|
||||
cascadeInsert?: boolean;
|
||||
readonly cascadeInsert?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be updated in the db.
|
||||
*/
|
||||
cascadeUpdate?: boolean;
|
||||
readonly cascadeUpdate?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object can be allowed to be remove from the db.
|
||||
*/
|
||||
cascadeRemove?: boolean;
|
||||
readonly cascadeRemove?: boolean;
|
||||
|
||||
/**
|
||||
* Column name used previously for this column. Used to make safe schema updates. Experimental and most probably
|
||||
* will be removed in the future. Avoid using it.
|
||||
*/
|
||||
oldColumnName?: string;
|
||||
readonly oldColumnName?: string;
|
||||
|
||||
/**
|
||||
* Indicates if relation column value can be nullable or not.
|
||||
*/
|
||||
nullable?: boolean;
|
||||
readonly nullable?: boolean;
|
||||
|
||||
/**
|
||||
* Database cascade action on delete.
|
||||
*/
|
||||
onDelete?: OnDeleteType;
|
||||
readonly onDelete?: OnDeleteType;
|
||||
|
||||
}
|
||||
@ -1,14 +1,19 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {JoinColumnMetadata} from "../../metadata/JoinColumnMetadata";
|
||||
import {JoinColumnOptions} from "../../metadata/options/JoinColumnOptions";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {JoinColumnOptions} from "../options/JoinColumnOptions";
|
||||
import {JoinColumnMetadataArgs} from "../../metadata-args/JoinColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
*/
|
||||
export function JoinColumn(options?: JoinColumnOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
options = options || {} as JoinColumnOptions;
|
||||
const metadata = new JoinColumnMetadata(object.constructor, propertyName, options);
|
||||
defaultMetadataStorage().joinColumnMetadatas.add(metadata);
|
||||
const args: JoinColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
name: options.name,
|
||||
referencedColumnName: options.referencedColumnName
|
||||
};
|
||||
getMetadataArgsStorage().joinColumns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {JoinTableOptions} from "../../metadata/options/JoinTableOptions";
|
||||
import {JoinTableMetadata} from "../../metadata/JoinTableMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {JoinTableOptions} from "../options/JoinTableOptions";
|
||||
import {JoinTableMetadataArgs} from "../../metadata-args/JoinTableMetadataArgs";
|
||||
|
||||
/**
|
||||
*/
|
||||
export function JoinTable(options?: JoinTableOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
options = options || {} as JoinTableOptions;
|
||||
const metadata = new JoinTableMetadata(object.constructor, propertyName, options);
|
||||
defaultMetadataStorage().joinTableMetadatas.add(metadata);
|
||||
const args: JoinTableMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
name: options.name,
|
||||
joinColumn: options.joinColumn,
|
||||
inverseJoinColumn: options.inverseJoinColumn
|
||||
};
|
||||
getMetadataArgsStorage().joinTables.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {RelationOptions} from "../../metadata/options/RelationOptions";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ConstructorFunction} from "../../common/ConstructorFunction";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
|
||||
/**
|
||||
* Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have
|
||||
@ -38,9 +38,9 @@ export function ManyToMany<T>(typeFunction: (type?: any) => ConstructorFunction<
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (<any> Reflect).getMetadata("design:type", object, propertyName);
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
|
||||
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
|
||||
const args: RelationMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
@ -48,7 +48,8 @@ export function ManyToMany<T>(typeFunction: (type?: any) => ConstructorFunction<
|
||||
type: typeFunction,
|
||||
inverseSideProperty: inverseSideProperty,
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {RelationOptions} from "../../metadata/options/RelationOptions";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ConstructorFunction} from "../../common/ConstructorFunction";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
|
||||
/**
|
||||
* Many-to-one relation allows to create type of relation when Entity1 can have single instance of Entity2, but
|
||||
@ -38,9 +38,9 @@ export function ManyToOne<T>(typeFunction: (type?: any) => ConstructorFunction<T
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (<any> Reflect).getMetadata("design:type", object, propertyName);
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
|
||||
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
|
||||
const args: RelationMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
@ -48,6 +48,7 @@ export function ManyToOne<T>(typeFunction: (type?: any) => ConstructorFunction<T
|
||||
type: typeFunction,
|
||||
inverseSideProperty: inverseSideProperty,
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {RelationOptions} from "../../metadata/options/RelationOptions";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ConstructorFunction} from "../../common/ConstructorFunction";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
|
||||
// todo: make decorators which use inverse side string separate
|
||||
|
||||
@ -40,9 +40,9 @@ export function OneToMany<T>(typeFunction: (type?: any) => ConstructorFunction<T
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (<any> Reflect).getMetadata("design:type", object, propertyName);
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
|
||||
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
|
||||
const args: RelationMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
@ -50,7 +50,8 @@ export function OneToMany<T>(typeFunction: (type?: any) => ConstructorFunction<T
|
||||
type: typeFunction,
|
||||
inverseSideProperty: inverseSideProperty,
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {RelationOptions} from "../../metadata/options/RelationOptions";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ConstructorFunction} from "../../common/ConstructorFunction";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
|
||||
/**
|
||||
* One-to-one relation allows to create direct relation between two entities. Entity1 have only one Entity2.
|
||||
@ -35,9 +35,9 @@ export function OneToOne<T>(typeFunction: (type?: any) => ConstructorFunction<T>
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (<any> Reflect).getMetadata("design:type", object, propertyName);
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
|
||||
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
|
||||
const args: RelationMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
@ -45,6 +45,7 @@ export function OneToOne<T>(typeFunction: (type?: any) => ConstructorFunction<T>
|
||||
type: typeFunction,
|
||||
inverseSideProperty: inverseSideProperty,
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,11 +1,16 @@
|
||||
import {TableMetadata} from "../../metadata/TableMetadata";
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {TableMetadataArgs} from "../../metadata-args/TableMetadataArgs";
|
||||
|
||||
/**
|
||||
* Allows to use columns and relations data from the inherited metadata.
|
||||
*/
|
||||
export function AbstractTable() {
|
||||
return function (cls: Function) {
|
||||
defaultMetadataStorage().tableMetadatas.add(new TableMetadata(cls, undefined, "abstract"));
|
||||
return function (target: Function) {
|
||||
const args: TableMetadataArgs = {
|
||||
target: target,
|
||||
name: undefined,
|
||||
type: "abstract"
|
||||
};
|
||||
getMetadataArgsStorage().tables.add(args);
|
||||
};
|
||||
}
|
||||
@ -1,12 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {TableMetadata} from "../../metadata/TableMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {TableMetadataArgs} from "../../metadata-args/TableMetadataArgs";
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be a tables. Database schema will be created for all classes
|
||||
* decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function ClosureTable(name?: string) {
|
||||
return function (cls: Function) {
|
||||
defaultMetadataStorage().tableMetadatas.add(new TableMetadata(cls, name, "closure"));
|
||||
return function (target: Function) {
|
||||
const args: TableMetadataArgs = {
|
||||
target: target,
|
||||
name: name,
|
||||
type: "closure"
|
||||
};
|
||||
getMetadataArgsStorage().tables.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {TableMetadata} from "../../metadata/TableMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {TableMetadataArgs} from "../../metadata-args/TableMetadataArgs";
|
||||
|
||||
/**
|
||||
* This decorator is used to mark classes that will be a tables. Database schema will be created for all classes
|
||||
* decorated with it, and Repository can be retrieved and used for it.
|
||||
*/
|
||||
export function Table(name?: string) {
|
||||
return function (cls: Function) {
|
||||
defaultMetadataStorage().tableMetadatas.add(new TableMetadata(cls, name));
|
||||
return function (target: Function) {
|
||||
const args: TableMetadataArgs = {
|
||||
target: target,
|
||||
name: name,
|
||||
type: "regular"
|
||||
};
|
||||
getMetadataArgsStorage().tables.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {RelationOptions} from "../../metadata/options/RelationOptions";
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
|
||||
/**
|
||||
* Marks a specific property of the class as a children of the tree.
|
||||
@ -10,10 +10,10 @@ export function TreeChildren(options?: RelationOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (<any> Reflect).getMetadata("design:type", object, propertyName);
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
|
||||
// add one-to-many relation for this
|
||||
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
|
||||
const args: RelationMetadataArgs = {
|
||||
isTreeChildren: true,
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
@ -21,7 +21,8 @@ export function TreeChildren(options?: RelationOptions): Function {
|
||||
relationType: RelationTypes.ONE_TO_MANY,
|
||||
type: () => object.constructor,
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
import {ColumnOptions} from "../../metadata/options/ColumnOptions";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* Creates a "level"/"length" column to the table that holds a closure table.
|
||||
@ -9,7 +9,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
export function TreeLevelColumn(): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
const reflectedType = ColumnTypes.typeToString((<any> Reflect).getMetadata("design:type", object, propertyName));
|
||||
const reflectedType = ColumnTypes.typeToString((Reflect as any).getMetadata("design:type", object, propertyName));
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
const options: ColumnOptions = {};
|
||||
@ -18,13 +18,14 @@ export function TreeLevelColumn(): Function {
|
||||
options.type = ColumnTypes.INTEGER;
|
||||
|
||||
// create and register a new column metadata
|
||||
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
propertyType: reflectedType,
|
||||
mode: "treeLevel",
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {defaultMetadataStorage} from "../../index";
|
||||
import {RelationOptions} from "../../metadata/options/RelationOptions";
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
|
||||
/**
|
||||
* Marks a specific property of the class as a parent of the tree.
|
||||
@ -10,8 +10,8 @@ export function TreeParent(options?: RelationOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (<any> Reflect).getMetadata("design:type", object, propertyName);
|
||||
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
const args: RelationMetadataArgs = {
|
||||
isTreeParent: true,
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
@ -19,7 +19,8 @@ export function TreeParent(options?: RelationOptions): Function {
|
||||
relationType: RelationTypes.MANY_TO_ONE,
|
||||
type: () => object.constructor,
|
||||
options: options
|
||||
}));
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -254,9 +254,9 @@ export class MysqlDriver extends BaseDriver implements Driver {
|
||||
case ColumnTypes.DATE:
|
||||
return moment(value).format("YYYY-MM-DD");
|
||||
case ColumnTypes.TIME:
|
||||
return moment(value).format("hh:mm:ss");
|
||||
return moment(value).format("HH:mm:ss");
|
||||
case ColumnTypes.DATETIME:
|
||||
return moment(value).format("YYYY-MM-DD hh:mm:ss");
|
||||
return moment(value).format("YYYY-MM-DD HH:mm:ss");
|
||||
case ColumnTypes.JSON:
|
||||
return JSON.stringify(value);
|
||||
case ColumnTypes.SIMPLE_ARRAY:
|
||||
@ -273,6 +273,9 @@ export class MysqlDriver extends BaseDriver implements Driver {
|
||||
*/
|
||||
prepareHydratedValue(value: any, column: ColumnMetadata): any {
|
||||
switch (column.type) {
|
||||
case ColumnTypes.BOOLEAN:
|
||||
return value ? true : false;
|
||||
|
||||
case ColumnTypes.DATE:
|
||||
if (value instanceof Date)
|
||||
return value;
|
||||
@ -280,13 +283,13 @@ export class MysqlDriver extends BaseDriver implements Driver {
|
||||
return moment(value, "YYYY-MM-DD").toDate();
|
||||
|
||||
case ColumnTypes.TIME:
|
||||
return moment(value, "hh:mm:ss").toDate();
|
||||
return moment(value, "HH:mm:ss").toDate();
|
||||
|
||||
case ColumnTypes.DATETIME:
|
||||
if (value instanceof Date)
|
||||
return value;
|
||||
|
||||
return moment(value, "YYYY-MM-DD hh:mm:ss").toDate();
|
||||
return moment(value, "YYYY-MM-DD HH:mm:ss").toDate();
|
||||
|
||||
case ColumnTypes.JSON:
|
||||
return JSON.parse(value);
|
||||
|
||||
@ -268,6 +268,66 @@ export class EntityManager {
|
||||
.then(() => runInTransactionResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets given relatedEntityId to the value of the relation of the entity with entityId id.
|
||||
* Should be used when you want quickly and efficiently set a relation (for many-to-one and one-to-many) to some entity.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
setRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string, entityId: any, relatedEntityId: any): Promise<void>;
|
||||
setRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: ((t: Entity) => string|any), entityId: any, relatedEntityId: any): Promise<void>;
|
||||
setRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityId: any): Promise<void> {
|
||||
return this.getRepository(entityClass).setRelation(relationName as any, entityId, relatedEntityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new relation between two entities into relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently add a relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addToRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string, entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
addToRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
addToRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
return this.getRepository(entityClass).addToRelation(relationName as any, entityId, relatedEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a relation between two entities from relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string, entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
removeFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
removeFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
return this.getRepository(entityClass).removeFromRelation(relationName as any, entityId, relatedEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs both #addToRelation and #removeFromRelation operations.
|
||||
* Should be used when you want quickly and efficiently and and remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addAndRemoveFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relation: string, entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Promise<void>;
|
||||
addAndRemoveFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relation: ((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Promise<void>;
|
||||
addAndRemoveFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relation: string|((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Promise<void> {
|
||||
return this.getRepository(entityClass).addAndRemoveFromRelation(relation as any, entityId, addRelatedEntityIds, removeRelatedEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity with the given id.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeById<Entity>(entityClass: ConstructorFunction<Entity>|Function, id: any): Promise<void> {
|
||||
return this.getRepository(entityClass).removeById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entities with the given ids.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeByIds<Entity>(entityClass: ConstructorFunction<Entity>|Function, ids: any[]): Promise<void> {
|
||||
return this.getRepository(entityClass).removeByIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Roots are entities that have no ancestors. Finds them all.
|
||||
*/
|
||||
@ -331,5 +391,4 @@ export class EntityManager {
|
||||
return this.getTreeRepository(entityClass).countAncestors(entity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
23
src/entity-manager/EntityManagerFactory.ts
Normal file
23
src/entity-manager/EntityManagerFactory.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityManager} from "./EntityManager";
|
||||
import {ReactiveEntityManager} from "./ReactiveEntityManager";
|
||||
|
||||
/**
|
||||
* Entity manager supposed to work with any entity, automatically find its repository and call its method, whatever
|
||||
* entity type are you passing.
|
||||
*/
|
||||
export class EntityManagerFactory {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
createEntityManager(connection: Connection) {
|
||||
return new EntityManager(connection);
|
||||
}
|
||||
|
||||
createReactiveEntityManager(connection: Connection) {
|
||||
return new ReactiveEntityManager(connection);
|
||||
}
|
||||
|
||||
}
|
||||
@ -241,10 +241,70 @@ export class ReactiveEntityManager {
|
||||
.then(() => runInTransactionResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets given relatedEntityId to the value of the relation of the entity with entityId id.
|
||||
* Should be used when you want quickly and efficiently set a relation (for many-to-one and one-to-many) to some entity.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
setRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string, entityId: any, relatedEntityId: any): Rx.Observable<void>;
|
||||
setRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: ((t: Entity) => string|any), entityId: any, relatedEntityId: any): Rx.Observable<void>;
|
||||
setRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityId: any): Rx.Observable<void> {
|
||||
return this.getReactiveRepository(entityClass).setRelation(relationName as any, entityId, relatedEntityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new relation between two entities into relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently add a relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addToRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string, entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addToRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addToRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void> {
|
||||
return this.getReactiveRepository(entityClass).addToRelation(relationName as any, entityId, relatedEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a relation between two entities from relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string, entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
removeFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
removeFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void> {
|
||||
return this.getReactiveRepository(entityClass).removeFromRelation(relationName as any, entityId, relatedEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs both #addToRelation and #removeFromRelation operations.
|
||||
* Should be used when you want quickly and efficiently and and remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addAndRemoveFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relation: string, entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addAndRemoveFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relation: ((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addAndRemoveFromRelation<Entity>(entityClass: ConstructorFunction<Entity>|Function, relation: string|((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Rx.Observable<void> {
|
||||
return this.getReactiveRepository(entityClass).addAndRemoveFromRelation(relation as any, entityId, addRelatedEntityIds, removeRelatedEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity with the given id.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeById<Entity>(entityClass: ConstructorFunction<Entity>|Function, id: any): Rx.Observable<void> {
|
||||
return this.getReactiveRepository(entityClass).removeById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entities with the given ids.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeByIds<Entity>(entityClass: ConstructorFunction<Entity>|Function, ids: any[]): Rx.Observable<void> {
|
||||
return this.getReactiveRepository(entityClass).removeByIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Roots are entities that have no ancestors. Finds them all.
|
||||
*/
|
||||
findRoots<Entity>(entityClass: ConstructorFunction<Entity>|Function): Promise<Entity[]> {
|
||||
findRoots<Entity>(entityClass: ConstructorFunction<Entity>|Function): Rx.Observable<Entity[]> {
|
||||
return this.getReactiveTreeRepository(entityClass).findRoots();
|
||||
}
|
||||
|
||||
@ -258,21 +318,21 @@ export class ReactiveEntityManager {
|
||||
/**
|
||||
* Gets all children (descendants) of the given entity. Returns them all in a flat array.
|
||||
*/
|
||||
findDescendants<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Promise<Entity[]> {
|
||||
findDescendants<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Rx.Observable<Entity[]> {
|
||||
return this.getReactiveTreeRepository(entityClass).findDescendants(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all children (descendants) of the given entity. Returns them in a tree - nested into each other.
|
||||
*/
|
||||
findDescendantsTree<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Promise<Entity> {
|
||||
findDescendantsTree<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Rx.Observable<Entity> {
|
||||
return this.getReactiveTreeRepository(entityClass).findDescendantsTree(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of descendants of the entity.
|
||||
*/
|
||||
countDescendants<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Promise<number> {
|
||||
countDescendants<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Rx.Observable<number> {
|
||||
return this.getReactiveTreeRepository(entityClass).countDescendants(entity);
|
||||
}
|
||||
|
||||
@ -286,21 +346,21 @@ export class ReactiveEntityManager {
|
||||
/**
|
||||
* Gets all parents (ancestors) of the given entity. Returns them all in a flat array.
|
||||
*/
|
||||
findAncestors<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Promise<Entity[]> {
|
||||
findAncestors<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Rx.Observable<Entity[]> {
|
||||
return this.getReactiveTreeRepository(entityClass).findAncestors(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all parents (ancestors) of the given entity. Returns them in a tree - nested into each other.
|
||||
*/
|
||||
findAncestorsTree<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Promise<Entity> {
|
||||
findAncestorsTree<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Rx.Observable<Entity> {
|
||||
return this.getReactiveTreeRepository(entityClass).findAncestorsTree(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of ancestors of the entity.
|
||||
*/
|
||||
countAncestors<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Promise<number> {
|
||||
countAncestors<Entity>(entityClass: ConstructorFunction<Entity>|Function, entity: Entity): Rx.Observable<number> {
|
||||
return this.getReactiveTreeRepository(entityClass).countAncestors(entity);
|
||||
}
|
||||
|
||||
|
||||
43
src/index.ts
43
src/index.ts
@ -6,7 +6,7 @@ import {ConnectionOptions} from "./connection/ConnectionOptions";
|
||||
import {ConnectionManager} from "./connection-manager/ConnectionManager";
|
||||
import {Connection} from "./connection/Connection";
|
||||
import {MysqlDriver} from "./driver/MysqlDriver";
|
||||
import {MetadataStorage} from "./metadata-storage/MetadataStorage";
|
||||
import {MetadataArgsStorage} from "./metadata-args/MetadataArgsStorage";
|
||||
import {CreateConnectionOptions} from "./connection-manager/CreateConnectionOptions";
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -14,21 +14,33 @@ import {CreateConnectionOptions} from "./connection-manager/CreateConnectionOpti
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Container to be used by TypeORM for inversion control.
|
||||
* Container to be used by this library for inversion control. If container was not implicitly set then by default
|
||||
* container simply creates a new instance of the given class.
|
||||
*/
|
||||
let container: { get(someClass: any): any };
|
||||
let container: { get<T>(someClass: { new (...args: any[]): T }|Function): T } = new (class {
|
||||
private instances: any[] = [];
|
||||
get<T>(someClass: { new (...args: any[]): T }): T {
|
||||
if (!this.instances[<any>someClass])
|
||||
this.instances[<any>someClass] = new someClass();
|
||||
|
||||
return this.instances[<any>someClass];
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Sets container to be used by TypeORM.
|
||||
*
|
||||
* Sets container to be used by this library.
|
||||
*
|
||||
* @param iocContainer
|
||||
*/
|
||||
export function useContainer(iocContainer: { get(someClass: any): any }) {
|
||||
container = iocContainer;
|
||||
}
|
||||
|
||||
export function getContainer() {
|
||||
return container;
|
||||
/**
|
||||
* Gets the IOC container used by this library.
|
||||
*/
|
||||
export function getFromContainer<T>(someClass: { new (...args: any[]): T }|Function): T {
|
||||
return container.get<T>(someClass);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -38,17 +50,20 @@ export function getContainer() {
|
||||
/**
|
||||
* Default metadata storage used as singleton and can be used to storage all metadatas in the system.
|
||||
*/
|
||||
let metadataStorage: MetadataStorage;
|
||||
let metadataArgsStorage: MetadataArgsStorage;
|
||||
|
||||
export function defaultMetadataStorage() {
|
||||
if (!metadataStorage && container) {
|
||||
metadataStorage = container.get(MetadataStorage);
|
||||
/**
|
||||
* Gets metadata args storage.
|
||||
*/
|
||||
export function getMetadataArgsStorage() {
|
||||
if (!metadataArgsStorage && container) {
|
||||
metadataArgsStorage = container.get(MetadataArgsStorage);
|
||||
|
||||
} else if (!metadataStorage) {
|
||||
metadataStorage = new MetadataStorage();
|
||||
} else if (!metadataArgsStorage) {
|
||||
metadataArgsStorage = new MetadataArgsStorage();
|
||||
}
|
||||
|
||||
return metadataStorage;
|
||||
return metadataArgsStorage;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
34
src/metadata-args/ColumnMetadataArgs.ts
Normal file
34
src/metadata-args/ColumnMetadataArgs.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import {ColumnOptions} from "../decorator/options/ColumnOptions";
|
||||
import {ColumnMode} from "../metadata/ColumnMetadata";
|
||||
|
||||
/**
|
||||
* Arguments for ColumnMetadata class.
|
||||
*/
|
||||
export interface ColumnMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which column is applied.
|
||||
*/
|
||||
readonly target?: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which column is applied.
|
||||
*/
|
||||
readonly propertyName?: string;
|
||||
|
||||
/**
|
||||
* Class's property type (reflected) to which column is applied.
|
||||
*/
|
||||
readonly propertyType: string;
|
||||
|
||||
/**
|
||||
* Column mode in which column will work.
|
||||
*/
|
||||
readonly mode: ColumnMode;
|
||||
|
||||
/**
|
||||
* Extra column options.
|
||||
*/
|
||||
readonly options: ColumnOptions;
|
||||
|
||||
}
|
||||
23
src/metadata-args/EntityListenerMetadataArgs.ts
Normal file
23
src/metadata-args/EntityListenerMetadataArgs.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {EventListenerType} from "../metadata/types/EventListenerTypes";
|
||||
|
||||
/**
|
||||
* Arguments for EntityListenerMetadata class.
|
||||
*/
|
||||
export interface EntityListenerMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which listener is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which listener is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* The type of the listener.
|
||||
*/
|
||||
readonly type: EventListenerType;
|
||||
|
||||
}
|
||||
12
src/metadata-args/EventSubscriberMetadataArgs.ts
Normal file
12
src/metadata-args/EventSubscriberMetadataArgs.ts
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
* Arguments for EventSubscriberMetadata class.
|
||||
*/
|
||||
export interface EventSubscriberMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which subscriber is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
}
|
||||
27
src/metadata-args/IndexMetadataArgs.ts
Normal file
27
src/metadata-args/IndexMetadataArgs.ts
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
/**
|
||||
* Arguments for IndexMetadata class.
|
||||
*/
|
||||
export interface IndexMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which index is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Index name.
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Columns combination to be used as index.
|
||||
*/
|
||||
readonly columns: ((object: any) => any[])|string[];
|
||||
|
||||
/**
|
||||
* Indicates if index must be unique or not.
|
||||
*/
|
||||
readonly unique: boolean;
|
||||
|
||||
}
|
||||
26
src/metadata-args/JoinColumnMetadataArgs.ts
Normal file
26
src/metadata-args/JoinColumnMetadataArgs.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Arguments for JoinColumnMetadata class.
|
||||
*/
|
||||
export interface JoinColumnMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which this column is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which this column is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* Name of the column.
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Name of the column in the entity to which this column is referenced.
|
||||
*/
|
||||
readonly referencedColumnName?: string;
|
||||
|
||||
}
|
||||
34
src/metadata-args/JoinTableMetadataArgs.ts
Normal file
34
src/metadata-args/JoinTableMetadataArgs.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import {JoinColumnOptions} from "../decorator/options/JoinColumnOptions";
|
||||
|
||||
/**
|
||||
* Arguments for JoinTableMetadata class.
|
||||
*/
|
||||
export interface JoinTableMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which this column is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which this column is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* Name of the table that will be created to store values of the both tables (join table).
|
||||
* By default is auto generated.
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* First column of the join table.
|
||||
*/
|
||||
readonly joinColumn?: JoinColumnOptions;
|
||||
|
||||
/**
|
||||
* Second (inverse) column of the join table.
|
||||
*/
|
||||
readonly inverseJoinColumn?: JoinColumnOptions;
|
||||
|
||||
}
|
||||
128
src/metadata-args/MetadataArgsStorage.ts
Normal file
128
src/metadata-args/MetadataArgsStorage.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import {TargetMetadataArgsCollection} from "./collection/TargetMetadataArgsCollection";
|
||||
import {PropertyMetadataArgsCollection} from "./collection/PropertyMetadataArgsCollection";
|
||||
import {RelationMetadataArgs} from "./RelationMetadataArgs";
|
||||
import {ColumnMetadataArgs} from "./ColumnMetadataArgs";
|
||||
import {RelationsCountMetadataArgs} from "./RelationsCountMetadataArgs";
|
||||
import {IndexMetadataArgs} from "./IndexMetadataArgs";
|
||||
import {EntityListenerMetadataArgs} from "./EntityListenerMetadataArgs";
|
||||
import {TableMetadataArgs} from "./TableMetadataArgs";
|
||||
import {NamingStrategyMetadataArgs} from "./NamingStrategyMetadataArgs";
|
||||
import {EventSubscriberMetadataArgs} from "./EventSubscriberMetadataArgs";
|
||||
import {JoinTableMetadataArgs} from "./JoinTableMetadataArgs";
|
||||
import {JoinColumnMetadataArgs} from "./JoinColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* Storage all metadatas of all available types: tables, fields, subscribers, relations, etc.
|
||||
* Each metadata represents some specifications of what it represents.
|
||||
*/
|
||||
export class MetadataArgsStorage {
|
||||
|
||||
// todo: type in function validation, inverse side function validation
|
||||
// todo: check on build for duplicate names, since naming checking was removed from MetadataStorage
|
||||
// todo: duplicate name checking for: table, relation, column, index, naming strategy, join tables/columns?
|
||||
// todo: check for duplicate targets too since this check has been removed too
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
readonly tables = new TargetMetadataArgsCollection<TableMetadataArgs>();
|
||||
readonly namingStrategies = new TargetMetadataArgsCollection<NamingStrategyMetadataArgs>();
|
||||
readonly eventSubscribers = new TargetMetadataArgsCollection<EventSubscriberMetadataArgs>();
|
||||
readonly indices = new TargetMetadataArgsCollection<IndexMetadataArgs>();
|
||||
readonly columns = new PropertyMetadataArgsCollection<ColumnMetadataArgs>();
|
||||
readonly relations = new PropertyMetadataArgsCollection<RelationMetadataArgs>();
|
||||
readonly joinColumns = new PropertyMetadataArgsCollection<JoinColumnMetadataArgs>();
|
||||
readonly joinTables = new PropertyMetadataArgsCollection<JoinTableMetadataArgs>();
|
||||
readonly entityListeners = new PropertyMetadataArgsCollection<EntityListenerMetadataArgs>();
|
||||
readonly relationCounts = new PropertyMetadataArgsCollection<RelationsCountMetadataArgs>();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets merged (with all abstract classes) table metadatas for the given classes.
|
||||
*/
|
||||
getMergedTableMetadatas(classes: Function[]) {
|
||||
const allTableMetadataArgs = this.tables.filterByClasses(classes);
|
||||
const tableMetadatas = this.tables.filterByClasses(classes).filter(table => table.type !== "abstract");
|
||||
|
||||
return tableMetadatas.map(tableMetadata => {
|
||||
return this.mergeWithAbstract(allTableMetadataArgs, tableMetadata);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new copy of the MetadataStorage with same metadatas as in current metadata storage, but filtered
|
||||
* by classes.
|
||||
*/
|
||||
private mergeWithAbstract(allTableMetadatas: TargetMetadataArgsCollection<TableMetadataArgs>,
|
||||
tableMetadata: TableMetadataArgs) {
|
||||
|
||||
const indices = this.indices.filterByClass(tableMetadata.target);
|
||||
const columns = this.columns.filterByClass(tableMetadata.target);
|
||||
const relations = this.relations.filterByClass(tableMetadata.target);
|
||||
const joinColumns = this.joinColumns.filterByClass(tableMetadata.target);
|
||||
const joinTables = this.joinTables.filterByClass(tableMetadata.target);
|
||||
const entityListeners = this.entityListeners.filterByClass(tableMetadata.target);
|
||||
const relationCounts = this.relationCounts.filterByClass(tableMetadata.target);
|
||||
|
||||
allTableMetadatas
|
||||
.filter(metadata => this.isInherited(tableMetadata.target, metadata.target))
|
||||
.forEach(parentMetadata => {
|
||||
const metadatasFromAbstract = this.mergeWithAbstract(allTableMetadatas, parentMetadata);
|
||||
|
||||
metadatasFromAbstract.columns
|
||||
.filterRepeatedMetadatas(columns)
|
||||
.forEach(metadata => columns.push(metadata));
|
||||
|
||||
metadatasFromAbstract.relations
|
||||
.filterRepeatedMetadatas(relations)
|
||||
.forEach(metadata => relations.push(metadata));
|
||||
|
||||
metadatasFromAbstract.joinColumns
|
||||
.filterRepeatedMetadatas(joinColumns)
|
||||
.forEach(metadata => joinColumns.push(metadata));
|
||||
|
||||
metadatasFromAbstract.joinTables
|
||||
.filterRepeatedMetadatas(joinTables)
|
||||
.forEach(metadata => joinTables.push(metadata));
|
||||
|
||||
metadatasFromAbstract.entityListeners
|
||||
.filterRepeatedMetadatas(entityListeners)
|
||||
.forEach(metadata => entityListeners.push(metadata));
|
||||
|
||||
metadatasFromAbstract.relationCounts
|
||||
.filterRepeatedMetadatas(relationCounts)
|
||||
.forEach(metadata => relationCounts.push(metadata));
|
||||
});
|
||||
|
||||
return {
|
||||
table: tableMetadata,
|
||||
indices: indices,
|
||||
columns: columns,
|
||||
relations: relations,
|
||||
joinColumns: joinColumns,
|
||||
joinTables: joinTables,
|
||||
entityListeners: entityListeners,
|
||||
relationCounts: relationCounts
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this table is inherited from another table.
|
||||
*/
|
||||
private isInherited(target1: Function, target2: Function) {
|
||||
// we cannot use instanceOf in this method, because we need order of inherited tables, to ensure that
|
||||
// properties get inherited in a right order. To achieve it we can only check a first parent of the class
|
||||
// return this.target.prototype instanceof anotherTable.target;
|
||||
return Object.getPrototypeOf(target1.prototype).constructor === target2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
16
src/metadata-args/NamingStrategyMetadataArgs.ts
Normal file
16
src/metadata-args/NamingStrategyMetadataArgs.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Arguments for NamingStrategyMetadata class.
|
||||
*/
|
||||
export interface NamingStrategyMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which this column is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Strategy name.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
}
|
||||
@ -1,56 +1,56 @@
|
||||
import {RelationType} from "../types/RelationTypes";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {PropertyTypeInFunction, RelationTypeInFunction} from "../RelationMetadata";
|
||||
import {RelationType} from "../metadata/types/RelationTypes";
|
||||
import {RelationOptions} from "../decorator/options/RelationOptions";
|
||||
import {PropertyTypeInFunction, RelationTypeInFunction} from "../metadata/RelationMetadata";
|
||||
|
||||
/**
|
||||
* Relation metadata constructor arguments.
|
||||
* Arguments for RelationMetadata class.
|
||||
*/
|
||||
export interface RelationMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which this relation is applied.
|
||||
*/
|
||||
target: Function;
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which this relation is applied.
|
||||
*/
|
||||
propertyName: string;
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* Original (reflected) class's property type.
|
||||
*/
|
||||
propertyType: any;
|
||||
readonly propertyType: any;
|
||||
|
||||
/**
|
||||
* Type of relation. Can be one of the value of the RelationTypes class.
|
||||
*/
|
||||
relationType: RelationType;
|
||||
readonly relationType: RelationType;
|
||||
|
||||
/**
|
||||
* Type of the relation. This type is in function because of language specifics and problems with recursive
|
||||
* referenced classes.
|
||||
*/
|
||||
type: RelationTypeInFunction;
|
||||
readonly type: RelationTypeInFunction;
|
||||
|
||||
/**
|
||||
* Inverse side of the relation.
|
||||
*/
|
||||
inverseSideProperty?: PropertyTypeInFunction<any>;
|
||||
readonly inverseSideProperty?: PropertyTypeInFunction<any>;
|
||||
|
||||
/**
|
||||
* Additional relation options.
|
||||
*/
|
||||
options: RelationOptions;
|
||||
readonly options: RelationOptions;
|
||||
|
||||
/**
|
||||
* Indicates if this is a parent (can be only many-to-one relation) relation in the tree tables.
|
||||
*/
|
||||
isTreeParent?: boolean;
|
||||
readonly isTreeParent?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this is a children (can be only one-to-many relation) relation in the tree tables.
|
||||
*/
|
||||
isTreeChildren?: boolean;
|
||||
readonly isTreeChildren?: boolean;
|
||||
|
||||
}
|
||||
21
src/metadata-args/RelationsCountMetadataArgs.ts
Normal file
21
src/metadata-args/RelationsCountMetadataArgs.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Arguments for RelationsCountMetadata class.
|
||||
*/
|
||||
export interface RelationsCountMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which this column is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which this column is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
/**
|
||||
* Target's relation which it should count.
|
||||
*/
|
||||
readonly relation: string|((object: any) => any);
|
||||
|
||||
}
|
||||
23
src/metadata-args/TableMetadataArgs.ts
Normal file
23
src/metadata-args/TableMetadataArgs.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {TableType} from "../metadata/TableMetadata";
|
||||
|
||||
/**
|
||||
* Arguments for TableMetadata class.
|
||||
*/
|
||||
export interface TableMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which table is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Table name.
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Table type.
|
||||
*/
|
||||
readonly type: TableType;
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import {EntityMetadata} from "../EntityMetadata";
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
import {EntityMetadataNotFound} from "../error/EntityMetadataNotFound";
|
||||
|
||||
/**
|
||||
@ -21,6 +21,14 @@ 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,7 +1,6 @@
|
||||
import {TargetMetadataCollection} from "./TargetMetadataCollection";
|
||||
import {PropertyMetadata} from "../PropertyMetadata";
|
||||
import {TargetMetadataArgsCollection} from "./TargetMetadataArgsCollection";
|
||||
|
||||
export class PropertyMetadataCollection<T extends PropertyMetadata> extends TargetMetadataCollection<T> {
|
||||
export class PropertyMetadataArgsCollection<T extends { target?: Function, propertyName?: string }> extends TargetMetadataArgsCollection<T> {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
@ -1,7 +1,6 @@
|
||||
import {TargetMetadata} from "../TargetMetadata";
|
||||
import {MetadataAlreadyExistsError} from "../../metadata-storage/error/MetadataAlreadyExistsError";
|
||||
import {MetadataAlreadyExistsError} from "../../metadata-builder/error/MetadataAlreadyExistsError";
|
||||
|
||||
export class TargetMetadataCollection<T extends TargetMetadata> extends Array<T> {
|
||||
export class TargetMetadataArgsCollection<T extends { target?: Function }> extends Array<T> {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
@ -13,21 +12,23 @@ export class TargetMetadataCollection<T extends TargetMetadata> extends Array<T>
|
||||
|
||||
filterByClasses(classes: Function[]): this {
|
||||
const collection = new (<any> this.constructor)();
|
||||
this.filter(metadata => classes.indexOf(metadata.target) !== -1)
|
||||
this
|
||||
.filter(metadata => {
|
||||
if (!metadata.target) return false;
|
||||
return classes.indexOf(metadata.target) !== -1;
|
||||
})
|
||||
.forEach(metadata => collection.add(metadata));
|
||||
return collection;
|
||||
}
|
||||
|
||||
add(metadata: T, checkForDuplicateTargets = false) {
|
||||
if (checkForDuplicateTargets && this.hasWithTarget(metadata.target))
|
||||
throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
|
||||
if (checkForDuplicateTargets) {
|
||||
if (!metadata.target)
|
||||
throw new Error(`Target is not set in the given metadata.`);
|
||||
|
||||
this.push(metadata);
|
||||
}
|
||||
|
||||
addUniq(metadata: T) {
|
||||
if (this.hasWithTarget(metadata.target))
|
||||
throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
|
||||
if (this.hasWithTarget(metadata.target))
|
||||
throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
|
||||
}
|
||||
|
||||
this.push(metadata);
|
||||
}
|
||||
@ -1,18 +1,20 @@
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {ColumnOptions} from "../metadata/options/ColumnOptions";
|
||||
import {ColumnOptions} from "../decorator/options/ColumnOptions";
|
||||
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
|
||||
import {EntityMetadataValidator} from "./EntityMetadataValidator";
|
||||
import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {CompositeIndexMetadata} from "../metadata/CompositeIndexMetadata";
|
||||
import {PropertyMetadataCollection} from "../metadata/collection/PropertyMetadataCollection";
|
||||
import {TargetMetadataCollection} from "../metadata/collection/TargetMetadataCollection";
|
||||
import {JoinColumnMetadata} from "../metadata/JoinColumnMetadata";
|
||||
import {JoinColumnOptions} from "../metadata/options/JoinColumnOptions";
|
||||
import {JoinColumnOptions} from "../decorator/options/JoinColumnOptions";
|
||||
import {TableMetadata} from "../metadata/TableMetadata";
|
||||
import {ColumnTypes} from "../metadata/types/ColumnTypes";
|
||||
import {defaultMetadataStorage} from "../index";
|
||||
import {getMetadataArgsStorage} from "../index";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
|
||||
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
|
||||
import {PropertyMetadataArgsCollection} from "../metadata-args/collection/PropertyMetadataArgsCollection";
|
||||
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
|
||||
@ -23,105 +25,71 @@ export class EntityMetadataBuilder {
|
||||
|
||||
// todo: type in function validation, inverse side function validation
|
||||
// todo: check on build for duplicate names, since naming checking was removed from MetadataStorage
|
||||
|
||||
// todo: duplicate name checking for: table, relation, column, index, naming strategy, join tables/columns?
|
||||
// todo: check if multiple tree parent metadatas in validator
|
||||
// todo: tree decorators can be used only on closure table (validation)
|
||||
// todo: throw error if parent tree metadata was not specified in a closure table
|
||||
|
||||
private entityValidator = new EntityMetadataValidator();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private namingStrategy: NamingStrategyInterface) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private mergeIndicesAndCompositeIndices(indices: PropertyMetadataCollection<IndexMetadata>,
|
||||
compositeIndices: TargetMetadataCollection<CompositeIndexMetadata>) {
|
||||
indices.forEach(index => {
|
||||
const compositeIndex = new CompositeIndexMetadata(index.target, index.name, [index.propertyName]);
|
||||
compositeIndex.namingStrategy = index.namingStrategy;
|
||||
compositeIndices.add(compositeIndex);
|
||||
});
|
||||
|
||||
// later need to check if no duplicate keys in composite indices?
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a complete metadata aggregations for the given entity classes.
|
||||
*/
|
||||
build(entityClasses: Function[]): EntityMetadata[] {
|
||||
build(namingStrategy: NamingStrategyInterface, entityClasses: Function[]): EntityMetadata[] {
|
||||
|
||||
const allMetadataStorage = defaultMetadataStorage();
|
||||
const entityMetadatas = getMetadataArgsStorage().getMergedTableMetadatas(entityClasses).map(mergedArgs => {
|
||||
|
||||
// filter the only metadata we need - those which are bind to the given table classes
|
||||
const allTableMetadatas = allMetadataStorage.tableMetadatas.filterByClasses(entityClasses);
|
||||
const tableMetadatas = allTableMetadatas.filterByClasses(entityClasses).filter(table => !table.isAbstract);
|
||||
// create metadatas from args
|
||||
const table = new TableMetadata(mergedArgs.table);
|
||||
const columns = mergedArgs.columns.map(args => new ColumnMetadata(args));
|
||||
const relations = mergedArgs.relations.map(args => new RelationMetadata(args));
|
||||
const indices = mergedArgs.indices.map(args => new IndexMetadata(args));
|
||||
|
||||
// set naming strategy
|
||||
// allMetadataStorage.tableMetadatas.forEach(tableMetadata => tableMetadata.namingStrategy = this.namingStrategy);
|
||||
// allTableMetadatas.forEach(column => column.namingStrategy = this.namingStrategy);
|
||||
// entityMetadata.relations.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
|
||||
const entityMetadatas = tableMetadatas.map(tableMetadata => {
|
||||
|
||||
const mergedMetadata = allMetadataStorage.mergeWithAbstract(allTableMetadatas, tableMetadata);
|
||||
|
||||
// set naming strategy
|
||||
// tableMetadata.namingStrategy = this.namingStrategy;
|
||||
mergedMetadata.columnMetadatas.forEach(column => column.namingStrategy = this.namingStrategy);
|
||||
mergedMetadata.relationMetadatas.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
mergedMetadata.indexMetadatas.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
mergedMetadata.compositeIndexMetadatas.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
|
||||
// merge indices and composite indices because simple indices actually are compose indices with only one column
|
||||
this.mergeIndicesAndCompositeIndices(mergedMetadata.indexMetadatas, mergedMetadata.compositeIndexMetadatas);
|
||||
|
||||
// todo: check if multiple tree parent metadatas in validator
|
||||
// todo: tree decorators can be used only on closure table (validation)
|
||||
// todo: throw error if parent tree metadata was not specified in a closure table
|
||||
|
||||
// create a new entity metadata
|
||||
const entityMetadata = new EntityMetadata(
|
||||
this.namingStrategy,
|
||||
tableMetadata,
|
||||
mergedMetadata.columnMetadatas,
|
||||
mergedMetadata.relationMetadatas,
|
||||
mergedMetadata.compositeIndexMetadatas
|
||||
);
|
||||
const entityMetadata = new EntityMetadata(namingStrategy, table, columns, relations, indices);
|
||||
|
||||
// set entity metadata everywhere its used
|
||||
table.entityMetadata = entityMetadata;
|
||||
columns.forEach(column => column.entityMetadata = entityMetadata);
|
||||
relations.forEach(relation => relation.entityMetadata = entityMetadata);
|
||||
indices.forEach(index => index.entityMetadata = entityMetadata);
|
||||
|
||||
// create entity's relations join tables
|
||||
entityMetadata.manyToManyRelations.forEach(relation => {
|
||||
const joinTable = mergedMetadata.joinTableMetadatas.findByProperty(relation.propertyName);
|
||||
if (joinTable) {
|
||||
const joinTableMetadata = mergedArgs.joinTables.findByProperty(relation.propertyName);
|
||||
if (joinTableMetadata) {
|
||||
const joinTable = new JoinTableMetadata(joinTableMetadata);
|
||||
relation.joinTable = joinTable;
|
||||
joinTable.relation = relation;
|
||||
}
|
||||
});
|
||||
|
||||
// create entity's relations join columns
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
const joinColumn = mergedMetadata.joinColumnMetadatas.findByProperty(relation.propertyName);
|
||||
if (joinColumn) {
|
||||
relation.joinColumn = joinColumn;
|
||||
joinColumn.relation = relation;
|
||||
}
|
||||
});
|
||||
entityMetadata.oneToOneRelations
|
||||
.concat(entityMetadata.manyToOneRelations)
|
||||
.forEach(relation => {
|
||||
|
||||
// since for many-to-one relations having JoinColumn is not required on decorators level, we need to go
|
||||
// throw all of them which don't have JoinColumn decorators and create it for them
|
||||
let joinColumnMetadata = mergedArgs.joinColumns.findByProperty(relation.propertyName);
|
||||
if (!joinColumnMetadata && relation.isManyToOne) {
|
||||
joinColumnMetadata = {
|
||||
target: relation.target,
|
||||
propertyName: relation.propertyName
|
||||
};
|
||||
}
|
||||
|
||||
if (joinColumnMetadata) {
|
||||
const joinColumn = new JoinColumnMetadata(joinColumnMetadata);
|
||||
relation.joinColumn = joinColumn;
|
||||
joinColumn.relation = relation;
|
||||
}
|
||||
});
|
||||
|
||||
// since for many-to-one relations having JoinColumn is not required on decorators level, we need to go
|
||||
// throw all of them which don't have JoinColumn decorators and create it for them
|
||||
entityMetadata.manyToOneRelations.forEach(relation => {
|
||||
let joinColumn = mergedMetadata.joinColumnMetadatas.findByProperty(relation.propertyName);
|
||||
if (!joinColumn) {
|
||||
joinColumn = new JoinColumnMetadata(relation.target, relation.propertyName, <JoinColumnOptions> {});
|
||||
relation.joinColumn = joinColumn;
|
||||
joinColumn.relation = relation;
|
||||
}
|
||||
});
|
||||
|
||||
return entityMetadata;
|
||||
});
|
||||
|
||||
@ -153,11 +121,11 @@ export class EntityMetadataBuilder {
|
||||
oldColumnName: relation.oldColumnName,
|
||||
nullable: relation.isNullable
|
||||
};
|
||||
relationalColumn = new ColumnMetadata({
|
||||
relationalColumn = new ColumnMetadata(metadata, {
|
||||
target: metadata.target,
|
||||
propertyName: relation.name,
|
||||
propertyType: inverseSideColumn.propertyType,
|
||||
isVirtual: true,
|
||||
mode: "virtual",
|
||||
options: options
|
||||
});
|
||||
metadata.columns.push(relationalColumn);
|
||||
@ -165,6 +133,7 @@ export class EntityMetadataBuilder {
|
||||
|
||||
// create and add foreign key
|
||||
const foreignKey = new ForeignKeyMetadata(
|
||||
metadata,
|
||||
metadata.table,
|
||||
[relationalColumn],
|
||||
relation.inverseEntityMetadata.table,
|
||||
@ -180,42 +149,50 @@ export class EntityMetadataBuilder {
|
||||
entityMetadatas
|
||||
.filter(metadata => metadata.table.isClosure)
|
||||
.forEach(metadata => {
|
||||
const closureTableName = this.namingStrategy.closureJunctionTableName(metadata.table.name);
|
||||
const closureTableName = namingStrategy.closureJunctionTableName(metadata.table.name);
|
||||
const closureJunctionTableMetadata = new TableMetadata(undefined, closureTableName, "closureJunction");
|
||||
|
||||
const column1Args: ColumnMetadataArgs = {
|
||||
propertyType: metadata.primaryColumn.type,
|
||||
mode: "virtual",
|
||||
options: <ColumnOptions> {
|
||||
length: metadata.primaryColumn.length,
|
||||
type: metadata.primaryColumn.type,
|
||||
name: "ancestor"
|
||||
}
|
||||
};
|
||||
const column2Args: ColumnMetadataArgs = {
|
||||
propertyType: metadata.primaryColumn.type,
|
||||
mode: "virtual",
|
||||
options: <ColumnOptions> {
|
||||
length: metadata.primaryColumn.length,
|
||||
type: metadata.primaryColumn.type,
|
||||
name: "descendant"
|
||||
}
|
||||
};
|
||||
|
||||
const column3Args: ColumnMetadataArgs = {
|
||||
propertyType: ColumnTypes.INTEGER,
|
||||
mode: "virtual",
|
||||
options: {
|
||||
type: ColumnTypes.INTEGER,
|
||||
name: "level"
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
new ColumnMetadata({
|
||||
propertyType: metadata.primaryColumn.type,
|
||||
options: {
|
||||
length: metadata.primaryColumn.length,
|
||||
type: metadata.primaryColumn.type,
|
||||
name: "ancestor"
|
||||
}
|
||||
}),
|
||||
new ColumnMetadata({
|
||||
propertyType: metadata.primaryColumn.type,
|
||||
options: {
|
||||
length: metadata.primaryColumn.length,
|
||||
type: metadata.primaryColumn.type,
|
||||
name: "descendant"
|
||||
}
|
||||
})
|
||||
new ColumnMetadata(metadata, column1Args),
|
||||
new ColumnMetadata(metadata, column2Args)
|
||||
];
|
||||
|
||||
if (metadata.hasTreeLevelColumn) {
|
||||
columns.push(new ColumnMetadata({
|
||||
propertyType: ColumnTypes.INTEGER,
|
||||
options: {
|
||||
type: ColumnTypes.INTEGER,
|
||||
name: "level"
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (metadata.hasTreeLevelColumn)
|
||||
columns.push(new ColumnMetadata(metadata, column3Args));
|
||||
|
||||
const closureJunctionEntityMetadata = new EntityMetadata(this.namingStrategy, closureJunctionTableMetadata, columns, [], []);
|
||||
const closureJunctionEntityMetadata = new EntityMetadata(namingStrategy, closureJunctionTableMetadata, columns, [], []);
|
||||
closureJunctionTableMetadata.entityMetadata = closureJunctionEntityMetadata;
|
||||
closureJunctionEntityMetadata.foreignKeys.push(
|
||||
new ForeignKeyMetadata(closureJunctionTableMetadata, [columns[0]], metadata.table, [metadata.primaryColumn]),
|
||||
new ForeignKeyMetadata(closureJunctionTableMetadata, [columns[1]], metadata.table, [metadata.primaryColumn])
|
||||
new ForeignKeyMetadata(closureJunctionEntityMetadata, closureJunctionTableMetadata, [columns[0]], metadata.table, [metadata.primaryColumn]),
|
||||
new ForeignKeyMetadata(closureJunctionEntityMetadata, closureJunctionTableMetadata, [columns[1]], metadata.table, [metadata.primaryColumn])
|
||||
);
|
||||
closureJunctionEntityMetadatas.push(closureJunctionEntityMetadata);
|
||||
|
||||
@ -226,7 +203,11 @@ export class EntityMetadataBuilder {
|
||||
const junctionEntityMetadatas: EntityMetadata[] = [];
|
||||
entityMetadatas.forEach(metadata => {
|
||||
metadata.ownerManyToManyRelations.map(relation => {
|
||||
const tableMetadata = new TableMetadata(undefined, relation.joinTable.name, "junction");
|
||||
const tableMetadata = new TableMetadata({
|
||||
target: Function,
|
||||
name: relation.joinTable.name,
|
||||
type: "junction"
|
||||
});
|
||||
const column1 = relation.joinTable.referencedColumn;
|
||||
const column2 = relation.joinTable.inverseReferencedColumn;
|
||||
|
||||
@ -241,19 +222,26 @@ export class EntityMetadataBuilder {
|
||||
name: relation.joinTable.inverseJoinColumnName // inverseSideMetadata.table.name + "_" + column2.name
|
||||
};
|
||||
const columns = [
|
||||
new ColumnMetadata({
|
||||
new ColumnMetadata(metadata, {
|
||||
target: Function, // todo: temp, fix it later
|
||||
propertyName: "", // todo: temp, fix it later
|
||||
propertyType: column2.type,
|
||||
mode: "regular", // or virtual?
|
||||
options: column1options
|
||||
}),
|
||||
new ColumnMetadata({
|
||||
new ColumnMetadata(metadata, {
|
||||
target: Function, // todo: temp, fix it later
|
||||
propertyName: "", // todo: temp, fix it later
|
||||
propertyType: column2.type,
|
||||
mode: "regular", // or virtual?
|
||||
options: column2options
|
||||
})
|
||||
];
|
||||
const junctionEntityMetadata = new EntityMetadata(this.namingStrategy, tableMetadata, columns, [], []);
|
||||
const junctionEntityMetadata = new EntityMetadata(namingStrategy, tableMetadata, columns, [], []);
|
||||
tableMetadata.entityMetadata = junctionEntityMetadata;
|
||||
junctionEntityMetadata.foreignKeys.push(
|
||||
new ForeignKeyMetadata(tableMetadata, [columns[0]], metadata.table, [column1]),
|
||||
new ForeignKeyMetadata(tableMetadata, [columns[1]], relation.inverseEntityMetadata.table, [column2])
|
||||
new ForeignKeyMetadata(junctionEntityMetadata, tableMetadata, [columns[0]], metadata.table, [column1]),
|
||||
new ForeignKeyMetadata(junctionEntityMetadata, tableMetadata, [columns[1]], relation.inverseEntityMetadata.table, [column2])
|
||||
);
|
||||
junctionEntityMetadatas.push(junctionEntityMetadata);
|
||||
relation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
@ -267,4 +255,9 @@ export class EntityMetadataBuilder {
|
||||
.concat(closureJunctionEntityMetadatas);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
}
|
||||
7
src/metadata-builder/EntityMetadataFactory.ts
Normal file
7
src/metadata-builder/EntityMetadataFactory.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class EntityMetadataFactory {
|
||||
|
||||
createEntityMetadataBuilder() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,7 +11,7 @@ export class MissingJoinColumnError extends Error {
|
||||
super();
|
||||
if (relation.hasInverseSide) {
|
||||
this.message = `JoinColumn is missing on both sides of ${entityMetadata.name}#${relation.propertyName} and ` +
|
||||
`${relation.inverseEntityMetadata.name}#${relation.inverseRelation.name} one-to-one relationship. ` +
|
||||
`${relation.inverseEntityMetadata.name}#${relation.inverseRelation.propertyName} one-to-one relationship. ` +
|
||||
`You need to put JoinColumn decorator on one of the sides.`;
|
||||
} else {
|
||||
this.message = `JoinColumn is missing on ${entityMetadata.name}#${relation.propertyName} one-to-one relationship. ` +
|
||||
@ -11,11 +11,11 @@ export class MissingJoinTableError extends Error {
|
||||
super();
|
||||
|
||||
if (relation.hasInverseSide) {
|
||||
this.message = `JoinTable is missing on both sides of ${entityMetadata.name}#${relation.name} and ` +
|
||||
`${relation.inverseEntityMetadata.name}#${relation.inverseRelation.name} many-to-many relationship. ` +
|
||||
this.message = `JoinTable is missing on both sides of ${entityMetadata.name}#${relation.propertyName} and ` +
|
||||
`${relation.inverseEntityMetadata.name}#${relation.inverseRelation.propertyName} many-to-many relationship. ` +
|
||||
`You need to put decorator decorator on one of the sides.`;
|
||||
} else {
|
||||
this.message = `JoinTable is missing on ${entityMetadata.name}#${relation.name} many-to-many relationship. ` +
|
||||
this.message = `JoinTable is missing on ${entityMetadata.name}#${relation.propertyName} many-to-many relationship. ` +
|
||||
`You need to put JoinTable decorator on it.`;
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ export class UsingJoinColumnIsNotAllowedError extends Error {
|
||||
|
||||
constructor(entityMetadata: EntityMetadata, relation: RelationMetadata) {
|
||||
super();
|
||||
this.message = `Using JoinColumn on ${entityMetadata.name}#${relation.name} is wrong. ` +
|
||||
this.message = `Using JoinColumn on ${entityMetadata.name}#${relation.propertyName} is wrong. ` +
|
||||
`You can use JoinColumn only on one-to-one and many-to-one relations.`;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export class UsingJoinColumnOnlyOnOneSideAllowedError extends Error {
|
||||
constructor(entityMetadata: EntityMetadata, relation: RelationMetadata) {
|
||||
super();
|
||||
this.message = `Using JoinColumn is allowed only on one side of the one-to-one relationship. ` +
|
||||
`Both ${entityMetadata.name}#${relation.name} and ${relation.inverseEntityMetadata.name}#${relation.inverseRelation.name} ` +
|
||||
`Both ${entityMetadata.name}#${relation.propertyName} and ${relation.inverseEntityMetadata.name}#${relation.inverseRelation.propertyName} ` +
|
||||
`has JoinTable decorators. Choose one of them and left JoinTable decorator only on it.`;
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@ export class UsingJoinTableIsNotAllowedError extends Error {
|
||||
|
||||
constructor(entityMetadata: EntityMetadata, relation: RelationMetadata) {
|
||||
super();
|
||||
this.message = `Using JoinTable on ${entityMetadata.name}#${relation.name} is wrong. ` +
|
||||
`${entityMetadata.name}#${relation.name} has ${relation.relationType} relation, ` +
|
||||
this.message = `Using JoinTable on ${entityMetadata.name}#${relation.propertyName} is wrong. ` +
|
||||
`${entityMetadata.name}#${relation.propertyName} has ${relation.relationType} relation, ` +
|
||||
`however you can use JoinTable only on many-to-many relations.`;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export class UsingJoinTableOnlyOnOneSideAllowedError extends Error {
|
||||
constructor(entityMetadata: EntityMetadata, relation: RelationMetadata) {
|
||||
super();
|
||||
this.message = `Using JoinTable is allowed only on one side of the many-to-many relationship. ` +
|
||||
`Both ${entityMetadata.name}#${relation.name} and ${relation.inverseEntityMetadata.name}#${relation.inverseRelation.name} ` +
|
||||
`Both ${entityMetadata.name}#${relation.propertyName} and ${relation.inverseEntityMetadata.name}#${relation.inverseRelation.propertyName} ` +
|
||||
`has JoinTable decorators. Choose one of them and left JoinColumn decorator only on it.`;
|
||||
}
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
import {TableMetadata} from "../metadata/TableMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {CompositeIndexMetadata} from "../metadata/CompositeIndexMetadata";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {EventSubscriberMetadata} from "../metadata/EventSubscriberMetadata";
|
||||
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
|
||||
import {NamingStrategyMetadata} from "../metadata/NamingStrategyMetadata";
|
||||
import {JoinColumnMetadata} from "../metadata/JoinColumnMetadata";
|
||||
import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
|
||||
import {TargetMetadataCollection} from "../metadata/collection/TargetMetadataCollection";
|
||||
import {PropertyMetadataCollection} from "../metadata/collection/PropertyMetadataCollection";
|
||||
import {RelationsCountMetadata} from "../metadata/RelationsCountMetadata";
|
||||
|
||||
/**
|
||||
* Storage all metadatas of all available types: tables, fields, subscribers, relations, etc.
|
||||
* Each metadata represents some specifications of what it represents.
|
||||
*/
|
||||
export class MetadataStorage {
|
||||
|
||||
// todo: type in function validation, inverse side function validation
|
||||
// todo: check on build for duplicate names, since naming checking was removed from MetadataStorage
|
||||
// todo: duplicate name checking for: table, relation, column, index, naming strategy, join tables/columns?
|
||||
// todo: check for duplicate targets too since this check has been removed too
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
readonly tableMetadatas = new TargetMetadataCollection<TableMetadata>();
|
||||
readonly namingStrategyMetadatas = new TargetMetadataCollection<NamingStrategyMetadata>();
|
||||
readonly eventSubscriberMetadatas = new TargetMetadataCollection<EventSubscriberMetadata>();
|
||||
readonly compositeIndexMetadatas = new TargetMetadataCollection<CompositeIndexMetadata>();
|
||||
readonly columnMetadatas = new PropertyMetadataCollection<ColumnMetadata>();
|
||||
readonly relationMetadatas = new PropertyMetadataCollection<RelationMetadata>();
|
||||
readonly joinColumnMetadatas = new PropertyMetadataCollection<JoinColumnMetadata>();
|
||||
readonly joinTableMetadatas = new PropertyMetadataCollection<JoinTableMetadata>();
|
||||
readonly indexMetadatas = new PropertyMetadataCollection<IndexMetadata>();
|
||||
readonly entityListenerMetadatas = new PropertyMetadataCollection<EntityListenerMetadata>();
|
||||
readonly relationCountMetadatas = new PropertyMetadataCollection<RelationsCountMetadata>();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new copy of the MetadataStorage with same metadatas as in current metadata storage, but filtered
|
||||
* by classes.
|
||||
*/
|
||||
mergeWithAbstract(allTableMetadatas: TargetMetadataCollection<TableMetadata>,
|
||||
tableMetadata: TableMetadata) {
|
||||
|
||||
const compositeIndexMetadatas = this.compositeIndexMetadatas.filterByClass(tableMetadata.target);
|
||||
const columnMetadatas = this.columnMetadatas.filterByClass(tableMetadata.target);
|
||||
const relationMetadatas = this.relationMetadatas.filterByClass(tableMetadata.target);
|
||||
const joinColumnMetadatas = this.joinColumnMetadatas.filterByClass(tableMetadata.target);
|
||||
const joinTableMetadatas = this.joinTableMetadatas.filterByClass(tableMetadata.target);
|
||||
const indexMetadatas = this.indexMetadatas.filterByClass(tableMetadata.target);
|
||||
const entityListenerMetadatas = this.entityListenerMetadatas.filterByClass(tableMetadata.target);
|
||||
const relationCountMetadatas = this.relationCountMetadatas.filterByClass(tableMetadata.target);
|
||||
|
||||
allTableMetadatas
|
||||
.filter(metadata => tableMetadata.isInherited(metadata))
|
||||
.forEach(parentMetadata => {
|
||||
const metadatasFromAbstract = this.mergeWithAbstract(allTableMetadatas, parentMetadata);
|
||||
|
||||
metadatasFromAbstract.columnMetadatas
|
||||
.filterRepeatedMetadatas(columnMetadatas)
|
||||
.forEach(metadata => columnMetadatas.push(metadata));
|
||||
|
||||
metadatasFromAbstract.relationMetadatas
|
||||
.filterRepeatedMetadatas(relationMetadatas)
|
||||
.forEach(metadata => relationMetadatas.push(metadata));
|
||||
|
||||
metadatasFromAbstract.joinColumnMetadatas
|
||||
.filterRepeatedMetadatas(joinColumnMetadatas)
|
||||
.forEach(metadata => joinColumnMetadatas.push(metadata));
|
||||
|
||||
metadatasFromAbstract.joinTableMetadatas
|
||||
.filterRepeatedMetadatas(joinTableMetadatas)
|
||||
.forEach(metadata => joinTableMetadatas.push(metadata));
|
||||
|
||||
metadatasFromAbstract.indexMetadatas
|
||||
.filterRepeatedMetadatas(indexMetadatas)
|
||||
.forEach(metadata => indexMetadatas.push(metadata));
|
||||
|
||||
metadatasFromAbstract.entityListenerMetadatas
|
||||
.filterRepeatedMetadatas(entityListenerMetadatas)
|
||||
.forEach(metadata => entityListenerMetadatas.push(metadata));
|
||||
|
||||
metadatasFromAbstract.relationCountMetadatas
|
||||
.filterRepeatedMetadatas(relationCountMetadatas)
|
||||
.forEach(metadata => relationCountMetadatas.push(metadata));
|
||||
});
|
||||
|
||||
return {
|
||||
compositeIndexMetadatas: compositeIndexMetadatas,
|
||||
columnMetadatas: columnMetadatas,
|
||||
relationMetadatas: relationMetadatas,
|
||||
joinColumnMetadatas: joinColumnMetadatas,
|
||||
joinTableMetadatas: joinTableMetadatas,
|
||||
indexMetadatas: indexMetadatas,
|
||||
entityListenerMetadatas: entityListenerMetadatas,
|
||||
relationCountMetadatas: relationCountMetadatas
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +1,16 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnMetadataArgs} from "./args/ColumnMetadataArgs";
|
||||
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
|
||||
import {ColumnType} from "./types/ColumnTypes";
|
||||
|
||||
export type ColumnMode = "regular"|"createDate"|"updateDate"|"version"|"treeChildrenCount"|"treeLevel";
|
||||
import {EntityMetadata} from "./EntityMetadata";
|
||||
|
||||
/**
|
||||
* This metadata contains all information about class's column.
|
||||
* Kinda type of the column. Not a type in the database, but locally used type to determine what kind of column
|
||||
* we are working with.
|
||||
*/
|
||||
export type ColumnMode = "regular"|"virtual"|"primary"|"createDate"|"updateDate"|"version"|"treeChildrenCount"|"treeLevel";
|
||||
|
||||
/**
|
||||
* This metadata contains all information about entity's column.
|
||||
*/
|
||||
export class ColumnMetadata extends PropertyMetadata {
|
||||
|
||||
@ -15,12 +19,12 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy to be used to generate column name.
|
||||
* Entity metadata where this column metadata is.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// Public Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@ -29,47 +33,38 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
readonly propertyType: string;
|
||||
|
||||
/**
|
||||
* The type of the column.
|
||||
* The database type of the column.
|
||||
*/
|
||||
readonly type: ColumnType;
|
||||
|
||||
/**
|
||||
* The mode of the column.
|
||||
* Column's mode in which this column is working.
|
||||
*/
|
||||
readonly mode: ColumnMode;
|
||||
|
||||
/**
|
||||
* Maximum length in the database.
|
||||
* Type's length in the database.
|
||||
*/
|
||||
readonly length = "";
|
||||
|
||||
/**
|
||||
* Indicates if this column is primary key.
|
||||
* Indicates if this column is generated (auto increment or generated other way).
|
||||
*/
|
||||
readonly isPrimary = false;
|
||||
readonly isGenerated = false;
|
||||
|
||||
/**
|
||||
* Indicates if this column is auto increment.
|
||||
*/
|
||||
readonly isAutoIncrement = false;
|
||||
|
||||
/**
|
||||
* Indicates if value should be unique or not.
|
||||
* Indicates if value in the database should be unique or not.
|
||||
*/
|
||||
readonly isUnique = false;
|
||||
|
||||
/**
|
||||
* Indicates if can contain nulls or not.
|
||||
* Indicates if column can contain nulls or not.
|
||||
*/
|
||||
readonly isNullable = false;
|
||||
|
||||
/**
|
||||
* Indicates if column is virtual. Virtual columns are not mapped to the entity.
|
||||
*/
|
||||
readonly isVirtual = false;
|
||||
|
||||
/**
|
||||
* Extra sql definition for the given column.
|
||||
* Extra sql definition for the given column.
|
||||
* Can be used to make temporary tweaks. Not recommended to use.
|
||||
*/
|
||||
readonly columnDefinition = "";
|
||||
|
||||
@ -79,7 +74,8 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
readonly comment = "";
|
||||
|
||||
/**
|
||||
* Old column name. Used to correctly alter tables when column name is changed.
|
||||
* Old column name. Used to correctly alter tables when column name is changed.
|
||||
* Can be used to make temporary tweaks. Not recommended to use.
|
||||
*/
|
||||
readonly oldColumnName: string;
|
||||
|
||||
@ -113,15 +109,22 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(args: ColumnMetadataArgs) {
|
||||
super(args.target, args.propertyName);
|
||||
constructor(args: ColumnMetadataArgs);
|
||||
constructor(entityMetadata: EntityMetadata, args: ColumnMetadataArgs);
|
||||
constructor(entityMetadataOrArgs: EntityMetadata|ColumnMetadataArgs, args?: ColumnMetadataArgs) {
|
||||
super(
|
||||
args ? args.target : (entityMetadataOrArgs as ColumnMetadataArgs).target,
|
||||
args ? args.propertyName : (entityMetadataOrArgs as ColumnMetadataArgs).propertyName
|
||||
);
|
||||
|
||||
if (entityMetadataOrArgs && args) {
|
||||
this.entityMetadata = entityMetadataOrArgs as EntityMetadata;
|
||||
}
|
||||
|
||||
args = args ? args : entityMetadataOrArgs as ColumnMetadataArgs;
|
||||
|
||||
if (args.isPrimaryKey)
|
||||
this.isPrimary = args.isPrimaryKey;
|
||||
if (args.mode)
|
||||
this.mode = args.mode;
|
||||
if (args.isVirtual)
|
||||
this.isVirtual = args.isVirtual;
|
||||
if (args.propertyType)
|
||||
this.propertyType = args.propertyType.toLowerCase();
|
||||
if (args.options.name)
|
||||
@ -132,7 +135,7 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
if (args.options.length)
|
||||
this.length = args.options.length;
|
||||
if (args.options.generated)
|
||||
this.isAutoIncrement = args.options.generated;
|
||||
this.isGenerated = args.options.generated;
|
||||
if (args.options.unique)
|
||||
this.isUnique = args.options.unique;
|
||||
if (args.options.nullable)
|
||||
@ -159,20 +162,46 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
* Column name in the database.
|
||||
*/
|
||||
get name(): string {
|
||||
if (this._name)
|
||||
return this._name;
|
||||
|
||||
return this.namingStrategy ? this.namingStrategy.columnName(this.propertyName) : this.propertyName;
|
||||
// if custom column name is set implicitly then return it
|
||||
if (this._name)
|
||||
return this.entityMetadata.namingStrategy.columnNameCustomized(this._name);
|
||||
|
||||
// if there is a naming strategy then use it to normalize propertyName as column name
|
||||
return this.entityMetadata.namingStrategy.columnName(this.propertyName);
|
||||
}
|
||||
|
||||
get isUpdateDate() {
|
||||
return this.mode === "updateDate";
|
||||
|
||||
/**
|
||||
* Indicates if this column is a primary key.
|
||||
*/
|
||||
get isPrimary() {
|
||||
return this.mode === "primary";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates if column is virtual. Virtual columns are not mapped to the entity.
|
||||
*/
|
||||
get isVirtual() {
|
||||
return this.mode === "virtual";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity creation date.
|
||||
*/
|
||||
get isCreateDate() {
|
||||
return this.mode === "createDate";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity update date.
|
||||
*/
|
||||
get isUpdateDate() {
|
||||
return this.mode === "updateDate";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity version.
|
||||
*/
|
||||
get isVersion() {
|
||||
return this.mode === "version";
|
||||
}
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {EntityMetadata} from "./EntityMetadata";
|
||||
import {CompositeIndexOptions} from "./options/CompositeIndexOptions";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about table's composite index.
|
||||
*/
|
||||
export class CompositeIndexMetadata extends TargetMetadata {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used to generate and normalize index name.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Entity metadata of the class to which this index is applied.
|
||||
*/
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Indicates if this index must be unique.
|
||||
*/
|
||||
readonly isUnique: boolean;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Composite index name.
|
||||
*/
|
||||
private readonly _name: string;
|
||||
|
||||
/**
|
||||
* Columns combination to be used as index.
|
||||
*/
|
||||
private readonly _columns: ((object: any) => any[])|string[];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function,
|
||||
name: string|undefined,
|
||||
columns: ((object: any) => any[])|string[],
|
||||
options?: CompositeIndexOptions) {
|
||||
super(target);
|
||||
this._columns = columns;
|
||||
if (name)
|
||||
this._name = name;
|
||||
if (options && options.unique)
|
||||
this.isUnique = options.unique;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
get name() { // throw exception if naming strategy is not set
|
||||
return this.namingStrategy.indexName(this.target, this._name, this.columns);
|
||||
}
|
||||
|
||||
get columns() {
|
||||
|
||||
// if columns already an array of string then simply return it
|
||||
if (this._columns instanceof Array)
|
||||
return this._columns;
|
||||
|
||||
// if columns is a function that returns array of field names then execute it and get columns names from it
|
||||
const propertiesMap = this.entityMetadata.createPropertiesMap();
|
||||
return this._columns(propertiesMap).map((i: any) => String(i));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {EventListenerType} from "./types/EventListenerTypes";
|
||||
import {EntityListenerMetadataArgs} from "../metadata-args/EntityListenerMetadataArgs";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about some index on a field.
|
||||
* This metadata contains all information about entity's listeners.
|
||||
*/
|
||||
export class EntityListenerMetadata extends PropertyMetadata {
|
||||
|
||||
@ -19,9 +20,9 @@ export class EntityListenerMetadata extends PropertyMetadata {
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, propertyName: string, type: EventListenerType) {
|
||||
super(target, propertyName);
|
||||
this.type = type;
|
||||
constructor(args: EntityListenerMetadataArgs) {
|
||||
super(args.target, args.propertyName);
|
||||
this.type = args.type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import {TableMetadata} from "./TableMetadata";
|
||||
import {ColumnMetadata} from "./ColumnMetadata";
|
||||
import {RelationMetadata} from "./RelationMetadata";
|
||||
import {CompositeIndexMetadata} from "./CompositeIndexMetadata";
|
||||
import {RelationMetadata, PropertyTypeInFunction} from "./RelationMetadata";
|
||||
import {IndexMetadata} from "./IndexMetadata";
|
||||
import {RelationTypes} from "./types/RelationTypes";
|
||||
import {ForeignKeyMetadata} from "./ForeignKeyMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
|
||||
/**
|
||||
* Contains all entity metadata.
|
||||
@ -16,21 +15,43 @@ export class EntityMetadata {
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* If entity's table is a closure-typed table, then this entity will have a closure junction table metadata.
|
||||
*/
|
||||
closureJunctionTable: EntityMetadata;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// Public Readonly Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used to generate and normalize column name.
|
||||
* Naming strategy used to generate and normalize names.
|
||||
*/
|
||||
readonly namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Entity's table metadata.
|
||||
*/
|
||||
readonly table: TableMetadata;
|
||||
|
||||
/**
|
||||
* Entity's column metadatas.
|
||||
*/
|
||||
readonly columns: ColumnMetadata[];
|
||||
|
||||
/**
|
||||
* Entity's relation metadatas.
|
||||
*/
|
||||
readonly relations: RelationMetadata[];
|
||||
readonly compositeIndices: CompositeIndexMetadata[];
|
||||
|
||||
/**
|
||||
* Entity's index metadatas.
|
||||
*/
|
||||
readonly indices: IndexMetadata[];
|
||||
|
||||
/**
|
||||
* Entity's foreign key metadatas.
|
||||
*/
|
||||
readonly foreignKeys: ForeignKeyMetadata[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -38,72 +59,52 @@ export class EntityMetadata {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(namingStrategy: NamingStrategyInterface,
|
||||
tableMetadata: TableMetadata,
|
||||
columnMetadatas: ColumnMetadata[],
|
||||
relationMetadatas: RelationMetadata[],
|
||||
compositeIndexMetadatas: CompositeIndexMetadata[]) {
|
||||
table: TableMetadata,
|
||||
columns: ColumnMetadata[],
|
||||
relations: RelationMetadata[],
|
||||
indices: IndexMetadata[]) {
|
||||
this.namingStrategy = namingStrategy;
|
||||
this.table = tableMetadata;
|
||||
this.columns = columnMetadatas;
|
||||
this.relations = relationMetadatas;
|
||||
this.compositeIndices = compositeIndexMetadatas;
|
||||
|
||||
this.table.entityMetadata = this;
|
||||
this.relations.forEach(relation => relation.entityMetadata = this);
|
||||
this.compositeIndices.forEach(index => index.entityMetadata = this);
|
||||
this.table = table;
|
||||
this.columns = columns;
|
||||
this.relations = relations;
|
||||
this.indices = indices;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Accessors
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity's name. Equal to entity target class's name if target is set to table, or equals to table name if its set.
|
||||
*/
|
||||
get name(): string {
|
||||
if (!this.table) {
|
||||
if (!this.table)
|
||||
throw new Error("No table target set to the entity metadata.");
|
||||
}
|
||||
|
||||
if (this.table.target)
|
||||
return (<any> this.table.target).name;
|
||||
if (this.target)
|
||||
return (<any> this.target).name;
|
||||
|
||||
return this.table.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Target class to which this entity metadata is bind.
|
||||
*/
|
||||
get target(): Function {
|
||||
return this.table.target;
|
||||
}
|
||||
|
||||
get oneToOneRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.ONE_TO_ONE);
|
||||
}
|
||||
|
||||
get ownerOneToOneRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.ONE_TO_ONE && relation.isOwning);
|
||||
}
|
||||
|
||||
get oneToManyRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.ONE_TO_MANY);
|
||||
}
|
||||
|
||||
get manyToOneRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_ONE);
|
||||
}
|
||||
|
||||
get manyToManyRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_MANY);
|
||||
}
|
||||
|
||||
get ownerManyToManyRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_MANY && relation.isOwning);
|
||||
}
|
||||
|
||||
get relationsWithJoinColumns() {
|
||||
return this.ownerOneToOneRelations.concat(this.manyToOneRelations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has a primary column. All user's entities must have a primary column.
|
||||
* Special entity metadatas like for junction tables and closure junction tables don't have a primary column.
|
||||
*/
|
||||
get hasPrimaryColumn(): boolean {
|
||||
return !!this.columns.find(column => column.isPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary column.
|
||||
*/
|
||||
get primaryColumn(): ColumnMetadata {
|
||||
const primaryKey = this.columns.find(column => column.isPrimary);
|
||||
if (!primaryKey)
|
||||
@ -111,11 +112,17 @@ export class EntityMetadata {
|
||||
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if entity has a create date column.
|
||||
*/
|
||||
get hasCreateDateColumn(): boolean {
|
||||
return !!this.columns.find(column => column.mode === "createDate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity column which contains a create date value.
|
||||
*/
|
||||
get createDateColumn(): ColumnMetadata {
|
||||
const column = this.columns.find(column => column.mode === "createDate");
|
||||
if (!column)
|
||||
@ -124,10 +131,16 @@ export class EntityMetadata {
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has an update date column.
|
||||
*/
|
||||
get hasUpdateDateColumn(): boolean {
|
||||
return !!this.columns.find(column => column.mode === "updateDate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity column which contains an update date value.
|
||||
*/
|
||||
get updateDateColumn(): ColumnMetadata {
|
||||
const column = this.columns.find(column => column.mode === "updateDate");
|
||||
if (!column)
|
||||
@ -136,10 +149,16 @@ export class EntityMetadata {
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has a version column.
|
||||
*/
|
||||
get hasVersionColumn(): boolean {
|
||||
return !!this.columns.find(column => column.mode === "version");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity column which contains an entity version.
|
||||
*/
|
||||
get versionColumn(): ColumnMetadata {
|
||||
const column = this.columns.find(column => column.mode === "version");
|
||||
if (!column)
|
||||
@ -148,18 +167,9 @@ export class EntityMetadata {
|
||||
return column;
|
||||
}
|
||||
|
||||
get hasTreeChildrenCountColumn(): boolean {
|
||||
return !!this.columns.find(column => column.mode === "treeChildrenCount");
|
||||
}
|
||||
|
||||
get treeChildrenCountColumn(): ColumnMetadata {
|
||||
const column = this.columns.find(column => column.mode === "treeChildrenCount");
|
||||
if (!column)
|
||||
throw new Error(`TreeChildrenCountColumn was not found in entity ${this.name}`);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has a tree level column.
|
||||
*/
|
||||
get hasTreeLevelColumn(): boolean {
|
||||
return !!this.columns.find(column => column.mode === "treeLevel");
|
||||
}
|
||||
@ -171,9 +181,54 @@ export class EntityMetadata {
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
get hasPrimaryKey(): boolean {
|
||||
return !!this.primaryColumn;
|
||||
|
||||
/**
|
||||
* Gets only one-to-one relations of the entity.
|
||||
*/
|
||||
get oneToOneRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.ONE_TO_ONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets only owner one-to-one relations of the entity.
|
||||
*/
|
||||
get ownerOneToOneRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.ONE_TO_ONE && relation.isOwning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets only one-to-many relations of the entity.
|
||||
*/
|
||||
get oneToManyRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.ONE_TO_MANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets only many-to-one relations of the entity.
|
||||
*/
|
||||
get manyToOneRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_ONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets only many-to-many relations of the entity.
|
||||
*/
|
||||
get manyToManyRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_MANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets only owner many-to-many relations of the entity.
|
||||
*/
|
||||
get ownerManyToManyRelations(): RelationMetadata[] {
|
||||
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_MANY && relation.isOwning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets only owner one-to-one and many-to-one relations.
|
||||
*/
|
||||
get relationsWithJoinColumns() {
|
||||
return this.ownerOneToOneRelations.concat(this.manyToOneRelations);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -187,29 +242,54 @@ export class EntityMetadata {
|
||||
return new (<any> this.table.target)();
|
||||
}
|
||||
|
||||
createPropertiesMap(): any {
|
||||
const entity: any = {};
|
||||
/**
|
||||
* Creates an object - map of columns and relations of the entity.
|
||||
*/
|
||||
createPropertiesMap(): { [name: string]: string|any } {
|
||||
const entity: { [name: string]: string|any } = {};
|
||||
this.columns.forEach(column => entity[column.propertyName] = column.propertyName);
|
||||
this.relations.forEach(relation => entity[relation.propertyName] = relation.propertyName);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes property name of the entity using given PropertyTypeInFunction.
|
||||
*/
|
||||
computePropertyName(nameOrFn: PropertyTypeInFunction<any>) {
|
||||
return typeof nameOrFn === "string" ? nameOrFn : nameOrFn(this.createPropertiesMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns entity id of the given entity.
|
||||
*/
|
||||
getEntityId(entity: any) {
|
||||
return entity[this.primaryColumn.propertyName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if column with the given property name exist.
|
||||
*/
|
||||
hasColumnWithPropertyName(propertyName: string): boolean {
|
||||
return !!this.columns.find(column => column.propertyName === propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if column with the given database name exist.
|
||||
*/
|
||||
hasColumnWithDbName(name: string): boolean {
|
||||
return !!this.columns.find(column => column.name === name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if relation with the given property name exist.
|
||||
*/
|
||||
hasRelationWithPropertyName(propertyName: string): boolean {
|
||||
return !!this.relations.find(relation => relation.propertyName === propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds relation with the given property name.
|
||||
*/
|
||||
findRelationWithPropertyName(propertyName: string): RelationMetadata {
|
||||
const relation = this.relations.find(relation => relation.propertyName === propertyName);
|
||||
if (!relation)
|
||||
@ -218,70 +298,34 @@ export class EntityMetadata {
|
||||
return relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if relation with the given name exist.
|
||||
*/
|
||||
hasRelationWithDbName(dbName: string): boolean {
|
||||
return !!this.relations.find(relation => relation.name === dbName);
|
||||
return !!this.relationsWithJoinColumns.find(relation => relation.name === dbName);
|
||||
}
|
||||
|
||||
findRelationWithDbName(propertyName: string): RelationMetadata {
|
||||
const relation = this.relations.find(relation => relation.name === propertyName);
|
||||
/**
|
||||
* Finds relation with the given name.
|
||||
*/
|
||||
findRelationWithDbName(name: string): RelationMetadata {
|
||||
const relation = this.relationsWithJoinColumns.find(relation => relation.name === name);
|
||||
if (!relation)
|
||||
throw new Error(`Relation with name ${propertyName} in ${this.name} entity was not found.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
hasRelationWithOneWithPropertyName(propertyName: string): boolean {
|
||||
return !!this.relations.find(relation => relation.propertyName === propertyName && (relation.isOneToMany || relation.isOneToOne));
|
||||
}
|
||||
|
||||
findRelationWithOneWithPropertyName(propertyName: string): RelationMetadata {
|
||||
const relation = this.relations.find(relation => relation.propertyName === propertyName && (relation.isOneToMany || relation.isOneToOne));
|
||||
if (!relation)
|
||||
throw new Error(`Relation with one with property name ${propertyName} in ${this.name} entity was not found.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
hasRelationWithOneWithDbName(name: string): boolean {
|
||||
return !!this.relations.find(relation => relation.name === name && (relation.isOneToMany || relation.isOneToOne));
|
||||
}
|
||||
|
||||
findRelationWithOneWithDbName(name: string): RelationMetadata {
|
||||
const relation = this.relations.find(relation => relation.name === name && (relation.isOneToMany || relation.isOneToOne));
|
||||
if (!relation)
|
||||
throw new Error(`Relation with one with name ${name} in ${this.name} entity was not found.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
hasRelationWithManyWithPropertyName(name: string): boolean {
|
||||
return !!this.relations.find(relation => relation.propertyName === name && (relation.isManyToOne || relation.isManyToMany));
|
||||
}
|
||||
|
||||
findRelationWithManyWithPropertyName(name: string): RelationMetadata {
|
||||
const relation = this.relations.find(relation => relation.propertyName === name && (relation.isManyToOne || relation.isManyToMany));
|
||||
if (!relation)
|
||||
throw new Error(`Relation with many with property name ${name} in ${this.name} entity was not found.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
hasRelationWithManyWithDbName(name: string): boolean {
|
||||
return !!this.relations.find(relation => relation.name === name && (relation.isManyToOne || relation.isManyToMany));
|
||||
}
|
||||
|
||||
findRelationWithManyWithDbName(name: string): RelationMetadata {
|
||||
const relation = this.relations.find(relation => relation.name === name && (relation.isManyToOne || relation.isManyToMany));
|
||||
if (!relation)
|
||||
throw new Error(`Relation with many with name ${name} in ${this.name} entity was not found.`);
|
||||
throw new Error(`Relation with name ${name} in ${this.name} entity was not found.`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a tree parent relation. Used only in tree-tables.
|
||||
*/
|
||||
get hasTreeParentRelation() {
|
||||
return !!this.relations.find(relation => relation.isTreeParent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tree parent relation. Used only in tree-tables.
|
||||
*/
|
||||
get treeParentRelation() {
|
||||
const relation = this.relations.find(relation => relation.isTreeParent);
|
||||
if (!relation)
|
||||
@ -290,10 +334,16 @@ export class EntityMetadata {
|
||||
return relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a tree children relation. Used only in tree-tables.
|
||||
*/
|
||||
get hasTreeChildrenRelation() {
|
||||
return !!this.relations.find(relation => relation.isTreeChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tree children relation. Used only in tree-tables.
|
||||
*/
|
||||
get treeChildrenRelation() {
|
||||
const relation = this.relations.find(relation => relation.isTreeChildren);
|
||||
if (!relation)
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
import {EventSubscriberMetadataArgs} from "../metadata-args/EventSubscriberMetadataArgs";
|
||||
|
||||
/**
|
||||
* Contains metadata information about ORM event subscribers.
|
||||
*/
|
||||
export class EventSubscriberMetadata extends TargetMetadata {
|
||||
|
||||
constructor(args: EventSubscriberMetadataArgs) {
|
||||
super(args.target);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,24 +1,33 @@
|
||||
import {ColumnMetadata} from "./ColumnMetadata";
|
||||
import {TableMetadata} from "./TableMetadata";
|
||||
import {EntityMetadata} from "./EntityMetadata";
|
||||
|
||||
/**
|
||||
* ON_DELETE type to be used to specify delete strategy when some relation is being deleted from the database.
|
||||
*/
|
||||
export type OnDeleteType = "RESTRICT"|"CASCADE"|"SET NULL";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information foreign keys.
|
||||
* Contains all information about entity's foreign key.
|
||||
*/
|
||||
export class ForeignKeyMetadata {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// Public Readonly Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata where this foreign key is.
|
||||
*/
|
||||
readonly entityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Table to which this foreign key is applied.
|
||||
*/
|
||||
readonly table: TableMetadata;
|
||||
|
||||
/**
|
||||
* Array of columns.
|
||||
* Array of columns of this foreign key.
|
||||
*/
|
||||
readonly columns: ColumnMetadata[];
|
||||
|
||||
@ -41,11 +50,13 @@ export class ForeignKeyMetadata {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(table: TableMetadata,
|
||||
constructor(entityMetadata: EntityMetadata,
|
||||
table: TableMetadata,
|
||||
columns: ColumnMetadata[],
|
||||
referencedTable: TableMetadata,
|
||||
referencedColumns: ColumnMetadata[],
|
||||
onDelete?: OnDeleteType) {
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.table = table;
|
||||
this.columns = columns;
|
||||
this.referencedTable = referencedTable;
|
||||
@ -59,27 +70,24 @@ export class ForeignKeyMetadata {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Array of column names.
|
||||
* Gets array of column names.
|
||||
*/
|
||||
get columnNames(): string[] {
|
||||
return this.columns.map(column => column.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of referenced column names.
|
||||
* Gets array of referenced column names.
|
||||
*/
|
||||
get referencedColumnNames(): string[] {
|
||||
return this.referencedColumns.map(column => column.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreign key name.
|
||||
* Gets foreign key name.
|
||||
*/
|
||||
get name() {
|
||||
// todo: use naming strategy
|
||||
const key = `${this.table.name}_${this.columnNames.join("_")}` +
|
||||
`_${this.referencedTable.name}_${this.referencedColumnNames.join("_")}`;
|
||||
return "fk_" + require("sha1")(key); // todo: use crypto instead?
|
||||
return this.entityMetadata.namingStrategy.foreignKeyName(this.table.name, this.columnNames, this.referencedTable.name, this.referencedColumnNames);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,39 +1,78 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
import {EntityMetadata} from "./EntityMetadata";
|
||||
import {IndexMetadataArgs} from "../metadata-args/IndexMetadataArgs";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about some index on a field.
|
||||
* Index metadata contains all information about table's index.
|
||||
*/
|
||||
export class IndexMetadata extends PropertyMetadata {
|
||||
export class IndexMetadata extends TargetMetadata {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used to generate and normalize index name.
|
||||
* Entity metadata of the class to which this index is applied.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
entityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The name of the index.
|
||||
* Indicates if this index must be unique.
|
||||
*/
|
||||
readonly name: string;
|
||||
readonly isUnique: boolean;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Composite index name.
|
||||
*/
|
||||
private readonly _name: string|undefined;
|
||||
|
||||
/**
|
||||
* Columns combination to be used as index.
|
||||
*/
|
||||
private readonly _columns: ((object: any) => any[])|string[];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, propertyName: string, name?: string) {
|
||||
super(target, propertyName);
|
||||
|
||||
if (name)
|
||||
this.name = name; // todo: if there is no name, then generate it (using naming strategy?)
|
||||
constructor(args: IndexMetadataArgs) {
|
||||
super(args.target);
|
||||
this._columns = args.columns;
|
||||
this._name = args.name;
|
||||
this.isUnique = args.unique;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets index's name.
|
||||
*/
|
||||
get name() {
|
||||
return this.entityMetadata.namingStrategy.indexName(this.target, this._name, this.columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the column names which are in this index.
|
||||
*/
|
||||
get columns(): string[] {
|
||||
|
||||
// if columns already an array of string then simply return it
|
||||
if (this._columns instanceof Array)
|
||||
return this._columns;
|
||||
|
||||
// if columns is a function that returns array of field names then execute it and get columns names from it
|
||||
const propertiesMap = this.entityMetadata.createPropertiesMap();
|
||||
return this._columns(propertiesMap).map((i: any) => String(i));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {JoinColumnOptions} from "./options/JoinColumnOptions";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {RelationMetadata} from "./RelationMetadata";
|
||||
import {ColumnMetadata} from "./ColumnMetadata";
|
||||
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* JoinColumnMetadata contains all information about relation's join column.
|
||||
*/
|
||||
export class JoinColumnMetadata extends PropertyMetadata {
|
||||
|
||||
@ -24,26 +24,21 @@ export class JoinColumnMetadata extends PropertyMetadata {
|
||||
/**
|
||||
* Join column name.
|
||||
*/
|
||||
private readonly _name: string;
|
||||
private readonly _name: string|undefined;
|
||||
|
||||
/**
|
||||
* Join column referenced column name.
|
||||
*/
|
||||
private readonly _referencedColumnName: string;
|
||||
private readonly referencedColumnName: string|undefined;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function,
|
||||
propertyName: string,
|
||||
options: JoinColumnOptions) {
|
||||
super(target, propertyName);
|
||||
|
||||
if (options.name)
|
||||
this._name = options.name;
|
||||
if (options.referencedColumnName)
|
||||
this._referencedColumnName = options.referencedColumnName;
|
||||
constructor(args: JoinColumnMetadataArgs) {
|
||||
super(args.target, args.propertyName);
|
||||
this._name = args.name;
|
||||
this.referencedColumnName = args.referencedColumnName;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@ -61,10 +56,10 @@ export class JoinColumnMetadata extends PropertyMetadata {
|
||||
* Referenced join column.
|
||||
*/
|
||||
get referencedColumn(): ColumnMetadata {
|
||||
if (this._referencedColumnName) {
|
||||
const referencedColumn = this.relation.inverseEntityMetadata.columns.find(column => column.name === this._referencedColumnName);
|
||||
if (this.referencedColumnName) {
|
||||
const referencedColumn = this.relation.inverseEntityMetadata.columns.find(column => column.name === this.referencedColumnName);
|
||||
if (!referencedColumn)
|
||||
throw new Error(`Referenced column ${this._referencedColumnName} was not found in entity ${this.name}`);
|
||||
throw new Error(`Referenced column ${this.referencedColumnName} was not found in entity ${this.name}`);
|
||||
}
|
||||
|
||||
return this.relation.inverseEntityMetadata.primaryColumn;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {JoinTableOptions} from "./options/JoinTableOptions";
|
||||
import {RelationMetadata} from "./RelationMetadata";
|
||||
import {ColumnMetadata} from "./ColumnMetadata";
|
||||
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
|
||||
|
||||
/**
|
||||
* JoinTableMetadata contains all information about relation's join table.
|
||||
*/
|
||||
export class JoinTableMetadata extends PropertyMetadata {
|
||||
|
||||
@ -49,24 +50,24 @@ export class JoinTableMetadata extends PropertyMetadata {
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, propertyName: string, options: JoinTableOptions) {
|
||||
super(target, propertyName);
|
||||
constructor(args: JoinTableMetadataArgs) {
|
||||
super(args.target, args.propertyName);
|
||||
|
||||
if (options.name)
|
||||
this._name = options.name;
|
||||
if (args.name)
|
||||
this._name = args.name;
|
||||
|
||||
if (options.joinColumn) {
|
||||
if (options.joinColumn.name)
|
||||
this._joinColumnName = options.joinColumn.name;
|
||||
if (options.joinColumn.referencedColumnName)
|
||||
this._joinColumnReferencedColumnName = options.joinColumn.referencedColumnName;
|
||||
if (args.joinColumn) {
|
||||
if (args.joinColumn.name)
|
||||
this._joinColumnName = args.joinColumn.name;
|
||||
if (args.joinColumn.referencedColumnName)
|
||||
this._joinColumnReferencedColumnName = args.joinColumn.referencedColumnName;
|
||||
}
|
||||
|
||||
if (options.inverseJoinColumn) {
|
||||
if (options.inverseJoinColumn.name)
|
||||
this._inverseJoinColumnName = options.inverseJoinColumn.name;
|
||||
if (options.inverseJoinColumn.referencedColumnName)
|
||||
this._inverseJoinColumnReferencedColumnName = options.inverseJoinColumn.referencedColumnName;
|
||||
if (args.inverseJoinColumn) {
|
||||
if (args.inverseJoinColumn.name)
|
||||
this._inverseJoinColumnName = args.inverseJoinColumn.name;
|
||||
if (args.inverseJoinColumn.referencedColumnName)
|
||||
this._inverseJoinColumnReferencedColumnName = args.inverseJoinColumn.referencedColumnName;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +85,8 @@ export class JoinTableMetadata extends PropertyMetadata {
|
||||
return this.relation.entityMetadata.namingStrategy.joinTableName(
|
||||
this.relation.entityMetadata.table.name,
|
||||
this.relation.inverseEntityMetadata.table.name,
|
||||
this.relation.name,
|
||||
this.relation.hasInverseSide ? this.relation.inverseRelation.name : "",
|
||||
this.relation.propertyName,
|
||||
this.relation.hasInverseSide ? this.relation.inverseRelation.propertyName : "",
|
||||
this.referencedColumn.name,
|
||||
this.inverseReferencedColumn.name
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
import {NamingStrategyMetadataArgs} from "../metadata-args/NamingStrategyMetadataArgs";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about naming strategy.
|
||||
@ -18,9 +19,9 @@ export class NamingStrategyMetadata extends TargetMetadata {
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, name: string) {
|
||||
super(target);
|
||||
this.name = name;
|
||||
constructor(args: NamingStrategyMetadataArgs) {
|
||||
super(args.target);
|
||||
this.name = args.name;
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,7 +10,7 @@ export class PropertyMetadata extends TargetMetadata {
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class's property name to which this decorator is applied.
|
||||
* Target's property name to which this metadata is applied.
|
||||
*/
|
||||
readonly propertyName: string;
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {RelationTypes, RelationType} from "./types/RelationTypes";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {EntityMetadata} from "./EntityMetadata";
|
||||
import {OnDeleteType} from "./ForeignKeyMetadata";
|
||||
import {JoinTableMetadata} from "./JoinTableMetadata";
|
||||
import {JoinColumnMetadata} from "./JoinColumnMetadata";
|
||||
import {RelationMetadataArgs} from "./args/RelationMetadataArgs";
|
||||
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);
|
||||
|
||||
|
||||
/**
|
||||
* Contains the name of the property of the object, or the function that returns this name.
|
||||
*/
|
||||
@ -19,7 +19,7 @@ export type PropertyTypeInFunction<T> = string|((t: T) => string|any);
|
||||
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about some document's relation.
|
||||
* Contains all information about some entity's relation.
|
||||
*/
|
||||
export class RelationMetadata extends PropertyMetadata {
|
||||
|
||||
@ -27,11 +27,6 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used to generate and normalize column name.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Its own entity metadata.
|
||||
*/
|
||||
@ -115,11 +110,6 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Column name for this relation.
|
||||
*/
|
||||
private _name: string;
|
||||
|
||||
/**
|
||||
* The type of the field.
|
||||
*/
|
||||
@ -140,8 +130,6 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
|
||||
if (args.inverseSideProperty)
|
||||
this._inverseSideProperty = args.inverseSideProperty;
|
||||
if (args.options.name)
|
||||
this._name = args.options.name;
|
||||
if (args.propertyType)
|
||||
this.propertyType = args.propertyType;
|
||||
if (args.options.cascadeInsert || args.options.cascadeAll)
|
||||
@ -163,87 +151,150 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
|
||||
if (!this._type)
|
||||
this._type = args.type;
|
||||
if (!this._name)
|
||||
this._name = args.propertyName;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets the name of column in the database.
|
||||
* Cannot be used with many-to-many relations since they don't have a column in the database.
|
||||
* Also only owning sides of the relations have this property.
|
||||
*/
|
||||
get name(): string {
|
||||
if (this.joinColumn && this.joinColumn.name)
|
||||
return this.joinColumn.name;
|
||||
if (!this.isOwning || this.isManyToMany)
|
||||
throw new Error(`Relation name cannot be retrieved for many-to-many relations or not owning relations.`);
|
||||
|
||||
return this.namingStrategy ? this.namingStrategy.relationName(this._name) : this._name;
|
||||
return this.joinColumn.name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of column to which this relation is referenced.
|
||||
* Cannot be used with many-to-many relations since all referenced are in the junction table.
|
||||
* Also only owning sides of the relations have this property.
|
||||
*/
|
||||
get referencedColumnName(): string {
|
||||
if (!this.isOwning)
|
||||
throw new Error(`Only owning side of the relations can have information about referenced column names.`);
|
||||
|
||||
// for many-to-one and owner one-to-one relations we get referenced column from join column
|
||||
if (this.joinColumn && this.joinColumn.referencedColumn && this.joinColumn.referencedColumn.name)
|
||||
return this.joinColumn.referencedColumn.name;
|
||||
|
||||
return this.inverseEntityMetadata.primaryColumn.propertyName;
|
||||
// for many-to-many relation we give referenced column depend of owner side
|
||||
if (this.joinTable) { // need to check if this algorithm works correctly
|
||||
if (this.isOwning) {
|
||||
return this.joinTable.referencedColumn.name;
|
||||
} else {
|
||||
return this.joinTable.inverseReferencedColumn.name;
|
||||
}
|
||||
}
|
||||
|
||||
// this should not be possible, but anyway throw error
|
||||
throw new Error(`Cannot get referenced column name of the relation ${this.entityMetadata.name}#${this.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property's type to which this relation is applied.
|
||||
*/
|
||||
get type(): Function {
|
||||
return this._type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation is lazy-load style relation.
|
||||
*/
|
||||
get isLazy(): boolean {
|
||||
return this.propertyType && this.propertyType.name && this.propertyType.name.toLowerCase() === "promise";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this side is an owner of this relation.
|
||||
*/
|
||||
get isOwning() {
|
||||
return this.isManyToOne ||
|
||||
(this.isManyToMany && this.joinTable) ||
|
||||
(this.isOneToOne && this.joinColumn);
|
||||
}
|
||||
|
||||
get type(): Function {
|
||||
return this._type();
|
||||
}
|
||||
|
||||
get inverseSideProperty(): string {
|
||||
|
||||
if (this._inverseSideProperty) {
|
||||
return this.computeInverseSide(this._inverseSideProperty);
|
||||
|
||||
} else if (this.isTreeParent) {
|
||||
return this.entityMetadata.treeChildrenRelation.propertyName;
|
||||
|
||||
} else if (this.isTreeChildren) {
|
||||
return this.entityMetadata.treeParentRelation.propertyName;
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
get inverseRelation(): RelationMetadata {
|
||||
const relation = this.inverseEntityMetadata.findRelationWithPropertyName(this.inverseSideProperty);
|
||||
if (!relation)
|
||||
throw new Error(`Inverse side was not found in the relation ${this.entityMetadata.name}#${this.inverseSideProperty}`);
|
||||
|
||||
return relation;
|
||||
return !!(this.isManyToOne ||
|
||||
(this.isManyToMany && this.joinTable) ||
|
||||
(this.isOneToOne && this.joinColumn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "one-to-one".
|
||||
*/
|
||||
get isOneToOne(): boolean {
|
||||
return this.relationType === RelationTypes.ONE_TO_ONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation is owner side of the "one-to-one" relation.
|
||||
*/
|
||||
get isOneToOneOwner(): boolean {
|
||||
return this.isOneToOne && this.isOwning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation is NOT owner side of the "one-to-one" relation.
|
||||
*/
|
||||
get isOneToOneNotOwner(): boolean {
|
||||
return this.isOneToOne && !this.isOwning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "one-to-many".
|
||||
*/
|
||||
get isOneToMany(): boolean {
|
||||
return this.relationType === RelationTypes.ONE_TO_MANY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "many-to-one".
|
||||
*/
|
||||
get isManyToOne(): boolean {
|
||||
return this.relationType === RelationTypes.MANY_TO_ONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this relation's type is "many-to-many".
|
||||
*/
|
||||
get isManyToMany(): boolean {
|
||||
return this.relationType === RelationTypes.MANY_TO_MANY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if inverse side is specified by a relation.
|
||||
*/
|
||||
get hasInverseSide(): boolean {
|
||||
return this.inverseEntityMetadata && this.inverseEntityMetadata.hasRelationWithPropertyName(this.inverseSideProperty);
|
||||
}
|
||||
|
||||
get isLazy(): boolean {
|
||||
return this.propertyType && this.propertyType.name && this.propertyType.name.toLowerCase() === "promise";
|
||||
|
||||
/**
|
||||
* Gets the property name of the inverse side of the relation.
|
||||
*/
|
||||
get inverseSideProperty(): string {
|
||||
|
||||
if (this._inverseSideProperty) {
|
||||
return this.computeInverseSide(this._inverseSideProperty);
|
||||
|
||||
} else if (this.isTreeParent && this.entityMetadata.hasTreeChildrenRelation) {
|
||||
return this.entityMetadata.treeChildrenRelation.propertyName;
|
||||
|
||||
} else if (this.isTreeChildren && this.entityMetadata.hasTreeParentRelation) {
|
||||
return this.entityMetadata.treeParentRelation.propertyName;
|
||||
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relation metadata of the inverse side of this relation.
|
||||
*/
|
||||
get inverseRelation(): RelationMetadata {
|
||||
const relation = this.inverseEntityMetadata.findRelationWithPropertyName(this.inverseSideProperty);
|
||||
if (!relation)
|
||||
throw new Error(`Inverse side was not found in the relation ${this.entityMetadata.name}#${this.inverseSideProperty}`);
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {RelationsCountMetadataArgs} from "../metadata-args/RelationsCountMetadataArgs";
|
||||
|
||||
/**
|
||||
* Contains all information about entity's relation count.
|
||||
*/
|
||||
export class RelationsCountMetadata extends PropertyMetadata {
|
||||
|
||||
@ -9,7 +11,7 @@ export class RelationsCountMetadata extends PropertyMetadata {
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The real reflected property type.
|
||||
* Relation which need to count.
|
||||
*/
|
||||
readonly relation: string|((object: any) => any);
|
||||
|
||||
@ -17,9 +19,9 @@ export class RelationsCountMetadata extends PropertyMetadata {
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, propertyName: string, relation: string|((object: any) => any)) {
|
||||
super(target, propertyName);
|
||||
this.relation = relation;
|
||||
constructor(args: RelationsCountMetadataArgs) {
|
||||
super(args.target, args.propertyName);
|
||||
this.relation = args.relation;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
import {EntityMetadata} from "./EntityMetadata";
|
||||
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
|
||||
|
||||
/**
|
||||
* Table type.
|
||||
@ -27,35 +28,51 @@ export class TableMetadata extends TargetMetadata {
|
||||
/**
|
||||
* Indicates if this table is abstract or not. Regular tables can inherit columns from abstract tables.
|
||||
*/
|
||||
private readonly tableType: TableType;
|
||||
private readonly tableType: TableType = "regular";
|
||||
|
||||
/**
|
||||
* Table name in the database.
|
||||
*/
|
||||
private readonly _name: string;
|
||||
private readonly _name: string|undefined;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target?: Function, name?: string, type: TableType = "regular") {
|
||||
super(target);
|
||||
|
||||
if (name)
|
||||
constructor(target?: Function, name?: string, type?: TableType);
|
||||
constructor(args: TableMetadataArgs);
|
||||
constructor(argsOrTarget: TableMetadataArgs|Function|undefined, name?: string, type: TableType = "regular") {
|
||||
super(arguments.length === 1 ? (argsOrTarget as TableMetadataArgs).target : argsOrTarget as Function);
|
||||
if (arguments.length === 1) {
|
||||
const metadata = argsOrTarget as TableMetadataArgs;
|
||||
this.tableType = metadata.type;
|
||||
this._name = metadata.name;
|
||||
|
||||
} else {
|
||||
this._name = name;
|
||||
if (type)
|
||||
this.tableType = type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks if this table is abstract.
|
||||
* Table name in the database.
|
||||
*/
|
||||
get isAbstract() {
|
||||
return this.tableType === "abstract";
|
||||
get name() {
|
||||
|
||||
// if custom name is given then use it
|
||||
if (this._name)
|
||||
return this.entityMetadata.namingStrategy.tableNameCustomized(this._name);
|
||||
|
||||
// otherwise use target's table name
|
||||
if (this.target)
|
||||
return this.entityMetadata.namingStrategy.tableName((this.target as any).name);
|
||||
|
||||
// in the case if error
|
||||
throw new Error("Table does not have neither table name neither target specified.");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,35 +82,18 @@ export class TableMetadata extends TargetMetadata {
|
||||
return this.tableType === "regular";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this table is abstract.
|
||||
*/
|
||||
get isAbstract() {
|
||||
return this.tableType === "abstract";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this table is a closure table.
|
||||
*/
|
||||
get isClosure() {
|
||||
return this.tableType === "closure";
|
||||
}
|
||||
|
||||
/**
|
||||
* Table name in the database.
|
||||
*/
|
||||
get name() {
|
||||
if (this._name)
|
||||
return this._name;
|
||||
|
||||
return this.entityMetadata.namingStrategy.tableName((<any>this.target).name);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks if this table is inherited from another table.
|
||||
*/
|
||||
isInherited(anotherTable: TableMetadata) {
|
||||
return Object.getPrototypeOf(this.target.prototype).constructor === anotherTable.target;
|
||||
// we cannot use instanceOf in this method, because we need order of inherited tables, to ensure that
|
||||
// properties get inherited in a right order. To achieve it we can only check a first parent of the class
|
||||
// return this.target.prototype instanceof anotherTable.target;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,10 @@ export abstract class TargetMetadata {
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Target class to which metadata is applied.
|
||||
*/
|
||||
readonly target: Function;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnMode} from "../ColumnMetadata";
|
||||
|
||||
/**
|
||||
* Constructor arguments for ColumnMetadata class.
|
||||
*/
|
||||
export interface ColumnMetadataArgs {
|
||||
|
||||
/**
|
||||
* Class to which this column is applied.
|
||||
*/
|
||||
target?: Function;
|
||||
|
||||
/**
|
||||
* Class's property name to which this column is applied.
|
||||
*/
|
||||
propertyName?: string;
|
||||
|
||||
/**
|
||||
* Class's property type (reflected) to which this column is applied.
|
||||
*/
|
||||
propertyType: string;
|
||||
|
||||
/**
|
||||
* Indicates if this column is primary key or not.
|
||||
*/
|
||||
isPrimaryKey?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if this column is virtual or not.
|
||||
*/
|
||||
isVirtual?: boolean;
|
||||
|
||||
/**
|
||||
* Column mode.
|
||||
*/
|
||||
mode?: ColumnMode;
|
||||
|
||||
/**
|
||||
* Indicates if this column is order id column.
|
||||
*/
|
||||
isOrderId?: boolean;
|
||||
|
||||
/**
|
||||
* Extra column options.
|
||||
*/
|
||||
options: ColumnOptions;
|
||||
}
|
||||
@ -10,15 +10,27 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
return _.snakeCase(className);
|
||||
}
|
||||
|
||||
tableNameCustomized(customName: string): string {
|
||||
return customName;
|
||||
}
|
||||
|
||||
columnName(propertyName: string): string {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
columnNameCustomized(customName: string): string {
|
||||
return customName;
|
||||
}
|
||||
|
||||
relationName(propertyName: string): string {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
indexName(target: Function, name: string, columns: string[]): string {
|
||||
relationNameCustomized(customName: string): string {
|
||||
return customName;
|
||||
}
|
||||
|
||||
indexName(target: Function, name: string|undefined, columns: string[]): string {
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
@ -34,11 +46,11 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
|
||||
joinTableName(firstTableName: string,
|
||||
secondTableName: string,
|
||||
firstColumnName: string,
|
||||
secondColumnName: string,
|
||||
firstPropertyName: string,
|
||||
secondPropertyName: string): string {
|
||||
return _.snakeCase(firstTableName + "_" + firstColumnName + "_" + secondTableName + "_" + secondPropertyName);
|
||||
secondPropertyName: string,
|
||||
firstColumnName: string,
|
||||
secondColumnName: string): string {
|
||||
return _.snakeCase(firstTableName + "_" + firstPropertyName + "_" + secondTableName + "_" + secondColumnName);
|
||||
}
|
||||
|
||||
joinTableColumnName(tableName: string, columnName: string, secondTableName: string, secondColumnName: string): string {
|
||||
@ -56,5 +68,10 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
closureJunctionTableName(tableName: string): string {
|
||||
return tableName + "_closure";
|
||||
}
|
||||
|
||||
foreignKeyName(tableName: string, columnNames: string[], referencedTableName: string, referencedColumnNames: string[]): string {
|
||||
const key = `${tableName}_${columnNames.join("_")}_${referencedTableName}_${referencedColumnNames.join("_")}`;
|
||||
return "fk_" + require("sha1")(key); // todo: use crypto instead?
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,20 +9,35 @@ export interface NamingStrategyInterface {
|
||||
*/
|
||||
tableName(className: string): string;
|
||||
|
||||
/**
|
||||
* Gets the table name from the given custom table name.
|
||||
*/
|
||||
tableNameCustomized(customName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the table's column name from the given property name.
|
||||
*/
|
||||
columnName(propertyName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the column name from the given custom column name.
|
||||
*/
|
||||
columnNameCustomized(customName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the table's relation name from the given property name.
|
||||
*/
|
||||
relationName(propertyName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the relation name from the given custom relation name.
|
||||
*/
|
||||
relationNameCustomized(customName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the name of the index - simple and compose index.
|
||||
*/
|
||||
indexName(target: Function, name: string, columns: string[]): string;
|
||||
indexName(target: Function, name: string|undefined, columns: string[]): string;
|
||||
|
||||
/**
|
||||
* Gets the name of the join column used in the one-to-one and many-to-one relations.
|
||||
@ -33,11 +48,11 @@ export interface NamingStrategyInterface {
|
||||
* Gets the name of the join table used in the many-to-many relations.
|
||||
*/
|
||||
joinTableName(firstTableName: string,
|
||||
secondTableName: string,
|
||||
firstColumnName: string,
|
||||
secondColumnName: string,
|
||||
secondTableName: string,
|
||||
firstPropertyName: string,
|
||||
secondPropertyName: string): string;
|
||||
secondPropertyName: string,
|
||||
firstColumnName: string,
|
||||
secondColumnName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the name of the column used for columns in the junction tables.
|
||||
@ -53,5 +68,10 @@ export interface NamingStrategyInterface {
|
||||
* Gets the name for the closure junction table.
|
||||
*/
|
||||
closureJunctionTableName(tableName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the name of the foreign key.
|
||||
*/
|
||||
foreignKeyName(tableName: string, columnNames: string[], referencedTableName: string, referencedColumnNames: string[]): string;
|
||||
|
||||
}
|
||||
@ -7,7 +7,7 @@ import {JunctionInsertOperation} from "./operation/JunctionInsertOperation";
|
||||
import {UpdateOperation} from "./operation/UpdateOperation";
|
||||
import {CascadesNotAllowedError} from "./error/CascadesNotAllowedError";
|
||||
import {RemoveOperation} from "./operation/RemoveOperation";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {UpdateByInverseSideOperation} from "./operation/UpdateByInverseSideOperation";
|
||||
|
||||
/**
|
||||
|
||||
@ -6,7 +6,7 @@ import {InsertOperation} from "./operation/InsertOperation";
|
||||
import {JunctionRemoveOperation} from "./operation/JunctionRemoveOperation";
|
||||
import {UpdateByRelationOperation} from "./operation/UpdateByRelationOperation";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {Driver} from "../driver/Driver";
|
||||
import {UpdateByInverseSideOperation} from "./operation/UpdateByInverseSideOperation";
|
||||
|
||||
@ -251,9 +251,9 @@ export class PersistOperationExecutor {
|
||||
// const parentLevel = parentEntity ? (parentEntity[metadata.treeLevelColumn.propertyName] || 0) : 0;
|
||||
insertOperation.entity[metadata.treeLevelColumn.propertyName] = insertOperation.treeLevel;
|
||||
}
|
||||
if (metadata.hasTreeChildrenCountColumn) {
|
||||
/*if (metadata.hasTreeChildrenCountColumn) {
|
||||
insertOperation.entity[metadata.treeChildrenCountColumn.propertyName] = 0;
|
||||
}
|
||||
}*/
|
||||
});
|
||||
persistOperation.updates.forEach(updateOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(updateOperation.entity.constructor);
|
||||
@ -439,10 +439,10 @@ export class PersistOperationExecutor {
|
||||
allValues.push(parentLevel + 1);
|
||||
}
|
||||
|
||||
if (metadata.hasTreeChildrenCountColumn) {
|
||||
/*if (metadata.hasTreeChildrenCountColumn) {
|
||||
allColumns.push(metadata.treeChildrenCountColumn.name);
|
||||
allValues.push(0);
|
||||
}
|
||||
}*/
|
||||
|
||||
return this.driver.insert(metadata.table.name, this.zipObject(allColumns, allValues));
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import {Alias} from "./alias/Alias";
|
||||
import {AliasMap} from "./alias/AliasMap";
|
||||
import {RawSqlResultsToEntityTransformer} from "./transformer/RawSqlResultsToEntityTransformer";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {Driver} from "../driver/Driver";
|
||||
|
||||
/**
|
||||
@ -574,11 +574,13 @@ export class QueryBuilder<Entity> {
|
||||
.filter(join => this.selects.indexOf(join.alias.name) !== -1)
|
||||
.forEach(join => {
|
||||
const joinMetadata = this.aliasMap.getEntityMetadataByAlias(join.alias);
|
||||
if (!joinMetadata)
|
||||
throw new Error("Cannot get entity metadata for the given alias " + join.alias.name);
|
||||
joinMetadata.columns.forEach(column => {
|
||||
allSelects.push(join.alias.name + "." + column.name + " AS " + join.alias.name + "_" + column.propertyName);
|
||||
});
|
||||
if (joinMetadata) {
|
||||
joinMetadata.columns.forEach(column => {
|
||||
allSelects.push(join.alias.name + "." + column.name + " AS " + join.alias.name + "_" + column.propertyName);
|
||||
});
|
||||
} else {
|
||||
allSelects.push(join.alias.name);
|
||||
}
|
||||
});
|
||||
|
||||
// add all other selects
|
||||
@ -595,7 +597,7 @@ export class QueryBuilder<Entity> {
|
||||
case "select":
|
||||
return "SELECT " + allSelects.join(", ") + " FROM " + tableName + " " + alias;
|
||||
case "delete":
|
||||
return "DELETE FROM " + tableName + " " + (alias ? alias : "");
|
||||
return "DELETE " + (alias ? alias : "") + " FROM " + tableName + " " + (alias ? alias : "");
|
||||
case "update":
|
||||
const updateSet = Object.keys(this.updateQuerySet).map(key => key + "=:updateQuerySet_" + key);
|
||||
const params = Object.keys(this.updateQuerySet).reduce((object, key) => {
|
||||
@ -633,10 +635,10 @@ export class QueryBuilder<Entity> {
|
||||
const metadata = this.aliasMap.getEntityMetadataByAlias(alias);
|
||||
if (!metadata) return;
|
||||
metadata.columns.forEach(column => {
|
||||
statement = statement.replace(new RegExp(alias.name + "." + column.propertyName, 'g'), alias.name + "." + column.name);
|
||||
statement = statement.replace(new RegExp(alias.name + "." + column.propertyName, "g"), alias.name + "." + column.name);
|
||||
});
|
||||
metadata.relations.forEach(relation => {
|
||||
statement = statement.replace(new RegExp(alias.name + "." + relation.propertyName, 'g'), alias.name + "." + relation.name);
|
||||
metadata.relationsWithJoinColumns.forEach(relation => {
|
||||
statement = statement.replace(new RegExp(alias.name + "." + relation.propertyName, "g"), alias.name + "." + relation.name);
|
||||
});
|
||||
});
|
||||
return statement;
|
||||
@ -775,9 +777,8 @@ export class QueryBuilder<Entity> {
|
||||
if (entityOrProperty instanceof Function) {
|
||||
aliasObj.target = entityOrProperty;
|
||||
|
||||
} else if (typeof entityOrProperty === "string" && entityOrProperty.indexOf(".") !== -1) {
|
||||
aliasObj.parentAliasName = entityOrProperty.split(".")[0];
|
||||
aliasObj.parentPropertyName = entityOrProperty.split(".")[1];
|
||||
} else if (this.isPropertyAlias(entityOrProperty)) {
|
||||
[aliasObj.parentAliasName, aliasObj.parentPropertyName] = entityOrProperty.split(".");
|
||||
|
||||
} else if (typeof entityOrProperty === "string") {
|
||||
tableName = entityOrProperty;
|
||||
@ -791,4 +792,24 @@ export class QueryBuilder<Entity> {
|
||||
return this;
|
||||
}
|
||||
|
||||
private isPropertyAlias(str: any): str is string {
|
||||
if (!(typeof str === "string"))
|
||||
return false;
|
||||
if (str.indexOf(".") === -1)
|
||||
return false;
|
||||
|
||||
const aliasName = str.split(".")[0];
|
||||
const propertyName = str.split(".")[1];
|
||||
|
||||
if (!aliasName || !propertyName)
|
||||
return false;
|
||||
|
||||
const aliasNameRegexp = /^[a-zA-Z0-9_-]+$/;
|
||||
const propertyNameRegexp = aliasNameRegexp;
|
||||
if (!aliasNameRegexp.test(aliasName) || !propertyNameRegexp.test(propertyName))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ export class PlainObjectToDatabaseEntityTransformer<Entity> {
|
||||
transform(object: any, metadata: EntityMetadata, queryBuilder: QueryBuilder<Entity>): Promise<Entity> {
|
||||
|
||||
// if object does not have id then nothing to load really
|
||||
if (!metadata.hasPrimaryKey || !object[metadata.primaryColumn.name])
|
||||
if (!metadata.hasPrimaryColumn || !object[metadata.primaryColumn.name])
|
||||
return Promise.reject<Entity>("Given object does not have a primary column, cannot transform it to database entity.");
|
||||
|
||||
const alias = queryBuilder.alias;
|
||||
|
||||
@ -2,6 +2,7 @@ import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {FindOptions} from "./FindOptions";
|
||||
import {Repository} from "./Repository";
|
||||
import * as Rx from "rxjs/Rx";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
|
||||
/**
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
@ -93,7 +94,7 @@ export class ReactiveRepository<Entity> {
|
||||
find(conditions: Object): Rx.Observable<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities with .
|
||||
* Finds entities that match given find options.
|
||||
*/
|
||||
find(options: FindOptions): Rx.Observable<Entity[]>;
|
||||
|
||||
@ -208,4 +209,72 @@ export class ReactiveRepository<Entity> {
|
||||
return Rx.Observable.fromPromise(this.repository.transaction(runInTransaction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets given relatedEntityId to the value of the relation of the entity with entityId id.
|
||||
* Should be used when you want quickly and efficiently set a relation (for many-to-one and one-to-many) to some entity.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
setRelation(relationName: string, entityId: any, relatedEntityId: any): Rx.Observable<void>;
|
||||
setRelation(relationName: ((t: Entity) => string|any), entityId: any, relatedEntityId: any): Rx.Observable<void>;
|
||||
setRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityId: any): Rx.Observable<void> {
|
||||
return Rx.Observable.fromPromise(this.repository.setRelation(relationName as any, entityId, relatedEntityId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new relation between two entities into relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently add a relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addToRelation(relationName: string, entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addToRelation(relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addToRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void> {
|
||||
return Rx.Observable.fromPromise(this.repository.addToRelation(relationName as any, entityId, relatedEntityIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a relation between two entities from relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeFromRelation(relationName: string, entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
removeFromRelation(relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void>;
|
||||
removeFromRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Rx.Observable<void> {
|
||||
return Rx.Observable.fromPromise(this.repository.removeFromRelation(relationName as any, entityId, relatedEntityIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs both #addToRelation and #removeFromRelation operations.
|
||||
* Should be used when you want quickly and efficiently and and remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addAndRemoveFromRelation(relation: string, entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addAndRemoveFromRelation(relation: ((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Rx.Observable<void>;
|
||||
addAndRemoveFromRelation(relation: string|((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Rx.Observable<void> {
|
||||
return Rx.Observable.fromPromise(this.repository.addAndRemoveFromRelation(relation as any, entityId, addRelatedEntityIds, removeRelatedEntityIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity with the given id.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeById(id: any): Rx.Observable<void> {
|
||||
return Rx.Observable.fromPromise(this.repository.removeById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entities with the given ids.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeByIds(ids: any[]): Rx.Observable<void> {
|
||||
return Rx.Observable.fromPromise(this.repository.removeByIds(ids));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Static Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static ownsMetadata(reactiveRepository: ReactiveRepository<any>, metadata: EntityMetadata) {
|
||||
return Repository.ownsMetadata(reactiveRepository.repository, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import {ReactiveRepository} from "./ReactiveRepository";
|
||||
import {TreeRepository} from "./TreeRepository";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import * as Rx from "rxjs/Rx";
|
||||
|
||||
/**
|
||||
* Tree repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
@ -23,8 +24,8 @@ export class ReactiveTreeRepository<Entity> extends ReactiveRepository<Entity> {
|
||||
/**
|
||||
* Roots are entities that have no ancestors. Finds them all.
|
||||
*/
|
||||
findRoots(): Promise<Entity[]> {
|
||||
return this.repository.findRoots();
|
||||
findRoots(): Rx.Observable<Entity[]> {
|
||||
return Rx.Observable.fromPromise(this.repository.findRoots());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,22 +38,22 @@ export class ReactiveTreeRepository<Entity> extends ReactiveRepository<Entity> {
|
||||
/**
|
||||
* Gets all children (descendants) of the given entity. Returns them all in a flat array.
|
||||
*/
|
||||
findDescendants(entity: Entity): Promise<Entity[]> {
|
||||
return this.repository.findDescendants(entity);
|
||||
findDescendants(entity: Entity): Rx.Observable<Entity[]> {
|
||||
return Rx.Observable.fromPromise(this.repository.findDescendants(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all children (descendants) of the given entity. Returns them in a tree - nested into each other.
|
||||
*/
|
||||
findDescendantsTree(entity: Entity): Promise<Entity> {
|
||||
return this.repository.findDescendantsTree(entity);
|
||||
findDescendantsTree(entity: Entity): Rx.Observable<Entity> {
|
||||
return Rx.Observable.fromPromise(this.repository.findDescendantsTree(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of descendants of the entity.
|
||||
*/
|
||||
countDescendants(entity: Entity): Promise<number> {
|
||||
return this.repository.countDescendants(entity);
|
||||
countDescendants(entity: Entity): Rx.Observable<number> {
|
||||
return Rx.Observable.fromPromise(this.repository.countDescendants(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,28 +66,28 @@ export class ReactiveTreeRepository<Entity> extends ReactiveRepository<Entity> {
|
||||
/**
|
||||
* Gets all parents (ancestors) of the given entity. Returns them all in a flat array.
|
||||
*/
|
||||
findAncestors(entity: Entity): Promise<Entity[]> {
|
||||
return this.repository.findAncestors(entity);
|
||||
findAncestors(entity: Entity): Rx.Observable<Entity[]> {
|
||||
return Rx.Observable.fromPromise(this.repository.findAncestors(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all parents (ancestors) of the given entity. Returns them in a tree - nested into each other.
|
||||
*/
|
||||
findAncestorsTree(entity: Entity): Promise<Entity> {
|
||||
return this.repository.findAncestorsTree(entity);
|
||||
findAncestorsTree(entity: Entity): Rx.Observable<Entity> {
|
||||
return Rx.Observable.fromPromise(this.repository.findAncestorsTree(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of ancestors of the entity.
|
||||
*/
|
||||
countAncestors(entity: Entity): Promise<number> {
|
||||
return this.repository.countAncestors(entity);
|
||||
countAncestors(entity: Entity): Rx.Observable<number> {
|
||||
return Rx.Observable.fromPromise(this.repository.countAncestors(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves entity to the children of then given entity.
|
||||
*
|
||||
move(entity: Entity, to: Entity): Promise<void> {
|
||||
move(entity: Entity, to: Entity): Rx.Observable<void> {
|
||||
return this.repository.move(entity, to);
|
||||
}
|
||||
*/
|
||||
|
||||
@ -7,7 +7,7 @@ import {EntityPersistOperationBuilder} from "../persistment/EntityPersistOperati
|
||||
import {PersistOperationExecutor} from "../persistment/PersistOperationExecutor";
|
||||
import {EntityWithId} from "../persistment/operation/PersistOperation";
|
||||
import {FindOptions, FindOptionsUtils} from "./FindOptions";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {Driver} from "../driver/Driver";
|
||||
|
||||
@ -15,23 +15,23 @@ import {Driver} from "../driver/Driver";
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
*/
|
||||
export class Repository<Entity> {
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private driver: Driver;
|
||||
private broadcaster: Broadcaster;
|
||||
private persistOperationExecutor: PersistOperationExecutor;
|
||||
private entityPersistOperationBuilder: EntityPersistOperationBuilder;
|
||||
private plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer;
|
||||
private plainObjectToDatabaseEntityTransformer: PlainObjectToDatabaseEntityTransformer<Entity>;
|
||||
protected driver: Driver;
|
||||
protected broadcaster: Broadcaster;
|
||||
protected persistOperationExecutor: PersistOperationExecutor;
|
||||
protected entityPersistOperationBuilder: EntityPersistOperationBuilder;
|
||||
protected plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer;
|
||||
protected plainObjectToDatabaseEntityTransformer: PlainObjectToDatabaseEntityTransformer<Entity>;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected connection: Connection,
|
||||
constructor(protected connection: Connection,
|
||||
protected entityMetadatas: EntityMetadataCollection,
|
||||
protected metadata: EntityMetadata) {
|
||||
this.driver = connection.driver;
|
||||
@ -270,6 +270,141 @@ export class Repository<Entity> {
|
||||
.then(() => runInTransactionResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets given relatedEntityId to the value of the relation of the entity with entityId id.
|
||||
* Should be used when you want quickly and efficiently set a relation (for many-to-one and one-to-many) to some entity.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
setRelation(relationName: string, entityId: any, relatedEntityId: any): Promise<void>;
|
||||
setRelation(relationName: ((t: Entity) => string|any), entityId: any, relatedEntityId: any): Promise<void>;
|
||||
setRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityId: any): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
// if (relation.isManyToMany || relation.isOneToMany || relation.isOneToOneNotOwner)
|
||||
// throw new Error(`Only many-to-one and one-to-one with join column are supported for this operation. ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
if (relation.isManyToMany)
|
||||
throw new Error(`Many-to-many relation is not supported for this operation. Use #addToRelation method for many-to-many relations.`);
|
||||
|
||||
let table: string, values: any = {}, conditions: any = {};
|
||||
if (relation.isOwning) {
|
||||
table = relation.entityMetadata.table.name;
|
||||
values[relation.name] = relatedEntityId;
|
||||
conditions[relation.joinColumn.referencedColumn.name] = entityId;
|
||||
} else {
|
||||
table = relation.inverseEntityMetadata.table.name;
|
||||
values[relation.inverseRelation.name] = relatedEntityId;
|
||||
conditions[relation.inverseRelation.joinColumn.referencedColumn.name] = entityId;
|
||||
}
|
||||
return this.driver.update(table, values, conditions).then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new relation between two entities into relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently add a relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addToRelation(relationName: string, entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
addToRelation(relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
addToRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
if (!relation.isManyToMany)
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
|
||||
const insertPromises = relatedEntityIds.map(relatedEntityId => {
|
||||
const values: any = { };
|
||||
if (relation.isOwning) {
|
||||
values[relation.junctionEntityMetadata.columns[0].name] = entityId;
|
||||
values[relation.junctionEntityMetadata.columns[1].name] = relatedEntityId;
|
||||
} else {
|
||||
values[relation.junctionEntityMetadata.columns[1].name] = entityId;
|
||||
values[relation.junctionEntityMetadata.columns[0].name] = relatedEntityId;
|
||||
}
|
||||
|
||||
return this.driver.insert(relation.junctionEntityMetadata.table.name, values);
|
||||
});
|
||||
return Promise.all(insertPromises).then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a relation between two entities from relation's many-to-many table.
|
||||
* Should be used when you want quickly and efficiently remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeFromRelation(relationName: string, entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
removeFromRelation(relationName: ((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void>;
|
||||
removeFromRelation(relationName: string|((t: Entity) => string|any), entityId: any, relatedEntityIds: any[]): Promise<void> {
|
||||
const propertyName = this.metadata.computePropertyName(relationName);
|
||||
if (!this.metadata.hasRelationWithPropertyName(propertyName))
|
||||
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
|
||||
|
||||
const relation = this.metadata.findRelationWithPropertyName(propertyName);
|
||||
if (!relation.isManyToMany)
|
||||
throw new Error(`Only many-to-many relation supported for this operation. However ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
|
||||
|
||||
const qb = this.createQueryBuilder("junctionEntity")
|
||||
.delete(relation.junctionEntityMetadata.table.name);
|
||||
|
||||
const firstColumnName = relation.isOwning ? relation.junctionEntityMetadata.columns[0].name : relation.junctionEntityMetadata.columns[1].name;
|
||||
const secondColumnName = relation.isOwning ? relation.junctionEntityMetadata.columns[1].name : relation.junctionEntityMetadata.columns[0].name;
|
||||
|
||||
relatedEntityIds.forEach((relatedEntityId, index) => {
|
||||
qb.orWhere(`(${firstColumnName}=:entityId AND ${secondColumnName}=:relatedEntity_${index})`)
|
||||
.setParameter("relatedEntity_" + index, relatedEntityId);
|
||||
});
|
||||
|
||||
return qb
|
||||
.setParameter("entityId", entityId)
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs both #addToRelation and #removeFromRelation operations.
|
||||
* Should be used when you want quickly and efficiently and and remove a many-to-many relation between two entities.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
addAndRemoveFromRelation(relation: string, entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Promise<void>;
|
||||
addAndRemoveFromRelation(relation: ((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Promise<void>;
|
||||
addAndRemoveFromRelation(relation: string|((t: Entity) => string|any), entityId: any, addRelatedEntityIds: any[], removeRelatedEntityIds: any[]): Promise<void> {
|
||||
return Promise.all([
|
||||
this.addToRelation(relation as any, entityId, addRelatedEntityIds),
|
||||
this.removeFromRelation(relation as any, entityId, removeRelatedEntityIds)
|
||||
]).then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity with the given id.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeById(id: any) {
|
||||
const alias = this.metadata.table.name;
|
||||
return this.createQueryBuilder(alias)
|
||||
.delete()
|
||||
.where(alias + "." + this.metadata.primaryColumn.propertyName + "=:id", { id: id })
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entities with the given ids.
|
||||
* Note that event listeners and event subscribers won't work (and will not send any events) when using this operation.
|
||||
*/
|
||||
removeByIds(ids: any[]) {
|
||||
const alias = this.metadata.table.name;
|
||||
return this.createQueryBuilder(alias)
|
||||
.delete()
|
||||
.where(alias + "." + this.metadata.primaryColumn.propertyName + " IN (:ids)", { ids: ids })
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
@ -412,4 +547,12 @@ export class Repository<Entity> {
|
||||
return entity;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Static Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static ownsMetadata(repository: Repository<any>, metadata: EntityMetadata) {
|
||||
return repository.metadata === metadata;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user