custom join table and join columns implementation

This commit is contained in:
Umed Khudoiberdiev 2016-05-04 13:56:09 +05:00
parent df28a26e58
commit e4647748c3
7 changed files with 159 additions and 34 deletions

View File

@ -0,0 +1,42 @@
import {createConnection, CreateConnectionOptions} from "../../src/typeorm";
import {Post} from "./entity/Post";
import {Author} from "./entity/Author";
import {Category} from "./entity/Category";
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
}
},
entities: [Post, Author, Category]
};
createConnection(options).then(connection => {
let postRepository = connection.getRepository(Post);
let author = new Author();
author.name = "Umed";
let post = new Post();
post.text = "Hello how are you?";
post.title = "hello";
post.author = author;
postRepository
.persist(post)
.then(post => {
console.log("Post has been saved.");
})
.catch(error => console.log(error.stack));
}, error => console.log("Cannot connect: ", error));

View File

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

View File

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

View File

@ -0,0 +1,38 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {Author} from "./Author";
import {ManyToOne} from "../../../src/decorator/relations/ManyToOne";
import {Category} from "./Category";
import {ManyToMany} from "../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../src/decorator/relations/JoinTable";
import {JoinColumn} from "../../../src/decorator/relations/JoinColumn";
@Table("sample21_post")
export class Post {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
title: string;
@Column()
text: string;
@ManyToOne(type => Author, author => author.posts, {
cascadeAll: true
})
@JoinColumn({
name: "user"
})
author: Author;
@ManyToMany(type => Category, category => category.posts, {
cascadeAll: true
})
@JoinTable({
name: "_post_categories"
})
categories: Category[];
}

View File

@ -30,7 +30,6 @@ export class EntityMetadataBuilder {
// todo: duplicate name checking for: table, relation, column, index, naming strategy, join tables/columns?
private metadataStorage: MetadataStorage = defaultMetadataStorage();
private entityValidator = new EntityMetadataValidator();
// -------------------------------------------------------------------------
@ -62,7 +61,7 @@ export class EntityMetadataBuilder {
const allMetadataStorage = defaultMetadataStorage();
// filter the metadata only we need - those which are bind to the given table classes
// 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);
@ -85,60 +84,59 @@ export class EntityMetadataBuilder {
// merge indices and composite indices because simple indices actually are compose indices with only one column
this.mergeIndicesAndCompositeIndices(mergedMetadata.indexMetadatas, mergedMetadata.compositeIndexMetadatas);
// create a new entity metadata
const entityMetadata = new EntityMetadata(
tableMetadata,
mergedMetadata.columnMetadatas,
mergedMetadata.relationMetadatas,
// mergedMetadata.indexMetadatas,
mergedMetadata.compositeIndexMetadatas,
[]
mergedMetadata.compositeIndexMetadatas
);
// find entity's relations join tables
entityMetadata.relations.forEach(relation => {
const relationJoinTable = mergedMetadata.joinTableMetadatas.find(joinTable => joinTable.propertyName === relation.propertyName);
if (relationJoinTable)
relation.joinTable = relationJoinTable;
const joinTable = mergedMetadata.joinTableMetadatas.findByProperty(relation.propertyName);
if (joinTable)
relation.joinTable = joinTable;
});
// find entity's relations join columns
entityMetadata.relations.forEach(relation => {
const relationJoinColumn = mergedMetadata.joinColumnMetadatas.find(joinColumn => joinColumn.propertyName === relation.propertyName);
if (relationJoinColumn)
relation.joinColumn = relationJoinColumn;
const joinColumn = mergedMetadata.joinColumnMetadatas.findByProperty(relation.propertyName);
if (joinColumn)
relation.joinColumn = joinColumn;
});
return entityMetadata;
});
// set inverse side (related) entity metadatas for all relation metadatas
// after all metadatas created we set inverse side (related) entity metadatas for all relation metadatas
entityMetadatas.forEach(entityMetadata => {
entityMetadata.relations.forEach(relation => {
relation.relatedEntityMetadata = entityMetadatas.find(m => m.target === relation.type);
});
});
// check for errors in build metadata schema (we need to check after relationEntityMetadata is set)
// check for errors in a built metadata schema (we need to check after relationEntityMetadata is set)
this.entityValidator.validateMany(entityMetadatas);
// generate columns and foreign keys for tables with relations
entityMetadatas.forEach(metadata => {
const foreignKeyRelations = metadata.ownerOneToOneRelations.concat(metadata.manyToOneRelations);
foreignKeyRelations.map(relation => {
const inverseSideMetadata = entityMetadatas.find(metadata => metadata.target === relation.type);
metadata.relationsWithJoinColumns.forEach(relation => {
// find relational columns and if it does not exist - add it
let relationalColumn = metadata.columns.find(column => column.name === relation.name);
// find relational column and if it does not exist - add it
const inverseSideColumn = relation.relatedEntityMetadata.primaryColumn;
let relationalColumn = metadata.columns.find(column => column.name === relation.name); // todo?: ColumnCollection.findByName
if (!relationalColumn) {
const options: ColumnOptions = {
type: inverseSideMetadata.primaryColumn.type,
type: inverseSideColumn.type,
oldColumnName: relation.oldColumnName,
nullable: relation.isNullable
};
relationalColumn = new ColumnMetadata({
target: metadata.target,
propertyName: relation.name,
propertyType: inverseSideMetadata.primaryColumn.type,
propertyType: inverseSideColumn.propertyType,
isVirtual: true,
options: options
});
@ -146,10 +144,11 @@ export class EntityMetadataBuilder {
}
// create and add foreign key
const foreignKey = new ForeignKeyMetadata(metadata.table,
const foreignKey = new ForeignKeyMetadata(
metadata.table,
[relationalColumn],
inverseSideMetadata.table,
[inverseSideMetadata.primaryColumn],
relation.relatedEntityMetadata.table,
[inverseSideColumn],
relation.onDelete
);
metadata.foreignKeys.push(foreignKey);
@ -186,11 +185,11 @@ export class EntityMetadataBuilder {
options: column2options
})
];
const foreignKeys = [
const junctionEntityMetadata = new EntityMetadata(tableMetadata, columns, [], []);
junctionEntityMetadata.foreignKeys.push(
new ForeignKeyMetadata(tableMetadata, [columns[0]], metadata.table, [metadata.primaryColumn]),
new ForeignKeyMetadata(tableMetadata, [columns[1]], inverseSideMetadata.table, [inverseSideMetadata.primaryColumn]),
];
const junctionEntityMetadata = new EntityMetadata(tableMetadata, columns, [], [], foreignKeys);
new ForeignKeyMetadata(tableMetadata, [columns[1]], inverseSideMetadata.table, [inverseSideMetadata.primaryColumn])
);
junctionEntityMetadatas.push(junctionEntityMetadata);
relation.junctionEntityMetadata = junctionEntityMetadata;
if (relation.hasInverseSide)

View File

@ -18,9 +18,8 @@ export class EntityMetadata {
readonly table: TableMetadata;
readonly columns: ColumnMetadata[];
readonly relations: RelationMetadata[];
// readonly indices: IndexMetadata[];
readonly compositeIndices: CompositeIndexMetadata[];
readonly foreignKeys: ForeignKeyMetadata[];
readonly foreignKeys: ForeignKeyMetadata[] = [];
// -------------------------------------------------------------------------
// Constructor
@ -29,15 +28,11 @@ export class EntityMetadata {
constructor(table: TableMetadata,
columns: ColumnMetadata[],
relations: RelationMetadata[],
// indices: IndexMetadata[],
compositeIndices: CompositeIndexMetadata[],
foreignKeys: ForeignKeyMetadata[]) {
compositeIndices: CompositeIndexMetadata[]) {
this.table = table;
this.columns = columns;
this.relations = relations;
// this.indices = indices;
this.compositeIndices = compositeIndices;
this.foreignKeys = foreignKeys;
// this.relations.forEach(relation => relation.entityMetadata = this);
this.compositeIndices.forEach(index => index.entityMetadata = this);
@ -78,6 +73,10 @@ export class EntityMetadata {
get ownerManyToManyRelations(): RelationMetadata[] {
return this.relations.filter(relation => relation.relationType === RelationTypes.MANY_TO_MANY && relation.isOwning);
}
get relationsWithJoinColumns() {
return this.ownerOneToOneRelations.concat(this.manyToOneRelations);
}
get primaryColumn(): ColumnMetadata {
return this.columns.find(column => column.isPrimary);

View File

@ -18,4 +18,12 @@ export class PropertyMetadataCollection<T extends PropertyMetadata> extends Targ
return collection;
}
findByProperty(propertyName: string) {
return this.find(item => item.propertyName === propertyName);
}
hasWithProperty(propertyName: string) {
return !!this.findByProperty(propertyName);
}
}