knex.raw(...) 在迁移中似乎不起作用
knex.raw(...) in migrations seems not to work
我将 knex 与 MySql 一起使用。这是我的迁移文件:
const { onUpdateTrigger } = require('../../../../knexfile')
const { onInsertTrigger } = require('../../../../knexfile')
exports.up = function (knex, Promise) {
return knex.schema.createTable('users', (table) => {
console.info("------> Creating table");
table.increments('id').unsigned().primary();
table.string('username').unique().notNullable();
table.string('password').notNullable();
table.timestamp('modified').notNullable();
table.timestamp('created').notNullable().defaultTo(knex.raw('NOW()'));
}).then(function () {
console.info("------> Creating trigger");
knex.raw(onUpdateTrigger('users'));
knex.raw(onInsertTrigger('users'));
});
};
exports.down = function (knex, Promise) {
return knex.schema.dropTable('users');
};
knexfile.js如下:
...
development: {
client: 'mysql',
connection: {
host: 'localhost',
user: 'pbrause',
password: '********',
database: 'mydb',
charset: 'utf8',
multipleStatements : true
},
debug: true,
migrations: {
directory: __dirname + '/src/server/db/migrations'
},
seeds: {
directory: __dirname + '/src/server/db/seeds'
}
},
...
onInsertTrigger: function(table) {
`DELIMITER $$
CREATE TRIGGER \`mydatabase\`.\`${table}_BEFORE_INSERT\`
BEFORE INSERT ON \`${table}\`
FOR EACH ROW
BEGIN
SET new.modified = NOW();
END$$
DELIMITER ;`
},
onUpdateTrigger: function(table) {
`DELIMITER $$
CREATE TRIGGER \`mydatabase\`.\`${table}_BEFORE_UPDATE\`
BEFORE UPDATE ON \`${table}\`
FOR EACH ROW
BEGIN
SET new.modified = NOW();
END$$
DELIMITER ;`
}
...
我尝试了两种变体 - 一种是 SQL 代码位于 knex.raw(...)
语句内,另一种是您在上面看到的方式。在这两种情况下,都没有创建触发器,并且 knex 调试输出告诉我这些 'raw' 语句没有执行。 table 在这两种情况下均已正确创建。
知道为什么这不起作用吗?
首先你调用 knex.raw()
到时间并行,而不等待它们的执行完成:
}).then(function () {
console.info("------> Creating trigger");
knex.raw(onUpdateTrigger('users'));
knex.raw(onInsertTrigger('users'));
});
从那时起应该使用 knex.schema.raw
或 return 承诺:
}).then(function () {
console.info("------> Creating on update trigger");
return knex.raw(onUpdateTrigger('users'));
}).then(function () {
console.info("------> Creating on insert trigger");
return knex.raw(onInsertTrigger('users'));
});
或
return knex.schema.createTable('users', (table) => {
console.info("------> Creating table");
table.increments('id').unsigned().primary();
table.string('username').unique().notNullable();
table.string('password').notNullable();
table.timestamp('modified').notNullable();
table.timestamp('created').notNullable().defaultTo(knex.raw('NOW()'));
})
.raw(onUpdateTrigger('users'))
.raw(onInsertTrigger('users'));
另一个问题可能是 knex.raw()
(实际上数据库驱动程序通常不允许这样做)默认情况下不支持将多个 SQL 语句作为单个查询发送到数据库。
如果您正在使用 mysql,您可以通过为驱动程序 (https://github.com/mysqljs/mysql#connection-options) 设置 multipleStatements : true
配置选项来启用对它的支持。
编辑(为什么那些 knex 原始查询不是 运行 的真正答案):
以上所有内容都是正确的,如果您按照我建议的方式更改代码,它就会解决您的问题。
那些 knex.raw()
查询不是 运行 的原因是因为您只是构建查询,但从未执行它们(它们不是 return 从 promise 中编辑的,这将自动触发查询,您也不会为那些将查询显式发送到数据库的查询调用 .then()
。
// queries like this are only built, but never sent to server
knex.raw(onUpdateTrigger('users'));
knex.raw(onInsertTrigger('users'));
// one more the correct way to do it so that queries will be executed too
return knex.raw(onUpdateTrigger('users'))
.then(() => knex.raw(onInsertTrigger('users')));
我将 knex 与 MySql 一起使用。这是我的迁移文件:
const { onUpdateTrigger } = require('../../../../knexfile')
const { onInsertTrigger } = require('../../../../knexfile')
exports.up = function (knex, Promise) {
return knex.schema.createTable('users', (table) => {
console.info("------> Creating table");
table.increments('id').unsigned().primary();
table.string('username').unique().notNullable();
table.string('password').notNullable();
table.timestamp('modified').notNullable();
table.timestamp('created').notNullable().defaultTo(knex.raw('NOW()'));
}).then(function () {
console.info("------> Creating trigger");
knex.raw(onUpdateTrigger('users'));
knex.raw(onInsertTrigger('users'));
});
};
exports.down = function (knex, Promise) {
return knex.schema.dropTable('users');
};
knexfile.js如下:
...
development: {
client: 'mysql',
connection: {
host: 'localhost',
user: 'pbrause',
password: '********',
database: 'mydb',
charset: 'utf8',
multipleStatements : true
},
debug: true,
migrations: {
directory: __dirname + '/src/server/db/migrations'
},
seeds: {
directory: __dirname + '/src/server/db/seeds'
}
},
...
onInsertTrigger: function(table) {
`DELIMITER $$
CREATE TRIGGER \`mydatabase\`.\`${table}_BEFORE_INSERT\`
BEFORE INSERT ON \`${table}\`
FOR EACH ROW
BEGIN
SET new.modified = NOW();
END$$
DELIMITER ;`
},
onUpdateTrigger: function(table) {
`DELIMITER $$
CREATE TRIGGER \`mydatabase\`.\`${table}_BEFORE_UPDATE\`
BEFORE UPDATE ON \`${table}\`
FOR EACH ROW
BEGIN
SET new.modified = NOW();
END$$
DELIMITER ;`
}
...
我尝试了两种变体 - 一种是 SQL 代码位于 knex.raw(...)
语句内,另一种是您在上面看到的方式。在这两种情况下,都没有创建触发器,并且 knex 调试输出告诉我这些 'raw' 语句没有执行。 table 在这两种情况下均已正确创建。
知道为什么这不起作用吗?
首先你调用 knex.raw()
到时间并行,而不等待它们的执行完成:
}).then(function () {
console.info("------> Creating trigger");
knex.raw(onUpdateTrigger('users'));
knex.raw(onInsertTrigger('users'));
});
从那时起应该使用 knex.schema.raw
或 return 承诺:
}).then(function () {
console.info("------> Creating on update trigger");
return knex.raw(onUpdateTrigger('users'));
}).then(function () {
console.info("------> Creating on insert trigger");
return knex.raw(onInsertTrigger('users'));
});
或
return knex.schema.createTable('users', (table) => {
console.info("------> Creating table");
table.increments('id').unsigned().primary();
table.string('username').unique().notNullable();
table.string('password').notNullable();
table.timestamp('modified').notNullable();
table.timestamp('created').notNullable().defaultTo(knex.raw('NOW()'));
})
.raw(onUpdateTrigger('users'))
.raw(onInsertTrigger('users'));
另一个问题可能是 knex.raw()
(实际上数据库驱动程序通常不允许这样做)默认情况下不支持将多个 SQL 语句作为单个查询发送到数据库。
如果您正在使用 mysql,您可以通过为驱动程序 (https://github.com/mysqljs/mysql#connection-options) 设置 multipleStatements : true
配置选项来启用对它的支持。
编辑(为什么那些 knex 原始查询不是 运行 的真正答案):
以上所有内容都是正确的,如果您按照我建议的方式更改代码,它就会解决您的问题。
那些 knex.raw()
查询不是 运行 的原因是因为您只是构建查询,但从未执行它们(它们不是 return 从 promise 中编辑的,这将自动触发查询,您也不会为那些将查询显式发送到数据库的查询调用 .then()
。
// queries like this are only built, but never sent to server
knex.raw(onUpdateTrigger('users'));
knex.raw(onInsertTrigger('users'));
// one more the correct way to do it so that queries will be executed too
return knex.raw(onUpdateTrigger('users'))
.then(() => knex.raw(onInsertTrigger('users')));