added a new table option skipSchemaSync to prevent schema-builder to sync a table with the database. Added a test in functional/connection/connection.ts

This commit is contained in:
Arnaud PEZEL 2016-11-16 17:33:18 +01:00
parent 1c403fd107
commit 82b51b0348
11 changed files with 67 additions and 14 deletions

View File

@ -16,5 +16,10 @@ export interface TableOptions {
* If you update this value and table is already created, it will not change table's engine type.
*/
readonly engine?: string;
/**
* Specifies if this table will be skipped during schema synchronization.
*/
readonly skipSchemaSync?: boolean;
}

View File

@ -11,7 +11,8 @@ export function ClassTableChild(tableName?: string, options?: TableOptions) {
target: target,
name: tableName,
type: "class-table-child",
orderBy: options && options.orderBy ? options.orderBy : undefined
orderBy: options && options.orderBy ? options.orderBy : undefined,
skipSchemaSync: (options && options.skipSchemaSync === true) || false
};
getMetadataArgsStorage().tables.add(args);
};

View File

@ -11,7 +11,8 @@ export function ClosureTable(name?: string, options?: TableOptions) {
target: target,
name: name,
type: "closure",
orderBy: options && options.orderBy ? options.orderBy : undefined
orderBy: options && options.orderBy ? options.orderBy : undefined,
skipSchemaSync: (options && options.skipSchemaSync === true) || false
};
getMetadataArgsStorage().tables.add(args);
};

View File

@ -14,6 +14,7 @@ export function Table(name?: string, options?: TableOptions) {
type: "regular",
orderBy: options && options.orderBy ? options.orderBy : undefined,
engine: options && options.engine ? options.engine : undefined,
skipSchemaSync: (options && options.skipSchemaSync === true) || false
};
getMetadataArgsStorage().tables.add(args);
};

View File

@ -41,6 +41,7 @@ export interface EntitySchema {
* Specifies a property name by which queries will perform ordering by default when fetching rows.
*/
orderBy?: OrderByCondition;
};
/**

View File

@ -33,5 +33,10 @@ export interface TableMetadataArgs {
* Table's database engine type (like "InnoDB", "MyISAM", etc).
*/
readonly engine?: string;
/**
* Whether table must be synced during schema build or not
*/
readonly skipSchemaSync?: boolean;
}

View File

@ -191,6 +191,7 @@ export class EntityMetadataBuilder {
const allMergedArgs = metadataArgsStorage.getMergedTableMetadatas(entityClasses);
allMergedArgs.forEach(mergedArgs => {
const tables = [mergedArgs.table].concat(mergedArgs.children);
tables.forEach(tableArgs => {
@ -207,6 +208,7 @@ export class EntityMetadataBuilder {
// create metadatas from args
const argsForTable = mergedArgs.inheritance && mergedArgs.inheritance.type === "single-table" ? mergedArgs.table : tableArgs;
const table = new TableMetadata(argsForTable);
const columns = mergedArgs.columns.map(args => {
@ -224,7 +226,6 @@ export class EntityMetadataBuilder {
const discriminatorValueArgs = mergedArgs.discriminatorValues.find(discriminatorValueArgs => {
return discriminatorValueArgs.target === tableArgs.target;
});
// create a new entity metadata
const entityMetadata = new EntityMetadata({
target: tableArgs.target,
@ -239,7 +240,6 @@ export class EntityMetadataBuilder {
discriminatorValue: discriminatorValueArgs ? discriminatorValueArgs.value : (tableArgs.target as any).name // todo: pass this to naming strategy to generate a name
}, lazyRelationsWrapper);
entityMetadatas.push(entityMetadata);
// create entity's relations join tables
entityMetadata.manyToManyRelations.forEach(relation => {
const joinTableMetadata = mergedArgs.joinTables.findByProperty(relation.propertyName);

View File

@ -32,6 +32,11 @@ export class TableMetadata {
*/
readonly engine?: string;
/**
* Whether table must be synced during schema build or not
*/
readonly skipSchemaSync?: boolean;
// ---------------------------------------------------------------------
// Private Properties
// ---------------------------------------------------------------------
@ -64,6 +69,7 @@ export class TableMetadata {
this._name = args.name;
this._orderBy = args.orderBy;
this.engine = args.engine;
this.skipSchemaSync = args.skipSchemaSync;
}
// ---------------------------------------------------------------------

View File

@ -95,11 +95,15 @@ export class SchemaBuilder {
// Private Methods
// -------------------------------------------------------------------------
protected get entityToSyncMetadatas(): EntityMetadataCollection {
return <EntityMetadataCollection>this.entityMetadatas.filter( metadata => !metadata.table.skipSchemaSync );
}
/**
* Loads all table schemas from the database.
*/
protected loadTableSchemas(): Promise<TableSchema[]> {
const tableNames = this.entityMetadatas.map(metadata => metadata.table.name);
const tableNames = this.entityToSyncMetadatas.map(metadata => metadata.table.name);
return this.queryRunner.loadSchemaTables(tableNames, this.namingStrategy);
}
@ -107,7 +111,7 @@ export class SchemaBuilder {
* Drops all (old) foreign keys that exist in the table schemas, but do not exist in the entity metadata.
*/
protected async dropOldForeignKeys(): Promise<void> {
await Promise.all(this.entityMetadatas.map(async metadata => {
await Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (!tableSchema)
@ -136,8 +140,7 @@ export class SchemaBuilder {
* Primary key only can be created in conclusion with auto generated column.
*/
protected async createNewTables(): Promise<void> {
await Promise.all(this.entityMetadatas.map(async metadata => {
await Promise.all(this.entityToSyncMetadatas.map(async metadata => {
// check if table does not exist yet
const existTableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (existTableSchema)
@ -157,7 +160,7 @@ export class SchemaBuilder {
* We drop their keys too, since it should be safe.
*/
protected dropRemovedColumns() {
return Promise.all(this.entityMetadatas.map(async metadata => {
return Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (!tableSchema) return;
@ -194,7 +197,7 @@ export class SchemaBuilder {
* Columns are created without keys.
*/
protected addNewColumns() {
return Promise.all(this.entityMetadatas.map(async metadata => {
return Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (!tableSchema)
return;
@ -220,7 +223,7 @@ export class SchemaBuilder {
* Still don't create keys. Also we don't touch foreign keys of the changed columns.
*/
protected updateExistColumns() {
return Promise.all(this.entityMetadatas.map(async metadata => {
return Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (!tableSchema)
return;
@ -267,7 +270,7 @@ export class SchemaBuilder {
* Creates primary keys which does not exist in the table yet.
*/
protected updatePrimaryKeys() {
return Promise.all(this.entityMetadatas.map(async metadata => {
return Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name && !table.justCreated);
if (!tableSchema)
return;
@ -297,7 +300,7 @@ export class SchemaBuilder {
* Creates foreign keys which does not exist in the table yet.
*/
protected createForeignKeys() {
return Promise.all(this.entityMetadatas.map(async metadata => {
return Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (!tableSchema)
return;
@ -321,7 +324,7 @@ export class SchemaBuilder {
*/
protected createIndices() {
// return Promise.all(this.entityMetadatas.map(metadata => this.createIndices(metadata.table, metadata.indices)));
return Promise.all(this.entityMetadatas.map(async metadata => {
return Promise.all(this.entityToSyncMetadatas.map(async metadata => {
const tableSchema = this.tableSchemas.find(table => table.name === metadata.table.name);
if (!tableSchema)
return;

View File

@ -1,6 +1,7 @@
import "reflect-metadata";
import {expect} from "chai";
import {Post} from "./entity/Post";
import {View} from "./entity/View";
import {Category} from "./entity/Category";
import {setupTestingConnections, closeConnections, createTestingConnectionOptions} from "../../utils/test-utils";
import {Connection} from "../../../src/connection/Connection";
@ -14,6 +15,7 @@ import {ConnectionOptions} from "../../../src/connection/ConnectionOptions";
import {CannotSyncNotConnectedError} from "../../../src/connection/error/CannotSyncNotConnectedError";
import {NoConnectionForRepositoryError} from "../../../src/connection/error/NoConnectionForRepositoryError";
import {RepositoryNotFoundError} from "../../../src/connection/error/RepositoryNotFoundError";
import {DefaultNamingStrategy} from "../../../src/naming-strategy/DefaultNamingStrategy";
import {FirstCustomNamingStrategy} from "./naming-strategy/FirstCustomNamingStrategy";
import {SecondCustomNamingStrategy} from "./naming-strategy/SecondCustomNamingStrategy";
import {CannotUseNamingStrategyNotConnectedError} from "../../../src/connection/error/CannotUseNamingStrategyNotConnectedError";
@ -351,5 +353,19 @@ describe("Connection", () => {
});
});
describe("skip schema generation when skipSchemaSync option is used", function() {
let connections: Connection[];
beforeEach(() => setupTestingConnections({ entities: [View] }).then(all => connections = all));
afterEach(() => closeConnections(connections));
it("database should be empty after schema sync", () => Promise.all(connections.map(async connection => {
await connection.syncSchema(true);
const queryRunner = await connection.driver.createQueryRunner();
let schema = await queryRunner.loadSchemaTables(["view"], new DefaultNamingStrategy());
expect(!schema.some( table => table.name === "view" )).to.be.true;
})));
});
});

View File

@ -0,0 +1,14 @@
import {Table} from "../../../../src/decorator/tables/Table";
import {PrimaryColumn} from "../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../src/decorator/columns/Column";
@Table("view", {skipSchemaSync: true})
export class View {
@PrimaryColumn()
id: number;
@Column()
title: string;
}