Expressjs: Open view after foreach with mysql result. - mysql

I'm trying to loop with a result from a mysql query and execute a new query in that loop. After looping thru the results I want to open a new view with the result of the first and second query.
Now my problem: The looping al works and I can open the new view, but when opening the view I only get the result of the first query and part of the second query, it opens the view before running the second query another time.
My code:
function getActivitiesDate(req,res, rows)
{
var rows2 = [];
rows.forEach(function getoutput(item) {
console.log("getActivitiesDate openend");
connectionpool.getConnection(function(err, connection3) {
console.log('Trying to connect to activities date');
if (!err) {
console.log('Trying to execute query for activities now'),
connectionpool.query('SELECT * FROM CALENDAR_ACTIVITY WHERE ACTIVITY_ID =' + connection3.escape(item.ACTIVITY_ID),
function (err, rows2) {
if (err) {
console.error(err);
res.statusCode = 500;
res.send({
result: 'error',
err: err.code
});
connection3.release();
console.log('It doesnt work error 500');
}
if (rows2.length == 0) {
res.render('index', {title: 'Index - Calendar Activity not found'});
}
console.log('Show activities + dates');
// connection3.release();
});
} else {
console.error('CONNECTION error: ', err);
res.statusCode = 503;
res.send({
result: 'error',
err: err.code
});
console.log('It does not work...');
}
});
});
console.log('This is the result of rows2Result: ');
console.log(rows2);
res.render('viewUserActivities', {activities:rows, activityDates:rows2 , title: 'Created activities'});
}
the "rows" attribute contains 2 rows for the moment (this could be more, or less, that's why I have the "rows.forEach") but I'm not sure how to pass the result of "rows2" to the new view AFTER it's looped thru the foreach loop for every result.
Any help would be greatly appreciated. I've it's not clear, or you need more information don't hesitate to ask.

I do not have sufficient rep to comment so I might be misinterpreting the question, but can you render the view after the forEach callback has finished?
Like so:
function getActivitiesDate(req,res, rows)
{
var rows2 = [];
rows.forEach(function getoutput(item) {
console.log("getActivitiesDate openend");
connectionpool.getConnection(function(err, connection3) {
console.log('Trying to connect to activities date');
if (!err) {
console.log('Trying to execute query for activities now'),
connectionpool.query('SELECT * FROM CALENDAR_ACTIVITY WHERE ACTIVITY_ID =' + connection3.escape(item.ACTIVITY_ID),
function (err, rows2) {
if (err) {
console.error(err);
res.statusCode = 500;
res.send({
result: 'error',
err: err.code
});
connection3.release();
console.log('It doesnt work error 500');
}
else if (rows2.length == 0) {
res.render('index', {title: 'Index - Calendar Activity not found'});
} else {
console.log('This is the result of rows2Result: ');
console.log(rows2);
res.render('viewUserActivities', {activities:rows, activityDates:rows2 , title: 'Created activities'});
console.log('Show activities + dates');
}
// connection3.release();
});
} else {
console.error('CONNECTION error: ', err);
res.statusCode = 503;
res.send({
result: 'error',
err: err.code
});
console.log('It does not work...');
}
});

Related

How to execute two mysql queries using promise in nodejs?

Here is what i going to achieve, i want to have an JSON data that returned from my node.js server is joined based on the value of first mysql queries (array JSON data)
if i just want to execute two mysql queries i just enable multipleStatements: true then the code will be like this :
app.post('/product', function (req, res) {
connection.query('call getProductList; call rowCountTotal', function (err, rows, fields) {
if (!err) {
var response = [];
if (rows.length != 0) {
response.push({ 'result': 'success', 'data': rows });
} else {
response.push({ 'result': 'error', 'msg': 'No Results Found' });
}
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(response));
} else {
res.status(400).send(err);
}
});
});
than the data will showed up two JSON that are separated in two arrays, but what i want to build here is one JSON with multiple array JSON data, which is looked like this :
Sample JSON that i want :
[
{
"product_id":"1",
"product_name":"MX-001",
"product_attachment":[
{
"product_id":"1",
"product_attachment_id":"1",
"file_name":"maxgrand5.jpg",
"file_path":"assets"
}
]
}
]
And here is what i trying to do in my node.js server side code, i trying to use
Promise.all (i think this code i should use right?) :
return new Promise(function(resolve, reject) {
Promise.all(connection.query('call getProductSingle("'+ product_series +'")', function (err, rows, fields) {
if (!err) {
var response = [];
if (rows.length != 0) {
response.push({ 'result': 'success', 'data': rows });
} else {
response.push({ 'result': 'error', 'msg': 'No Results Found' });
}
connection.query('call getProductAttachment("'+ rows[0][0].product_id +'")', function (err, rowsAttachment, fields) {
if (!err) {
console.log("second query");
if (rowsAttachment.length != 0) {
response.push({'product_attachment': rowsAttachment });
} else {
response.push({ 'result': 'error', 'msg': 'No Results Found' });
}
}
});
console.log("outside second query");
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(response));
} else {
res.status(400).send(err);
}
console.log("last");
if (err) {
return reject(err);
}
resolve(res);
}));
});
here is my Stored Procedure result which named in 'getProductSingle' :
product_id = 1
product_name = MX-001
and here is my second procedure result 'getProductAttachment' :
product_id = 1
file_name = maxgrand5.jpg
file_path = assets
product_attachment_id = 1
one single product_id can have more than 1 product_attachment_id
how can i get the data joined?
I just updated my question, the problem is the second query is too late when i make the request, i should use promise to make it not late, how to do this?
First I created the query, in which the product_single table is joined to the product_attachments, maybe you want to restrict it with an WHERE clause or a paging mechanism with LIMIT and OFFSET:
SELECT ps.product_id, ps.product_name, pa.product_attachment_id,
pa.file_name, pa.file_path
FROM product_single ps
LEFT JOIN product_attachment pa
ON ps.product_id = pa.product_id
ORDER by ps.product_id,pa.product_attachment_id;
In the following code I will refer to this query with a call product_join_att.
return new Promise(function(resolve, reject) {
var result_products = [];
var result_attachments = [];
var old_id = undefined;
var old_name = undefined;
var new_id = undefined;
var row_index = 0;
connection.query('call product_join_att', function (err, rows, fields) {
if (err) {
reject(err);
return;
} else if (rows.length == 0) {
reject(new Error('No Results found'));
return;
}
while (row_index < rows.length ) {
new_id = rows[row_index].product_id;
if (old_id !== new_id) { // any new line with an new id...
if (typeof old_id !== 'undefined') { // when the old line is existing
result_products.push( // push the product
{"product_id": old_id.toString(),
"product_name":old_name,
"product_attachment": result_attachments
});
}
old_id = new_id; // remember the new_id
old_name = rows[row_index].product_name;// and name
product_attachments = []; // and initialize attachments.
}
product_attachments.push({ // provide the inner attachment informations.
"product_id": new_id,
"product_attachment_id" : rows[row_index].product_attachment_id,
"file_name" : rows[row_index].file_name;
"file_path" : rows[row_index].file_path;
});
row_index++; // and go to the next row.
}
if (typeof old_id !== 'undefined') { // if there are still data
result_products.push( // push the last line also.
{"product_id": old_id.toString(),
"product_name":old_name,
"product_attachment": result_attachments
});
}
} // query
resolve(result_products);
} // end of promise...
i figure it out with simplier solution but this way is not a "promise" way to get it done, so decide it which to use if you guys face the problem like this. Since i dont need many data to loop, just a single root array JSON with one dimension JSON array, i will just put it this way :
app.post('/getsingleproduct', function (req, res) {
var product_series = req.body.product_series;
connection.query('call getProductSingle("'+ product_series +'")', function (err, rows, fields) {
if (!err) {
var response = [];
if (rows.length != 0) {
response.push({ 'result': 'success', 'data': rows[0] });
} else {
response.push({ 'result': 'error', 'msg': 'No Results Found' });
}
connection.query('call getProductAttachment("'+ rows[0][0].product_id +'")', function (err, rowsAttachment, fields) {
if (!err) {
if (rowsAttachment.length != 0) {
response[0].data[0]['product_attachment']= rowsAttachment[0];
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(response));
} else {
response.push({ 'result': 'error', 'msg': 'No Results Found' });
res.status(400).send(err);
}
}
});
} else {
res.status(400).send(err);
}
});
});
this is a non-promise way, if you need promise way, look for #Myonara answer, i think that the best

AWS Lambda Invoke does not execute the lambda function

I created 4 Lambda functions to process information that will be written into a MySQL table. the first three function just select, insert and update a MYSQL table record respectively.
I then created a 4th function to accept the record detail as part of the event parameter. This function will first try to select the record by invoking the first lambda function and if it finds it, will update the record on the table using the update lambda function. If it does not find it, it will invoke the insert function to add the record. I am using pool.query on the 3 functions that manipulates the MySQL table. I am also using lambda.invoke to call those three functions from the 4th function.
I was able to successfully test the 4th function locally by passing the record details as parameter and it was able to successfully call the three Lambda function and update the mySQL table record. The problem that I am having is that when I upload the function in AWS Lambda, it does not invoke any of the three functions. I am not seeing any errors in the log so I don't know how to check where the problem is. Here's ,y code that invokes the other functions:
exports.handler = (event, context, callback) => {
var err = null;
var payload = {
qryString : event.qryString,
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
var params = {
FunctionName: 'getInventory',
Payload: JSON.stringify(payload)
}
console.log(' before invoke ' + JSON.stringify(params) )
lambda.invoke(params, function(err, data) {
console.log(' aftr invoke ' + JSON.stringify(params) )
if (err) {
console.log('err ' + err, err.stack); // an error occurred
event.message = err + ' query error';
}
else {
console.log('success' + JSON.stringify(data));
console.log(' status code ' + JSON.stringify(data.StatusCode));
console.log(' Payload ' + JSON.stringify(JSON.parse(data.Payload)));
var rowsTemp = JSON.parse(data.Payload);
var rows = rowsTemp.data;
if (!rowsTemp.recordExist) {
console.log('insert')
// Update inventory record only if quantity is not negative
var newQuantity = 0
newQuantity = parseFloat(event.updaterecord.quantity);
if (Math.sign(newQuantity) === 1) {
var payload = {
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
console.log('insert' + JSON.stringify(payload));
var params = {
FunctionName: 'insertInventory',
Payload: JSON.stringify(payload)
}
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
}
}
else {
newQuantity = 0
newQuantity = parseFloat(event.updaterecord.quantity) + parseFloat(rows[0].quantity);
if (Math.sign(newQuantity) === 1) {
event.updaterecord.quantity = newQuantity;
} else {
// Set to zero if the result is negative
event.updaterecord.quantity = 0;
}
console.log('value ' + JSON.stringify(newQuantity) + ' updaterecord' + JSON.stringify(event.updaterecord.quantity) );
var payload = {
qryString : event.qryString,
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
console.log('update' + JSON.stringify(payload));
var params = {
FunctionName: 'updateInventory',
Payload: JSON.stringify(payload)
}
console.log(' before invoke ' + JSON.stringify(params) )
lambda.invoke(params, function(err, data) {
console.log(' after invoke ' + JSON.stringify(params) )
if (err) {
console.log('err ' + err, err.stack); // an error occurred
event.message = err + ' query error';
} else {
console.log(data);
} // else
}); // lambda invoke
}
} // successful response
});
console.log(' end of function');
var completed = true;
context.callbackWaitsForEmptyEventLoop = false;
callback(null, completed);
}
Apologies if the code is quite long. But I wanted to show that I did put a number of console.logs to monitor where is goes through. The cloudwatch logs only shows the first message before the first lambda.invoke and then it shows the last message for the end of the function.
I am also not seeing any log entries in cloudwatch for the three functions that has been invoked.
06/17
ok, since I am still unable to make this work, I simplified the code to just the following:
exports.handler = (event, context, callback) => {
var err = null;
var updatedRecord = false;
var responseDetail = {};
var payload = {
qryString : event.qryString,
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
var params = {
FunctionName: 'getInventory',
Payload: JSON.stringify(payload)
}
console.log(' before invoke ' + JSON.stringify(params));
lambda.invoke(params, function(err, data) {
if (err) {
event.message = err + ' query error';
callback(err,event.message);
}
else {
console.log('success' + JSON.stringify(data));
console.log(' status code ' + JSON.stringify(data.StatusCode));
console.log(' Payload ' + JSON.stringify(JSON.parse(data.Payload)));
callback(null, data);
} // successful response
});
console.log(' end of function');
// var completed = true;
// context.callbackWaitsForEmptyEventLoop = false;
// callback(null, completed);
}
However, the function is timing out when I do my test. I have also given the role attached to the functions full Lambda and RDS access.
First of all - welcome to callback hell! I will return to this later.
This is a simple code that invokes a lambda function.
var params = {
FunctionName: 'LAMBDA_FUNCTION_NAME', /* required */
};
lambda.invoke(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log(data); // successful response
}
});
lambda.invoke function has two parameters (params, function(err,data){..}). The first one is a simple JSON object. The second one is a function - a callback function. This function will be "called back" when the execution of lambda.invoke (you could think LAMBDA_FUNCTION_NAME) ends. If an error occurs it would be "stored" in err variable otherwise returned data will be stored in data variable (This is not the right explanation but I am trying to keep it simple here).
What happens when you want to invoke two lambda functions one after another?
var params1 = {
FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
};
lambda.invoke(params1, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 1 invoked!');
console.log(data); // successful response
}
});
var params2 = {
FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
};
lambda.invoke(params2, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 2 invoked!');
console.log(data); // successful response
}
});
console.log('I am done!');
Depending on execution time of LAMBDA_FUNCTION_1_NAME and LAMBDA_FUNCTION_2_NAME you could see different outputs like:
Lambda function 1 invoked!
I am done!
or
Lambda function 1 invoked!
Lambda function 2 invoked!
I am done!
or even
Lambda function 1 invoked!
I am done!
Lambda function 2 invoked!
This is because you are calling lambda.invoke and after that (without waiting) you are calling lambda.invoke again. After that (of course without waiting) the previous functions to end you are calling console.log('I am done!');
You could solve this by putting each function in previous function's callback. Something like this:
var params1 = {
FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
};
lambda.invoke(params1, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 1 invoked!');
console.log(data); // successful response
var params2 = {
FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
};
lambda.invoke(params2, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 2 invoked!');
console.log(data); // successful response
console.log('I am done!');
}
});
}
});
That way your output would be:
Lambda function 1 invoked!
Lambda function 2 invoked!
I am done!
But if you want to invoke 3 or more functions one after another you will end up with nested code. This is the callback hell. You could re-write you code in that way. But in my opinion it is a good idea to check waterfall async library
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
})
Pseudo code should look like this:
async.waterfall([
function(callback1) {
var params1 = {
FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
};
lambda.invoke(params1, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('LAMBDA_FUNCTION_1_NAME finished!');
callback1(null,data);
}
});
},
function(result_from_function_1, callback2) {
console.log(result_from_function_1); // outputs result from LAMBDA_FUNCTION_1_NAME
var params2 = {
FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
};
lambda.invoke(params2, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('LAMBDA_FUNCTION_2_NAME finished!');
callback2(null,data);
}
});
},
function(result_from_function_2, callback3) {
console.log(result_from_function_2); // outputs result from LAMBDA_FUNCTION_2_NAME
var params3 = {
FunctionName: 'LAMBDA_FUNCTION_3_NAME', /* required */
};
lambda.invoke(params3, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('LAMBDA_FUNCTION_3_NAME finished!');
callback3(null,data);
}
});
}
], function (err, result) {
// result now equals LAMBDA_FUNCTION_3_NAME result
})
Please note that all callbacks (callback1, callback2 and callback3) could be with name "callback" only. I changed their names for better understanding.
Think about what this does:
lambda.invoke(params, function(err, data) { ...
It starts "doing something" (the fact that happens to be invoking another lambda function is actually unimportant) and when "something" is done, it calls function(), right?
But it also returns immediately, and the next statement executes.
Meanwhile "something" is being handled by the asynchronous event loop.
The "next statement" is
console.log(' end of function');
Then, you're telling lambda "hey, I may have some async stuff going on, but don't worry about waiting for it to finish":
context.callbackWaitsForEmptyEventLoop = false;
So the good news is your code is doing what is was written to do... but that turns out to be wrong.
Everywhere you have one of these two things...
// an error occurred
// successful response
...those are the places you should be calling callback(), not at the end of the handler function, which your code reaches very quickly, as it is supposed to.
You shouldn't need to use context.callbackWaitsForEmptyEventLoop = false; if you properly disconnect your database connections and all modules you include are well-behaved. While that has its purposes, there seem to be a lot of people using it to cover up for subtle unfinished business.
Or, for a tidier solution, where you only have one mention of the callback at the end and your function { function { function { nesting doesn't get so out of control, use async-waterfall.

Connection.query() return the rows undefined

I have a method getconnection() that creates connection for me.
Here's the function.
I get undefined even if I am fetching result of query that is rows to callback function to send the response to controller.
Can someone help me out.Thanks in advance.
function fetchUserInfo(req,res)
{
var getUserInfo = "select First_Name, Last_Name,COUNT(*) FROM twitter.user_information INNER join twitter.tweets ON user_information.Twitter_Handle = tweets.Twitter_handle WHERE user_information.Twitter_Handle = '"+req.session.handle+"' ";
console.log("Fetch User-------------"+getUserInfo);
mysql.fetchUser(function(err,result){
if(err)
{ console.log("cannot be retrieved");
throw err;
}
else
{
console.log("Successfully retrieved");
console.log("---------------" + result);
console.log(result.First_Name);
res.send(result);
}
},getUserInfo);
}
// mysql function
function fetchUser(callback,sqlQuery)
{
console.log("In fetch Data");
console.log("SQLquery is:::"+sqlQuery);
connection.getConnection().query(sqlQuery, function(err,rows,fields){
if(err)
{
console.log("Error :"+err.message);
}
else
{
console.log("Result is :" + rows.First_Name);
callback(err, rows);
}
})
}
Try this:
console.log("Result is :" + rows[0].First_Name);
or try to print the rows variable to the console instead of rows.First_Name:
console.log(rows);

better way of selecting 1 to many?

I have an express.js based rest application. Please have a look on following code and suggest me what would be better way.
I want to select user and its associated images (1 user has many images).
function getUser (connection, req, res) {
var userId = req.params.id;
connection.query('SELECT * FROM user p'
+ ' WHERE p.id = ' + connection.escape(userId), function handleSql(err, rows) {
if (err){ logAndRespond(err,res); return; }
if (rows.length === 0){ res.send(204); return; }
var adId = rows[0].adId;
// load images
connection.query('SELECT id, url FROM image WHERE ad_id = ' + connection.escape(adId), function (err, imgRows) {
if (err){ logAndRespond(err,res); return; }
if (rows.length != 0){
rows[0].images = imgRows;
}
res.json({'user': rows});
connection.release();
});
});
}
You don't have to escape parameters by yourself
You don't release the connection if an error occurred
The problem now is I don't know what you want to do with selected rows. You are also checking the rows.length twice but if there weren't any records in the first query then the second one will not be executed.
function getUser(conn, req, res) {
conn.query("SELECT * FROM user p WHERE p.id = ?;", [req.params.id], function(err, rows) {
if (err) {
return logAndRespond(err, res);
}
if (!rows.length) {
return res.send(204);
}
conn.query("SELECT id, url FROM image WHERE ad_id = ?;", [rows[0].adId], function(err, imgRows) {
if (err) {
return logAndRespond(err, res);
}
if (rows.length) { // ???
rows[0].images = imgRows;
}
res.json({"user": rows});
conn.release();
});
});
}

Node.js mysql transaction

Can anyone provide an example of how I could achieve MySQL transactions in Node.js. I am trying to get my head around using the node-mysql driver and node-mysql-queue.
As far are I can tell, using node-mysql-queue greatly reduces the asynchronous nature of Node.js as new queries have to wait until existing ones have completed. To get around this, has anyone attempted to combine node-mysql-queue with node-mysql's connection-pooling capabilities. i.e starting a new mysql connection for each new http request, and starting transaction queues on individual connections?
Update
See the edit below for async/await syntax
I spent some time writing a generalized version of the transaction example given by node mysql, so I thought I would share it here. I am using Bluebird as my promise library, and used it to 'promisify' the connection object which simplified the asynchronous logic a lot.
const Promise = ('bluebird');
const mysql = ('mysql');
/**
* Run multiple queries on the database using a transaction. A list of SQL queries
* should be provided, along with a list of values to inject into the queries.
* #param {array} queries An array of mysql queries. These can contain `?`s
* which will be replaced with values in `queryValues`.
* #param {array} queryValues An array of arrays that is the same length as `queries`.
* Each array in `queryValues` should contain values to
* replace the `?`s in the corresponding query in `queries`.
* If a query has no `?`s, an empty array should be provided.
* #return {Promise} A Promise that is fulfilled with an array of the
* results of the passed in queries. The results in the
* returned array are at respective positions to the
* provided queries.
*/
function transaction(queries, queryValues) {
if (queries.length !== queryValues.length) {
return Promise.reject(
'Number of provided queries did not match the number of provided query values arrays'
)
}
const connection = mysql.createConnection(databaseConfigs);
Promise.promisifyAll(connection);
return connection.connectAsync()
.then(connection.beginTransactionAsync())
.then(() => {
const queryPromises = [];
queries.forEach((query, index) => {
queryPromises.push(connection.queryAsync(query, queryValues[index]));
});
return Promise.all(queryPromises);
})
.then(results => {
return connection.commitAsync()
.then(connection.endAsync())
.then(() => {
return results;
});
})
.catch(err => {
return connection.rollbackAsync()
.then(connection.endAsync())
.then(() => {
return Promise.reject(err);
});
});
}
If you wanted to use pooling as you suggested in the question, you could easily switch the createConnection line with myPool.getConnection(...), and switch the connection.end lines with connection.release().
Edit
I made another iteration of the code using the mysql2 library (same api as mysql but with promise support) and the new async/await operators. Here is that
const mysql = require('mysql2/promise')
/** See documentation from original answer */
async function transaction(queries, queryValues) {
if (queries.length !== queryValues.length) {
return Promise.reject(
'Number of provided queries did not match the number of provided query values arrays'
)
}
const connection = await mysql.createConnection(databaseConfigs)
try {
await connection.beginTransaction()
const queryPromises = []
queries.forEach((query, index) => {
queryPromises.push(connection.query(query, queryValues[index]))
})
const results = await Promise.all(queryPromises)
await connection.commit()
await connection.end()
return results
} catch (err) {
await connection.rollback()
await connection.end()
return Promise.reject(err)
}
}
The following transaction example was added to the documentation a month ago:
https://github.com/felixge/node-mysql#transactions
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
var log = 'Post ' + result.insertId + ' added';
connection.query('INSERT INTO log SET data=?', log, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
I am using the following approach. There is an add function in my Model where I am performing database operations.
add : function (data, callback) {
//Begin transaction
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
var user_query = "INSERT INTO `calldata`.`users` (`username`, `password`, `enabled`, `accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`) VALUES ('" + data.mobile + "', '" + sha1(data.password) + "', '1', '1', '1', '1')";
connection.query(user_query, function(err, results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
var accnt_dtls_query = "INSERT INTO `calldata`.`accnt_dtls` (`req_mob_nmbr`, `usr_nme`, `dvce_id`, `mngr_id`, `cmpny_id`, `actve_flg`, `crtd_on`, `usr`) VALUES (" + data.mobile + ", '" + data.name + "', '', " + data.managerId + ", " + data.companyId + ", 1, now(), '" + data.mobile+ "')";
connection.query(accnt_dtls_query, function(err, results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
var user_role_query = "INSERT INTO `calldata`.`user_roles` (`username`, `ROLE`) VALUES ('" + data.mobile + "', '" + data.role + "')";
connection.query(user_role_query, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
//add an entry to manager table
var mngr_dtls_query = "INSERT INTO `calldata`.`mngr_dtls` (`mngr_nm`, `cmpny_id`, `crtd_on`, `usr_nm`, `eml_id`) VALUES ('" + data.name + "'," + data.companyId + " , now(), '" + data.mobile + "', '" + data.mobile + "')";
connection.query(mngr_dtls_query, function(err, result) {
if (err) {
return connection.rollback(function () {
throw err;
});
}
console.log('Changed ' + result.changedRows + ' results');
connection.commit(function (err) {
console.log('Commiting transaction.....');
if (err) {
return connection.rollback(function () {
throw err;
});
}
console.log('Transaction Complete.');
connection.end();
callback(null, result);
});
});
});
});
});
});
//transaction ends here
}
and calling from controller:
agentAccountModel.add(data, function(err, results) {
if(err)
{
res.status(500);
res.json({
"status": 500,
"message": err
});
}
res.status(200);
res.json({
"status": 200,
"message": "Saved successfully"
});
});
I have come up with a solution using a recursive function.
var sql = 'INSERT INTO logs SET data = ?';
// array of rows to insert
var rows = [[/*first row*/], [/*additional row*/]];
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
var insertEachRow = function () {
var row = rows.shift();
if (! row) {
// Done, now commit
return noMoreRows();
}
connection.query(sql, row, function (err, result) {
if (err) {
connection.rollback(function () {
throw err;
});
}
insertEachRow();
});
};
var noMoreRows = function () {
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
throw err;
});
}
console.log('success!');
});
};
insertEachRow();
});
I found one useful links which uses node js mysql pooling with transaction. Database Connection pooling is always useful. One can check this link
https://github.com/mysqljs/mysql
I created a wrapper ORM type thing for this specific purpose, hope it helps SQl-connecton - pool ORM type helper methods
You can use it with the mysql also, just use this function without any parameters /call back .
Make sure you have async function and use these instead
await con.rollback();
await con.beginTransaction();
await con.commit();
Life set.....