Back in the day, it was common to manage database transactions in Java by writing code that did it. Something like this:
Transaction tx = session.startTransaction();
...
try {
tx.commit();
} catch (SomeException e){
tx.rollback();
}
at the beginning and end of every method. This had some obvious problems - it's redundant, hides the intent of what's happening, etc. So, along came annotation-driven transactions:
@Transaction
public SomeResultObj getResult(...){
...
}
Is there any support for declarative transaction management in node.js?
-
didn't know that was ever common... I've always written code to have a central place to execute JDBC code from and handle transactions, it's just a good idea...jwenting– jwenting2014年06月11日 09:29:02 +00:00Commented Jun 11, 2014 at 9:29
-
Most people have avoided writing their own database transaction API because they dislike re-inventing the wheel, combined with the fact that most of the available options are safer, and easier to use than rolling your own.Ampt– Ampt2014年10月20日 17:59:32 +00:00Commented Oct 20, 2014 at 17:59
-
Agreed with not rolling one's own database transaction API! I would certainly like to know if there are any existing declarative database transaction APIs in Node.js.James Kingsbery– James Kingsbery2014年10月20日 18:40:40 +00:00Commented Oct 20, 2014 at 18:40
1 Answer 1
In case you're using Express + Sequelize, you can do this by using a module called continuation-local-storage
(henceforth cls
, for brevity).
Show me the code
// When initializing your Sequelize instance
var Sequelize = require( "sequelize" );
Sequelize.cls = require( "continuation-local-storage" ).createNamespace( "db" );
module.exports = new Sequelize(...);
Then, create some middleware to initialize your transaction in every request (in case no one has come up with one yet):
var sequelize = require(...);
app.use(function ( req, res, next ) {
sequelize.transaction(function () {
// Do something else, but be sure to invoke next middleware
next();
});
});
What's going on here?
- A
cls
namespace is declared for Sequelize to use. Must be set on the constructor; - A transaction is initialized in an Express middleware;
- Every subsequent sequelize query will use the request transaction by default (you can override this behavior, obviously).
How this works?
This works because cls
assigns a new storage for each new async chain (the request in this example), and they are passed down consistently and transparently.
It's some kind of magic that happens inside Node :)
Tips
It's very likely that you'll need to use some monkeypatch to take that approach. Depends on your Promises implementation.