Sequelize transaction executes a rollback but database not - mysql

Here is a snapshot of the database Tables and constraints in PostgreSQL:
CREATE TABLE garage (
garage_id integer NOT NULL,
garage_name text,
garage_description text
);
CREATE TABLE auto (
auto_id serial PRIMARY KEY,
auto_name text,
auto_description text,
auto_price numeric(20,2),
auto_category text,
garage_id integer
);
ALTER TABLE ONLY auto
ADD CONSTRAINT auto_garage_id_fkey FOREIGN KEY (gerage_id)
REFERENCES gerage(gerage_id);
Here I am defining the database objects in nodejs using Sequelize:
var Auto = sequelize.define('auto', {
auto_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
auto_name: Sequelize.STRING,
auto_description: Sequelize.STRING,
auto_price: Sequelize.NUMERIC,
auto_category: Sequelize.STRING,
garage_id: Sequelize.INTEGER
}, {freezeTableName: true,
tableName: "auto",
timestamps: false,
paranoid: false,
underscored: true});
function createAutos(auto_1,auto_2){
return sequelize.transaction().then(function(t){
return Auto.create(auto_1,
{fields: ['auto_name', 'auto_description', 'auto_price', 'auto_category', 'garage_id']},
{transaction: t}
).then(function(){
return Auto.create(auto_2,
{fields: ['auto_name', 'auto_description', 'auto_price', 'auto_category', 'garage_id']},
{transaction: t});
}).then(function(){
t.commit();
}).catch(function(err){
t.rollback();
});
});
}
Here I am executing the following method to test the transactional createAutos():
createAutos({
"auto_name": 'bmw',
"auto_description": 'sport',
"auto_price":4.95,
"auto_category": 'luxes',
"garage_id": 1 // Exists in the database
},{
"auto_name": 'SSSS',
"auto_description": 'sport',
"auto_price":4.95,
"auto_category": 'luxes',
"garage_id": 200 // Doesn't exist in the database.
});
When executed, I can see the following output log on the console:
Executing (bf8cb998-657b-49b7-b29e-957bcf770b40): START TRANSACTION;
Executing (bf8cb998-657b-49b7-b29e-957bcf770b40): SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Executing (bf8cb998-657b-49b7-b29e-957bcf770b40): SET autocommit = 1;
Executing (default): INSERT INTO "auto" ("auto_name","auto_description","auto_price","auto_category","garage_id") VALUES ('bmw','sport',4.95,'luxes',1) RETURNING *;
Executing (default): INSERT INTO "auto" ("auto_name","auto_description","auto_price","auto_category","garage_id") VALUES ('SSSS','sport',4.95,'luxes',200) RETURNING *;
Executing (bf8cb998-657b-49b7-b29e-957bcf770b40): ROLLBACK;
But in the database the first auto "bmw" gets written despite the ROLLBACK of the whole transaction.
I tested the program with PostgreSQL 9.3.10, Ubuntu, MySQL 5.5.46, sequelize 3.13.0 & 3.0.0
Does anyone notice here a mistake in the code or is it a bug...?

In your output log, we can see two transactions, one bf8cb998-657b-49b7-b29e-957bcf770b40 and the other Default. The first one is rollbacked, the second one is not, and that's where you insert.
You try to pass the transaction to the Create function but it looks like Sequelize doesnt get it. Some releases ago, the syntax for transactions was changed, can you try to put the 'transaction: t' property in the second object and not the third ? Something like this:
Auto.create(auto_1,
{fields: ['auto_name', 'auto_description', 'auto_price', 'auto_category', 'garage_id'],
transaction: t}

Related

Sequelize - how to define a trigger in a model?

I am currently working with sequelize on RDS Aurora DB and I need to track which records in which tables were deleted - for this, I created a new table dbLog. Now I need to add a trigger to the database which saves the id of the record into dbLog table whenever that record in table t1 gets deleted. Basically, I need to cover two scenarios for already deployed databases and those who dont yet exist.
Database already exists. This is easy since I can create the trigger by raw query like this
CREATE TRIGGER trigger AFTER DELETE ON t1 FOR EACH ROW
INSERT INTO dbLog ( id, tableName, status, updatedAt )
VALUES (OLD.id, 't1', 'D', NOW())`
Database doesn't exist. This is problematic since it gets created by initiation of the model and then sequelize.sync(). So I cant just call a raw query, instead, I need to define this trigger in the model for t1 table. This is how I initiate the table (simplified)
t1.init(
{
id: {
type: new DataTypes.BIGINT,
autoIncrement: true,
primaryKey: true
},
name: {
type: new DataTypes.STRING,
allowNull: false
}
},
{
sequelize,
tableName: 't1',
}
);
The problem is that I dont know how to create that trigger. I tried putting something like this into the attributes section of t1.Init. But there is some problem, when I check the database for triggers, none was created. How can I fix it? And are even triggers created by this way 1:1 equivalent of triggers created by raw query? Thanks a lot.
hooks: {
afterDestroy: function(t1) {
DbLog.create({
id: t1.id,
tableName: 't1',
status: 'D',
updatedAt: '2015-10-02'
})
}
}
You should create triggers in a DB manually by executing a raw SQL query. If you are using migrations then just create a migration with a trigger creation (also a raw SQL query).

how i can fix the SequelizeDatabaseError: Field 'id' doesn't have a default value?

im working on a To do list in node using MVC node express pug and sequelize as ORM and the views part i made it with pug, so when i start my app works ok but when i try to create a new proyect and this proyect its gone to insert in the db the error 1364 appears
in principle it is a personal project to add to my portfolio, I saw that there is a possible solution by deactivating the STRICT SQL mode but I would like to know if there is another solution
model code Proyect.js
const Sequelize = require('sequelize');
//conect db
const db = require('../config/db');
const Proyects = db.define('proyects', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
name: Sequelize.STRING,
url: Sequelize.STRING
});
module.exports = Proyects;
I expect with this model create a register in my table but when I try the actual error its the next
Executing (default): CREATE TABLE IF NOT EXISTS `proyects` (`id` INTEGER NOT NULL auto_increment , `name` VARCHAR(255), `url` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `proyects`
conected to db
Executing (default): INSERT INTO `proyects` (`id`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?);
{ SequelizeDatabaseError: Field 'id' doesn't have a default value
code: 'ER_NO_DEFAULT_FOR_FIELD',
errno: 1364,
sqlState: 'HY000',
sqlMessage: 'Field \'id\' doesn\'t have a default value',
sql:
'INSERT INTO `proyects` (`id`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?);' },
As per my understanding this is a problem with mysql database. The 'id' field in your table is marked as not null, and while inserting values you are not providing any value for the 'id' field.
You can avoid facing this error either allowing null values in the 'id' field or providing a default value (You can use defaultValue flag) while creating the table. Otherwise you already got an alternate solution.
I got this error initially as I had misspelled autoIncrement. To fix this, I used {force:true} as below after applying correct spelling and once the problem was gone, removed it from the sync() method.
I will never use {force:true} in my production deployment as it drops the table (and deletes all your records).
in app.js, call sync method as follows
sequelize.sync({force:true})
.then(result => {
//console.log(result);
app.listen(3000);
})
.catch(err => {
console.log(err);
});
you can install uuid
npm install uuid
then
const uuid = require('uuid')
use it anywhere you want for example
id:uuid.v4()

SailsJS update and mySQL custom ID column not working

In SailsJS, I created a model Profiles including a custom primary key as follows:
module.exports = {
tableName: 'tbl_profiles',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
user_id: {
type: 'integer',
size: 11,
columnName: 'user_id',
unique: true,
primaryKey: true,
},
...
Now, when calling the blueprint route to update a user profile, I get the following error:
ER_BAD_FIELD_ERROR: Unknown column 'tbl_profiles.id' in 'where clause'
Debugging this down (and seeing question SailsJS and mySQL custom ID name not working with blue prints not helping) I found out that the update is carried through all right in the db and that the record is changed but in the controller callback function an error and status 400 is raised nevertheless:
Profiles.update({user_id: req.param('id')}, req.body).exec(function(err, profile) {
if (err) {
return res.status(400).json(err);
} else {
return res.status(200).json(profile);
}
});
Tracing down the SQL involved in /node_modules/sails-mysql/node_modules/mysql/lib/protocol/sequences/Sequence.js:48:14, it seems the following statement is executed just after the update is finished (note the final WHERE clause):
SELECT `tbl_profiles`.`user_id`,
`tbl_profiles`.`lastName`,
`tbl_profiles`.`firstName`,
`tbl_profiles`.`date_of_birth`,
`tbl_profiles`.`address_line1`,
`tbl_profiles`.`address_line2`,
`tbl_profiles`.`zip_code`,
`tbl_profiles`.`city`,
`tbl_profiles`.`gender`,
`tbl_profiles`.`country_id`,
`tbl_profiles`.`phone`,
`tbl_profiles`.`user_id`
FROM `tbl_profiles` AS `tbl_profiles`
WHERE `tbl_profiles`.`id` = undefined
Where could I set SailsJS/Waterline to use the custom column ID? Setting autoPK true either in the beginning or the end of the model wouldn't do the trick..

Defining Sequelize model for existing MySQL database

Please advise on the following issue. I'd like to use Sequelize to work with existing database.
Stack used:
- Node.js v6.10.3
- MySQL 2nd Gen 5.7 running on Google Cloud
- Sequelize 4.7.5
MySQL contains 1 table - 'test_table' with columns 'id', 'content', 'status' and holds some data.
I want to define testTable model which would "explain" my MySQL 'test_table' for sequelize, so I run:
const connection = new Sequelize("dbName", "user", "pass", {
host: xxx.xxx.xxx.xxx,
dialect: mysql
});
const testTable = connection.define('test_table', {
id: {type: Sequelize.INTEGER,
allowNull: false, unique: true,
primaryKey: true, autoIncrement:
true},
content: {type: Sequelize.STRING},
status: {type: Sequelize.STRING},
});
First surprise I get is that from this moment onwards Node completely ignores existence of 'test_table' and creates its 'analogue' (???).
C:\Users\dmitri\Desktop\app>node query.js
Executing (default): CREATE TABLE IF NOT EXISTS `test_tables` (`id` INTEGER NOT NULL auto_increment UNIQUE , `content` VARCHAR(255), `status` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DAT
ETIME NOT NULL, UNIQUE `test_tables_id_unique` (`id`), PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SELECT `id`, `content`, `status`, `createdAt`, `updatedAt` FROM `test_tables` AS `test_table`;
Executing (default): SHOW INDEX FROM `test_tables`
Now MySQL contains two tables:
- 'test_table' (singular)
- 'test_tables' (plural) identical to "test_table" but empty.
I am trying to SELECTid,contentFROMtest_table` by executing the following Sequelize code:
testTable
.findAll({attributes: ['id', 'content']})
.then(result=>{
console.log(result)
})
Instead of returning contents of 'test_table', node keeps ignoring it:
C:\Users\dmitri\Desktop\app>node query.js
Executing (default): SELECT id, content FROM test_tables AS test_table;
and returns [].
What may be the reason it voluntarily jumps to query 'test_tables' instead of 'test_table'? Am I completely missing something here?
Many thanks!
You should have specified the table name in the options. Delete this new table, add the table name option and re-lift the app.
connection.define('test_table', {
//.... your fields
}, {
tableName: 'test_table'
});
On a side note, I like to have my models named with camelcase. TestTable. Same goes for connection.define('TestTable'...
EDIT:
As your comments I clarify a couple of things:
You can/have to specify the table name when defining the model. Look at my example. Specifying the tableName property guarantees the pluralization doesnt screw you over.
You shouldnt do sequelize.sync() (unless you know what it does and you want it). Otherwise you might get problems (like sequelize creating another table because it cant find the one it looks for).
Defining the models is something you have to do if you want to use sequelize. Otherwise it doesnt make sense and you should use just mysql2. You have to define the models to match the tables in your database.

sequelize returns null for column 'id' in the dataValues of a query result.

I am using the sequelize in combination with my nodejs typescript application and am facing the following issue. I created a class called OrmMapper which is responsible for creating my tables. I created a table called "PCS" and initialized it with the following code:
PCS.init({
id: {
primaryKey : true,
autoIncrement: true,
type: DataTypes.INTEGER
},
owner : DataTypes.INTEGER,
created: DataTypes.DATE,
year : DataTypes.STRING
}, {sequelize});
The generated DDL is fine and the problem occurs whenever I run this piece of code:
handleGetByOwner(request: TrackerRequest, res:Response):void{
this.pcsModel.findAll({where:{owner: request.queryParams.ownerId}}).then((dataPackage:any)=>{
let PCSS = [];
console.log(dataPackage);
for(let pcs of dataPackage){
PCSS.push(pcs.dataValues);
}
res.status(200).send(new TrackerResponse({success: true, payload: PCSS, timeStamp: new Date()}));
});
}
which generates the following SQL:
SELECT `id`, `owner`, `created`, `year`, `createdAt`, `updatedAt` FROM `PCs` AS `PCS` WHERE `PCS`.`owner` = '1';
When I look in my database, all the data inserted is correct, when i execute the above SQL statement everything works. However when I run the server code above the result of my query is:
All of the datavalues are correct, expect for id which is null.