mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
added indices generation to single-table inheritance, added validation and default nullable columns
This commit is contained in:
parent
036d7016d1
commit
cf2d6f5f9b
@ -5,7 +5,7 @@ import {ChildTable} from "../../../src/decorator/tables/ChildTable";
|
||||
@ChildTable()
|
||||
export class Employee extends Person {
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Column()
|
||||
salary: number;
|
||||
|
||||
}
|
||||
@ -7,7 +7,7 @@ import {ChildTable} from "../../../src/decorator/tables/ChildTable";
|
||||
@DiscriminatorValue("home-sitter") // can be omitted
|
||||
export class Homesitter extends Person {
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Column()
|
||||
numberOfKids: number;
|
||||
|
||||
}
|
||||
@ -6,10 +6,9 @@ import {PrimaryColumn} from "../../../src/decorator/columns/PrimaryColumn";
|
||||
|
||||
// todo: some things left to do:
|
||||
// * check how it works when is join (conditions are not added in the joins right now)
|
||||
// * add key for discriminator column
|
||||
|
||||
@Table()
|
||||
@TableInheritance("single-table") // also can be a class-table
|
||||
@TableInheritance("single-table")
|
||||
@DiscriminatorColumn({ name: "type", type: "string"})
|
||||
export abstract class Person {
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import {ChildTable} from "../../../src/decorator/tables/ChildTable";
|
||||
@ChildTable()
|
||||
export class Student extends Person {
|
||||
|
||||
@Column({ nullable: true })
|
||||
@Column()
|
||||
faculty: string;
|
||||
|
||||
}
|
||||
@ -18,6 +18,7 @@ export function DiscriminatorColumn(discriminatorOptions: { name: string, type:
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: target,
|
||||
mode: "discriminator",
|
||||
propertyName: discriminatorOptions.name,
|
||||
options: options
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
|
||||
@ -40,7 +40,7 @@ export interface ColumnOptions {
|
||||
/**
|
||||
* Indicates if column's value can be set to NULL.
|
||||
*/
|
||||
readonly nullable?: boolean;
|
||||
nullable?: boolean;
|
||||
|
||||
/**
|
||||
* Extra column definition. Should be used only in emergency situations. Note that if you'll use this property
|
||||
|
||||
@ -11,6 +11,7 @@ import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
|
||||
*/
|
||||
export interface EntityMetadataArgs {
|
||||
|
||||
readonly inheritanceType?: "single-table"|"class-table";
|
||||
readonly discriminatorValue?: string;
|
||||
readonly namingStrategy: NamingStrategyInterface;
|
||||
readonly tableMetadata: TableMetadata;
|
||||
|
||||
@ -7,7 +7,7 @@ export interface IndexMetadataArgs {
|
||||
/**
|
||||
* Class to which index is applied.
|
||||
*/
|
||||
readonly target?: Function;
|
||||
readonly target?: Function|string;
|
||||
|
||||
/**
|
||||
* Index name.
|
||||
|
||||
@ -199,7 +199,17 @@ export class EntityMetadataBuilder {
|
||||
|
||||
// create metadatas from args
|
||||
const table = new TableMetadata(mergedArgs.table);
|
||||
const columns = mergedArgs.columns.map(args => new ColumnMetadata(args));
|
||||
const columns = mergedArgs.columns.map(args => {
|
||||
|
||||
// if column's target is a child table then this column should have all nullable columns
|
||||
if (mergedArgs.inheritance &&
|
||||
mergedArgs.inheritance.type === "single-table" &&
|
||||
args.target !== mergedArgs.table.target &&
|
||||
!!mergedArgs.children.find(childTable => childTable.target === args.target)) {
|
||||
args.options.nullable = true;
|
||||
}
|
||||
return new ColumnMetadata(args);
|
||||
});
|
||||
const relations = mergedArgs.relations.map(args => new RelationMetadata(args));
|
||||
const indices = mergedArgs.indices.map(args => new IndexMetadata(args));
|
||||
const discriminatorValueArgs = mergedArgs.discriminatorValues.find(discriminatorValueArgs => {
|
||||
@ -214,6 +224,7 @@ export class EntityMetadataBuilder {
|
||||
relationMetadatas: relations,
|
||||
indexMetadatas: indices,
|
||||
embeddedMetadatas: embeddeds,
|
||||
inheritanceType: mergedArgs.inheritance ? mergedArgs.inheritance.type : undefined,
|
||||
discriminatorValue: discriminatorValueArgs ? discriminatorValueArgs.value : (tableArgs.target as any).name
|
||||
}, lazyRelationsWrapper);
|
||||
entityMetadatas.push(entityMetadata);
|
||||
@ -373,6 +384,27 @@ export class EntityMetadataBuilder {
|
||||
});
|
||||
});
|
||||
|
||||
// generate keys for tables with single-table inheritance
|
||||
entityMetadatas
|
||||
.filter(metadata => metadata.inheritanceType === "single-table" && metadata.hasDiscriminatorColumn)
|
||||
.forEach(metadata => {
|
||||
const indexForKey = new IndexMetadata({
|
||||
target: metadata.target,
|
||||
columns: [metadata.discriminatorColumn.name],
|
||||
unique: false
|
||||
});
|
||||
indexForKey.entityMetadata = metadata;
|
||||
metadata.indices.push(indexForKey);
|
||||
|
||||
const indexForKeyWithPrimary = new IndexMetadata({
|
||||
target: metadata.target,
|
||||
columns: [metadata.firstPrimaryColumn.propertyName, metadata.discriminatorColumn.propertyName],
|
||||
unique: false
|
||||
});
|
||||
indexForKeyWithPrimary.entityMetadata = metadata;
|
||||
metadata.indices.push(indexForKeyWithPrimary);
|
||||
});
|
||||
|
||||
return entityMetadatas;
|
||||
}
|
||||
|
||||
|
||||
@ -22,18 +22,35 @@ export class EntityMetadataValidator {
|
||||
* Validates all given entity metadatas.
|
||||
*/
|
||||
validateMany(entityMetadatas: EntityMetadata[]) {
|
||||
entityMetadatas.forEach(entityMetadata => this.validate(entityMetadata));
|
||||
entityMetadatas.forEach(entityMetadata => this.validate(entityMetadata, entityMetadatas));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates given entity metadata.
|
||||
*/
|
||||
validate(entityMetadata: EntityMetadata) {
|
||||
validate(entityMetadata: EntityMetadata, allEntityMetadatas: EntityMetadata[]) {
|
||||
|
||||
// check if table metadata has an id
|
||||
if (!entityMetadata.primaryColumns.length)
|
||||
throw new MissingPrimaryColumnError(entityMetadata);
|
||||
|
||||
|
||||
// validate if table is using inheritance it has a discriminator
|
||||
// also validate if discriminator values are not empty and not repeated
|
||||
if (entityMetadata.inheritanceType === "single-table") {
|
||||
if (!entityMetadata.hasDiscriminatorColumn)
|
||||
throw new Error(`Entity ${entityMetadata.name} using single-table inheritance, it should also have a discriminator column. Did you forget to put @DiscriminatorColumn decorator?`);
|
||||
|
||||
if (["", undefined, null].indexOf(entityMetadata.discriminatorValue) !== -1)
|
||||
throw new Error(`Entity ${entityMetadata.name} has empty discriminator value. Discriminator value should not be empty.`);
|
||||
|
||||
const sameDiscriminatorValueEntityMetadata = allEntityMetadatas.find(metadata => {
|
||||
return metadata !== entityMetadata && metadata.discriminatorValue === entityMetadata.discriminatorValue;
|
||||
});
|
||||
if (sameDiscriminatorValueEntityMetadata)
|
||||
throw new Error(`Entities ${entityMetadata.name} and ${sameDiscriminatorValueEntityMetadata.name} as equal discriminator values. Make sure their discriminator values are not equal using @DiscriminatorValue decorator.`);
|
||||
}
|
||||
|
||||
// validate relations
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
|
||||
// check join tables:
|
||||
|
||||
@ -129,7 +129,7 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(args: ColumnMetadataArgs) {
|
||||
super(undefined, args.propertyName);
|
||||
super(args.target, args.propertyName);
|
||||
|
||||
if (args.mode)
|
||||
this.mode = args.mode;
|
||||
@ -186,10 +186,6 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
throw new Error(`Column ${this._name ? this._name + " " : ""}is not attached to any entity or embedded.`);
|
||||
}
|
||||
|
||||
get target() {
|
||||
return this.entityMetadata.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column is in embedded, not directly in the table.
|
||||
*/
|
||||
@ -204,6 +200,13 @@ export class ColumnMetadata extends PropertyMetadata {
|
||||
return this.mode === "virtual";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if column is discriminator. Discriminator columns are not mapped to the entity.
|
||||
*/
|
||||
get isDiscriminator() {
|
||||
return this.mode === "discriminator";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an entity creation date.
|
||||
*/
|
||||
|
||||
@ -64,6 +64,12 @@ export class EntityMetadata {
|
||||
*/
|
||||
readonly embeddeds: EmbeddedMetadata[];
|
||||
|
||||
/**
|
||||
* If this entity metadata's table using one of the inheritance patterns,
|
||||
* then this will contain what pattern it uses.
|
||||
*/
|
||||
readonly inheritanceType?: "single-table"|"class-table";
|
||||
|
||||
/**
|
||||
* If this entity metadata is a child table of some table, it should have a discriminator value.
|
||||
* Used to store a value in a discriminator column.
|
||||
@ -95,6 +101,7 @@ export class EntityMetadata {
|
||||
this.foreignKeys = args.foreignKeyMetadatas || [];
|
||||
this.embeddeds = args.embeddedMetadatas || [];
|
||||
this.discriminatorValue = args.discriminatorValue;
|
||||
this.inheritanceType = args.inheritanceType;
|
||||
|
||||
this.table.entityMetadata = this;
|
||||
this._columns.forEach(column => column.entityMetadata = this);
|
||||
|
||||
@ -455,7 +455,7 @@ export class EntityPersistOperationBuilder {
|
||||
|
||||
private diffColumns(metadata: EntityMetadata, newEntity: any, dbEntity: any) {
|
||||
return metadata.columns
|
||||
.filter(column => !column.isVirtual && !column.isUpdateDate && !column.isVersion && !column.isCreateDate)
|
||||
.filter(column => !column.isVirtual && !column.isDiscriminator && !column.isUpdateDate && !column.isVersion && !column.isCreateDate)
|
||||
.filter(column => column.getEntityValue(newEntity) !== column.getEntityValue(dbEntity))
|
||||
.filter(column => {
|
||||
// filter out "relational columns" only in the case if there is a relation object in entity
|
||||
|
||||
@ -450,7 +450,7 @@ export class PersistOperationExecutor {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
|
||||
const columns = metadata.columns
|
||||
.filter(column => !column.isVirtual && column.hasEntityValue(entity));
|
||||
.filter(column => !column.isVirtual && !column.isDiscriminator && column.hasEntityValue(entity));
|
||||
|
||||
const columnNames = columns.map(column => column.name);
|
||||
const values = columns.map(column => this.driver.preparePersistentValue(column.getEntityValue(entity), column));
|
||||
|
||||
@ -84,7 +84,7 @@ export class RawSqlResultsToEntityTransformer {
|
||||
metadata.columns.forEach(column => {
|
||||
const columnName = column.name;
|
||||
const valueInObject = rawSqlResults[0][alias.name + "_" + columnName]; // we use zero index since its grouped data
|
||||
if (valueInObject !== undefined && valueInObject !== null && column.propertyName && !column.isVirtual) {
|
||||
if (valueInObject !== undefined && valueInObject !== null && column.propertyName && !column.isVirtual && !column.isDiscriminator) {
|
||||
const value = this.driver.prepareHydratedValue(valueInObject, column);
|
||||
|
||||
if (column.isInEmbedded) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user