# Sequelize-egg 中的使用

# 安装

npm install --save egg-sequelize mysql2

# 配置更改

  • 在 config/plugin.js 中引入 egg-sequelize 插件
exports.sequelize = {
    enable: true,
    package: 'egg-sequelize',
};
  • 在 config/config.default.js 中编写 sequelize 配置
config.sequelize = {
    dialect: 'mysql',
    host: '127.0.0.1',
    port: 3306,
    database: 'egg-sequelize-doc-default',
};

# 编写代码

# 创建所需的MOdel

app/model/ 目录下编写Model

可使用 sequelize-automate 插件来自定义生成Model


npm install sequelize-automate --save

# 详细文档地址 (opens new window)

# sequelize-automate 命令详解


sequelize-automate 命令支持的参数主要有:

--type, -t 指定 models 代码风格,当前可选值:js ts egg midway
--dialect, -e 数据库类型,可选值:mysql sqlite postgres mssql mariadb
--host, -h 数据库 host
--database, -d 数据库名
--user, -u 数据库用户名
--password, -p 数据库密码
--port, -P 数据库端口,默认:MySQL/MariaDB 3306,Postgres 5432,SSQL: 1433
--output, -o 指定输出 models 文件的目录,默认会生成在当前目录下 models 文件夹中
--camel, -C models 文件中代码是否使用驼峰发命名,默认 false
--emptyDir, -r 是否清空 models 目录(即 -o 指定的目录),如果为 true,则生成 models 之前会清空对应目录,默认 false
--config, -c 指定配置文件,可以在一个配置文件中指定命令的参数

自定义的命令

sequelize - automate - t js - h 127.0 .0 .1 - d Test - u Test - p 123456 - P 3306 - e mysql - o models

# 生成的Module 需要注意的事项

生成的Moudle 建议改成一下格式

TIP

  • Model 的名字首字母建议大写
'use strict';
module.exports = sequelize => {
    const {
        model,
        Sequelize: {
            DataTypes
        }
    } = sequelize;
    const attributes = {
        id: {
            type: DataTypes.INTEGER(11),
            allowNull: false,
            defaultValue: null,
            primaryKey: true,
            autoIncrement: true,
            comment: null,
            field: 'id',
        },
        user: {
            type: DataTypes.STRING(255),
            allowNull: false,
            defaultValue: null,
            primaryKey: false,
            autoIncrement: false,
            comment: null,
            field: 'user',
        },
        pass: {
            type: DataTypes.STRING(255),
            allowNull: false,
            defaultValue: null,
            primaryKey: false,
            autoIncrement: false,
            comment: null,
            field: 'pass',
        },
        create_data: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: null,
            primaryKey: false,
            autoIncrement: false,
            comment: null,
            field: 'create_data',
        },
        update_data: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: null,
            primaryKey: false,
            autoIncrement: false,
            comment: null,
            field: 'update_data',
        },
        logo: {
            type: DataTypes.STRING(255),
            allowNull: false,
            defaultValue: null,
            primaryKey: false,
            autoIncrement: false,
            comment: null,
            field: 'logo',
        },
    };
    const options = {
        tableName: 'user',
        comment: '',
        indexes: [],
    };
    const UserModel = model.define('user_model', attributes, options);
    return UserModel;
};

# sequelize (opens new window)的一些使用方法

TIP

此处只有部分的使用案例,具体移步到上述中文文档

# INSERT

Model.create() 方法是使用 Model.build() 构建未保存实例并使用 instance.save() 保存实例的简写形式.

  • 方式一:调用 build 方法后对象只存在于内存中,需要进一步调用 save 方法才会保存到数据库中。
let user = UsersModel.build({
    username: "swnd",
    password: "q11111"
});
user = await user.save();
  • 方式二:调用 create 方法后,会直接保存到数据库中。
const user = UsersModel.create({
    username: 'zhangsan',
    password: '123456'
}, {
    fields: ['username'] // 只允许更新这个
})

# SELECT

attributes 选择查出数据需要展示的字段

Model.findAll({
    attributes: ['foo', 'bar']
});

可以使用嵌套数组来重命名属性:

Model.findAll({
    attributes: ['foo', ['bar', 'baz'], 'qux']
});

SELECT foo, bar AS baz, qux FROM...

你可以使用 sequelize.fn 进行聚合:

Model.findAll({
    attributes: [
        'foo',
        [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']
        'bar'
    ]
});
SELECT foo, COUNT(hats) AS n_hats, bar FROM...

使用聚合函数时, 必须为它提供一个别名, 以便能够从模型中访问它. 在上面的示例中, 你可以通过 instance.n_hats 获取帽子数量.

有时, 如果只想添加聚合, 那么列出模型的所有属性可能会很麻烦:

// 这是获取帽子数量的烦人方法(每列都有)
Model.findAll({
    attributes: [
        'id', 'foo', 'bar', 'baz', 'qux', 'hats', // 我们必须列出所有属性...
        [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'] // 添加聚合...
    ]
});

// 这个更短,并且更不易出错. 如果以后在模型中添加/删除属性,它仍然可以正常工作
Model.findAll({
    attributes: {
        include: [
            [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']
        ]
    }
});
SELECT id, foo, bar, baz, qux, hats, COUNT(hats) AS n_hats FROM...

同样, 也可以排除某些属性:

Model.findAll({
    attributes: {
        exclude: ['baz']
    }
});
--Assuming all columns are 'id', 'foo', 'bar', 'baz'
and 'qux'
SELECT id, foo, bar, qux FROM...

# WHERE

where 参数用于过滤查询.where 子句有很多运算符, 可以从 Op 中以 Symbols 的形式使用.

基础

Post.findAll({
    where: {
        authorId: 2
    }
});
// SELECT * FROM post WHERE authorId = 2

可以看到没有显式传递任何运算符(来自Op), 因为默认情况下 Sequelize 假定进行相等比较. 上面的代码等效于:

const {
    Op
} = require("sequelize");
Post.findAll({
    where: {
        authorId: {
            [Op.eq]: 2
        }
    }
});
// SELECT * FROM post WHERE authorId = 2

可以传递多个校验:

Post.findAll({
    where: {
        authorId: 12
        status: 'active'
    }
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';

就像在第一个示例中 Sequelize 推断出 Op.eq 运算符一样, 在这里 Sequelize 推断出调用者希望对两个检查使用 AND. 上面的代码等效于:

const {
    Op
} = require("sequelize");
Post.findAll({
    where: {
        [Op.and]: [{
                authorId: 12
            },
            {
                status: 'active'
            }
        ]
    }
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';

OR 可以通过类似的方式轻松执行:

const {
    Op
} = require("sequelize");
Post.findAll({
    where: {
        [Op.or]: [{
                authorId: 12
            },
            {
                authorId: 13
            }
        ]
    }
});
// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;

由于以上的 OR 涉及相同字段 , 因此 Sequelize 允许你使用稍有不同的结构, 该结构更易读并且作用相同:

const {
    Op
} = require("sequelize");
Post.destroy({
    where: {
        authorId: {
            [Op.or]: [12, 13]
        }
    }
});
// DELETE FROM post WHERE authorId = 12 OR authorId = 13;

操作符 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: {
            // 基本
            [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)

            // 使用方言特定的列标识符 (以下示例中使用 PG):
            [Op.col]: 'user.organization_id', // = "user"."organization_id"

            // 数字比较
            [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

            // 其它操作符

            [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' (不区分大小写) (仅 PG)
            [Op.notILike]: '%hat', // NOT ILIKE '%hat'  (仅 PG)
            [Op.regexp]: '^[h|a|t]', // REGEXP/~ '^[h|a|t]' (仅 MySQL/PG)
            [Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (仅 MySQL/PG)
            [Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (仅 PG)
            [Op.notIRegexp]: '^[h|a|t]', // !~* '^[h|a|t]' (仅 PG)

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

            // 在 Postgres 中, Op.like/Op.iLike/Op.notLike 可以结合 Op.any 使用:
            [Op.like]: {
                [Op.any]: ['cat', 'hat']
            } // LIKE ANY ARRAY['cat', 'hat']

            // 还有更多的仅限 postgres 的范围运算符,请参见下文
        }
    }
});

Op.in 的简写语法 直接将数组参数传递给 where 将隐式使用 IN 运算符:


Post.findAll({
  where: {
    id: [1,2,3] // 等同使用 `id: { [Op.in]: [1,2,3] }`
  }
});
// SELECT ... FROM "posts" AS "post" WHERE "post"."id" IN (1, 2, 3);

运算符的逻辑组合 运算符 Op.and, Op.or 和 Op.not 可用于创建任意复杂的嵌套逻辑比较.

使用 Op.and 和 Op.or 示例#

const {
    Op
} = require("sequelize");

Foo.findAll({
    where: {
        rank: {
            [Op.or]: {
                [Op.lt]: 1000,
                [Op.eq]: null
            }
        },
        // rank < 1000 OR rank IS NULL

        {
            createdAt: {
                [Op.lt]: new Date(),
                [Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
            }
        },
        // createdAt < [timestamp] AND createdAt > [timestamp]

        {
            [Op.or]: [{
                    title: {
                        [Op.like]: 'Boat%'
                    }
                },
                {
                    description: {
                        [Op.like]: '%boat%'
                    }
                }
            ]
        }
        // title LIKE 'Boat%' OR description LIKE '%boat%'
    }
});

使用 Op.not 示例#

Project.findAll({
    where: {
        name: 'Some Project',
        [Op.not]: [{
                id: [1, 2, 3]
            },
            {
                description: {
                    [Op.like]: 'Hello%'
                }
            }
        ]
    }
});
上面将生成:

SELECT *
    FROM `Projects`
WHERE(
    `Projects`.
    `name` = 'a project'
    AND NOT(
        `Projects`.
        `id`
        IN(1, 2, 3) OR `Projects`.
        `description`
        LIKE 'Hello%'
    )
)

# UPDATE

// 将所有没有姓氏的人更改为 "Doe"
await UsersModel.update({
    lastName: "Doe"
}, {
    where: {
        lastName: null
    }
});

# DELETE

// 删除所有名为 "Jane" 的人 
await UsersModel.destroy({
  where: {

    firstName: "Jane"

  }
}); 

要销毁所有内容,可以使用 TRUNCATE SQL:

// 截断表格
await User.destroy({
  truncate: true
});

# findAll

findAll 方法. 它生成一个标准的 SELECT 查询, 该查询将从表中检索所有条目(除非受到 where 子句的限制).

# findByPk

findByPk 方法使用提供的主键从表中仅获得一个条目.

const project = await Project.findByPk(123);
if (project === null) {
    console.log('Not found!');
} else {
    console.log(project instanceof Project); // true
    // 它的主键是 123
}

# findOne

findOne 方法获得它找到的第一个条目(它可以满足提供的可选查询参数).

const project = await Project.findOne({
    where: {
        title: 'My Title'
    }
});
if (project === null) {
    console.log('Not found!');
} else {
    console.log(project instanceof Project); // true
    console.log(project.title); // 'My Title'
}

# findOrCreate

除非找到一个满足查询参数的结果, 否则方法 findOrCreate 将在表中创建一个条目. 在这两种情况下, 它将返回一个实例(找到的实例或创建的实例)和一个布尔值, 指示该实例是已创建还是已经存在.

使用 where 参数来查找条目, 而使用 defaults 参数来定义必须创建的内容. 如果 defaults 不包含每一列的值, 则 Sequelize 将采用 where 的值(如果存在).

假设我们有一个空的数据库, 该数据库具有一个 User 模型, 该模型具有一个 username 和一个 job.

const [user, created] = await User.findOrCreate({
    where: {
        username: 'sdepold'
    },
    defaults: {
        job: 'Technical Lead JavaScript'
    }
});
console.log(user.username); // 'sdepold'
console.log(user.job); // 这可能是也可能不是 'Technical Lead JavaScript'
console.log(created); // 指示此实例是否刚刚创建的布尔值
if (created) {
    console.log(user.job); // 这里肯定是 'Technical Lead JavaScript'
}

# findAndCountAll

findAndCountAll 方法是结合了 findAll 和 count 的便捷方法. 在处理与分页有关的查询时非常有用, 在分页中, 你想检索带有 limit 和 offset 的数据, 但又需要知道与查询匹配的记录总数.

findAndCountAll 方法返回一个具有两个属性的对象:

count - 一个整数 - 符合查询条件的记录总数
rows - 一个数组对象 - 获得的记录
const {
    count,
    rows
} = await Project.findAndCountAll({
    where: {
        title: {
            [Op.like]: 'foo%'
        }
    },
    offset: 10,
    limit: 2
});
console.log(count);
console.log(rows);

# 关联查的一些方法

Sequelize 支持标准关联关系: 一对一, 一对多 和 多对多.

  • A.hasOne(B) 关联意味着 A 和 B 之间存在一对一的关系, 外键在目标模型(B)中定义.

  • A.belongsTo(B)关联意味着 A 和 B 之间存在一对一的关系, 外键在源模型中定义(A).

  • A.hasMany(B) 关联意味着 A 和 B 之间存在一对多关系, 外键在目标模型(B)中定义.

这三个调用将导致 Sequelize 自动将外键添加到适当的模型中(除非它们已经存在).

  • A.belongsToMany(B, { through: 'C' }) 关联意味着将表 C 用作联结表, 在 A 和 B 之间存在多对多关系. 具有外键(例如, aId 和 bId). Sequelize 将自动创建此模型 C(除非已经存在), 并在其上定义适当的外

# 一对一关系

对于本示例的其余部分, 我们假设我们有两个模型, 即 Foo 和 Bar. 我们想要在它们之间建立一对一的关系, 以便 Bar 获得 fooId 列.

实现该目标的主要设置如下:

Foo.hasOne(Bar);
Bar.belongsTo(Foo);

自定义外键

上面显示的 hasOne 和 belongsTo 调用都会推断出要创建的外键应称为 fooId. 如要使用其他名称, 例如 myFooId:

// 方法 1
Foo.hasOne(Bar, {
    foreignKey: 'myFooId', //从表的id
    as: 'acticle', //定义别名
    sourceKey: 'article_id' //当前主表的id 
});
Bar.belongsTo(Foo);

// 方法 2
Foo.hasOne(Bar, {
    foreignKey: {
        name: 'myFooId'
    }
});
Bar.belongsTo(Foo);

// 方法 3
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
    foreignKey: 'myFooId'
});

// 方法 4
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
    foreignKey: {
        name: 'myFooId'
    }
});

TIP

Ship.belongsTo(Captain, {
    targetKey: 'name',
    foreignKey: 'captainName'
});
A.hasOne(B)A.hasMany(B) 将外键保留在目标模型(B),因此引用的键在源模型中,因此使用了 sourceKey. 
原模型 需要关联的字段id 是  sourceKey
foreignKey 对应的id 是目标源的id
A.belongsTo(B) 将外键保留在源模型中(A),因此引用的键在目标模型中,因此使用了 targetKey.
外键保存源模型  foreignKey  为查询的从 id
外键保存目标模型  foreignKey  为主的 id

以上方法可用于一对一

强制性与可选性关联

默认情况下, 该关联被视为可选. 换句话说, 在我们的示例中, fooId 允许为空, 这意味着一个 Bar 可以不存在 Foo 而存在. 只需在外键选项中指定 allowNull: false 即可更改此设置:

Foo.hasOne(Bar, {
    foreignKey: {
        allowNull: false
    }
});

# 一对多关系

在这个例子中, 我们有模型 Team 和 Player. 我们要告诉 Sequelize, 他们之间存在一对多的关系, 这意味着一个 Team 有 Player , 而每个 Player 都属于一个 Team.

Team.hasMany(Player);
Player.belongsTo(Team);
Team.hasMany(Player, {
    foreignKey: 'myFooId', //从表的id
    as: 'acticle', //定义别名
    sourceKey: 'article_id' //当前主表的id 
});
Player.belongsTo(Team);