基于SailsJS与Mysql的ORM事务操作以及数据复制

Github地址

https://github.com/postmanlabs/sails-mysql-transactions

##介绍
sails-mysql-transaction是一个Sails.js与MySQL连接的ORM适配器,支持事务和集群复制。这个适配器基本上包含了流行的sails-mysql适配器,并提供额外的API来执行围绕数据库事务的操作。 它还提供以负载均衡的方式从一组只读副本中读取数据。

安装

  1. 在你的项目中的package.json文件中添加sails-mysql-transactions。不要在没有包含或安装sails的情况下直接执行install。
  2. 如已经安装了sails-mysql,那它可能会干扰这个组件。将其从package.json中移除然后执行npm remove sails-mysql进行卸载。
  3. 这个组件只有在sails已安装的前提下才可成功安装。如果此组件已正确安装,执行npm install sails-mysql-transactions --save或直接运行npm install,组件将会自动执行安装过程。

使用postinstall script进行安全安装

如果遇到npm install时由于安装顺序不稳定而导致问题,你可以将一下内容加入到package.json文件中,作为postinstall script of npm。这将确保此组件在sails已安装的前提下被安装。需要注意的是,你并不需要将sails-mysql-transactions作为一个依赖进行添加你的package.json文件中。

1
2
3
4
5
{
"scripts": {
"postinstall": "npm install sails-mysql-transactions"
}
}

安装备注:

此包将会重写sails内置的由Postman维护的waterline组件。所以如果你需要重新安装或更新sails时,本适配器也需要重装。

快速开始

一个Sails的集成测试应用在本项目的tests/integration/app目录下,里面包含完整功能,只需要执行在test/integration/app目录下执行npm install即可。

Sails config/local.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
module.exports = {
/* your other config stay as is */
connections: {
mySQLT: {
adapter: 'sails-mysql-transactions',
host: '{{your-db-host}}',
user: '{{your-db-username}}',
password: '{{your-db-password}}',
database: '{{your-db-tablename}}',
transactionConnectionLimit: 10,
rollbackTransactionOnError: true,
queryCaseSensitive: false,
/* this section is needed only if replication feature is required */
replication: {
enabled: true,
inheritMaster: true,
canRetry: true,
removeNodeErrorCount: 5,
restoreNodeTimeout: 1000 * 60 * 5,
defaultSelector: 'RR', // 'RANDOM' or 'ORDER'
sources: {
readonly: {
enabled: true,
host: '{{replica-1-host}}',
user: '{{replica-1-user}}',
password: '{{replica-1-password}}'
}
}
}
}
},
models: {
connection: 'mySQLT'
}
}

在Controller中使用事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
var Transaction = require('sails-mysql-transactions').Transaction;
module.exports = {
create: function (req, res) {
// start a new transaction
Transaction.start(function (err, transaction) {
if (err) {
// the first error might even fail to return a transaction object, so double-check.
transaction && transaction.rollback();
return res.serverError(err);
}
OneModel.transact(transaction).create(req.params.all(), function (err, modelInstance) {
if (err) {
transaction.rollback();
return res.serverError(err);
}
// using transaction to update another model and using the promises architecture
AnotherModel.transact(transaction).findOne(req.param('id')).exec(function (err, anotherInstance) {
if (err) {
transaction.rollback();
return res.serverError(err);
}
// using update and association changes
modelInstance.someAssociatedModel.remove(req.param('remove_id'));
// standard .save() works when in transaction
modelInstance.save(function (err, savedModel) {
if (err) {
transaction.rollback();
return res.serverError(err);
}
// finally commit the transaction before sending response
transaction.commit();
return res.json({
one: savedModel,
another: anotherInstance
});
});
});
});
});
}
};

可用的事物操作列表:

1
2
3
4
5
6
7
8
9
10
11
route = function (req, res) {
Transaction.start(function (err, transaction) {
OneModel.transact(transaction).create(/* ... */);
OneModel.transact(transaction).update(/* ... */);
OneModel.transact(transaction).find(/* ... */);
OneModel.transact(transaction).findOrCreate(/* ... */);
OneModel.transact(transaction).findOne(/* ... */);
OneModel.transact(transaction).destroy(/* ... */);
OneModel.transact(transaction).count(/* ... */);
});
};

除此之外,还有事务中实例方法上的更新,保存和关联操作,只要他们来自同一事务或通过事务进行包装(transaction.wrap(instance))。

事务错误异常处理

如果您正在对来源于.populate的实例执行模型实例操作(例如savedestroy等),事务可能会失败。在这种情况下,在对实例进行操作之前执行transaction.wrap(instance)会修正此问题。
如果要选择性地拦截来自此模块的错误,请使用Transaction.AdapterError进行前后效果。
需要注意的是,此适配器会自动添加一个名为transactionId的列。 如果不想在特定模型上使用事务,则可以通过在模型中设置autoTK:false来禁用针对该实例的自动列创建。

支持只读副本

当提供一个或多个只读副本源时,可以使用以下API来访问来某一个自定义复制源的数据。这会将您的数据库工作负载分布在多个系统上。
Readonly模式仍然使用正常的非事务性连接集而不使用只读副本。

1
2
3
4
5
action = function (req, res) {
OneModel.readonly().find();
OneModel.readonly().findOne();
OneModel.readonly().count();
};

支持在执行更新操作时获取变化内容

由于sails-mysql会在每次执行Update前进行SELECT查询,该查询可以用来比对数据更新前后的变化。.update的第三个参数返回一个数组,该数组的对象只包含已更改的字段及其原始值。

其他配置和功能

  1. queryCaseSensitive设置为true时,会将waterline的大小写敏感度功能禁用。(标注:这是waterline-sequel的wlNext选项的功能)
  2. 内置的waterline还提供了以下功能:
    • Model.<function:operate>().populateSome(Object<association:criteria>);允许你在一次调用中得到多个关联数据的内容,它的参数也接受数组形式。
    • Model的.populate方法允许以select: []形式作为查询参数之一。
    • Model的删除操作并不会在删除过程中先获取一遍完整数据模型。
  3. 另外提供了一个可以新建基于模型属性实例的异步方法fromObject()
    • 此方法允许将一个包含属性的对象或者回调函数作为参数。
    • 回调函数会返回错误对象以及模型实例对象。
1
2
3
4
5
OneModel.fromObject(attributesObject, function (err, instance) {
if (err) { return Error; }
// instance is the required object
});

贡献

你可以通过发起经过Travis CI测试通过的Pull请求来贡献你的代码。你需要使用npm install -d来安装本项目,并且在本地执行npm test,然后再发起Pull请求。