Node.js 的ORM(Sequelize) 的使用
<p> Sequelize是一个Node.js 的ORM。ORM是对象关系映射(Object Relational Mapping),编程语言的中对象与关系型数据库中的关系(表)进行映射,对象的属性和值映射成表中的列和值。有了ORM,就可以使用面向对象的方式(调用对象的方法)来操作数据库,不用再写SQL语句。登录MySQL,CREATE DATABASE airline; 创建 airline 数据库。mkdir airline && cd airline && npm init -y,npm install sequelize mysql2,就可以使用Sequelize来操作数据库了。touch server.mjs,先连接数据库,就是创建Sequelize的实例</p><div class="cnblogs_code">
<pre>import { Sequelize } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">sequelize</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> new Sequelize(数据库名, 登录数据库的用户名, 登录数据库的密码, {host: 哪台主机上的数据库, dialect: 使用什么数据库})</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> sequelize = <span style="color: rgba(0, 0, 255, 1)">new</span> Sequelize(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">airline</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">root</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">123</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
host: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">localhost</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
dialect: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">mysql</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
});
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> sequelize.authenticate();
console.log(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">连接成功</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
console.error(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">连接失败</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, error);
}</span></pre>
</div>
<p> node server.js,连接成功,开始操作数据库。Sequelize有一个Model的概念,它代表数据库中的一张表,所以操作数据库,就要先创建Model,提供表的名称,表的列及其数据类型,从而和数据库表进行关联,这样操作Model就相当于操作表。两种方式创建Model,sequelize.define()和继承Model并调用 init()方法。</p>
<div class="cnblogs_code">
<pre>import { Sequelize, DataTypes } from 'sequelize';</pre>
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 参数: model名, 表中的字段及其数据类型, 可选配置项</span>
sequelize.define(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">FlightSchedule</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
originAirport: DataTypes.STRING,
destinationAirport: DataTypes.STRING,
departureTime: DataTypes.DATE
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者 类名就是model名</span>
<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FlightSchedule extends Model { }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> int参数:属性(对应表中的字段), 配置项</span>
<span style="color: rgba(0, 0, 0, 1)">FlightSchedule.init({
originAirport: DataTypes.STRING,
destinationAirport: DataTypes.STRING,
departureTime: DataTypes.DATE
}, {
sequelize,
});</span></pre>
</div>
<p> 创建Model并没有提供表名,默认情况下,Sequelize会把Model名进行复数化,当作表名。FlightSchedule就代表flightschedules表。当然,表名可以在可选配置项中进行配置,也可以禁止复数化,数据库的字段名也可以不用驼峰命名,而是使用 _连接,</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
freezeTableName: </span><span style="color: rgba(0, 0, 255, 1)">true</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表名是flightschedule</span>
tableName: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 直接定义表名为a</span>
underscored: <span style="color: rgba(0, 0, 255, 1)">true</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 数据库中的对应的字段是origin_airport</span>
}</pre>
</div>
<p> 这里就不配置了,使用默认值就好。但此时数据库中并没有flightschedules表,怎么操作它?Sequelize提供了一个sync()方法,如果数据库中没有对应的表,它就会创建表,如果有,什么都不做。在define调用的后面</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> sequelize.sync();<br></span></pre>
</div>
<p> node server.js ,airline有了flightschedules表,但多了3个字段,id,createdAt和updatedAt。默认情况下,Sequelize会为Model增加三个属性(id, createdAt和updatedAt)。创建model时写了三个属性,实际上model有六个属性。当然createdAt和updatedAt 是可以配置的(在可选配置项里面),timestamps为false,就不会给model上添加createdAt 和updatedAt属性。也可以添加某一个字段,比如timestamps为true, createdAt 为true,就只为model 增加createdAt属性。</p>
<p> 作为演示代码,以上使用没有问题。但真正的应用开发,就有很多问题,首先,数据库的配置信息写到主文件中,不利于配置不同的环境,需要配置文件。其次,项目中有多个model,不可能都写到主文件,需要一个目录来维护model。再就是sync()方法,修改表就无能为力了,需要migiration。Sequlize 提供了sequelize-cli 来初始化项目结构。npm install sequelize-cli -D, 再npx sequelize init ,项目多了4个目录。config保存数据库配置信息,models包含项目中的所有Model。它默认包含index.js文件,从models目录下读取文件(fs.readdirSync(__dirname)),然后循环创建model(require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);),赋值给db对象(db = model;),最后把db对象暴露出去。暂时不用它的,删除它。在models下创建flightSchedule.mjs,</p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> (sequelize, DataTypes) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> sequelize.define(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">FlightSchedule</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
originAirport: DataTypes.STRING,
destinationAirport: DataTypes.STRING,
departureTime: DataTypes.DATE
});
};</span></pre>
</div>
<p> 然后在index.mjs</p>
<div class="cnblogs_code">
<pre>import { Sequelize, DataTypes } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">sequelize</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { createRequire } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">module</span><span style="color: rgba(128, 0, 0, 1)">'</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在esm中使用require,处理JSON</span>
<span style="color: rgba(0, 0, 0, 1)">
import createFlightSchedule </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./flightSchedule.mjs</span><span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入model</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> env = process.env.NODE_ENV || <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">development</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">const</span> require =<span style="color: rgba(0, 0, 0, 1)"> createRequire(import.meta.url);
</span><span style="color: rgba(0, 0, 255, 1)">const</span> config = require(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">../config/config.json</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">const</span> sequelize = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Sequelize(config.database, config.username, config.password, config);
</span><span style="color: rgba(0, 0, 255, 1)">const</span> FlightSchedule = createFlightSchedule(sequelize, DataTypes) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建Model</span>
<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 0, 1)">{
FlightSchedule,
sequelize
}</span></pre>
</div>
<p> 可以看到,默认情况下,连接的是development数据库,在config.json中配置development</p>
<div class="cnblogs_code">
<pre>"development"<span style="color: rgba(0, 0, 0, 1)">: {
</span>"username": "root"<span style="color: rgba(0, 0, 0, 1)">,
</span>"password": "123"<span style="color: rgba(0, 0, 0, 1)">,
</span>"database": "airline"<span style="color: rgba(0, 0, 0, 1)">,
</span>"host": "127.0.0.1"<span style="color: rgba(0, 0, 0, 1)">,
</span>"dialect": "mysql"<span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p> migrations目录包含对表Schema的定义和修改。创建了FlightSchedule Model,就要创建flightschedules表。新建一个建表migration,npx sequelize migration:generate --name create-flight-schedule,name参数表明这是什么migration。migrations目录下生成了一个文件,名字带有时间戳。它有两个方法,up(…) 用来定义migration要做的事情,down(…) 回滚这个migration做的事情,所以要在up中创建table,在down中删除table。需要注意的是,Sequelize默认会为Model增加id,createdAt和updatedAt属性,所以创建表时,表也要有对应的列</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* @type {import('sequelize-cli').Migration} </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
module.exports </span>=<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> up(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.createTable(<span style="color: rgba(128, 0, 0, 1)">'f</span><span style="color: rgba(128, 0, 0, 1)">lightSchedules</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
id: {
allowNull: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
autoIncrement: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
primaryKey: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
type: Sequelize.INTEGER
},
originAirport: {
type: Sequelize.STRING
},
destinationAirport: {
type: Sequelize.STRING
},
departureTime: {
type: Sequelize.DATE
},
createdAt: {
allowNull: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
type: Sequelize.DATE
},
updatedAt: {
allowNull: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
type: Sequelize.DATE
}
});
},
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> down(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.dropTable(<span style="color: rgba(128, 0, 0, 1)">'f</span><span style="color: rgba(128, 0, 0, 1)">lightSchedules</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
}
};</span></pre>
</div>
<p> 在MySQL中删除flightschedules表。再npx sequelize db:migrate 执行migrations目录下的迁移操作,查看数据库,有了flightschedules表,但也多了SequelizeMeta表(记录执行过的迁移脚本)。执行migrate的时候,Sequelize会按时间戳的顺序遍历整个migrations目录, 然后跳过SequelizeMeta 表中包含的文件,也就是以前执行过的migration文件,不用再重复执行了。</p>
<p> seeders目录快速向数据库中填充数据,方便测试程序。npx sequelize seed:generate --name initial-flight-schedules. seeders目录创建了一个js文件,up(…) 填充数据,down(…) 回滚数据。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* @type {import('sequelize-cli').Migration} </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
module.exports </span>=<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> up(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.bulkInsert(<span style="color: rgba(128, 0, 0, 1)">'f</span><span style="color: rgba(128, 0, 0, 1)">lightSchedules</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, [{
originAirport: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">济南</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
destinationAirport: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">武汉</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
departureTime: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2022-01-01 08:00:00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
updatedAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
}], {});
},
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> down(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.bulkDelete(<span style="color: rgba(128, 0, 0, 1)">'f</span><span style="color: rgba(128, 0, 0, 1)">lightSchedules</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">, {});
}
};</span></pre>
</div>
<p> npx sequelize db:seed:all,执行seeders目录下的所有文件。npx sequelize db:seed --seed=fileName,指定执行seeders目录下哪个文件。无论执行哪一个命令,flightschedules表中都有一条记录。需要注意的是, db:migrate and db:seed 命令使用 NODE_ENV 环境变量来决定执行到哪个数据库,默认是development 配置下的数据库</p>
<p> 再创建一个Airplane Model,在models下新建airplane.mjs</p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> (sequelize, DataTypes) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> sequelize.define(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Airplane</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
planeModel: DataTypes.STRING,
totalSeats: DataTypes.STRING,
});
};</span></pre>
</div>
<p> 再在index.mjs 引入并创建Airplane Model</p>
<div class="cnblogs_code">
<pre>import createAirplane <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./airplane.mjs</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">const</span> Airplane =<span style="color: rgba(0, 0, 0, 1)"> createAirplane(sequelize, DataTypes)
export</span><span style="color: rgba(0, 0, 0, 1)"> {
Airplane,
FlightSchedule,
sequelize
}</span></pre>
</div>
<p> 创建migration,npx sequelize migration:generate --name create-airplane, </p>
<div class="cnblogs_code">
<pre>module.exports =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> up(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.createTable(<span style="color: rgba(128, 0, 0, 1)">'a</span><span style="color: rgba(128, 0, 0, 1)">irplanes</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> id, createdAt和updatedAt都一样,这里就省略,没有写了</span>
<span style="color: rgba(0, 0, 0, 1)"> planeModel: {
type: Sequelize.STRING
},
totalSeats: {
type: Sequelize.INTEGER
},
});
},
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> down(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.dropTable(<span style="color: rgba(128, 0, 0, 1)">'a</span><span style="color: rgba(128, 0, 0, 1)">irplanes</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
}
};</span></pre>
</div>
<p> npx sequelize db:migrate 执行migration,再npx sequelize seed:generate --name initial-airplanes 建一个seeder文件,填充数据</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">module.exports </span>=<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> up(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.bulkInsert(<span style="color: rgba(128, 0, 0, 1)">'a</span><span style="color: rgba(128, 0, 0, 1)">irplanes</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, [{
planeModel: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Airbus A220-100</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
totalSeats: </span><span style="color: rgba(128, 0, 128, 1)">110</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
updatedAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date()
}, {
planeModel: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Airbus A220-300</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
totalSeats: </span><span style="color: rgba(128, 0, 128, 1)">110</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
updatedAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date()
}, {
planeModel: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Airbus A 318</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
totalSeats: </span><span style="color: rgba(128, 0, 128, 1)">115</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
updatedAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date()
}, {
planeModel: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Boeing 707-100</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
totalSeats: </span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
updatedAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
}, {
planeModel: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Boeing 737-100</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
totalSeats: </span><span style="color: rgba(128, 0, 128, 1)">85</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date(),
updatedAt: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date()
}], {});
},
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> down(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.bulkDelete(<span style="color: rgba(128, 0, 0, 1)">'a</span><span style="color: rgba(128, 0, 0, 1)">irplanes</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">, {});
}
};</span></pre>
</div>
<p> npx sequelize db:seed --seed=20231017074324-initial-airplanes.js(需要改成自己的文件名)。再创建一个customer Model,在models目录下,创建customer.mjs </p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> (sequelize, DataTypes) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> sequelize.define('Customer'<span style="color: rgba(0, 0, 0, 1)">, {
name: DataTypes.STRING,
email: DataTypes.STRING,
});
}</span></pre>
</div>
<p> 然后index.mjs中引入</p>
<div class="cnblogs_code">
<pre>import createCustomer from './customer.mjs'<span style="color: rgba(0, 0, 0, 1)">;
const Customer </span>=<span style="color: rgba(0, 0, 0, 1)"> createCustomer(sequelize, DataTypes);
export </span><span style="color: rgba(0, 0, 0, 1)">{
Airplane,
Customer,
FlightSchedule,
sequelize
}</span></pre>
</div>
<p> 最后生成一个migration文件,npx sequelize migration:generate --name create-customer</p>
<div class="cnblogs_code">
<pre>module.exports =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> up(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.createTable(<span style="color: rgba(128, 0, 0, 1)">'c</span><span style="color: rgba(128, 0, 0, 1)">ustomers</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 还是省略了id,createdAt和updateAt</span>
<span style="color: rgba(0, 0, 0, 1)"> name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
});
},
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> down(queryInterface, Sequelize) {
queryInterface.dropTable(</span><span style="color: rgba(128, 0, 0, 1)">'c</span><span style="color: rgba(128, 0, 0, 1)">ustomers</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
}
};</span></pre>
</div>
<p> npx sequelize db:migrate执行migration。现在server.mjs</p>
<div class="cnblogs_code">
<pre>import { sequelize, Customer, Airplane, FlightSchedule } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./models/index.mjs</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">await</span> sequelize.authenticate()</pre>
</div>
<p> <strong>增删改查</strong></p>
<p><strong> </strong>新增一条记录:Model名.create(),参数是一个对象,对象的属性就是创建model时定义的属性,Sequelize自动添加的属性,它自己会处理好。返回值是一个model实例,就是插入的表的一行,它有toJSON() 方法</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> record = <span style="color: rgba(0, 0, 255, 1)">await</span> Customer.create({name: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Sam</span><span style="color: rgba(128, 0, 0, 1)">'</span>, email: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">123@qq.com</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">});
console.log(record.toJSON())</span></pre>
</div>
<p> node server.mjs, 数据库中成功插入一条数据。这时可以npm i nodemon -D, npx nodemon server.mjs启动服务器。model名.bulkCreate()批量插入数据,它接受的是数组,数组的每一个元素是对象,对象和create()接受的对象一样</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> record = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Customer.bulkCreate([
{ name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">张三</span><span style="color: rgba(128, 0, 0, 1)">'</span>, email: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">456@qq.com</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> },
{ name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">李四</span><span style="color: rgba(128, 0, 0, 1)">'</span>, email: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">789@qq.com</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> },</span> { name: '王五', email: '1789@qq.com' }<br><span>])</span></pre>
<pre><span style="color: rgba(0, 0, 0, 1)">console.log(record.map(r </span>=> r.toJSON()))</pre>
</div>
<p> 删除:删除一条记录,可以先model名.findOne() 查出这个实例, 然后再在实例上调用destroy()方法</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> record = <span style="color: rgba(0, 0, 255, 1)">await</span> Customer.findOne({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)"> } });
</span><span style="color: rgba(0, 0, 255, 1)">await</span> record.destroy();</pre>
</div>
<p> 删除多条记录,那就要用model名.destroy(),destroy的参数就是筛选条件。 </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">await</span> Customer.destroy({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> } });<br></span></pre>
</div>
<p> 删除还有硬删除和软删除之分。创建model时配置了paranoid: true,就表示软删除,删除只是在model实例上添加deleteAt字段,不会真正的从数据库中删除数据,当然前提是timestamps 设为true,此时要硬删除,就要在调用destroy方法时,添加force: true。如果软删除,在migration的时候,要在表中创建deleteAt字段。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">await</span> Customer.destroy({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: 4 }, force: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> });<br></span></pre>
</div>
<p> 更新:更新多条记录,使用model名.update(),第一个参数是要更新成什么, 第二个参数是查询条件</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Customer.update(
{ name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">张三</span><span style="color: rgba(128, 0, 0, 1)">'</span>, email: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">456@qq.com</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> },
{ </span><span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> } }
);</span></pre>
</div>
<p> 如果只想更新一条记录,先调用model名.findOne()获取到实例,然后修改实例的属性或调用increment 和decrement 方法修改属性,最后调用实例上的save()。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> record = <span style="color: rgba(0, 0, 255, 1)">await</span> Customer.findOne({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> } });
record.name </span>= <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Sam</span><span style="color: rgba(128, 0, 0, 1)">'</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> record.save()</pre>
</div>
<p> 查询:modal名.方法名,比如findAll,findone。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> airplances = <span style="color: rgba(0, 0, 255, 1)">await</span> Airplane.findAll(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> findAll返回的是数组。</span>
console.log(airplances.map(ap =><span style="color: rgba(0, 0, 0, 1)"> ap.toJSON()));
</span><span style="color: rgba(0, 0, 255, 1)">const</span> airplance = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Airplane.findOne();
console.log(airplance.toJSON())</span></pre>
</div>
<p> 查询方法中,带有attributes参数,列出需要的某个或某些字段</p>
<div class="cnblogs_code">
<pre>const airplance =<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findOne({
attributes: [</span>'planeModel', 'totalSeats'<span style="color: rgba(0, 0, 0, 1)">]
});
console.log(airplance.toJSON())</span></pre>
</div>
<p> 如果要排除某个字段,用exclude</p>
<div class="cnblogs_code">
<pre>const airplance =<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findOne({
attributes: {exclude: [</span>'createdAt'<span style="color: rgba(0, 0, 0, 1)">]}
});</span></pre>
</div>
<p> 如果对字段进行重命名,数组中的元素需要是一个数组,它的第一项是原字段名,第二项是新字段名。</p>
<div class="cnblogs_code">
<pre>const airplance =<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findOne({
attributes: [</span>'planeModel', ['totalSeats', 'seats']] <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> totalSeats 重命名为 seats</span>
});</pre>
</div>
<p> 查询方法中,提供where进行条件查询,为此Sequelize还专门提供了Op。</p>
<div class="cnblogs_code">
<pre>import { Op } from 'sequelize'<span style="color: rgba(0, 0, 0, 1)">;
const airplance </span>=<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findOne({
where: {
id: {
: [</span>1, 2<span style="color: rgba(0, 0, 0, 1)">]
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 或
* : {
* : 2,
* : null
* }
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
}
}
});</span></pre>
</div>
<p> 如果查询中调用函数,要用sequelize.fn,第一个参数是函数名,第二个参数用sequelize.col来指定对哪一个属性来进行函数操作。如果在where条件中使用函数,sequelize.where 来表示相等性。</p>
<div class="cnblogs_code">
<pre>const airplances =<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findAll({
where: sequelize.where(
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> sequelize.where的第二个参数是基本类型,它就进行相等比较</span>
sequelize.fn('char_length', sequelize.col('planeModel')), 5<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 进行其他比较,第二个参数是个对象,定义比较</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> sequelize.fn('char_length', sequelize.col('planeModel')), {</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> : 12</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> })</span>
});</pre>
</div>
<p> 如果在查询字段中调用函数,要定义别名</p>
<div class="cnblogs_code">
<pre>const airplance = await Airplane.findOne({
<span style="color: rgba(0, 0, 0, 1)"> attributes: [
],
});</span></pre>
</div>
<p> 查询方法参数中带有order,就是排序,它是一个数组,数组的每一项也是一个数组,子数组的第一项指定按哪个字段进行排序,第二项指定按升序还是降序进行排序</p>
<div class="cnblogs_code">
<pre>const airplances =<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findAll({
order: [
[</span>'totalSeats', 'DESC'<span style="color: rgba(0, 0, 0, 1)">]
]
});</span></pre>
</div>
<p> 查询方法的参数中带有group,就是分组,指定按哪一个字段进行分组。分组之后,通常使用聚合函数,聚合函数的使用和普通函数的用法相同,</p>
<div class="cnblogs_code">
<pre>const airplances =<span style="color: rgba(0, 0, 0, 1)"> await Airplane.findAll({
attributes: [</span>'planeModel', ],
group: </span>'planeModel'<span style="color: rgba(0, 0, 0, 1)">
});</span></pre>
</div>
<p> 查询方法中,带有offset和limit,就是用于分页</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> airplances = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Airplane.findAll({
offset: </span><span style="color: rgba(128, 0, 128, 1)">1</span>, limit: <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
});</span></pre>
</div>
<p> sequlieze.query 可以执行原生sql。</p>
<div class="cnblogs_code">
<pre>const airplances = await sequelize.query('SELECT * FROM airplanes'<span style="color: rgba(0, 0, 0, 1)">, {
type: QueryTypes.SELECT,
});</span></pre>
</div>
<p> 在model的定义中,除了定义必要的属性,还可以对每一个属性定义setter,getter函数。getter函数对从数据库获取回来的数据进行转化,比如小写转化成大写。setter函数先对数据进行转化,再存储到数据库,比如对密码进行加密。甚至可以设置虚拟字段,该字段不存数据库,sequlize自己做的转化,主要作用是组合不同的属性,它的类型是DataType.VIRTUAL,然后设置get方法返回值。把Customer modal 修改如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> hash(value) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> value + '@xdsd'<span style="color: rgba(0, 0, 0, 1)">
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> (sequelize, DataTypes) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> sequelize.define('Customer'<span style="color: rgba(0, 0, 0, 1)">, {
name: {
type: DataTypes.STRING,
get() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> getDataValue 返回存储在数据库中的原始值</span>
const rawValue = <span style="color: rgba(0, 0, 255, 1)">this</span>.getDataValue('name'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> rawValue.toUpperCase()
}
},
email: {
type: DataTypes.STRING,
set(value) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> setDataValue对用户输入的值进行操作</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.setDataValue('email'<span style="color: rgba(0, 0, 0, 1)">, hash(value));
},
},
fullName: {
type: DataTypes.VIRTUAL,
get() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> `${<span style="color: rgba(0, 0, 255, 1)">this</span>.name} ${<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.email}`;
}
}
});
}</span></pre>
</div>
<p> 新增一个user 并查询</p>
<div class="cnblogs_code">
<pre>await Customer.create({name: 'Jason', email: '2345@qq.com'<span style="color: rgba(0, 0, 0, 1)">})
const user </span>= await Customer.findAll()</pre>
</div>
<p> 可以看到</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
</span>"name": "JASON", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> name 大写</span>
"fullName": "JASON 2345@qq.com@xdsd", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 虚拟字段</span>
"email": "2345@qq.com@xdsd" <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> email进行了hash</span>
}</pre>
</div>
<p> <strong>关系</strong></p>
<p> 关系型数据库中,表与表之间存在1对1,1对多,和多对多的关系,那在Sequelize中,怎么用model来表示这些关系?每一个model都有四个方法,hasOne, BelongsTo, hasMany, BelongsToMany。由于关系是相对的,所以每一种关系都用两个方法实现。 </p>
<p> 实现1对1,用hasOne和BelongsTo。比如飞机(Airplanes)和飞机详情表(AirplaneDetails), 由于表与表之间的关系是通过外键实现的,外键放到哪张表上?哪张表能单独存在,airplanes能单独存在,details则是依附airplane的,所以外键放到details上,airplanes有一个(hasOne)details, details属于(BelongsTo)airplane,</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Airplane.hasOne(AirplaneDetail)
AirplaneDetail.BelongsTo(Airplane)</span></pre>
</div>
<p> hasOne把外键放到它的参数上,AirplaneDetail model中,多了一个AirplaneId属性,它所引用的model名+Id,相应的,airplaneDetail表多了AirplaneId列,除非AirplaneId列已存在。当有了关系后,调用hasOne和BelongsTo的model 实例多了几个辅助方法,来方便的设置关系。从数据库查到airplane后,airplane有getAirplaneDetail, setAirplaneDetail 和createAirplaneDetail 方法。AirplaneDetail实例setAirplane等方法。</p>
<p> 实现1对多,用hasMany和belongsTo。比如 一架飞机可以执行多次航班(hasMany),但一个航班只能属于一架飞机(BelongsTo)。models下的index.mjs</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Airplane.hasMany(FlightSchedule)
FlightSchedule.belongsTo(Airplane)</span></pre>
</div>
<p> 1对多的关系,外键放到多的那一边,所以FlightSchedule多了AirplaneId属性,对应的,flightschedules表多airplaneId列。为了满足关系,需要做migration。给flightschedules表添加外键,npx sequelize migration:generate --name add-flightschedules-references</p>
<div class="cnblogs_code">
<pre>module.exports =<span> {
async<span> up (queryInterface, Sequelize) {
await queryInterface.addColumn('FlightSchedules', 'AirplaneId'<span>, {
type: Sequelize.INTEGER,
});
await queryInterface.addConstraint('FlightSchedules'<span>, {
type: 'foreign key'<span>,
fields: ['AirplaneId'<span>],
references: {
table: 'Airplanes'<span>,
field: 'id'<span>
},
name: 'fkey_flight_schedules_airplane'<span>,
onDelete: 'set null'<span>,
onUpdate: 'cascade'<span>
});
},
async<span> down (queryInterface, Sequelize) {
await<span> queryInterface.removeConstraint(
'FlightSchedules', 'fkey_flight_schedules_airplane'<span>
);
await queryInterface.removeColumn('FlightSchedules', 'AirplaneId'<span>);
}
};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</div>
<p> npx sequelize db:migrate 执行迁移。airplane实例有getFlightSchedules和addFlightSchedule(或addflightSchedules),createFlightSchedule, countFlightSchedules 等方法</p>
<div class="cnblogs_code">
<pre>const airplance = await Airplane.findOne({ where: { id: 1<span style="color: rgba(0, 0, 0, 1)"> } });
const flightSchedule </span>= await FlightSchedule.findOne({ where: { id: 1<span style="color: rgba(0, 0, 0, 1)"> } })
await airplance.addFlightSchedule(flightSchedule) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> flightSchedules表中,id=1的 AirplaneId 变成了 '1'</span><span style="color: rgba(0, 0, 0, 1)">
await airplance.createFlightSchedule({ </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建了一条flightSchedule记录,并且它的AirplaneId也是1</span>
originAirport: "深圳"<span style="color: rgba(0, 0, 0, 1)">,
destinationAirport: </span>"武汉"<span style="color: rgba(0, 0, 0, 1)">,
departureTime: </span>"2023-10-01 20:00:00"<span style="color: rgba(0, 0, 0, 1)">
})
const flightSchedules </span>=<span style="color: rgba(0, 0, 0, 1)"> await airplance.getFlightSchedules();
const count </span>=<span style="color: rgba(0, 0, 0, 1)"> await airplance.countFlightSchedules()
console.log(flightSchedules.map(r </span>=> r.toJSON()))</pre>
</div>
<p> 实现多对多要用BelongsToMany,因为多对多的关系,需要中间表,所以它还要一个through参数来指定使用哪张表,如果参数是字符串,Sequelize默认表中字段名是两个关联model的名字+id。比如Customer 和FlightSchedule,一个乘客可以乘多趟航班,一趟航班有多个客人,它们之间的关联是购买的飞机票。models下的index.mjs</p>
<div class="cnblogs_code">
<pre>Customer.belongsToMany(FlightSchedule, { through: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">BoardingTickets</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> });
FlightSchedule.belongsToMany(Customer, { through: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">BoardingTickets</span><span style="color: rgba(128, 0, 0, 1)">'</span> });</pre>
</div>
<p> 为了满足关系,需要做migration,创建BoardingTickets表, npx sequelize migration:generate --name create-board-ticket</p>
<div class="cnblogs_code">
<pre>module.exports =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> up(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.createTable(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">BoardingTickets</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, {
id: {
allowNull: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
autoIncrement: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
primaryKey: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
type: Sequelize.DATE
},
updatedAt: {
allowNull: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">,
type: Sequelize.DATE
},
CustomerId: {
type: Sequelize.INTEGER,
references: {
model: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Customers</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
key: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">id</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
},
onUpdate: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">set null</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
onDelete: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">cascade</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
},
FlightScheduleId: {
type: Sequelize.INTEGER,
references: {
model: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">FlightSchedules</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
field: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">id</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
},
onDelete: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">set null</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
onUpdate: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">cascade</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
}
});
},
</span><span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> down(queryInterface, Sequelize) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> queryInterface.dropTable(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">BoardingTickets</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
}
};</span></pre>
</div>
<p> npx sequelize db:migrate 执行迁移。model 实例上多了很多方法来建立联系,比如addFlightSchedule</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> customer = <span style="color: rgba(0, 0, 255, 1)">await</span> Customer.findOne({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> } });
</span><span style="color: rgba(0, 0, 255, 1)">const</span> flightSchedule = <span style="color: rgba(0, 0, 255, 1)">await</span> FlightSchedule.findOne({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> } })
customer.addFlightSchedule(flightSchedule)</span></pre>
</div>
<p> addFlightSchedule执行的Sql语句是INSERT INTO `BoardingTickets` (`createdAt`,`updatedAt`,`CustomerId`,`FlightScheduleId`) VALUES ('2024-08-15 08:52:37','2024-08-15 08:52:37',1,1); boardingtickets表多了一条记录。getFlightSchedules会把中间表查出来,</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> customer = <span style="color: rgba(0, 0, 255, 1)">await</span> Customer.findOne({ <span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> } });
</span><span style="color: rgba(0, 0, 255, 1)">const</span> flightSchedules = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> customer.getFlightSchedules();
console.log(JSON.stringify(flightSchedules, undefined, </span><span style="color: rgba(128, 0, 128, 1)">4</span>))</pre>
</div>
<p> 查询结果如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">[
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">id</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">originAirport</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">济南</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">destinationAirport</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">武汉</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">departureTime</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2022-01-01T08:00:00.000Z</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">createdAt</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2023-10-21T15:05:37.000Z</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">updatedAt</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2023-10-23T14:44:16.000Z</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">AirplaneId</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BoardingTickets</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">createdAt</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2023-10-23T15:00:55.000Z</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">updatedAt</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2023-10-23T15:00:55.000Z</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CustomerId</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FlightScheduleId</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
}
}
]</span></pre>
</div>
<p> 如果不想返回中间表,或只想返回中间表的某些属性,给getFlightSchedules传递joinTableAttributes参数,</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">const</span> flightSchedules = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> customer.getFlightSchedules({
joinTableAttributes: []
});</span></pre>
</div>
<p> 以上的查询称为 Lazy load,先用model 查出一个实例,再用实例的方法,查询另外需要的数据,就是先查一张表,再查另外一张表中,执行了两次query请求,那能不能一次性地把所有数据都查询出来,那就是Eager load,查询的时候,使用include,包含关联的表,连表查询。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> customer = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Customer.findOne({
</span><span style="color: rgba(0, 0, 255, 1)">where</span>: { id: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> },
include: [{
model: FlightSchedule, </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">连接FlightSchedules 表</span>
through: { attributes: [] } <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 不需要中间表的数据</span>
<span style="color: rgba(0, 0, 0, 1)"> }]
});</span></pre>
</div>
<p> 一次查出了customer和它关联的 FlightSchedules。</p>
<p> 事务:分为 unmanaged 事务和managed事务。 unmanaged事务,手动创建事务,提交或回滚事物 </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> tx = <span style="color: rgba(0, 0, 255, 1)">await</span> sequelize.transaction(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一个事务</span>
<span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">const</span> plane = <span style="color: rgba(0, 0, 255, 1)">await</span> Airplane.findByPk(<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">const</span> schedule = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> FlightSchedule.create({
originAirport: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">上海</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
destinationAirport: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">北京</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
departureTime: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">2023-10-24 10:10:00</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
}, { transaction: tx });
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> schedule.setAirplane(plane, { transaction: tx });
</span><span style="color: rgba(0, 0, 255, 1)">await</span> tx.commit(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交事务</span><span style="color: rgba(0, 0, 0, 1)">
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 0, 255, 1)">await</span> tx.rollback(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 回滚事务</span>
}</pre>
</div>
<p> managed transactions 就是自动提交和回滚事务。把要做的事性作为回调函数,传递给sequelize.transaction 就是managed transactions</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">const</span> plane = <span style="color: rgba(0, 0, 255, 1)">await</span> Airplane.findByPk(<span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">const</span> flight = <span style="color: rgba(0, 0, 255, 1)">await</span> sequelize.transaction(<span style="color: rgba(0, 0, 255, 1)">async</span> (tx) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">const</span> schedule = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> FlightSchedule.create({
originAirport: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">上海</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
destinationAirport: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">深圳</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
departureTime: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">2023-10-24 10:10:00</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
}, { transaction: tx });
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> schedule.setAirplane(plane, { transaction: tx });
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> schedule
})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在这里自动提交事务</span>
} <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在这里,事务已经回滚了</span>
}</pre>
</div>
<p> 对已经存在的数据库使用Sequelize,由于数据库已经存在,我们需要创建model来适配它的Schema。比如数据库有一张表是foo_bars, 它的属性是</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">id INTEGER AUTOINCREMENT
first_name VARCHAR(</span><span style="color: rgba(128, 0, 128, 1)">200</span><span style="color: rgba(0, 0, 0, 1)">)
last_name VARCHAR(</span><span style="color: rgba(128, 0, 128, 1)">200</span><span style="color: rgba(0, 0, 0, 1)">)
email VARCHAR(</span><span style="color: rgba(128, 0, 128, 1)">200</span><span style="color: rgba(0, 0, 0, 1)">)
date_created DATETIME
date_updated DATETIME</span></pre>
</div>
<p> 没有createdAt 和UpdatedAt, 并且表名和字段名都使用下划线,所以在定义model的时候,都需要自定义</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> options</span>
<span style="color: rgba(0, 0, 0, 1)"> sequelize,
modelName: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">FooBar</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
tableName: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">foo_bars</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
createdAt: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">date_created</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
updatedAt: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">date_updated</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
underscore: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
},</span></pre>
</div>
<p> 第二个要注意的是如果没有表中没有使用id作为主键,需要在创建的model中删除id,</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> FooBar extends Model {}
FooBar.removeAttribute(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">id</span><span style="color: rgba(128, 0, 0, 1)">'</span>);</pre>
</div>
<p> </p><br><br>
来源:https://www.cnblogs.com/SamWeb/p/17750418.html
頁:
[1]