first interation of refactoring of metadata, metadata storage and builder

This commit is contained in:
Umed Khudoiberdiev 2016-04-28 14:38:00 +05:00
parent 30775c3820
commit c43c2faa43
51 changed files with 586 additions and 406 deletions

View File

@ -793,6 +793,7 @@ Feel free to contribute ;)
* fixtures and migrations
* lazy loading of properties throw promises? Property can have a Promise\<Entity\> type.
* cover everything with tests
* logging
[1]: https://en.wikipedia.org/wiki/Object-relational_mapping

View File

@ -0,0 +1,83 @@
import {createConnection, CreateConnectionOptions} from "../../src/typeorm";
import {Post} from "./entity/Post";
import {PostCategory} from "./entity/PostCategory";
import {PostAuthor} from "./entity/PostAuthor";
import {Blog} from "./entity/Blog";
const options: CreateConnectionOptions = {
driver: "mysql",
connection: {
host: "192.168.99.100",
port: 3306,
username: "root",
password: "admin",
database: "test",
autoSchemaCreate: true,
logging: {
logOnlyFailedQueries: true,
logFailedQueryError: true
}
},
entityDirectories: [__dirname + "/entity"]
};
createConnection(options).then(connection => {
let category1 = new PostCategory();
category1.name = "post category #1";
let category2 = new PostCategory();
category2.name = "post category #2";
let author = new PostAuthor();
author.name = "Umed";
author.firstName = "Uma";
author.secondName = "Edi";
let post = new Post();
post.text = "Hello how are you?";
post.title = "hello";
post.author = author;
post.title2312312 = "awesome title!";
post.categories.push(category1, category2);
/*category1 = new PostCategory();
category1.name = "post category #1";
category2 = new PostCategory();
category2.name = "post category #2";
author = new PostAuthor();
author.name = "Umed";*/
let blog = new Blog();
blog.text = "Hello how are you?";
blog.title = "hello";
blog.author = author;
blog.title2312312 = "awesome title!";
blog.categories.push(category1, category2);
let postRepository = connection.getRepository(Post);
let blogRepository = connection.getRepository(Blog);
postRepository
.persist(post)
.then(post => {
console.log("Post has been saved");
return postRepository.findById(post.id);
})
.then(loadedPost => {
console.log("post is loaded: ", loadedPost);
return blogRepository.persist(blog);
})
.then(blog => {
console.log("Blog has been saved");
return blogRepository.findById(blog.id);
})
.then(loadedBlog => {
console.log("blog is loaded: ", loadedBlog);
return blogRepository.persist(blog);
})
.catch(error => console.log("Cannot save. Error: ", error.stack ? error.stack : error));
}, error => console.log("Cannot connect: ", error.stack ? error.stack : error));

View File

@ -0,0 +1,14 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {AbstractTable} from "../../../src/decorator/tables/AbstractTable";
import {BasePost} from "./BasePost";
@AbstractTable()
export class BaseObject extends BasePost {
@PrimaryColumn("double", { generated: true })
id: number;
@Column()
title: string;
}

View File

@ -0,0 +1,16 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {AbstractTable} from "../../../src/decorator/tables/AbstractTable";
@AbstractTable()
export class BasePost {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
title: string;
@Column()
title2312312: string;
}

View File

@ -0,0 +1,31 @@
import {Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {ManyToOne} from "../../../src/decorator/relations/ManyToOne";
import {PostAuthor} from "./PostAuthor";
import {ManyToMany} from "../../../src/decorator/relations/ManyToMany";
import {PostCategory} from "./PostCategory";
import {JoinTable} from "../../../src/decorator/relations/JoinTable";
import {BaseObject} from "./BaseObject";
@Table("sample13_blog")
export class Blog extends BaseObject {
@Column()
text: string;
@ManyToOne(type => PostAuthor, post => post.posts, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
author: PostAuthor;
@ManyToMany(type => PostCategory, category => category.posts, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
@JoinTable()
categories: PostCategory[] = [];
}

View File

@ -0,0 +1,31 @@
import {Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {PostCategory} from "./PostCategory";
import {ManyToMany} from "../../../src/decorator/relations/ManyToMany";
import {PostAuthor} from "./PostAuthor";
import {ManyToOne} from "../../../src/decorator/relations/ManyToOne";
import {JoinTable} from "../../../src/decorator/relations/JoinTable";
import {BaseObject} from "./BaseObject";
@Table("sample13_post")
export class Post extends BaseObject {
@Column()
text: string;
@ManyToOne(type => PostAuthor, post => post.posts, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
author: PostAuthor;
@ManyToMany(type => PostCategory, category => category.posts, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
@JoinTable()
categories: PostCategory[] = [];
}

View File

@ -0,0 +1,19 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {Post} from "./Post";
import {OneToMany} from "../../../src/decorator/relations/OneToMany";
import {PostUser} from "./PostUser";
@Table("sample13_post_author")
export class PostAuthor extends PostUser {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
name: string;
@OneToMany(type => Post, post => post.author)
posts: Post[];
}

View File

@ -0,0 +1,22 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {Post} from "./Post";
import {ManyToMany} from "../../../src/decorator/relations/ManyToMany";
@Table("sample13_post_category")
export class PostCategory {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
name: string;
@ManyToMany(type => Post, post => post.categories, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: true
})
posts: Post[] = [];
}

View File

@ -0,0 +1,19 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
@Table("sample13_post_user")
export class PostUser {
@PrimaryColumn("int", { generated: true })
id: number;
@Column("int")
name: string;
@Column()
firstName: string;
@Column()
secondName: string;
}

View File

@ -1,9 +1,4 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {ManyToMany} from "../../../src/relations";
import {PostCategory} from "./PostCategory";
import {PostAuthor} from "./PostAuthor";
import {ManyToOne} from "../../../src/decorator/relations/ManyToOne";
import {AbstractTable} from "../../../src/decorator/tables/AbstractTable";
@AbstractTable()

View File

@ -12,7 +12,7 @@ import {importClassesFromDirectories} from "../util/DirectoryExportedClassesLoad
import {defaultMetadataStorage, getContainer} from "../typeorm";
import {EntityMetadataBuilder} from "../metadata-storage/EntityMetadataBuilder";
import {DefaultNamingStrategy} from "../naming-strategy/DefaultNamingStrategy";
import {EntityMetadataArray} from "../metadata/EntityMetadataArray";
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
import {NamingStrategyMetadata} from "../metadata/NamingStrategyMetadata";
import {NoConnectionForRepositoryError} from "./error/NoConnectionForRepositoryError";
import {CannotImportAlreadyConnectedError} from "./error/CannotImportAlreadyConnectedError";
@ -76,7 +76,7 @@ export class Connection {
/**
* All entity metadatas that are registered for this connection.
*/
private readonly entityMetadatas = new EntityMetadataArray();
private readonly entityMetadatas = new EntityMetadataCollection();
/**
* All naming strategy metadatas that are registered for this connection.
@ -255,19 +255,20 @@ export class Connection {
private buildMetadatas() {
// first register naming strategies
const metadatas = defaultMetadataStorage().findNamingStrategiesForClasses(this.namingStrategyClasses);
const metadatas = defaultMetadataStorage().namingStrategyMetadatas.filterByClasses(this.namingStrategyClasses);
this.namingStrategyMetadatas.push(...metadatas);
// second register subscriber metadatas
const subscribers = defaultMetadataStorage()
.findEventSubscribersForClasses(this.subscriberClasses)
.eventSubscriberMetadatas
.filterByClasses(this.subscriberClasses)
.map(metadata => this.createContainerInstance(metadata.target));
this.subscriberMetadatas.push(...subscribers);
// third register entity and entity listener metadatas
const entityMetadataBuilder = new EntityMetadataBuilder(this.createNamingStrategy());
const entityMetadatas = entityMetadataBuilder.build(this.entityClasses);
const entityListenerMetadatas = defaultMetadataStorage().findEntityListenersForClasses(this.entityClasses);
const entityListenerMetadatas = defaultMetadataStorage().entityListenerMetadatas.filterByClasses(this.entityClasses);
this.entityMetadatas.push(...entityMetadatas);
this.entityListeners.push(...entityListenerMetadatas);

View File

@ -8,6 +8,6 @@ import {defaultMetadataStorage} from "../typeorm";
export function NamingStrategy(name?: string): Function {
return function (target: Function) {
const strategyName = name ? name : (<any> target).name;
defaultMetadataStorage().addNamingStrategyMetadata(new NamingStrategyMetadata(target, strategyName));
defaultMetadataStorage().namingStrategyMetadatas.add(new NamingStrategyMetadata(target, strategyName));
};
}

View File

@ -53,7 +53,7 @@ export function Column(typeOrOptions?: ColumnType|ColumnOptions, options?: Colum
throw new AutoIncrementOnlyForPrimaryError(object, propertyName);
// create and register a new column metadata
defaultMetadataStorage().addColumnMetadata(new ColumnMetadata({
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
target: object.constructor,
propertyName: propertyName,
propertyType: reflectedType,

View File

@ -20,7 +20,7 @@ export function CreateDateColumn(options?: ColumnOptions): Function {
options.type = ColumnTypes.DATETIME;
// create and register a new column metadata
defaultMetadataStorage().addColumnMetadata(new ColumnMetadata({
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
target: object.constructor,
propertyName: propertyName,
propertyType: reflectedType,

View File

@ -56,7 +56,7 @@ export function PrimaryColumn(typeOrOptions?: ColumnType|ColumnOptions, options?
throw new PrimaryColumnCannotBeNullableError(object, propertyName);
// create and register a new column metadata
defaultMetadataStorage().addColumnMetadata(new ColumnMetadata({
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
target: object.constructor,
propertyName: propertyName,
propertyType: reflectedType,

View File

@ -20,7 +20,7 @@ export function UpdateDateColumn(options?: ColumnOptions): Function {
options.type = ColumnTypes.DATETIME;
// create and register a new column metadata
defaultMetadataStorage().addColumnMetadata(new ColumnMetadata({
defaultMetadataStorage().columnMetadatas.add(new ColumnMetadata({
target: object.constructor,
propertyName: propertyName,
propertyType: reflectedType,

View File

@ -6,6 +6,6 @@ import {defaultMetadataStorage} from "../../typeorm";
*/
export function CompoundIndex(fields: string[]) {
return function (cls: Function) {
defaultMetadataStorage().addCompoundIndexMetadata(new CompoundIndexMetadata(cls, fields));
defaultMetadataStorage().compoundIndexMetadatas.add(new CompoundIndexMetadata(cls, fields));
};
}

View File

@ -6,6 +6,6 @@ import {IndexMetadata} from "../../metadata/IndexMetadata";
*/
export function Index(name?: string) {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addIndexMetadata(new IndexMetadata(object.constructor, propertyName, name));
defaultMetadataStorage().indexMetadatas.add(new IndexMetadata(object.constructor, propertyName, name));
};
}

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function AfterInsert() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.AFTER_INSERT

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function AfterLoad() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.AFTER_LOAD

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function AfterRemove() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.AFTER_REMOVE

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function AfterUpdate() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.AFTER_UPDATE

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function BeforeInsert() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.BEFORE_INSERT

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function BeforeRemove() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.BEFORE_REMOVE

View File

@ -7,7 +7,7 @@ import {EntityListenerMetadata} from "../../metadata/EntityListenerMetadata";
*/
export function BeforeUpdate() {
return function (object: Object, propertyName: string) {
defaultMetadataStorage().addEntityListenerMetadata(new EntityListenerMetadata(
defaultMetadataStorage().entityListenerMetadatas.add(new EntityListenerMetadata(
object.constructor,
propertyName,
EventListenerTypes.BEFORE_UPDATE

View File

@ -7,6 +7,6 @@ import {EventSubscriberMetadata} from "../../metadata/EventSubscriberMetadata";
*/
export function EventSubscriber() {
return function (target: Function) {
defaultMetadataStorage().addEventSubscriberMetadata(new EventSubscriberMetadata(target));
defaultMetadataStorage().eventSubscriberMetadatas.add(new EventSubscriberMetadata(target));
};
}

View File

@ -1,14 +1,14 @@
import {defaultMetadataStorage} from "../../typeorm";
import {JoinTableOptions} from "../../metadata/options/JoinTableOptions";
import {JoinColumnMetadata} from "../../metadata/JoinColumnMetadata";
import {JoinColumnOptions} from "../../metadata/options/JoinColumnOptions";
/**
*/
export function JoinColumn(options?: JoinTableOptions): Function {
export function JoinColumn(options?: JoinColumnOptions): Function {
return function (object: Object, propertyName: string) {
options = options || {} as JoinTableOptions;
options = options || {} as JoinColumnOptions;
const metadata = new JoinColumnMetadata(object.constructor, propertyName, options);
defaultMetadataStorage().addJoinColumnMetadata(metadata);
defaultMetadataStorage().joinColumnMetadatas.add(metadata);
};
}

View File

@ -8,7 +8,7 @@ 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().addJoinTableMetadata(metadata);
defaultMetadataStorage().joinTableMetadatas.add(metadata);
};
}

View File

@ -38,7 +38,7 @@ export function ManyToMany<T>(typeFunction: (type?: any) => ConstructorFunction<
return function (object: Object, propertyName: string) {
if (!options) options = {} as RelationOptions;
defaultMetadataStorage().addRelationMetadata(new RelationMetadata({
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
target: object.constructor,
propertyName: propertyName,
relationType: RelationTypes.MANY_TO_MANY,

View File

@ -38,7 +38,7 @@ export function ManyToOne<T>(typeFunction: (type?: any) => ConstructorFunction<T
return function (object: Object, propertyName: string) {
if (!options) options = {} as RelationOptions;
defaultMetadataStorage().addRelationMetadata(new RelationMetadata({
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
target: object.constructor,
propertyName: propertyName,
relationType: RelationTypes.MANY_TO_ONE,

View File

@ -35,7 +35,7 @@ export function OneToMany<T>(typeFunction: (type?: any) => ConstructorFunction<T
return function (object: Object, propertyName: string) {
if (!options) options = {} as RelationOptions;
defaultMetadataStorage().addRelationMetadata(new RelationMetadata({
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
target: object.constructor,
propertyName: propertyName,
relationType: RelationTypes.ONE_TO_MANY,

View File

@ -35,7 +35,7 @@ export function OneToOne<T>(typeFunction: (type?: any) => ConstructorFunction<T>
return function (object: Object, propertyName: string) {
if (!options) options = {} as RelationOptions;
defaultMetadataStorage().addRelationMetadata(new RelationMetadata({
defaultMetadataStorage().relationMetadatas.add(new RelationMetadata({
target: object.constructor,
propertyName: propertyName,
relationType: RelationTypes.ONE_TO_ONE,

View File

@ -6,6 +6,6 @@ import {defaultMetadataStorage} from "../../typeorm";
*/
export function AbstractTable() {
return function (cls: Function) {
defaultMetadataStorage().addTableMetadata(new TableMetadata(cls, true));
defaultMetadataStorage().tableMetadatas.add(new TableMetadata(cls, true));
};
}

View File

@ -7,6 +7,6 @@ import {TableMetadata} from "../../metadata/TableMetadata";
*/
export function Table(name?: string) {
return function (cls: Function) {
defaultMetadataStorage().addTableMetadata(new TableMetadata(cls, name));
defaultMetadataStorage().tableMetadatas.add(new TableMetadata(cls, name));
};
}

View File

@ -1,6 +1,5 @@
import {MetadataStorage} from "./MetadataStorage";
import {PropertyMetadata} from "../metadata/PropertyMetadata";
import {TableMetadata} from "../metadata/TableMetadata";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategy";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
@ -8,6 +7,8 @@ import {ColumnOptions} from "../metadata/options/ColumnOptions";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {JunctionTableMetadata} from "../metadata/JunctionTableMetadata";
import {defaultMetadataStorage} from "../typeorm";
import {TableMetadata} from "../metadata/TableMetadata";
import {TargetMetadataCollection} from "../metadata/collection/TargetMetadataCollection";
/**
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
@ -17,6 +18,9 @@ import {defaultMetadataStorage} from "../typeorm";
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?
private metadataStorage: MetadataStorage = defaultMetadataStorage();
@ -35,70 +39,50 @@ export class EntityMetadataBuilder {
* Builds a complete metadata aggregations for the given entity classes.
*/
build(entityClasses: Function[]): EntityMetadata[] {
const allMetadataStorage = defaultMetadataStorage();
// filter the metadata only we need - those which are bind to the given table classes
const tableMetadatas = this.metadataStorage.findTableMetadatasForClasses(entityClasses);
const abstractTableMetadatas = this.metadataStorage.findAbstractTableMetadatasForClasses(entityClasses);
const columnMetadatas = this.metadataStorage.findFieldMetadatasForClasses(entityClasses);
const relationMetadatas = this.metadataStorage.findRelationMetadatasForClasses(entityClasses);
const joinTableMetadatas = this.metadataStorage.findJoinTableMetadatasForClasses(entityClasses);
const joinColumnMetadatas = this.metadataStorage.findJoinColumnMetadatasForClasses(entityClasses);
const indexMetadatas = this.metadataStorage.findIndexMetadatasForClasses(entityClasses);
const compoundIndexMetadatas = this.metadataStorage.findCompoundIndexMetadatasForClasses(entityClasses);
const allTableMetadatas = allMetadataStorage.tableMetadatas.filterByClasses(entityClasses);
const tableMetadatas = allTableMetadatas.filterByClasses(entityClasses).filter(table => !table.isAbstract);
// const abstractTableMetadatas = allTableMetadatas.filterByClasses(entityClasses).filter(table => table.isAbstract);
const entityMetadatas = tableMetadatas.map(tableMetadata => {
const mergedMetadata = allMetadataStorage.mergeWithAbstract(allTableMetadatas, tableMetadata);
const constructorChecker = (opm: PropertyMetadata) => opm.target === tableMetadata.target;
const constructorChecker2 = (opm: { target: Function }) => opm.target === tableMetadata.target;
let entityColumns = columnMetadatas.filter(constructorChecker);
let entityRelations = relationMetadatas.filter(constructorChecker);
let entityCompoundIndices = compoundIndexMetadatas.filter(constructorChecker2);
let entityIndices = indexMetadatas.filter(constructorChecker);
let entityJoinTables = joinTableMetadatas.filter(constructorChecker);
let entityJoinColumns = joinColumnMetadatas.filter(constructorChecker);
// merge all columns in the abstract table extendings of this table
abstractTableMetadatas.forEach(abstractMetadata => {
if (!this.isTableMetadataExtendsAbstractMetadata(tableMetadata, abstractMetadata)) return;
const constructorChecker = (opm: PropertyMetadata) => opm.target === abstractMetadata.target;
const constructorChecker2 = (opm: { target: Function }) => opm.target === abstractMetadata.target;
const abstractColumns = columnMetadatas.filter(constructorChecker);
const abstractRelations = entityRelations.filter(constructorChecker);
const abstractCompoundIndices = entityCompoundIndices.filter(constructorChecker2);
const abstractIndices = indexMetadatas.filter(constructorChecker);
const inheritedFields = this.filterObjectPropertyMetadatasIfNotExist(abstractColumns, entityColumns);
const inheritedRelations = this.filterObjectPropertyMetadatasIfNotExist(abstractRelations, entityRelations);
const inheritedIndices = this.filterObjectPropertyMetadatasIfNotExist(abstractIndices, entityIndices);
entityCompoundIndices = entityCompoundIndices.concat(abstractCompoundIndices);
entityColumns = entityColumns.concat(inheritedFields);
entityRelations = entityRelations.concat(inheritedRelations);
entityIndices = entityIndices.concat(inheritedIndices);
});
const entityMetadata = new EntityMetadata(tableMetadata, entityColumns, entityRelations, entityIndices, entityCompoundIndices, []);
const entityMetadata = new EntityMetadata(
tableMetadata,
mergedMetadata.columnMetadatas,
mergedMetadata.relationMetadatas,
mergedMetadata.indexMetadatas,
mergedMetadata.compoundIndexMetadatas,
[]
);
// find entity's relations join tables
entityRelations.forEach(relation => {
const relationJoinTable = entityJoinTables.find(joinTable => joinTable.propertyName === relation.propertyName);
entityMetadata.relations.forEach(relation => {
const relationJoinTable = mergedMetadata.joinTableMetadatas.find(joinTable => joinTable.propertyName === relation.propertyName);
if (relationJoinTable)
relation.joinTable = relationJoinTable;
});
// find entity's relations join columns
entityRelations.forEach(relation => {
const relationJoinColumn = entityJoinColumns.find(joinColumn => joinColumn.propertyName === relation.propertyName);
entityMetadata.relations.forEach(relation => {
const relationJoinColumn = mergedMetadata.joinColumnMetadatas.find(joinColumn => joinColumn.propertyName === relation.propertyName);
if (relationJoinColumn)
relation.joinColumn = relationJoinColumn;
});
// set naming strategies
tableMetadata.namingStrategy = this.namingStrategy;
entityColumns.forEach(column => column.namingStrategy = this.namingStrategy);
entityRelations.forEach(relation => relation.namingStrategy = this.namingStrategy);
entityMetadata.columns.forEach(column => column.namingStrategy = this.namingStrategy);
entityMetadata.relations.forEach(relation => relation.namingStrategy = this.namingStrategy);
// check if table metadata has an id
if (!entityMetadata.primaryColumn)
throw new Error(`Entity "${entityMetadata.name}" (table "${tableMetadata.name}") does not have a primary column. Primary column is required to have in all your entities. Use @PrimaryColumn decorator to add a primary column to your entity.`);
return entityMetadata;
});
@ -128,9 +112,9 @@ export class EntityMetadataBuilder {
}
// create and add foreign key
const foreignKey = new ForeignKeyMetadata(metadata.table,
[relationalColumn],
inverseSideMetadata.table,
const foreignKey = new ForeignKeyMetadata(metadata.table,
[relationalColumn],
inverseSideMetadata.table,
[inverseSideMetadata.primaryColumn],
relation.onDelete
);
@ -153,6 +137,7 @@ export class EntityMetadataBuilder {
const tableName = metadata.table.name + "_" + relation.name + "_" +
inverseSideMetadata.table.name + "_" + inverseSideMetadata.primaryColumn.name;
const tableMetadata = new JunctionTableMetadata(tableName);
const column1options: ColumnOptions = {
length: metadata.primaryColumn.length,
@ -193,14 +178,9 @@ export class EntityMetadataBuilder {
// Private Methods
// -------------------------------------------------------------------------
private isTableMetadataExtendsAbstractMetadata(tableMetadata: TableMetadata, abstractMetadata: TableMetadata): boolean {
return tableMetadata.target.prototype instanceof abstractMetadata.target;
}
private filterObjectPropertyMetadatasIfNotExist<T extends PropertyMetadata>(newMetadatas: T[], existsMetadatas: T[]): T[] {
private filterRepeatedMetadatas<T extends PropertyMetadata>(newMetadatas: T[], existsMetadatas: T[]): T[] {
return newMetadatas.filter(fieldFromMapped => {
return !!existsMetadatas.find(fieldFromDocument => fieldFromDocument.propertyName === fieldFromMapped.propertyName);
});
}
}

View File

@ -1,6 +1,4 @@
import {TableMetadata} from "../metadata/TableMetadata";
import {MetadataAlreadyExistsError} from "./error/MetadataAlreadyExistsError";
import {MetadataWithSuchNameAlreadyExistsError} from "./error/MetadataWithSuchNameAlreadyExistsError";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {CompoundIndexMetadata} from "../metadata/CompoundIndexMetadata";
@ -10,6 +8,8 @@ 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";
/**
* Storage all metadatas of all available types: tables, fields, subscribers, relations, etc.
@ -17,191 +17,103 @@ import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
*/
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
// -------------------------------------------------------------------------
private tableMetadatas: TableMetadata[] = [];
private eventSubscriberMetadatas: EventSubscriberMetadata[] = [];
private columnMetadatas: ColumnMetadata[] = [];
private indexMetadatas: IndexMetadata[] = [];
private entityListenerMetadatas: EntityListenerMetadata[] = [];
private compoundIndexMetadatas: CompoundIndexMetadata[] = [];
private namingStrategyMetadatas: NamingStrategyMetadata[] = [];
private relationMetadatas: RelationMetadata[] = [];
private joinColumnMetadatas: JoinColumnMetadata[] = [];
private joinTableMetadatas: JoinTableMetadata[] = [];
readonly tableMetadatas = new TargetMetadataCollection<TableMetadata>();
readonly namingStrategyMetadatas = new TargetMetadataCollection<NamingStrategyMetadata>();
readonly eventSubscriberMetadatas = new TargetMetadataCollection<EventSubscriberMetadata>();
readonly compoundIndexMetadatas = new TargetMetadataCollection<CompoundIndexMetadata>();
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>();
// -------------------------------------------------------------------------
// Adder Methods
// Constructor
// -------------------------------------------------------------------------
addTableMetadata(metadata: TableMetadata) {
if (this.hasTableMetadataWithObjectConstructor(metadata.target))
throw new MetadataAlreadyExistsError("Table", metadata.target);
if (metadata.name && this.hasTableMetadataWithName(metadata.name))
throw new MetadataWithSuchNameAlreadyExistsError("Table", metadata.name);
this.tableMetadatas.push(metadata);
constructor(tableMetadatas?: TargetMetadataCollection<TableMetadata>,
namingStrategyMetadatas?: TargetMetadataCollection<NamingStrategyMetadata>,
eventSubscriberMetadatas?: TargetMetadataCollection<EventSubscriberMetadata>,
compoundIndexMetadatas?: TargetMetadataCollection<CompoundIndexMetadata>,
columnMetadatas?: PropertyMetadataCollection<ColumnMetadata>,
relationMetadatas?: PropertyMetadataCollection<RelationMetadata>,
joinColumnMetadatas?: PropertyMetadataCollection<JoinColumnMetadata>,
joinTableMetadatas?: PropertyMetadataCollection<JoinTableMetadata>,
indexMetadatas?: PropertyMetadataCollection<IndexMetadata>,
entityListenerMetadatas?: PropertyMetadataCollection<EntityListenerMetadata>) {
if (tableMetadatas)
this.tableMetadatas = tableMetadatas;
if (namingStrategyMetadatas)
this.namingStrategyMetadatas = namingStrategyMetadatas;
if (eventSubscriberMetadatas)
this.eventSubscriberMetadatas = eventSubscriberMetadatas;
if (compoundIndexMetadatas)
this.compoundIndexMetadatas = compoundIndexMetadatas;
if (columnMetadatas)
this.columnMetadatas = columnMetadatas;
if (relationMetadatas)
this.relationMetadatas = relationMetadatas;
if (joinColumnMetadatas)
this.joinColumnMetadatas = joinColumnMetadatas;
if (joinTableMetadatas)
this.joinTableMetadatas = joinTableMetadatas;
if (indexMetadatas)
this.indexMetadatas = indexMetadatas;
if (entityListenerMetadatas)
this.entityListenerMetadatas = entityListenerMetadatas;
}
addRelationMetadata(metadata: RelationMetadata) {
if (this.hasRelationWithOneMetadataOnProperty(metadata.target, metadata.propertyName))
throw new MetadataAlreadyExistsError("RelationMetadata", metadata.target, metadata.propertyName);
if (metadata.name && this.hasRelationWithOneMetadataWithName(metadata.target, metadata.name))
throw new MetadataWithSuchNameAlreadyExistsError("RelationMetadata", metadata.name);
this.relationMetadatas.push(metadata);
}
addColumnMetadata(metadata: ColumnMetadata) {
if (this.hasFieldMetadataOnProperty(metadata.target, metadata.propertyName))
throw new MetadataAlreadyExistsError("Column", metadata.target);
if (metadata.name && this.hasFieldMetadataWithName(metadata.target, metadata.name))
throw new MetadataWithSuchNameAlreadyExistsError("Column", metadata.name);
this.columnMetadatas.push(metadata);
}
addEventSubscriberMetadata(metadata: EventSubscriberMetadata) {
if (this.hasEventSubscriberWithObjectConstructor(metadata.target))
throw new MetadataAlreadyExistsError("EventSubscriber", metadata.target);
this.eventSubscriberMetadatas.push(metadata);
}
addIndexMetadata(metadata: IndexMetadata) {
if (this.hasFieldMetadataOnProperty(metadata.target, metadata.propertyName))
throw new MetadataAlreadyExistsError("Index", metadata.target);
if (metadata.name && this.hasFieldMetadataWithName(metadata.target, metadata.name))
throw new MetadataWithSuchNameAlreadyExistsError("Index", metadata.name);
this.indexMetadatas.push(metadata);
}
addCompoundIndexMetadata(metadata: CompoundIndexMetadata) {
if (this.hasCompoundIndexMetadataWithObjectConstructor(metadata.target))
throw new MetadataAlreadyExistsError("CompoundIndex", metadata.target);
this.compoundIndexMetadatas.push(metadata);
}
addNamingStrategyMetadata(metadata: NamingStrategyMetadata) {
if (this.hasNamingStrategyMetadataWithObjectConstructor(metadata.target))
throw new MetadataAlreadyExistsError("NamingStrategy", metadata.target);
this.namingStrategyMetadatas.push(metadata);
}
addEntityListenerMetadata(metadata: EntityListenerMetadata) {
if (this.hasFieldMetadataOnProperty(metadata.target, metadata.propertyName))
throw new MetadataAlreadyExistsError("EventListener", metadata.target);
this.entityListenerMetadatas.push(metadata);
}
addJoinTableMetadata(metadata: JoinTableMetadata) {
// if (this.hasFieldMetadataOnProperty(metadata.target, metadata.propertyName)) // todo later
// throw new MetadataAlreadyExistsError("EventListener", metadata.target);
this.joinTableMetadatas.push(metadata);
}
addJoinColumnMetadata(metadata: JoinColumnMetadata) {
// if (this.hasFieldMetadataOnProperty(metadata.target, metadata.propertyName)) // todo later
// throw new MetadataAlreadyExistsError("EventListener", metadata.target);
this.joinColumnMetadatas.push(metadata);
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
findEventSubscribersForClasses(classes: Function[]): EventSubscriberMetadata[] {
return this.eventSubscriberMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
/**
* 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 compoundIndexMetadatas = this.compoundIndexMetadatas.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);
allTableMetadatas
.filter(metadata => tableMetadata.isInherited(metadata))
.forEach(parentMetadata => {
const metadatasFromAbstract = this.mergeWithAbstract(allTableMetadatas, parentMetadata);
columnMetadatas.push(...metadatasFromAbstract.columnMetadatas.filterRepeatedMetadatas(columnMetadatas));
relationMetadatas.push(...metadatasFromAbstract.relationMetadatas.filterRepeatedMetadatas(relationMetadatas));
joinColumnMetadatas.push(...metadatasFromAbstract.joinColumnMetadatas.filterRepeatedMetadatas(joinColumnMetadatas));
joinTableMetadatas.push(...metadatasFromAbstract.joinTableMetadatas.filterRepeatedMetadatas(joinTableMetadatas));
indexMetadatas.push(...metadatasFromAbstract.indexMetadatas.filterRepeatedMetadatas(indexMetadatas));
entityListenerMetadatas.push(...metadatasFromAbstract.entityListenerMetadatas.filterRepeatedMetadatas(entityListenerMetadatas));
});
return {
compoundIndexMetadatas: compoundIndexMetadatas,
columnMetadatas: columnMetadatas,
relationMetadatas: relationMetadatas,
joinColumnMetadatas: joinColumnMetadatas,
joinTableMetadatas: joinTableMetadatas,
indexMetadatas: indexMetadatas,
entityListenerMetadatas: entityListenerMetadatas
};
}
findEntityListenersForClasses(classes: Function[]): EntityListenerMetadata[] {
return this.entityListenerMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findTableMetadatasForClasses(classes: Function[]): TableMetadata[] {
return this.tableMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1 && !metadata.isAbstract);
}
findCompoundIndexMetadatasForClasses(classes: Function[]): CompoundIndexMetadata[] {
return this.compoundIndexMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findAbstractTableMetadatasForClasses(classes: Function[]): TableMetadata[] {
return this.tableMetadatas.filter(metadata => metadata.isAbstract && classes.indexOf(metadata.target) !== -1);
}
findIndexMetadatasForClasses(classes: Function[]): IndexMetadata[] {
return this.indexMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findFieldMetadatasForClasses(classes: Function[]): ColumnMetadata[] {
return this.columnMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findRelationMetadatasForClasses(classes: Function[]): RelationMetadata[] {
return this.relationMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findJoinTableMetadatasForClasses(classes: Function[]): JoinTableMetadata[] {
return this.joinTableMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findJoinColumnMetadatasForClasses(classes: Function[]): JoinColumnMetadata[] {
return this.joinColumnMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
findNamingStrategiesForClasses(classes: Function[]): NamingStrategyMetadata[] {
return this.namingStrategyMetadatas.filter(metadata => classes.indexOf(metadata.target) !== -1);
}
// -------------------------------------------------------------------------
// Private Methods
// -------------------------------------------------------------------------
private hasTableMetadataWithObjectConstructor(constructor: Function): boolean {
return !!this.tableMetadatas.find(metadata => metadata.target === constructor);
}
private hasCompoundIndexMetadataWithObjectConstructor(constructor: Function): boolean {
return !!this.compoundIndexMetadatas.find(metadata => metadata.target === constructor);
}
private hasNamingStrategyMetadataWithObjectConstructor(constructor: Function): boolean {
return !!this.namingStrategyMetadatas.find(metadata => metadata.target === constructor);
}
private hasEventSubscriberWithObjectConstructor(constructor: Function): boolean {
return !!this.eventSubscriberMetadatas.find(metadata => metadata.target === constructor);
}
private hasFieldMetadataOnProperty(constructor: Function, propertyName: string): boolean {
return !!this.columnMetadatas.find(metadata => metadata.target === constructor && metadata.propertyName === propertyName);
}
private hasRelationWithOneMetadataOnProperty(constructor: Function, propertyName: string): boolean {
return !!this.relationMetadatas.find(metadata => metadata.target === constructor && metadata.propertyName === propertyName);
}
private hasTableMetadataWithName(name: string): boolean {
return !!this.tableMetadatas.find(metadata => metadata.name === name);
}
private hasFieldMetadataWithName(constructor: Function, name: string): boolean {
return !!this.columnMetadatas.find(metadata => metadata.target === constructor && metadata.name === name);
}
private hasRelationWithOneMetadataWithName(constructor: Function, name: string): boolean {
return !!this.relationMetadatas.find(metadata => metadata.target === constructor && metadata.name === name);
}
}

View File

@ -1,53 +1,7 @@
import {PropertyMetadata} from "./PropertyMetadata";
import {ColumnOptions} from "./options/ColumnOptions";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategy";
import {ColumnType} from "./types/ColumnTypes";
/**
* 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 create date column or not.
*/
isCreateDate?: boolean;
/**
* Indicates if this column is update date column or not.
*/
isUpdateDate?: boolean;
/**
* Indicates if this column is virtual or not.
*/
isVirtual?: boolean;
/**
* Extra column options.
*/
options: ColumnOptions;
}
import {ColumnMetadataArgs} from "./args/ColumnMetadataArgs";
/**
* This metadata contains all information about class's column.

View File

@ -1,17 +1,14 @@
import {TargetMetadata} from "./TargetMetadata";
/**
* This metadata interface contains all information about table's compound index.
*/
export class CompoundIndexMetadata {
export class CompoundIndexMetadata extends TargetMetadata {
// ---------------------------------------------------------------------
// Readonly Properties
// ---------------------------------------------------------------------
/**
* Class to which this decorator is applied.
*/
readonly target: Function;
/**
* Fields combination to be used as index.
*/
@ -22,7 +19,7 @@ export class CompoundIndexMetadata {
// ---------------------------------------------------------------------
constructor(target: Function, fields: string[]) {
this.target = target;
super(target);
this.fields = fields;
}

View File

@ -1,23 +1,8 @@
import {TargetMetadata} from "./TargetMetadata";
/**
* Contains metadata information about ORM event subscribers.
*/
export class EventSubscriberMetadata {
// ---------------------------------------------------------------------
// Readonly Properties
// ---------------------------------------------------------------------
/**
* Class to which this decorator is applied.
*/
readonly target: Function;
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
constructor(target: Function) {
this.target = target;
}
export class EventSubscriberMetadata extends TargetMetadata {
}

View File

@ -12,7 +12,7 @@ export class IndexMetadata extends PropertyMetadata {
/**
* The name of the index.
*/
readonly name: string|undefined;
readonly name: string;
// ---------------------------------------------------------------------
// Constructor
@ -20,7 +20,9 @@ export class IndexMetadata extends PropertyMetadata {
constructor(target: Function, propertyName: string, name?: string) {
super(target, propertyName);
this.name = name; // todo: if there is no name, then generate it
if (name)
this.name = name; // todo: if there is no name, then generate it (using naming strategy?)
}

View File

@ -1,17 +1,14 @@
import {TargetMetadata} from "./TargetMetadata";
/**
* This metadata interface contains all information about naming strategy.
*/
export class NamingStrategyMetadata {
export class NamingStrategyMetadata extends TargetMetadata {
// ---------------------------------------------------------------------
// Readonly Properties
// ---------------------------------------------------------------------
/**
* Class to which this decorator is applied.
*/
readonly target: Function;
/**
* Naming strategy name.
*/
@ -22,7 +19,7 @@ export class NamingStrategyMetadata {
// ---------------------------------------------------------------------
constructor(target: Function, name: string) {
this.target = target;
super(target);
this.name = name;
}

View File

@ -1,17 +1,14 @@
import {TargetMetadata} from "./TargetMetadata";
/**
* This represents metadata of some object's property.
*/
export abstract class PropertyMetadata {
export abstract class PropertyMetadata extends TargetMetadata {
// ---------------------------------------------------------------------
// Readonly Properties
// ---------------------------------------------------------------------
/**
* Class to which this decorator is applied.
*/
readonly target: Function;
/**
* Class's property name to which this decorator is applied.
*/
@ -22,8 +19,7 @@ export abstract class PropertyMetadata {
// ---------------------------------------------------------------------
constructor(target?: Function, propertyName?: string) {
if (target)
this.target = target;
super(target);
if (propertyName)
this.propertyName = propertyName;

View File

@ -1,11 +1,11 @@
import {PropertyMetadata} from "./PropertyMetadata";
import {RelationTypes, RelationType} from "./types/RelationTypes";
import {RelationOptions} from "./options/RelationOptions";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategy";
import {EntityMetadata} from "./EntityMetadata";
import {OnDeleteType} from "./ForeignKeyMetadata";
import {JoinTableMetadata} from "./JoinTableMetadata";
import {JoinColumnMetadata} from "./JoinColumnMetadata";
import {RelationMetadataArgs} from "./args/RelationMetadataArgs";
/**
* Function that returns a type of the field. Returned value must be a class used on the relation.
@ -17,42 +17,6 @@ export type RelationTypeInFunction = ((type?: any) => Function);
*/
export type PropertyTypeInFunction<T> = string|((t: T) => string|any);
/**
* Relation metadata constructor arguments.
*/
export interface RelationMetadataArgs {
/**
* Class to which this relation is applied.
*/
target: Function;
/**
* Class's property name to which this relation is applied.
*/
propertyName: string;
/**
* Type of relation. Can be one of the value of the RelationTypes class.
*/
relationType: RelationType;
/**
* Type of the relation. This type is in function because of language specifics and problems with recursive
* referenced classes.
*/
type: RelationTypeInFunction;
/**
* Inverse side of the relation.
*/
inverseSideProperty: PropertyTypeInFunction<any>;
/**
* Additional relation options.
*/
options: RelationOptions;
}
/**
* This metadata interface contains all information about some document's relation.

View File

@ -1,9 +1,10 @@
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategy";
import {TargetMetadata} from "./TargetMetadata";
/**
* This metadata interface contains all information about specific table.
*/
export class TableMetadata {
export class TableMetadata extends TargetMetadata {
// ---------------------------------------------------------------------
// Public Properties
@ -18,11 +19,6 @@ export class TableMetadata {
// Readonly Properties
// ---------------------------------------------------------------------
/**
* Class to which this column is applied.
*/
readonly target: Function;
/**
* Indicates if this table is abstract or not. Regular tables can inherit columns from abstract tables.
*/
@ -44,8 +40,7 @@ export class TableMetadata {
constructor(target?: Function, name?: string);
constructor(target: Function, isAbstract: boolean);
constructor(target: Function, nameOrIsAbstract?: string|boolean, maybeIsAbstract?: boolean) {
if (target)
this.target = target;
super(target);
if (typeof nameOrIsAbstract === "string")
this._name = nameOrIsAbstract;
if (typeof nameOrIsAbstract === "boolean")
@ -68,4 +63,14 @@ export class TableMetadata {
return this.namingStrategy ? this.namingStrategy.tableName((<any>this.target).name) : (<any>this.target).name;
}
/**
* 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;
}
}

View File

@ -0,0 +1,21 @@
/**
* This represents metadata of some object.
*/
export abstract class TargetMetadata {
// ---------------------------------------------------------------------
// Readonly Properties
// ---------------------------------------------------------------------
readonly target: Function;
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
constructor(target?: Function) {
if (target)
this.target = target;
}
}

View File

@ -0,0 +1,47 @@
import {ColumnOptions} from "../options/ColumnOptions";
/**
* 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 create date column or not.
*/
isCreateDate?: boolean;
/**
* Indicates if this column is update date column or not.
*/
isUpdateDate?: boolean;
/**
* Indicates if this column is virtual or not.
*/
isVirtual?: boolean;
/**
* Extra column options.
*/
options: ColumnOptions;
}

View File

@ -0,0 +1,41 @@
import {RelationType} from "../types/RelationTypes";
import {RelationOptions} from "../options/RelationOptions";
import {PropertyTypeInFunction, RelationTypeInFunction} from "../RelationMetadata";
/**
* Relation metadata constructor arguments.
*/
export interface RelationMetadataArgs {
/**
* Class to which this relation is applied.
*/
target: Function;
/**
* Class's property name to which this relation is applied.
*/
propertyName: string;
/**
* Type of relation. Can be one of the value of the RelationTypes class.
*/
relationType: RelationType;
/**
* Type of the relation. This type is in function because of language specifics and problems with recursive
* referenced classes.
*/
type: RelationTypeInFunction;
/**
* Inverse side of the relation.
*/
inverseSideProperty: PropertyTypeInFunction<any>;
/**
* Additional relation options.
*/
options: RelationOptions;
}

View File

@ -1,12 +1,12 @@
import {EntityMetadata} from "./EntityMetadata";
import {EntityMetadataNotFound} from "./error/EntityMetadataNotFound";
import {EntityMetadata} from "../EntityMetadata";
import {EntityMetadataNotFound} from "../error/EntityMetadataNotFound";
/**
* Array for the entity metadatas.
*
* @internal
*/
export class EntityMetadataArray extends Array<EntityMetadata> {
export class EntityMetadataCollection extends Array<EntityMetadata> {
// -------------------------------------------------------------------------
// Public Methods

View File

@ -0,0 +1,17 @@
import {TargetMetadataCollection} from "./TargetMetadataCollection";
import {PropertyMetadata} from "../PropertyMetadata";
export class PropertyMetadataCollection<T extends PropertyMetadata> extends TargetMetadataCollection<T> {
filterRepeatedMetadatas(existsMetadatas: T[]): this {
const collection = new (<any> this.constructor)();
this
.filter(metadata => {
return !existsMetadatas.find(fieldFromDocument => fieldFromDocument.propertyName === metadata.propertyName);
})
.forEach(metadata => collection.add(metadata));
return collection;
}
}

View File

@ -0,0 +1,30 @@
import {TargetMetadata} from "../TargetMetadata";
export class TargetMetadataCollection<T extends TargetMetadata> extends Array<T> {
filterByClass(cls: Function): this {
return this.filterByClasses([cls]);
}
filterByClasses(classes: Function[]): this {
const collection = new (<any> this.constructor)();
this.filter(metadata => classes.indexOf(metadata.target) !== -1)
.forEach(metadata => collection.add(metadata));
return collection;
}
add(metadata: T) {
// if (this.hasWithClass(metadata.target))
// throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
// if (metadata.name && this.hasTableMetadataWithName(metadata.name))
// throw new MetadataWithSuchNameAlreadyExistsError("Table", metadata.name);
this.push(metadata);
}
private hasWithClass(constructor: Function): boolean {
return !!this.find(metadata => metadata.target === constructor);
}
}

View File

@ -3,7 +3,7 @@ import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
import {EntityMetadataArray} from "../metadata/EntityMetadataArray";
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
/**
* Creates indexes based on the given metadata.
@ -17,7 +17,7 @@ export class SchemaCreator {
// -------------------------------------------------------------------------
constructor(private schemaBuilder: SchemaBuilder,
private entityMetadatas: EntityMetadataArray) {
private entityMetadatas: EntityMetadataCollection) {
}
// -------------------------------------------------------------------------