Express中使用Sequelize实现ORM

起步

第一步当然是环境,首先使用Express生成器安装express环境

express myapp	// myapp,你自己的项目名称(需要全局安装express)
npm install

然后安装sequelize

npm install --save sequelize
npm install --save mysql2	// 驱动,这里主要说mysql,其他的类型见官网文档

连接

连接到数据库必须有sequelize实例,实例化方法如下

const connect = new DB('dbname', 'username', 'password', {
    host: 'localhost',
    dialect: 'mysql'
})

配置好实例之后可以使用.authenticate()来测试连接

connect.authenticate().then(() => {
    console.log('连接成功')
}).catch(error => {
    console.error('连接失败:', error);
})

关闭连接

默认情况下,Sequelize将使连接保持打开状态,并对所有查询使用相同的连接。如果需要关闭连接,请调用sequelize.close()(它是异步的并返回Promise)

模型基础

定义模型

模型是Sequelize的本质。模型是代表数据库中表的抽象。在Sequelize中,它是扩展Model的类

使用sequelize.define方法来定义模型,sequelize会根据定义的模型名称的复数自动建表

const Book = connect.define('Book', {
    bookName: DB.STRING,	// 属性名: 数据类型
    bookCounts: DB.INTEGER,
    detail: DB.STRING,
}, {
    timestamps: false	// 自动生成createAt和updateAt
})

此外也可以直接提供表名

sequelize.define('Book', {
  // ... attributes
}, {
  tableName: 'literature'
});

更多数据类型:

// CHAR
Sequelize.STRING                      // VARCHAR(255)
Sequelize.STRING(1234)                // VARCHAR(1234)
Sequelize.STRING.BINARY               // VARCHAR BINARY
Sequelize.TEXT                        // TEXT
Sequelize.TEXT('tiny')                // TINYTEXT
// NUMBER
Sequelize.INTEGER                     // INTEGER
Sequelize.BIGINT                      // BIGINT
Sequelize.BIGINT(11)                  // BIGINT(11)
Sequelize.FLOAT                       // FLOAT
Sequelize.FLOAT(11)                   // FLOAT(11)
Sequelize.FLOAT(11, 10)               // FLOAT(11,10)
Sequelize.DOUBLE                      // DOUBLE
Sequelize.DOUBLE(11)                  // DOUBLE(11)
Sequelize.DOUBLE(11, 10)              // DOUBLE(11,10)
Sequelize.DECIMAL                     // DECIMAL
Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)
// TIME
Sequelize.DATE                        // mysql / sqlite 为 DATETIME, postgres 为带时区的 TIMESTAMP
Sequelize.DATE(6)                     // DATETIME(6) 适用 mysql 5.6.4+. 小数秒支持最多6位精度
Sequelize.DATEONLY                    // DATE 不带时间.
// BOOLEAN
Sequelize.BOOLEAN                     // TINYINT(1)
//ENUM
Sequelize.ENUM('value 1', 'value 2')  // 一个允许值为'value 1'和'value 2'的ENUM
// blob
Sequelize.BLOB                        // BLOB (PostgreSQL 为 bytea)
Sequelize.BLOB('tiny')                // TINYBLOB (PostgreSQL 为 bytea. 其余参数是 medium 和 long)
// GEOMETRY
Sequelize.GEOMETRY                    // Spatial 列. 仅 PostgreSQL (带有 PostGIS) 或 MySQL.
Sequelize.GEOMETRY('POINT')           // 带有 geometry 类型的 spatial 列. 仅 PostgreSQL (带有 PostGIS) 或 MySQL.
Sequelize.GEOMETRY('POINT', 4326)     // 具有 geometry 类型和 SRID 的 spatial 列. 仅 PostgreSQL (带有 PostGIS) 或 MySQL.
// unsigned 和 zerofill 属性
Sequelize.INTEGER.UNSIGNED              // INTEGER UNSIGNED
Sequelize.INTEGER(11).UNSIGNED          // INTEGER(11) UNSIGNED
Sequelize.INTEGER(11).ZEROFILL          // INTEGER(11) ZEROFILL
Sequelize.INTEGER(11).ZEROFILL.UNSIGNED // INTEGER(11) UNSIGNED ZEROFILL
Sequelize.INTEGER(11).UNSIGNED.ZEROFILL // INTEGER(11) UNSIGNED ZEROFILL

数据类型来源,掘金,云影sky的详细易用的 Sequelize 解读

模型同步

使用model.async()方法来将模型与数据库同步

  • User.sync() -如果不存在则创建表(如果已经存在则不执行任何操作)
  • User.sync({ force: true }) -这将创建表,如果该表已经存在,则将其首先删除
  • User.sync({ alter: true }) -这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),然后在表中进行必要的更改以使其与模型匹配

创建模型实例

使用model.build方法来创建模型实例

const jsBook = Book.build({
    bookName: '你不知道的JS',
    bookCounts: 3,
    detail: 'JS 进阶系列',
})

上面的创建实例之后使用jsBook.save();方法来保存到数据库

模型查询

INSERT查询

上面的模型保存还有一种简便方法,直接使用model.create方法,直接保存到数据库

Book.create({
    bookName: '你不知道的JS',
    bookCounts: 3,
    detail: 'JS 进阶系列',
})

image-20200810110734948

可以看到数据库中已经生成了这条记录

image-20200810113908645

控制台可以看到sql语句

SELECT查询

find查询

使用findAll方法查询整个表

Book.findAll()

效果图

image-20200810114828228

可以使用attributes选项来进行列查询,数组中列出要查询的字段名

Book.findAll({
    attributes: ['id', 'bookName'],
})

效果图

image-20200810115039418

使用exclude删除一些选定的属性

Book.findAll({
    attributes: { exclude: ['bookCounts'] },
})

效果图

image-20200810115403040

其他find查询
  • findByPk:使用提供的主键仅从表中获得一个条目

  • findOne:获取它找到的第一个条目(如果提供的话,它满足可选的查询选项)

  • findOrCreate:查询不到实例就创建一个,返回值是实例(查到的或者创建的)和一个布尔值(创建为true)

  • findAndCountAll:查询并计数,有两个返回值,一个是查询到的记录另一个是count

    image-20200810142105550

where子句

使用where选项可以进行where查询

Book.findAll({
    where: {
        bookName: '你不知道的JS',
        id: 1
    }
})
// SELECT * FROM books WHERE bookName = '你不知道的JS' AND id = 1;

效果如图

image-20200810125605150

有了AND就有OR,使用Op.or来进行or操作

Book.findAll({
    where: {
        bookName: '你不知道的JS',
        id: {
            [Op.or]: [1, 2]
        }
    }
})
// SELECT * FROM books WHERE bookName = '你不知道的JS' AND (id = 1 OR id = 2);

效果图

image-20200810133131211

AND也可以使用Op.and来操作,但是易读性不强(不常用)

Book.findAll({
    where: {
        [Op.and]: [
            { bookName: '你不知道的JS' },
            {
                id: {
                    [Op.or]: [1, 2]
                }
            }
        ]
    }
})

sequelize提供了更多运算

const { Op } = require("sequelize");
Post.findAll({
  where: {
    [Op.and]: [{ a: 5 }, { b: 6 }],            // (a = 5) AND (b = 6)
    [Op.or]: [{ a: 5 }, { b: 6 }],             // (a = 5) OR (b = 6)
    someAttribute: {
      // Basics
      [Op.eq]: 3,                              // = 3
      [Op.ne]: 20,                             // != 20
      [Op.is]: null,                           // IS NULL
      [Op.not]: true,                          // IS NOT TRUE
      [Op.or]: [5, 6],                         // (someAttribute = 5) OR (someAttribute = 6)

      // Using dialect specific column identifiers (PG in the following example):
      [Op.col]: 'user.organization_id',        // = "user"."organization_id"

      // Number comparisons
      [Op.gt]: 6,                              // > 6
      [Op.gte]: 6,                             // >= 6
      [Op.lt]: 10,                             // < 10
      [Op.lte]: 10,                            // <= 10
      [Op.between]: [6, 10],                   // BETWEEN 6 AND 10
      [Op.notBetween]: [11, 15],               // NOT BETWEEN 11 AND 15

      // Other operators

      [Op.all]: sequelize.literal('SELECT 1'), // > ALL (SELECT 1)

      [Op.in]: [1, 2],                         // IN [1, 2]
      [Op.notIn]: [1, 2],                      // NOT IN [1, 2]

      [Op.like]: '%hat',                       // LIKE '%hat'
      [Op.notLike]: '%hat',                    // NOT LIKE '%hat'
      [Op.startsWith]: 'hat',                  // LIKE 'hat%'
      [Op.endsWith]: 'hat',                    // LIKE '%hat'
      [Op.substring]: 'hat',                   // LIKE '%hat%'
      [Op.iLike]: '%hat',                      // ILIKE '%hat' (case insensitive) (PG only)
      [Op.notILike]: '%hat',                   // NOT ILIKE '%hat'  (PG only)
      [Op.regexp]: '^[h|a|t]',                 // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
      [Op.notRegexp]: '^[h|a|t]',              // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
      [Op.iRegexp]: '^[h|a|t]',                // ~* '^[h|a|t]' (PG only)
      [Op.notIRegexp]: '^[h|a|t]',             // !~* '^[h|a|t]' (PG only)

      [Op.any]: [2, 3],                        // ANY ARRAY[2, 3]::INTEGER (PG only)

      // In Postgres, Op.like/Op.iLike/Op.notLike can be combined to Op.any:
      [Op.like]: { [Op.any]: ['cat', 'hat'] }  // LIKE ANY ARRAY['cat', 'hat']

      // There are more postgres-only range operators, see below
    }
  }
});

UPDATE查询

使用model.update方法来进行数据更新

Book.update({
    bookCounts: 5
}, {
    where: {
        id: 2
    }
})
// UPDATE books SET bookCounts =5 WHERE id = 2;

修改后的数据如下

image-20200810134545193

DELETE查询

使用model.destory方法进行删除操作

Book.destory({
    where: {
        id: 2
    }
})

限制和分页

使用limitoffset选项进行限制/分页

  • limit:每一页的记录数量限制
  • offset:第几页,从0开始
Book.findAll({
    where: {
        bookName: '你不知道的JS',
    },
    offset: parseInt(req.query.page) ? parseInt(req.query.page) : 0,
    limit: 1
})

image-20200810140552684

实用方法

image-20200810140836869

进阶用法

getter&setter

image-20200810142713058

image-20200810142730547

事务

image-20200810142930896


前端小白