淮北 网站建设,网店怎么运营和推广,中国建设银行总部网站,标书制作标准数据库初始化 在软件开发阶段和测试阶段#xff0c;为了方便调试#xff0c;我们通常会进行一系列的数据库初始化操作#xff0c;比如重置数据表#xff0c;插入记录等等#xff0c;或者在部署阶段进行数据初始化的操作 根据前面章节介绍过的 knex.js 和 sequelize.js为了方便调试我们通常会进行一系列的数据库初始化操作比如重置数据表插入记录等等或者在部署阶段进行数据初始化的操作 根据前面章节介绍过的 knex.js 和 sequelize.js我们可以利用它们提供的方法进行DDL本节就数据库表重置的初始化行为做一点探讨表结果为User{id: num, name: string, age: num}数据库采用sqlite
Knex DDL
以下是利用 knex.schema 的一个简单示例: knex.js
const knex require(knex);
const fs require(fs);const sqlClient knex({client: sqlite3,connection: {filename: ${__root}/db/data.db,acquireConnectionTimeout: 1000},useNullAsDefault: true
});module.exports sqlClient;init.js
global.__root __dirname;const knex require(./knex.js);const drop knex.schema.dropTableIfExists(user);
const create knex.schema.createTable(user, (user){user.increments(id).notNullable().primary();user.text(name).notNullable();user.integer(age).notNullable()
});
const promises [drop,create];
Promise.all(promises)
.then(res{console.log(Database inits successfully!)
}).catch(err{console.error(err);
})Sequelize DDL
以下是利用 Sequelize.Model 的一个简单示例: sequelize.js
const { Sequelize,DataTypes,Model } require(sequelize);
const fs require(fs);const sqlClient new Sequelize({dialect: sqlite,storage: ${__root}/db/data.db
})const User sqlClient.define(User, {id: {primaryKey: true,type: DataTypes.INTEGER,allowNull: false,autoIncrement: true},name: {type: DataTypes.STRING,allowNull: false},age: {type: DataTypes.INTEGER,allowNull: false}
}, {tableName: user,timestamps: false,
});module.exports {sqlz: sqlClient,User
}init.js
global.__root __dirname;const { User } require(./sequelize);// User.drop();User.sync();
User.sync({ force: true }) //这个相当于前两个的结合体.then(res{console.log(Database inits successfully!);}).catch(err{console.error(err);
})SQL文件 Springboot作为Web后端最流行的框架之一想必各位都接触过或者听说过在Springboot中可以在配置文件中设置sql脚本的路径在项目启动时执行sql脚本来完成初始化。 这是一种非常好的方法因为有时候我们项目场景下的数据库表结构与关系可能非常复杂而且不同语言不同框架的实现有些区别用代码去完成初始化操作将是一件非常麻烦的事既然SQL是关系型数据库通用的语言那我们就可以通过SQL脚本来定义数据库表的结构和关系可以手写SQL脚本也可以借助如Navicat之类的工具设计表然后转储sql脚本然后交给我们的程序去执行或者手动执行。
Node的sql框架千千万我在几个主流框架中似乎都没看到有提供执行sql文件的特性其实没那么复杂不从构造完美的框架角度仅以为项目服务的角度考虑来说是这样的接下来我们就来简单实现一下通过sql脚本去初始化数据库。
有两条路
运行环境先安装sqlite3客户端node读取sql脚本内容node通过exec去指定目录下打开sqlite3命令行连接sqlite数据库同时把sql内容传递过去在sqlite3中执行sql脚本完成数据库初始化操作Node安装sqlite3依赖通过sql框架连接sqlite数据库node读取sql脚本内容对内容进行规范化处理只剩下纯净的sql语句后交给sql框架以sql语句的形式去运行
Springboot采用的就是第2种方法那我们也在Node中实现一下吧 实现准备好sql脚本 schemal.sql
-- 先删除user表
DROP TABLE IF EXISTS user;
-- 定义表结构并创建user表
CREATE TABLE user (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, --自增主键name TEXT NOT NULL,age INTEGER NOT NULL
);
Knex
先用Knex作为sql框架做个示范。获取到项目根目录路径后建立数据库连接 knex.js
const knex require(knex);
const fs require(fs);const sqlClient knex({client: sqlite3,connection: {filename: ${__root}/db/data.db,acquireConnectionTimeout: 1000},useNullAsDefault: true
});module.exports sqlClient;接下来为客户端实现执行sql文件的方法
定义runSql方法的传参和返回 我这里传入sql文件的路径返回sql语句执行的promise链内部实现首先通过fs模块读取sql脚本内容并转为字符串把内容中的注释去掉去掉内容首尾的空格去掉\r去掉\n我为了打印sql语段时更加美观省去了这一步不影响执行结果把内容按照;号分割成一个个独立的sql语句字串过滤掉空字串(由每2个sql语句间的空格形成)
sqlClient.runSql (path){const script fs.readFileSync(path).toString();console.log(Going to run a sql file:);console.log(script);/*** 拆成一句句sql来执行是因为knex执行一串语句时会把它们都算进一个事务内* 利用正则忽略注释* 去首尾空格* 按冒号分句* 校验字串是否为sql语句* type {string[]}*/const sqls script.replace(/\/\*[\s\S]*?\*\/|(--|\#)[^\r\n]*/gm, ).trim().replaceAll(\r,).split(;).filter(str{return str.trim() ? true : false;});console.log(sqls);console.log(sqls);console.log(start run:);const promises sqls.map(sql{sql ;; // knex会自动补上冒号加不加无所谓其实console.log(Going to run a sql:);console.log(sql);return sqlClient.raw(sql);})return promises;
}到这里我们就得到了纯净的一条条sql语句接下来把sql语句丢给knex即可 init.js
global.__root __dirname;const knex require(./knex.js)const promises knex.runSql(${__root}/db/schema.sql);
Promise.all(promises).then(res{console.log(Database inits successfully!)}).catch(err{console.error(err);
})输出结果
D:\Workstation\gitee-localRepo\express-demo\DatabaseInitnode index.js
Going to run a sql file:
-- 先删除user表
DROP TABLE IF EXISTS user;
-- 定义表结构并创建user表
CREATE TABLE user (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, --自增主键name TEXT NOT NULL,age INTEGER NOT NULL
);sqls
[DROP TABLE IF EXISTS user,\n \n CREATE TABLE user (\n id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \n name TEXT NOT NULL,\n age INTEGER NOT NULL\n )
]
start run:
Going to run a sql:
DROP TABLE IF EXISTS user;
Going to run a sql:CREATE TABLE user (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,age INTEGER NOT NULL
);
Server is ready on http://:::8080
Database inits successfully!很好我们可以很清晰的看到sql的执行过程
Sequelize 如果你把knex这套照搬过去把knex.raw换成sequelize.query你也许会尴尬的发现不太对劲它先创建了user表接着又把它给删了还大言不惭地打印了成功信息我的环境下是这样不清楚别人会不会但既然发生了就说明存在一定的问题。尝试反复执行knex示例和seuelize示例前者永远正确后者永远错误而且sequelize似乎更慢一点产生这样的区别可能是它们执行sql语句的实现机制不太一样花费精力去看它源码没有必要既然在这个场景下我们这两个步骤有着明确的先后顺序那我们就通过async/await让它们完全的顺序执行即可
sqlClient.runSql async (path) {const script fs.readFileSync(path).toString();console.log(Going to run a sql file:);console.log(script);/*** 拆成一句句sql来执行是因为knex执行一串语句时会把它们都算进一个事务内* 忽略注释* 去首尾空格* 按冒号分句* 校验字串是否为sql语句* type {string[]}*/const sqls script.replace(/\/\*[\s\S]*?\*\/|(--|\#)[^\r\n]*/gm, ).trim().replaceAll(\r,).split(;).filter(str{return str.trim() ? true : false;});console.log(sqls);console.log(sqls);console.log(start run:);for (let sql of sqls) {const res await sqlClient.query(${sql};);}
}输出结果
D:\Workstation\gitee-localRepo\express-demo\DatabaseInitnode index.js
Going to run a sql file:
-- 先删除user表
DROP TABLE IF EXISTS user;
-- 定义表结构并创建user表
CREATE TABLE user (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, --自增主键name TEXT NOT NULL,age INTEGER NOT NULL
);sqls
[DROP TABLE IF EXISTS user,\n \n CREATE TABLE user (\n id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, \n name TEXT NOT NULL,\n age INTEGER NOT NULL\n )
]
start run:
Server is ready on http://:::8080
Executing (default): DROP TABLE IF EXISTS user;
Executing (default): CREATE TABLE user (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,age INTEGER NOT NULL
);
Database inits successfully!Ok现在Sequelize也按照我们的意愿完成了重置user表的初始化工作 如果初始化过程中涉及严格的先后顺序务必做好同步流甚至回滚机制。此外在实际项目中为了项目的代码规范性应当将数据库路径初始化脚本路径都写在配置文件中而不是像本节为了方便直接写在需要调用的js文件中。
下一节-页面渲染