Skip to content

Commit

Permalink
Merge pull request #1062 from sequelize/features/transactions
Browse files Browse the repository at this point in the history
Transactions
  • Loading branch information
mickhansen committed Dec 7, 2013
2 parents 78e20e5 + 5f805e3 commit 631616c
Show file tree
Hide file tree
Showing 42 changed files with 2,417 additions and 875 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -7,4 +7,5 @@ npm-debug.log
*~
test/binary/tmp/*
test/tmp/*
test/sqlite/test.sqlite
coverage-*
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -58,7 +58,7 @@ A very basic roadmap. Chances aren't too bad, that not mentioned things are impl

### 1.7.0
- ~~Check if lodash is a proper alternative to current underscore usage.~~
- Transactions
- ~~Transactions~~
- Associations of not yet saved objects: [#864](https://github.com/sequelize/sequelize/issues/864)
- Support for update of tables without primary key
- ~~MariaDB support~~
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Expand Up @@ -86,6 +86,7 @@
- [FEATURE] Support for MariaDB. [#948](https://github.com/sequelize/sequelize/pull/948). Thanks to reedog117 and janmeier.
- [FEATURE] Filter through associations. [#991](https://github.com/sequelize/sequelize/pull/991). Thanks to snit-ram.
- [FEATURE] Possibility to disable loging for .sync [#937](https://github.com/sequelize/sequelize/pull/937). Thanks to durango
- [FEATURE] Support for transactions. [1062](https://github.com/sequelize/sequelize/pull/1062).
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
- [REFACTORING] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango
Expand Down
12 changes: 7 additions & 5 deletions lib/associations/belongs-to.js
Expand Up @@ -45,8 +45,9 @@ module.exports = (function() {
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'

obj[accessor] = function(params) {
var id = this[self.identifier]
, where = {}
var id = this[self.identifier]
, where = {}
, options = Utils._.pick(params || {}, 'transaction')

where[primaryKey] = id

Expand All @@ -60,7 +61,7 @@ module.exports = (function() {
params = id
}

return self.target.find(params)
return self.target.find(params, options)
}

return this
Expand All @@ -70,14 +71,15 @@ module.exports = (function() {
var self = this
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language)))

obj[accessor] = function(associatedObject) {
obj[accessor] = function(associatedObject, options) {
var primaryKeys = !!associatedObject && !!associatedObject.daoFactory ? Object.keys(associatedObject.daoFactory.primaryKeys) : []
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'

this[self.identifier] = associatedObject ? associatedObject[primaryKey] : null
options = Utils._.extend({ fields: [ self.identifier ], allowNull: [self.identifier] }, options)

// passes the changed field to save, so only that field get updated.
return this.save([ self.identifier ], {allowNull: [self.identifier]})
return this.save(options)
}

return this
Expand Down
41 changes: 25 additions & 16 deletions lib/associations/has-many-double-linked.js
@@ -1,4 +1,5 @@
var Utils = require('./../utils')
var Utils = require('./../utils')
, Transaction = require('./../transaction')

module.exports = (function() {
var HasManyDoubleLinked = function(definition, instance) {
Expand Down Expand Up @@ -48,14 +49,14 @@ module.exports = (function() {
if (options.joinTableAttributes) {
options.joinTableAttributes.forEach(function (elem) {
options.attributes.push(
self.QueryInterface.quoteIdentifiers(connectorDAO.tableName + '.' + elem) + ' as ' +
self.QueryInterface.quoteIdentifiers(connectorDAO.tableName + '.' + elem) + ' as ' +
self.QueryInterface.quoteIdentifier(connectorDAO.name + '.' + elem, true)
)
})
} else {
Utils._.forOwn(connectorDAO.rawAttributes, function (elem, key) {
options.attributes.push(
self.QueryInterface.quoteIdentifiers(connectorDAO.tableName + '.' + key) + ' as ' +
self.QueryInterface.quoteIdentifiers(connectorDAO.tableName + '.' + key) + ' as ' +
self.QueryInterface.quoteIdentifier(connectorDAO.name + '.' + key, true)
)
})
Expand Down Expand Up @@ -90,16 +91,22 @@ module.exports = (function() {
}

HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations, defaultAttributes) {
var self = this
, chainer = new Utils.QueryChainer()
, association = self.__factory.target.associations[self.__factory.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, sourceKeys = Object.keys(self.__factory.source.primaryKeys)
, targetKeys = Object.keys(self.__factory.target.primaryKeys)
var self = this
, chainer = new Utils.QueryChainer()
, association = self.__factory.target.associations[self.__factory.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, sourceKeys = Object.keys(self.__factory.source.primaryKeys)
, targetKeys = Object.keys(self.__factory.target.primaryKeys)
, obsoleteAssociations = []
, changedAssociations = []
, changedAssociations = []
, options = {}
, unassociatedObjects;

if ((defaultAttributes || {}).transaction instanceof Transaction) {
options.transaction = defaultAttributes.transaction
delete defaultAttributes.transaction
}

unassociatedObjects = newAssociations.filter(function (obj) {
return !Utils._.find(oldAssociations, function (old) {
return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id)
Expand All @@ -121,7 +128,7 @@ module.exports = (function() {

changedAssociation.where[self.__factory.identifier] = self.instance[self.__factory.identifier] || self.instance.id
changedAssociation.where[foreignIdentifier] = newObj[foreignIdentifier] || newObj.id

changedAssociations.push(changedAssociation)
}
})
Expand All @@ -132,31 +139,33 @@ module.exports = (function() {
})

var where = {}

where[self.__factory.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id)
where[foreignIdentifier] = foreignIds

chainer.add(self.__factory.connectorDAO.destroy(where))
chainer.add(self.__factory.connectorDAO.destroy(where, options))
}

if (unassociatedObjects.length > 0) {
var bulk = unassociatedObjects.map(function(unassociatedObject) {
var attributes = {}

attributes[self.__factory.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id)
attributes[foreignIdentifier] = ((targetKeys.length === 1) ? unassociatedObject[targetKeys[0]] : unassociatedObject.id)

if (association.hasJoinTableModel) {
attributes = Utils._.defaults(attributes, unassociatedObject[association.connectorDAO.name], defaultAttributes)
}

return attributes
})

chainer.add(self.__factory.connectorDAO.bulkCreate(bulk))
chainer.add(self.__factory.connectorDAO.bulkCreate(bulk, options))
}

if (changedAssociations.length > 0) {
changedAssociations.forEach(function (assoc) {
chainer.add(self.__factory.connectorDAO.update(assoc.attributes, assoc.where))
chainer.add(self.__factory.connectorDAO.update(assoc.attributes, assoc.where, options))
})
}

Expand Down Expand Up @@ -191,7 +200,7 @@ module.exports = (function() {
this.__factory.connectorDAO.create(attributes)
.success(function() { emitterProxy.emit('success', newAssociation) })
.error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) })
}
}

Expand Down
47 changes: 32 additions & 15 deletions lib/associations/has-many-single-linked.js
@@ -1,4 +1,5 @@
var Utils = require('./../utils')
var Utils = require('./../utils')
, Transaction = require('./../transaction')

module.exports = (function() {
var HasManySingleLinked = function(definition, instance) {
Expand Down Expand Up @@ -29,23 +30,29 @@ module.exports = (function() {
return this.__factory.target.all(options)
}

HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
, associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {daoFactory: {primaryKeys: {}}}).daoFactory.primaryKeys || {})
, associationKey = associationKeys.length === 1 ? associationKeys[0] : 'id'
, chainer = new Utils.QueryChainer()
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations, defaultAttributes) {
var self = this
, associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {daoFactory: {primaryKeys: {}}}).daoFactory.primaryKeys || {})
, associationKey = (associationKeys.length === 1) ? associationKeys[0] : 'id'
, chainer = new Utils.QueryChainer()
, options = {}
, obsoleteAssociations = oldAssociations.filter(function (old) {
return !Utils._.find(newAssociations, function (obj) {
return obj[associationKey] === old[associationKey]
})
})
, unassociatedObjects = newAssociations.filter(function (obj) {
, unassociatedObjects = newAssociations.filter(function (obj) {
return !Utils._.find(oldAssociations, function (old) {
return obj[associationKey] === old[associationKey]
})
})
, update

if ((defaultAttributes || {}).transaction instanceof Transaction) {
options.transaction = defaultAttributes.transaction
delete defaultAttributes.transaction
}

if (obsoleteAssociations.length > 0) {
// clear the old associations
var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
Expand All @@ -55,21 +62,26 @@ module.exports = (function() {

update = {}
update[self.__factory.identifier] = null

var primaryKeys = Object.keys(this.__factory.target.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, updateWhere = {}

updateWhere[primaryKey] = obsoleteIds
chainer.add(this.__factory.target.update(update, updateWhere, {allowNull: [self.__factory.identifier]}))
chainer.add(this.__factory.target.update(
update,
updateWhere,
Utils._.extend(options, { allowNull: [self.__factory.identifier] })
))
}

if (unassociatedObjects.length > 0) {
// For the self.instance
var pkeys = Object.keys(self.instance.daoFactory.primaryKeys)
, pkey = pkeys.length === 1 ? pkeys[0] : 'id'
var pkeys = Object.keys(self.instance.daoFactory.primaryKeys)
, pkey = pkeys.length === 1 ? pkeys[0] : 'id'
// For chainer
, primaryKeys = Object.keys(this.__factory.target.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, updateWhere = {}

// set the new associations
Expand All @@ -78,10 +90,15 @@ module.exports = (function() {
return associatedObject[associationKey]
})

update = {}
update = {}
update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id)
updateWhere[primaryKey] = unassociatedIds
chainer.add(this.__factory.target.update(update, updateWhere, {allowNull: [self.__factory.identifier]}))
updateWhere[primaryKey] = unassociatedIds

chainer.add(this.__factory.target.update(
update,
updateWhere,
Utils._.extend(options, { allowNull: [self.__factory.identifier] })
))
}

chainer
Expand Down
32 changes: 16 additions & 16 deletions lib/associations/has-many.js
Expand Up @@ -124,30 +124,30 @@ module.exports = (function() {
return new Class(self, this).injectGetter(options)
}

obj[this.accessors.hasAll] = function(objects) {
obj[this.accessors.hasAll] = function(objects, options) {
var instance = this;
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.error(function(err){ customEventEmitter.emit('error', err)})
.success(function(associatedObjects) {
customEventEmitter.emit('success',
Utils._.all(objects, function(o) {
return Utils._.any(associatedObjects, function(associatedObject) {
return Utils._.all(associatedObject.identifiers, function(key, identifier) {
return o[identifier] == associatedObject[identifier];
});
instance[self.accessors.get](options)
.error(function(err) { customEventEmitter.emit('error', err) })
.success(function(associatedObjects) {
customEventEmitter.emit('success',
Utils._.all(objects, function(o) {
return Utils._.any(associatedObjects, function(associatedObject) {
return Utils._.all(associatedObject.identifiers, function(key, identifier) {
return o[identifier] == associatedObject[identifier];
});
})
})
})
)
})
)
})
})
return customEventEmitter.run()
}

obj[this.accessors.hasSingle] = function(o) {
var instance = this;
obj[this.accessors.hasSingle] = function(o, options) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
instance[self.accessors.get](options)
.error(function(err){ customEventEmitter.emit('error', err)})
.success(function(associatedObjects) {
customEventEmitter.emit('success',
Expand Down
37 changes: 22 additions & 15 deletions lib/associations/has-one.js
Expand Up @@ -77,31 +77,38 @@ module.exports = (function() {
HasOne.prototype.injectSetter = function(obj) {
var self = this

obj[this.accessors.set] = function(associatedObject) {
var instance = this
obj[this.accessors.set] = function(associatedObject, options) {
var instance = this
, instanceKeys = Object.keys(instance.daoFactory.primaryKeys)
, instanceKey = instanceKeys.length === 1 ? instanceKeys[0] : 'id'
, instanceKey = instanceKeys.length === 1 ? instanceKeys[0] : 'id'

return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]().success(function(oldObj) {
if (oldObj) {
oldObj[self.identifier] = null
oldObj.save([self.identifier], {allowNull: [self.identifier]}).success(function() {
if (associatedObject) {
associatedObject[self.identifier] = instance[instanceKey]
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
.error(function(err) { emitter.emit('error', err) })
} else {
emitter.emit('success', null)
}
})
oldObj
.save(
Utils._.extend({}, options, {
fields: [self.identifier],
allowNull: [self.identifier]
})
)
.success(function() {
if (associatedObject) {
associatedObject[self.identifier] = instance[instanceKey]
associatedObject
.save(options)
.success(function() { emitter.emit('success', associatedObject) })
.error(function(err) { emitter.emit('error', err) })
} else {
emitter.emit('success', null)
}
})
} else {
if (associatedObject) {
associatedObject[self.identifier] = instance[instanceKey]
associatedObject
.save()
.save(options)
.success(function() { emitter.emit('success', associatedObject) })
.error(function(err) { emitter.emit('error', err) })
} else {
Expand Down

0 comments on commit 631616c

Please sign in to comment.