egg结合Sequelize连接mysql踩坑指南

2,299 阅读3分钟

目标:

使用egg配合Sequelize连接mysql数据库

准备工作:

1、初始化项目,安装brew和mysql

(1)安装brew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

(2)安装mysql

brew install mysql
brew services start mysql

(3)通过 npm 初始化一个项目:

> $ mkdir sequelize-project && cd sequelize-project 
> $ npm init egg
> --type=simple $ npm i

(4)安装并配置 egg-sequelize 插件(它会辅助我们将定义好的 Model 对象加载到 app 和 ctx 上)和 mysql2 模块:

安装

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',
};

完成上面的配置之后,一个使用 sequelize 的项目就初始化完成了。egg-sequelize 和 sequelize 还支持更多的配置项,可以在他们的文档中找到。

(5)编写代码

现在终于可以开始编写代码实现业务逻辑了,首先我们来在 app/model/ 目录下编写 user 这个 Model:

'use strict';

module.exports = app => {
  const { STRING, INTEGER, DATE } = app.Sequelize;

  const User = app.model.define('user', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    name: STRING(30),
    age: INTEGER,
    created_at: DATE,
    updated_at: DATE,
  });

  return User;
};

这个 Model 就可以在 Controller 和 Service 中通过 app.model.User 或者 ctx.model.User 访问到了,例如我们编写 app/controller/users.js:

// app/controller/users.js
const Controller = require('egg').Controller;

function toInt(str) {
  if (typeof str === 'number') return str;
  if (!str) return str;
  return parseInt(str, 10) || 0;
}

class UserController extends Controller {
  async index() {
    const ctx = this.ctx;
    const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };
    ctx.body = await ctx.model.User.findAll(query);
  }

  async show() {
    const ctx = this.ctx;
    ctx.body = await ctx.model.User.findByPk(toInt(ctx.params.id));
  }

  async create() {
    const ctx = this.ctx;
    const { name, age } = ctx.request.body;
    const user = await ctx.model.User.create({ name, age });
    ctx.status = 201;
    ctx.body = user;
  }

  async update() {
    const ctx = this.ctx;
    const id = toInt(ctx.params.id);
    const user = await ctx.model.User.findByPk(id);
    if (!user) {
      ctx.status = 404;
      return;
    }

    const { name, age } = ctx.request.body;
    await user.update({ name, age });
    ctx.body = user;
  }

  async destroy() {
    const ctx = this.ctx;
    const id = toInt(ctx.params.id);
    const user = await ctx.model.User.findByPk(id);
    if (!user) {
      ctx.status = 404;
      return;
    }

    await user.destroy();
    ctx.status = 200;
  }
}

module.exports = UserController;

最后我们将这个 controller 挂载到路由上:

// app/router.js
module.exports = app => {
  const { router, controller } = app;
  router.resources('users', '/users', controller.users);
};

针对 users 表的 CURD 操作的接口就开发完了。

踩坑指南:

1.自动将表名称变为复数。 我创建的demo里的user表,自动被加上了s,变成了users。 在这里插入图片描述 解决方案(以下方法2选1 即可):

  • 在定义model的时候,设置禁止转换为复数
const { STRING, INTEGER, DATE } = app.Sequelize;

  const User = app.model.define('user', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    real_name: STRING(10),
    emp_id: INTEGER,
    emp_name: STRING(6),
    created_at: DATE,
    updated_at: DATE,
  },{
       // 禁止修改表名,默认情况下,sequelize将自动将所有传递的模型名称(define的第一个参数)转换为复数
    // 但是为了安全着想,复数的转换可能会发生变化,所以禁止该行为
    freezeTableName: true
  });
  • 在配置文件里设置禁止转换为复数
// app/config/config.default.js
config.sequelize = {
     "username": "root",
    "password": null,
    "database": "egg-sequelize-doc-default",
    "host": "127.0.0.1",
    "dialect": "mysql"
    define: {
      freezeTableName: true,//禁止转换为复数
    },
  };

2、直接将下划线转为驼峰。 因created_at会被转变成 createdAt故报错。 在这里插入图片描述 解决方案:在配置中设置不转为驼峰

// app/config/config.default.js
config.sequelize = {
     "username": "root",
    "password": null,
    "database": "egg-sequelize-doc-default",
    "host": "127.0.0.1",
    "dialect": "mysql"
    define: {
       underscored: true,//禁止转换为驼峰
    },
  };

3、时间存储问题 mysql保存时会自动保存为UTC格式,可以在config中配置:

exports.sequelize = {
    dialect: 'mysql',
    ....
    timezone: '+08:00' // 保存为本地时区
  }

但是egg-sequelize在读取时间时,还是会返回UTC格式,还需要改一下配置,添加:

exports.sequelize = {
    dialect: 'mysql',
    ....
    timezone: '+08:00' ,// 保存为本地时区
    dialectOptions: {
      dateStrings: true,
      typeCast(field, next) {
        // for reading from database
        if (field.type === "DATETIME") {
          return field.string();
        }
        return next();
      }
    }
  }

4、 created_at 和 updated_at 字段 创建数据库表的时候,需要有这两个字段。

module.exports = app => {
  const { STRING, INTEGER, DATE } = app.Sequelize;

  const User = app.model.define('user', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    real_name: STRING(10),
    emp_id: INTEGER,
    emp_name: STRING(6),
    created_at: DATE,
    updated_at: DATE,
  });

  return User;
};