I am using TypeORM's repository pattern with a simple table, two columns, estate_num as primary key and estateId with mysql.
The repository.save method is supposed to insert if the record by primary key does not exist, otherwise, update it.
Instead, I get the following error:
query: SELECT `estate`.`estate_num` AS `estate_estate_num`, `estate`.`estateId` AS `estate_estateId` FROM `estate` `estate` WHERE (`estate`.`estate_num` = ?) -- PARAMETERS: ["0"]
query: START TRANSACTION
query: INSERT INTO `estate`(`estate_num`, `estateId`) VALUES (?, ?) -- PARAMETERS: ["0","caae4e67796e4ac000743f009210fcb0467fdf87d0fbbf6107359cf53f5b544b79eefdfdd78f95d50301f20a9c3212cd3970af22b722e59313813e8c5d2732d0"]
query failed: INSERT INTO `estate`(`estate_num`, `estateId`) VALUES (?, ?) -- PARAMETERS: ["0","caae4e67796e4ac000743f009210fcb0467fdf87d0fbbf6107359cf53f5b544b79eefdfdd78f95d50301f20a9c3212cd3970af22b722e59313813e8c5d2732d0"]
error: { Error: ER_DUP_ENTRY: Duplicate entry '0' for key 'PRIMARY'
I can run the SELECT manually, which is what I think determines whether to insert vs update. It returns a row as I would expect with the matching estate_num.
The entity model for this is:
#Entity()
export class Estate {
#PrimaryColumn({
name: 'estate_num',
})
estateNum: number;
#Column({
name: 'estateId',
})
estateId: string;
}
Note: the estate_num is not auto-increment, the key will be always supplied. It is odd, I understand.
Thanks!
I had the same problem. I was doing it wrong.
Before I was doing it multiple ways, but in the wrong way.
First I was doing it like this:
const user = User.create({ id: 1, username: "newName" })
user.save();
giving me an error of ID Duplicate Entry
Second I was doing it like this:
const user = User.create({ id: 1, username: "newName" })
User.save(user);
same error, Duplicate Entry.
Third I was doing it like this:
const user = User.findOneOrFaile(1);
const newUser = { ...user, ..req.body }
User.save(newUser);
same error.
And I just got helped and It should be done like this. You need to get the instance and merged it using Object.assign()
try {
const user: User = await User.findOneOrFail(id);
Object.assign(user, { ...req.body }); <---- SO ITS LIKE THIS
await user.save();
res.json({
meta: {},
payload: {
...req.body
}
});
} catch (error) {
logger.error(error);
res.status(404).json({
error
});
}
From your logs it's clear that you've passed ID as a string -- PARAMETERS: ["0"], but Estate entity estateNum is of type number. Convert the ID to number before passing it to repository.save.
I put together the following ts file to test this:
import 'reflect-metadata';
import { createConnection } from 'typeorm';
import { Estate } from './entity/Estate';
createConnection().then(async connection => {
const estate = new Estate();
estate.estateNum = 0;
estate.estateId = 'alpha';
await connection.getRepository(Estate).save(estate);
estate.estateId = 'beta';
await connection.getRepository(Estate).save(estate);
});
It works as expected (based on the following log output):
query: SELECT "Estate"."estate_num" AS "Estate_estate_num", "Estate"."estateId" AS
"Estate_estateId" FROM "estate" "Estate" WHERE ("Estate"."estate_num" = ?) --
PARAMETERS: [0]
query: BEGIN TRANSACTION
query: INSERT INTO "estate"("estate_num", "estateId") VALUES (?, ?) -- PARAMETERS:
[0,"alpha"]
query: COMMIT
query: SELECT "Estate"."estate_num" AS "Estate_estate_num", "Estate"."estateId" AS
"Estate_estateId" FROM "estate" "Estate" WHERE ("Estate"."estate_num" = ?) --
PARAMETERS: [0]
query: BEGIN TRANSACTION
query: UPDATE "estate" SET "estateId" = ? WHERE "estate_num" = ? -- PARAMETERS:
["beta",0]
query: COMMIT
I did notice that if you you do not await the save results, typeorm will attempt to insert the row twice (because the first save didn't complete before the second started).
Could this be your problem?
(Note I used sqlite3 in this example).
Related
Is there an elegant way to squash these two statements into one, similar to a plain SQL INSERT INTO t1 SELECT 'something', t2.x FROM t2 WHERE ...?:
const commentIds: Array<Pick<SectionComment, 'id'>> =
await this.articleRepository
.createQueryBuilder('article')
.select('sectionComment.id', 'id')
.innerJoin('article.sectionComments', 'sectionComment')
.where(
'article.vaultId = :vaultId and article.valueId = :valueId and sectionComment.userId = :userId',
{
valueId,
vaultId,
userId,
}
)
.getRawMany();
if (commentIds.length) {
await this.userSectionReadRepository
.createQueryBuilder()
.insert()
.into(UserSectionRead)
.values(
commentIds.map((sectionComment) => ({
userId,
commentId: sectionComment.id,
}))
)
.orIgnore()
.execute();
}
The problem is the values() method in InsertQueryBuilder does not accept a subquery function (qb: this) => string like the where() method does.
I'm new to TypeOrm and I'm trying to use an inner join, but the documentation doesn't explain it, so I have an example:
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm";
import {Photo} from "./Photo";
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToMany(type => Photo, photo => photo.user)
photos: Photo[];
}
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
import {User} from "./User";
#Entity()
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#ManyToOne(type => User, user => user.photos)
user: User;
}
If you want to use INNER JOIN instead of LEFT JOIN just use innerJoinAndSelect instead:
const user = await createQueryBuilder("user")
.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })
.where("user.name = :name", { name: "Timber" })
.getOne();
This will generate:
SELECT user.*, photo.* FROM users user
INNER JOIN photos photo ON photo.user = user.id AND photo.isRemoved = FALSE
WHERE user.name = 'Timber'
Can somebody explain me in more detail how it functions, for example, I don't know what the first("user") refers to, I mean, is it a column?, how does it change if I need to use it between columns?
Also, what is the explanation for innerJoinAndSelect, why does it have 3 values, how are the values defined in TypeOrm syntax?
When it is using :name and after {name: "Timber"}, that object is defining :name?, and at last what happens if I don't want get only one and I want to get all the information from table1 where matches with table2, because actually that is what I want to do.
For the First question the param of createQueryBuilder which is user in your case, it's the alias you use in your query:
SELECT user., photo. FROM users user
INNER JOIN photos photo ON photo.user = user.id AND photo.isRemoved = FALSE
WHERE user.name = 'Timber'
const user = await createQueryBuilder("user")
.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })
.where("user.name = :name", { name: "Timber" })
.getOne();
The second question:The first argument is the relation you want to load, the second argument is an alias you assign to this relation's table and the third one is optional for any condition you'd add
the forth question, you should use getMany() instead of getOne();
I didn't understand the third question.
I'm trying to execute raw query in typeorm with parameters
I tried following queries:
insert into data(id, name, gender) values(?, ?,?)
insert into data(id, name, gender) values($1, $2, $3)
insert into data(id, name, gender) values(:id, :name, :gender)
The typeorm code is:
import { getManager } from 'typeorm';
await getManager().query(query, [1, 'test', 'male']);
What is wrong? Is there any other way?
Issue solved with this link. It depends on the underlying database which syntax to use.
https://github.com/typeorm/typeorm/issues/881
as #ashutosh said, it depends on the driver of your database
For mysql/mysql2 you should use ? as placehoder. For example
manager.query('SELECT id FROM foos WHERE createdAt > ? AND id > ?', [new Date(), 3])
For oracle, you can use something like this for me like this :
const querySingleValue = SELECT * FROM TABLE1 WHERE name in (:param) ;
string value :
getManager().query(querySingleValue,[param]) ;
I`m trying to insert data from a json object ,the following code is a bout the table I used
I defined the database helper class like this:
class DatabaseHelper {
static DatabaseHelper _databaseHelper; // Singleton DatabaseHelper
static Database _database; // Singleton Database
String category_Table = 'category_Table';
String category_id = 'category_id';
String device_type_id = 'device_type_id';
String room_id = 'room_id ';
...
await db.execute(
'CREATE TABLE $category_Table($category_id INTEGER PRIMARY KEY UNIQUE , $device_type_id INTEGER, '
'$room_id INTEGER)');
print('category created!');
and here is the insert function
Database db = await this.database;
var result = await db.insert(category_Table, category.toMap());
print('category inserted');
return result;
}
here is the Error
Exception has occurred.
SqfliteDatabaseException (DatabaseException(table category_Table has no column named category_id (code 1): , while compiling: INSERT INTO category_Table (category_id, device_type_id, room_id) VALUES (?, ?, ?)) sql 'INSERT INTO category_Table (category_id, device_type_id, room_id) VALUES (?, ?, ?)' args [1, 1, 1]})
thanks for any helps:)
1) Firstly check if the same variable text you use in your model, Map is the same name with your column
2) update the Database version number
3) Uninstall and reinstall the App on your phone or emulator.
For me, the error was because of missing comma and space after the previous column.
What I had:
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
final String categoryTable = "categoryTable";
final String idColumn = "idColumn";
final String nameColumn = "nameColumn";
final String weightColumn = "weightColumn";
final String typeColumn = "typeColumn";
final String gradeColumn = "gradeColumn";
final String professorColumn = "professorColumn";
final String colorColumn = "colorColumn";
db.execute(
"CREATE TABLE $categoryTable("
"$idColumn INTEGER PRIMARY KEY, "
"$nameColumn TEXT, "
"$weightColumn NUMERIC, "
"$typeColumn NUMERIC" // <- missing comma and space
"$professorColumn TEXT, " // Error when I try to add something to this column
"$colorColumn TEXT)"
);
What solved for me was:
db.execute(
"CREATE TABLE $categoryTable("
"$idColumn INTEGER PRIMARY KEY, "
"$nameColumn TEXT, "
"$weightColumn NUMERIC, "
"$typeColumn NUMERIC, " // <- Fixed the error
"$professorColumn TEXT, "
"$colorColumn TEXT)"
);
I encountered this problem and i tried whole different sort of things but finally, what worked for me is written below
Step 1 : Change the database version number
Earlier it was like this
static final dbVersion = 1;
I later changed it to
static final dbVersion = 5;
This is the function where I initialized the database. I was creating a table with four columns viz id, title, description, and date.
Future _initDatabase() async {
Directory _directory = await getApplicationDocumentsDirectory();
var path = join(_directory.path, dbName);
print(path);
return await openDatabase(path, version: dbVersion,//here is the dbVersion
onCreate: (Database db, int dbVersion) async {
await db.execute(
'CREATE TABLE $table($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDescription TEXT, $colDate TEXT)');
});
}
Step 2: Clear all the app data and then re run the app.
I hope your problem will get solved :)
the variables I used toMAP method was not as same as the database helper class in dictation
Try to delete the application storage and reinstall it,as maybe you already created the database file on your phone in a wrong or old way or even a missing column. A fresh start might work if you properly used SQFLite.
Check if the DB generated was generated with all fields declared. In negative case, you can just delete the DB and run query to create the tables.
Work here in this way
I need to call a stored procedure when the Insert Script trigger is called.
The idea is use the stored procedure to insert rows in 3 tables, including the original table.
I have the following tables
- cars
- makers
- models
the stored procedure receives the car's data, maker's name and model's name. The SP looks in the makers and models table and insert a new record if not found.
This is my script trigger code:
function insert(item, user, request) {
var params = [item.name, item.year, item.maker_name, item.model];
var sql = "exec myCars.sp_insert_car ?, ?, ?, ?";
mssql.query(sql, params,
{
success: function(results) {
request.respond(statusCodes.OK,{
results: results,
count: 1
});
}
});
}
Because the sp_insert_car inserts a row in the cars table, the trigger is called again. The weird thing is that it is also called when a new maker or model is inserted.
Is there a way to disable the second time the trigger is called?
Also, the SP has an output parameter that returns the new car id. Is there a way to get this output parameter?
I appreciate any help you can provide
I think you want to run the request.execute first to get the data into the original table. Then in the success function run your stored procedure to add the data, if missing, to the other tables. Something along the lines of...
function insert(item, user, request) {
request.execute({
success: function() {
var params = [item.name, item.year, item.maker_name, item.model];
var sql = "myCars.sp_insert_car ?, ?, ?, ?";
mssql.query(sql, params, {
success: function(results) {
request.respond(statusCodes.OK,{
results: results,
count: 1
});
}
});
}
});
}
This also lets you grab the id of the newly inserted row in the original table, item.id, for use as a passed-in parameter for the stored procedure if you need it.
add this line ...
var mssql=request.service.mssql;
above underline
mssql.query ("EXEC dbo.Sp_User_Authentication ?, ?",request.query.username,request.query.password)