2017-05-07 48 views
1

我在節點v6.9.5上的Express v4.15.2服務器中使用了Bookshelf v0.10.3和Knex v0.12.9。如何在事務中插入後保存一對多關係

我有一個user表與一個關聯的identity表,由identity_user表加入。該表採用Knex創建:

Promise.all([ 
    knex.schema.createTable('user', table => { 
    table.increments('id'); 
    table.string('username'); 
    table.string('password'); 
    table.unique('username'); 
    }), 
    knex.schema.createTable('identity', table => { 
    table.increments('id'); 
    table.string('type'); 
    table.string('value'); 
    }), 
    knex.schema.createTable('identity_user', table => { 
    table.integer('user_id').references('user.id'); 
    table.integer('identity_id').references('identity.id'); 
    table.unique(['user_id', 'identity_id']); 
    }) 
]); 

我的書架模式是這樣的:

bookshelf.plugin([ 'registry', 'bookshelf-camelcase' ]); 

const Identity = bookshelf.model('Identity', { 
    tableName: 'identity', 

    user() { 
    return this.belongsTo('User', 'identity_user'); 
    } 
}); 

const User = bookshelf.model('User', { 
    tableName: 'user', 

    initialize() { 
    this.on('saving', model => { 
     if (model.hasChanged('username')) { 
     return model.set('username', String(model.attributes.username).toLowerCase()); 
     } 
    }); 
    this.on('saving', model => { 
     if (model.hasChanged('password')) { 
     return bcrypt.hash(model.attributes.password, 10) 
      .then(hash => model.set('password', hash)); 
     } 
    }); 
    }, 

    identities() { 
    return this.hasMany('Identity', 'identity_user'); 
    } 
}); 

在我createUser功能,我想添加一個新用戶,並在一個事務中的身份。

function createUser(req, res, next) { 
    const { email, username, password } = req.body; 

    // some data validation 

    return bookshelf.transaction((t) => { 
    return new User({ username, password }).save(null, { transacting: t }) 
     .then(user => new Identity({ type: 'email', value: email }).save({ userId: user.attributes.id }, { transacting: t })) 
     .then(() => res.sendStatus(204)) 
     .catch(err => { 
     // handle PostgreSQL unique violation error 
     }); 
    }); 
} 

當我運行的服務器,並嘗試註冊一個新用戶,我收到以下錯誤:

insert into "identity" ("type", "user_id", "value") values ($1, $2, $3) returning "id" - column "user_id" of relation "identity" does not exist { error: insert into "identity" ("type", "user_id", "value") values ($1, $2, $3) returning "id" - column "user_id" of relation "identity" does not exist 

這是PostgreSQL的錯誤(代碼42703 - 未定義列),但一切似乎正確設置。我真的可以用另一雙眼睛來看這個。我錯過了什麼?

在此先感謝!

回答

1

錯誤是因爲書架發現'userId'被插入標識,並推斷列名稱爲'user_id'。但對於連接到M×N的關係,你需要使用attach()代替:

function createUser(req, res, next) { 
    const { email, username, password } = req.body; 

    // some data validation 

    return bookshelf.transaction((t) => { 
    return new User({ username, password }) 
     .save(null, { transacting: t }) 
     .then(user => new Identity({ type: 'email', value: email }) 
     .save(null, { transacting: t })) 
     .then(identity => user.identities() 
     .attach(identity.id, { transacting: t })); 
    }) 
    .then(() => res.sendStatus(204)) 
    .catch(err => { 
    // handle PostgreSQL unique violation error 
    }); 
} 

的變化也似乎爲我的工作是直接連接模式(例如.attach(identity, { transacting: t })),但這種方法是不被支持的文件。

編輯我已經在你的模型定義忽略了兩個錯誤,如果hasMany()使用through()電話,要求連接表映射爲一個模型只映射到M×N的關係。在上面的例子中,我認爲使用簡單的'belongsToMany()`調用就足夠了。因此,通過替換的關係定義:

user() { 
    return this.belongsToMany('User'); 
} 

identities() { 
    return this.belongsToMany('Identity'); 
} 

EDIT2:移動catch()的交易範圍之外,所以異常會自動觸發回滾。

編輯3:平坦then()從事務範圍中刪除鏈和非db的東西。

+0

感謝您的回覆。我試過這個,但得到:「TypeError:user.identities(...)。attach不是一個函數」。更令人困惑的是,用戶和身份都被創建,儘管關係不被添加到鏈接表。我是否必須手動回滾捕獲中的更改? –

+0

對不起,我忽略了模型定義中的兩個錯誤。 – flaviodesousa

+0

如果事務範圍內沒有捕獲到異常'bookshelf.transaction(t => {<這裏是範圍>}),那麼事務會自動回滾。但是如果它被抓到(而不是重新拋出),那麼提交會自動被書架觸發, – flaviodesousa