mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
first step: introduce composite primary keys support
This commit is contained in:
parent
07f1b4b9b1
commit
e64dd9576d
39
sample/sample27-composite-primary-keys/app.ts
Normal file
39
sample/sample27-composite-primary-keys/app.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import "reflect-metadata";
|
||||
import {createConnection, ConnectionOptions} from "../../src/index";
|
||||
import {Post} from "./entity/Post";
|
||||
|
||||
const options: ConnectionOptions = {
|
||||
driver: {
|
||||
type: "mysql",
|
||||
host: "localhost",
|
||||
port: 3306,
|
||||
username: "root",
|
||||
password: "admin",
|
||||
database: "test"
|
||||
},
|
||||
logging: {
|
||||
logOnlyFailedQueries: true,
|
||||
logFailedQueryError: true
|
||||
},
|
||||
autoSchemaCreate: true,
|
||||
entities: [Post]
|
||||
};
|
||||
|
||||
createConnection(options).then(connection => {
|
||||
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
const post = new Post();
|
||||
post.id = 1;
|
||||
post.type = "person";
|
||||
post.text = "this is test post";
|
||||
|
||||
postRepository.persist(post)
|
||||
.then(savedPost => {
|
||||
console.log("Post has been saved: ", savedPost);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("error: ", error);
|
||||
});
|
||||
|
||||
}, error => console.log("Cannot connect: ", error));
|
||||
15
sample/sample27-composite-primary-keys/entity/Post.ts
Normal file
15
sample/sample27-composite-primary-keys/entity/Post.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {PrimaryColumn, Column, Table} from "../../../src/index";
|
||||
|
||||
@Table("sample27_composite_primary_keys")
|
||||
export class Post {
|
||||
|
||||
@PrimaryColumn("int")
|
||||
id: number;
|
||||
|
||||
@PrimaryColumn()
|
||||
type: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
}
|
||||
@ -56,7 +56,7 @@ export interface QueryRunner {
|
||||
/**
|
||||
* Inserts a new row into given table.
|
||||
*/
|
||||
insert(tableName: string, valuesMap: Object, idColumn?: ColumnMetadata): Promise<any>;
|
||||
insert(tableName: string, valuesMap: Object, generatedColumn?: ColumnMetadata): Promise<any>;
|
||||
|
||||
/**
|
||||
* Performs a simple DELETE query by a given conditions in a given table.
|
||||
|
||||
@ -163,7 +163,7 @@ export class MariaDbQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Insert a new row with given values into given table.
|
||||
*/
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, idColumn?: ColumnMetadata): Promise<any> {
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
if (this.isReleased)
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
@ -173,9 +173,9 @@ export class MariaDbQueryRunner implements QueryRunner {
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`;
|
||||
const result = await this.query(sql, parameters);
|
||||
if (result.info && idColumn) {
|
||||
if (result.info && generatedColumn) {
|
||||
const id = result.info.insertId;
|
||||
if (ColumnTypes.isNumeric(idColumn.type)) {
|
||||
if (ColumnTypes.isNumeric(generatedColumn.type)) {
|
||||
return +id;
|
||||
}
|
||||
return id;
|
||||
|
||||
@ -148,7 +148,7 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Insert a new row with given values into given table.
|
||||
*/
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, idColumn?: ColumnMetadata): Promise<any> {
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
if (this.isReleased)
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
@ -158,7 +158,7 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`;
|
||||
const result = await this.query(sql, parameters);
|
||||
return idColumn ? result.insertId : undefined;
|
||||
return generatedColumn ? result.insertId : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,7 +318,11 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const columnDefinitions = columns.map(column => this.buildCreateColumnSql(column, false)).join(", ");
|
||||
const sql = `CREATE TABLE \`${table.name}\` (${columnDefinitions}) ENGINE=InnoDB;`;
|
||||
let sql = `CREATE TABLE \`${table.name}\` (${columnDefinitions}`;
|
||||
const primaryKeyColumns = columns.filter(column => column.isPrimary);
|
||||
if (primaryKeyColumns.length > 0)
|
||||
sql += `, PRIMARY KEY(${primaryKeyColumns.map(column => `\`${column.name}\``).join(", ")})`;
|
||||
sql += `) ENGINE=InnoDB;`;
|
||||
await this.query(sql);
|
||||
return columns;
|
||||
}
|
||||
@ -507,8 +511,8 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
c += " NOT NULL";
|
||||
if (column.isUnique === true)
|
||||
c += " UNIQUE";
|
||||
if (column.isPrimary === true && !skipPrimary)
|
||||
c += " PRIMARY KEY";
|
||||
// if (column.isPrimary === true && !skipPrimary)
|
||||
// c += " PRIMARY KEY";
|
||||
if (column.isGenerated === true) // don't use skipPrimary here since updates can update already exist primary without auto inc.
|
||||
c += " AUTO_INCREMENT";
|
||||
if (column.comment)
|
||||
|
||||
@ -156,7 +156,7 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Insert a new row with given values into given table.
|
||||
*/
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, idColumn?: ColumnMetadata): Promise<any> {
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
if (this.isReleased)
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
@ -164,10 +164,10 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map(key => ":" + key).join(",");
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ idColumn ? " RETURNING " + this.driver.escapeColumnName(idColumn.name) : "" }`;
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ generatedColumn ? " RETURNING " + this.driver.escapeColumnName(generatedColumn.name) : "" }`;
|
||||
const result = await this.query(sql, parameters);
|
||||
if (idColumn)
|
||||
return result[0][idColumn.name];
|
||||
if (generatedColumn)
|
||||
return result[0][generatedColumn.name];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -141,18 +141,18 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Insert a new row into given table.
|
||||
*/
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, idColumn?: ColumnMetadata): Promise<any> {
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
if (this.isReleased)
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ idColumn ? " RETURNING " + this.driver.escapeColumnName(idColumn.name) : "" }`;
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ generatedColumn ? " RETURNING " + this.driver.escapeColumnName(generatedColumn.name) : "" }`;
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const result: ObjectLiteral[] = await this.query(sql, parameters);
|
||||
if (idColumn)
|
||||
return result[0][idColumn.name];
|
||||
if (generatedColumn)
|
||||
return result[0][generatedColumn.name];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Insert a new row into given table.
|
||||
*/
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, idColumn?: ColumnMetadata): Promise<any> {
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
if (this.isReleased)
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
@ -162,7 +162,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
_this.logger.logQueryError(err);
|
||||
fail(err);
|
||||
} else {
|
||||
if (idColumn)
|
||||
if (generatedColumn)
|
||||
return ok(this["lastID"]);
|
||||
|
||||
ok();
|
||||
|
||||
@ -148,7 +148,7 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
/**
|
||||
* Insert a new row with given values into given table.
|
||||
*/
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, idColumn?: ColumnMetadata): Promise<any> {
|
||||
async insert(tableName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
if (this.isReleased)
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
@ -158,7 +158,7 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`;
|
||||
const result = await this.query(sql, parameters);
|
||||
return idColumn ? result.insertId : undefined;
|
||||
return generatedColumn ? result.insertId : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -134,16 +134,10 @@ export class EntityMetadata {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has a primary column. All user's entities must have a primary column.
|
||||
* Special entity metadatas like for junction tables and closure junction tables don't have a primary column.
|
||||
*/
|
||||
get hasPrimaryColumn(): boolean {
|
||||
return !!this._columns.find(column => column.isPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary column.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get primaryColumn(): ColumnMetadata {
|
||||
const primaryKey = this._columns.find(column => column.isPrimary);
|
||||
@ -153,6 +147,13 @@ export class EntityMetadata {
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary columns.
|
||||
*/
|
||||
get primaryColumns(): ColumnMetadata[] {
|
||||
return this._columns.filter(column => column.isPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has a create date column.
|
||||
*/
|
||||
@ -457,5 +458,11 @@ export class EntityMetadata {
|
||||
&& object.hasOwnProperty(relation.propertyName);
|
||||
});
|
||||
}
|
||||
|
||||
checkIfObjectContainsAllPrimaryKeys(object: ObjectLiteral) {
|
||||
return this.primaryColumns.every(primaryColumn => {
|
||||
return object.hasOwnProperty(primaryColumn.propertyName);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,10 +9,8 @@ import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {Driver} from "../driver/Driver";
|
||||
import {UpdateByInverseSideOperation} from "./operation/UpdateByInverseSideOperation";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {DatabaseConnection} from "../driver/DatabaseConnection";
|
||||
import {QueryRunner} from "../driver/QueryRunner";
|
||||
|
||||
/**
|
||||
@ -479,8 +477,8 @@ export class PersistOperationExecutor {
|
||||
}*/
|
||||
|
||||
// console.log("inserting: ", this.zipObject(allColumns, allValues));
|
||||
let idColumn = metadata.hasPrimaryColumn && metadata.primaryColumn.isGenerated ? metadata.primaryColumn : undefined;
|
||||
return this.queryRunner.insert(metadata.table.name, this.zipObject(allColumns, allValues), idColumn);
|
||||
let generatedColumn = metadata.columns.find(column => column.isGenerated);
|
||||
return this.queryRunner.insert(metadata.table.name, this.zipObject(allColumns, allValues), generatedColumn);
|
||||
}
|
||||
|
||||
private insertIntoClosureTable(operation: InsertOperation, updateMap: ObjectLiteral) {
|
||||
|
||||
@ -23,7 +23,7 @@ export class PlainObjectToDatabaseEntityTransformer<Entity extends ObjectLiteral
|
||||
async transform(plainObject: ObjectLiteral, metadata: EntityMetadata, queryBuilder: QueryBuilder<Entity>): Promise<Entity> {
|
||||
|
||||
// if plain object does not have id then nothing to load really
|
||||
if (!metadata.hasPrimaryColumn || !plainObject.hasOwnProperty(metadata.primaryColumn.name))
|
||||
if (!metadata.checkIfObjectContainsAllPrimaryKeys(plainObject))
|
||||
return Promise.reject<Entity>("Given object does not have a primary column, cannot transform it to database entity.");
|
||||
|
||||
const alias = queryBuilder.alias;
|
||||
@ -31,9 +31,11 @@ export class PlainObjectToDatabaseEntityTransformer<Entity extends ObjectLiteral
|
||||
|
||||
this.join(queryBuilder, needToLoad, alias);
|
||||
|
||||
queryBuilder
|
||||
.where(alias + "." + metadata.primaryColumn.name + "=:id")
|
||||
.setParameter("id", plainObject[metadata.primaryColumn.name]);
|
||||
metadata.primaryColumns.forEach(primaryColumn => {
|
||||
queryBuilder
|
||||
.where(alias + "." + primaryColumn.name + "=:" + primaryColumn.name)
|
||||
.setParameter(primaryColumn.name, plainObject[primaryColumn.name]);
|
||||
});
|
||||
|
||||
return await queryBuilder.getSingleResult();
|
||||
}
|
||||
|
||||
@ -300,7 +300,7 @@ export class SchemaBuilder {
|
||||
*/
|
||||
protected removePrimaryKeys() {
|
||||
const queries = this.entityMetadatas
|
||||
.filter(metadata => !metadata.hasPrimaryColumn)
|
||||
.filter(metadata => !metadata.primaryColumns.length)
|
||||
.map(async metadata => {
|
||||
const dbTable = this.tableSchemas.find(table => table.name === metadata.table.name);
|
||||
if (dbTable && dbTable.primaryKey) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user