mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
custom join table and join columns implementation
This commit is contained in:
parent
df28a26e58
commit
e4647748c3
42
sample/sample21-custom-join-table-column/app.ts
Normal file
42
sample/sample21-custom-join-table-column/app.ts
Normal 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));
|
||||
21
sample/sample21-custom-join-table-column/entity/Author.ts
Normal file
21
sample/sample21-custom-join-table-column/entity/Author.ts
Normal 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[];
|
||||
|
||||
}
|
||||
18
sample/sample21-custom-join-table-column/entity/Category.ts
Normal file
18
sample/sample21-custom-join-table-column/entity/Category.ts
Normal 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[];
|
||||
|
||||
}
|
||||
38
sample/sample21-custom-join-table-column/entity/Post.ts
Normal file
38
sample/sample21-custom-join-table-column/entity/Post.ts
Normal 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[];
|
||||
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user