mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
fixes in query builder - insert and update methods
This commit is contained in:
parent
22c9a2a8d6
commit
e952161f41
@ -51,6 +51,8 @@ More env variable names you can find in `ConnectionOptionsEnvReader` class.
|
||||
* fixed how orm creates default values for SqlServer - now it creates constraints for it as well
|
||||
* migrations interface has changed - now `up` and `down` accept only `QueryRunner`. To use `Connection` and `EntityManager` use properties
|
||||
of `QueryRunner`, e.g. `queryRunner.connection` and `queryRunner.manager`
|
||||
* now `update` method in `QueryBuilder` accepts `Partial<Entity>` and property names used in update map are
|
||||
column property names and they are automatically mapped to column names
|
||||
|
||||
### DEPRECATIONS
|
||||
|
||||
@ -67,6 +69,8 @@ of `QueryRunner`, e.g. `queryRunner.connection` and `queryRunner.manager`
|
||||
* now ormconfig is read from `.env`, `.js`, `.json`, `.yml`, `.xml` formats
|
||||
* all database-specific types are supported now
|
||||
* now migrations generation is supported. Use `typeorm migrations:generate` command
|
||||
* `getGeneratedQuery` was renamed to `getQuery` in `QueryBuilder`
|
||||
* `getSqlWithParameters` was renamed to `getSqlAndParameters` in `QueryBuilder`
|
||||
|
||||
### OTHER API CHANGES
|
||||
|
||||
|
||||
@ -443,6 +443,13 @@ export class EntityMetadata {
|
||||
return this.compareIds(firstEntityIds, secondEntityIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds column with a given property name.
|
||||
*/
|
||||
findColumnWithPropertyName(propertyName: string) {
|
||||
return this.columns.find(column => column.propertyName === propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds relation with the given name.
|
||||
*/
|
||||
@ -603,5 +610,4 @@ export class EntityMetadata {
|
||||
this.relations.forEach(relation => OrmUtils.mergeDeep(map, relation.createValueMap(relation.propertyPath)));
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,8 +11,7 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Specifies FROM which entity's table select/update/delete will be executed.
|
||||
* Also sets a main string alias of the selection data.
|
||||
* Specifies INTO which entity's table insertion will be executed.
|
||||
*/
|
||||
into(entityTarget: Function|string): this {
|
||||
return this.setMainAlias(entityTarget);
|
||||
|
||||
@ -261,7 +261,7 @@ export abstract class QueryBuilder<Entity> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets given parameter's value.
|
||||
* Sets parameter name and its value.
|
||||
*/
|
||||
setParameter(key: string, value: any): this {
|
||||
this.expressionMap.parameters[key] = value;
|
||||
@ -304,23 +304,13 @@ export abstract class QueryBuilder<Entity> {
|
||||
* Parameters in the query are escaped for the currently used driver.
|
||||
*/
|
||||
getSql(): string {
|
||||
let sql = this.createSelectExpression();
|
||||
sql += this.createJoinExpression();
|
||||
sql += this.createWhereExpression();
|
||||
sql += this.createGroupByExpression();
|
||||
sql += this.createHavingExpression();
|
||||
sql += this.createOrderByExpression();
|
||||
sql += this.createLimitOffsetExpression();
|
||||
sql += this.createLockExpression();
|
||||
sql = this.createLimitOffsetOracleSpecificExpression(sql);
|
||||
[sql] = this.connection.driver.escapeQueryWithParameters(sql, this.expressionMap.parameters);
|
||||
return sql.trim();
|
||||
return this.connection.driver.escapeQueryWithParameters(this.getQuery(), this.expressionMap.parameters)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets generated sql without parameters being replaced.
|
||||
* Gets generated sql query without parameters being replaced.
|
||||
*/
|
||||
getGeneratedQuery(): string {
|
||||
getQuery(): string {
|
||||
let sql = this.createSelectExpression();
|
||||
sql += this.createJoinExpression();
|
||||
sql += this.createWhereExpression();
|
||||
@ -336,7 +326,7 @@ export abstract class QueryBuilder<Entity> {
|
||||
/**
|
||||
* Gets sql to be executed with all parameters used in it.
|
||||
*/
|
||||
getSqlWithParameters(options?: { skipOrderBy?: boolean }): [string, any[]] {
|
||||
getSqlAndParameters(options?: { skipOrderBy?: boolean }): [string, any[]] {
|
||||
let sql = this.createSelectExpression();
|
||||
sql += this.createJoinExpression();
|
||||
sql += this.createWhereExpression();
|
||||
@ -354,7 +344,7 @@ export abstract class QueryBuilder<Entity> {
|
||||
* Executes sql generated by query builder and returns raw database results.
|
||||
*/
|
||||
async execute(): Promise<any> {
|
||||
const [sql, parameters] = this.getSqlWithParameters();
|
||||
const [sql, parameters] = this.getSqlAndParameters();
|
||||
try {
|
||||
return await this.queryRunner.query(sql, parameters); // await is needed here because we are using finally
|
||||
|
||||
@ -564,21 +554,22 @@ export abstract class QueryBuilder<Entity> {
|
||||
}
|
||||
return "SELECT " + selection + " FROM " + this.escapeTable(tableName) + " " + ea(aliasName) + lock;
|
||||
case "delete":
|
||||
return "DELETE FROM " + et(tableName);
|
||||
// return "DELETE " + (alias ? ea(alias) : "") + " FROM " + this.escapeTable(tableName) + " " + (alias ? ea(alias) : ""); // TODO: only mysql supports aliasing, so what to do with aliases in DELETE queries? right now aliases are used however we are relaying that they will always match a table names
|
||||
return "DELETE FROM " + et(tableName) + this.createWhereExpression(); // TODO: only mysql supports aliasing, so what to do with aliases in DELETE queries? right now aliases are used however we are relaying that they will always match a table names // todo: replace aliases in where to nothing?
|
||||
case "update":
|
||||
let valuesSet = this.expressionMap.valuesSet as ObjectLiteral;
|
||||
if (!valuesSet)
|
||||
throw new Error(`Cannot perform update query because updation values are not defined.`);
|
||||
|
||||
const updateSet = Object.keys(valuesSet).map(key => ea(key) + "=:updateSet__" + key);
|
||||
const params = Object.keys(valuesSet).reduce((object, key) => {
|
||||
// todo: map propertyNames to names ?
|
||||
object["updateSet__" + key] = valuesSet[key];
|
||||
return object;
|
||||
}, {} as ObjectLiteral);
|
||||
this.setParameters(params);
|
||||
return "UPDATE " + this.escapeTable(tableName) + " " + (aliasName ? ea(aliasName) : "") + " SET " + updateSet;
|
||||
const updateSet: string[] = [];
|
||||
Object.keys(valuesSet).forEach(columnProperty => {
|
||||
const column = this.expressionMap.mainAlias!.metadata.findColumnWithPropertyName(columnProperty);
|
||||
if (column) {
|
||||
const paramName = "updated__" + column.databaseName;
|
||||
this.setParameter(paramName, valuesSet[column.propertyName]);
|
||||
updateSet.push(ea(column.databaseName) + "=:" + paramName);
|
||||
}
|
||||
});
|
||||
return `UPDATE ${this.escapeTable(tableName)} ${aliasName ? ea(aliasName) : ""} SET ${updateSet.join(", ")}` + this.createWhereExpression(); // todo: replace aliases in where to nothing?
|
||||
case "insert":
|
||||
let valuesSets: ObjectLiteral[] = []; // todo: check if valuesSet is defined and has items if its an array
|
||||
if (this.expressionMap.valuesSet instanceof Array && this.expressionMap.valuesSet.length > 0) {
|
||||
@ -589,18 +580,19 @@ export abstract class QueryBuilder<Entity> {
|
||||
throw new Error(`Cannot perform insert query because values are not defined.`);
|
||||
}
|
||||
|
||||
const parameters: ObjectLiteral = {};
|
||||
const columns = Object.keys(valuesSets[0]).map(columnName => ea(columnName));
|
||||
const columns: ColumnMetadata[] = [];
|
||||
Object.keys(valuesSets[0]).forEach(columnProperty => {
|
||||
const column = this.expressionMap.mainAlias!.metadata.findColumnWithPropertyName(columnProperty);
|
||||
if (column) columns.push(column);
|
||||
});
|
||||
const values = valuesSets.map((valueSet, key) => {
|
||||
return "(" + Object.keys(valueSet).map(columnName => {
|
||||
const paramName = ":inserted_" + key + "_" + columnName;
|
||||
parameters[paramName] = valueSet[columnName];
|
||||
return paramName;
|
||||
}).join(",") + ")";
|
||||
return "(" + columns.map(column => {
|
||||
const paramName = ":inserted_" + key + "_" + column.databaseName;
|
||||
this.setParameter(paramName, valueSet[column.propertyName]);
|
||||
return paramName;
|
||||
}).join(",") + ")";
|
||||
}).join(", ");
|
||||
|
||||
this.setParameters(parameters);
|
||||
return `INSERT INTO ${this.escapeTable(tableName)}(${columns}) VALUES ${values}`;
|
||||
return `INSERT INTO ${this.escapeTable(tableName)}(${columns.map(column => column.databaseName)}) VALUES ${values}`;
|
||||
}
|
||||
|
||||
throw new Error("No query builder type is specified.");
|
||||
|
||||
@ -816,7 +816,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> {
|
||||
.select(countSql);
|
||||
countQueryBuilder.expressionMap.ignoreParentTablesJoins = true;
|
||||
|
||||
const [countQuerySql, countQueryParameters] = countQueryBuilder.getSqlWithParameters();
|
||||
const [countQuerySql, countQueryParameters] = countQueryBuilder.getSqlAndParameters();
|
||||
|
||||
try {
|
||||
const results = await this.queryRunner.query(countQuerySql, countQueryParameters);
|
||||
@ -861,7 +861,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> {
|
||||
if (this.expressionMap.skip || this.expressionMap.take) {
|
||||
// we are skipping order by here because its not working in subqueries anyway
|
||||
// to make order by working we need to apply it on a distinct query
|
||||
const [sql, parameters] = this.getSqlWithParameters({ skipOrderBy: true });
|
||||
const [sql, parameters] = this.getSqlAndParameters({ skipOrderBy: true });
|
||||
const [selects, orderBys] = this.createOrderByCombinedWithSelectExpression("distinctAlias");
|
||||
|
||||
const distinctAlias = this.escapeTable("distinctAlias");
|
||||
@ -928,7 +928,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> {
|
||||
clonnedQb.expressionMap.extraAppendedAndWhereCondition = condition;
|
||||
const [queryWithIdsSql, queryWithIdsParameters] = clonnedQb
|
||||
.setParameters(parameters)
|
||||
.getSqlWithParameters();
|
||||
.getSqlAndParameters();
|
||||
rawResults = await this.queryRunner.query(queryWithIdsSql, queryWithIdsParameters);
|
||||
const rawRelationIdResults = await relationIdLoader.load(rawResults);
|
||||
const rawRelationCountResults = await relationCountLoader.load(rawResults);
|
||||
@ -945,7 +945,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> {
|
||||
|
||||
} else {
|
||||
|
||||
const [sql, parameters] = this.getSqlWithParameters();
|
||||
const [sql, parameters] = this.getSqlAndParameters();
|
||||
|
||||
const rawResults = await this.queryRunner.query(sql, parameters);
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ describe("github issues > #521 Attributes in UPDATE in QB arent getting replaced
|
||||
.where("name = :name", {
|
||||
name: "Toyota",
|
||||
})
|
||||
.getSqlWithParameters();
|
||||
.getSqlAndParameters();
|
||||
return parameters.length.should.eql(2);
|
||||
})));
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user