egg sequelize postgres 关联查询及分页问题

一、背景

我使用 egg 项目做 node 接口开发。
由于项目的特殊性,需要同时连接 mysql,mongoDB 和 postgres。

二、环境和技术栈

egg 创建模板还是非常方便的,不会的童鞋可以到官网看一下。

我比较喜欢自己搭建开发的 template,这样可以清楚的剔除冗余代码,也可以自由配置,出现问题能快读定位,不必花时间去理解别人的代码是怎样构建的,逻辑是如何实现的。

搭建环境

根据 egg 官方文档,创建如下项目目录

egg-example
├── app
   ├── controller
      └── XXXX.js
   └── router.js
├── config
   └── config.default.js
└── package.json

安装必要的插件

$ npm i egg-sequelize -S
$ npm i egg-mongoose -S
$ npm i mysql2 -S
$ npm i pg -S

这里对上面安装的插件做个简单解释

  • egg-bin 是开发环境用到的启动命令
  • egg-mongoose 是连接和操作 mongoDB 需要的插件,使用时需要仔细阅读文档
  • egg-sequelize 是连接和操作 mysql 和 postgres 需要的插件,使用时需要仔细阅读文档
  • mysql2 是 egg-sequelize 需要的依赖
  • pg 是 egg-postgres 需要的依赖

然后就可以开心的写 bug 了。

三、配置连接数据库

所有的配置都在 config/config.default.js中。 当然,如果想使用某个第三方包,需要在config/plugin.js 中注册一下。

// plugin.js
'use strict';

exports.sequelize = {
  enable: true,
  package: 'egg-sequelize',
};
exports.mongoose = {
  enable: true,
  package: 'egg-mongoose',
};
// config.default.js
exports.mongoose = {
  client: {
    url: 'mongodb://username:password@host:port/db',
    options: {
      useUnifiedTopology: true,
      useNewUrlParser: true, // 必须参数
    },
  },
};

exports.sequelize = {
  datasources: [
    {
      dialect: 'mysql',
      host: 'host',
      port: 'port',
      database: 'database',
      username: 'username',
      password: 'password',
      delegate: 'modelsql',
      baseDir: 'modelsql', // change default dir model to modelsql
      dialectOptions: {
        dateStrings: true,
        typeCast: true
      },
      define: {
        timestamps: false // don't add the timestamp attributes (updatedAt, createdAt)
      },
      timezone: '+08:00'  //timezone to local time
    },{
      dialect: 'postgres',
      database: 'database',
      username: 'username',
      password: 'password',
      host: 'host',
      port: 'port',
      delegate: 'postgres',
      baseDir: 'postgres',// change default dir model to postgres
      dialectOptions: {
        dateStrings: true,
        typeCast: true
      },
      define: {
        "createdAt": "created_at",
        "updatedAt": "updated_at"
      },
      timezone: '+08:00'
    }
  ]
}

这里有两点需要注意

  1. egg 中默认将数据库的描述文件放在app/model中,因此 mysql、postgres、mongoDB 会争夺 model 文件夹的权限而产生冲突和报错,所以,一定要配置baseDirdelegate 选项,为每个数据库配置一个文件夹来存放描述文件
  2. postgres 中存储的是created_atupdated_at,但是查询使用的是驼峰式的命名,因此,要利用define属性将二者对应起来

四、生成数据库模型

数据库模型其实就是针对每个 table 的描述文件,比如我有一个 user 表,它的模型可能是这样的

module.exports = app => {
  const DataTypes = app.Sequelize;

  const Model = app.modelsql.define('users', {
    id: {
      type: DataTypes.INTEGER,
      allowNull: false,
      primaryKey: true,
      autoIncrement: true
    },
    username: {
      type: DataTypes.STRING(18),
      allowNull: true
    },
    password: {
      type: DataTypes.STRING(255),
      allowNull: true
    },
  }, {
    tableName: 'users'
  });

  Model.associate = function() {

  }

  return Model;
};

问题是我有三个数据库,每个库都有许多张表,我们不能针对每个表去手写 model,会累死人的,也很蠢,所以,我利用一个插件来自动生成所有的 model

npm install -g egg-sequelize-auto
npm install -g mysql2

现在,使用这个命令来生成 user 表的 model

egg-sequelize-auto -o “./modelsql” -h host -d db -u username -x password -p port -t user

如果要生成 mysql 中所有表的模型

egg-sequelize-auto -o “./modelsql” -h host -d db -u username -x password -p port -e mysql

下面是一些参数说明

option description
-h –host 数据库的IP地址 [required]
-d –database 数据库名 [required]
-u –user 用户名
-x –pass 密码
-p –port 端口
-c –config 配置文件 [require json file]
-o –output 目标文件夹
-t, –tableNames 数据表表名
-e –dialect 数据库类型: postgres, mysql, sqlite …et

生成表模型以后,项目目录应该是这样的

egg-example
├── app
   ├── model       # mongoDB 
   ├── modelsql    # mysql
   ├── postgres    # postgres
      └── x.js
      └── y.js
      └── z.js
   ├── controller
      └── XXXX.js
   └── router.js
├── config
   └── config.default.js
└── package.json

五、更改模型添加表关联

这里以 postgres 为例。
先来理解三张表的关系:X 表是汽车品牌表,Y 表是品牌国别表,Z 表是品牌车型表。
如下图。

在这里插入图片描述

我就直接放代码了,需要注意的地方在注释里。

// x.js
const moment = require('moment'); // 时间插件

module.exports = app => {
  const DataTypes = app.Sequelize;

  const Model = app.postgres.define('x', {
    id: {
      type: DataTypes.INTEGER,
      allowNull: false,
      defaultValue: 'nextval(x_id_seq::regclass)',
      primaryKey: true
    },
    created_at: {
      type: DataTypes.TIME,
      get() { // postgres 默认存储 utc 格式,查询的时候想返回 local time
        return moment(this.getDataValue('created_at')).format('YYYY-MM-DD HH:mm:ss')
      },
      allowNull: false
    },
    updated_at: {
      type: DataTypes.TIME,
      allowNull: false
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false
    }
  }, {
    tableName: 'x'
  });

  Model.associate = function () {
	// 一对一关联,X 表的 id 唯一对应 Y 表的 cs_id
    app.postgres.X.hasOne(app.postgres.Y,{
      foreignKey: 'cs_id',
      sourceKey: 'id',  
      as: 'alias',  // 查询结果中,给 Y 表配置一个别名
    })
	// 一对多关联,Z 表的 cs_id 有多个与 X 表的 id 对应
    app.postgres.X.hasMany(app.postgres.Z, {
      foreignKey: 'cs_id',
      sourceKey: 'id'
    })
  }

  return Model;
};

在这里我遇到了一个问题,就是读不到 X、Y、Z 这几个模型,然后我去看官网,官网是这样的:
在这里插入图片描述

然后我往前翻,发现前面有个:
在这里插入图片描述

好吧,是我唐突了,告辞!

所以它的规则是这样的:
app.postgres.X => app/postgres/x.js
app.postgres.Example => app/postgres/example .js
app.postgres.ExampleOne => app/postgres/example_one .js

到了这里,表关联就建立好了。

六、实现一个接口

app/router,js下添加一个 get 请求: router.get('/api/all_car_list', controller.XXXX.list)
然后在app/controller/XXXX.js中编写这个请求的逻辑,将车辆的全部信息返回给前端。

const Controller = require('egg').Controller;
class UserController extends Controller {
	async list() {
		let {
      	   ctx
    	} = this
    	let res = await ctx.postgres.models.x.findAndCountAll({
      		distinct: true, // 去重,否则总数量会出错
      		include: [
        		{
          			model: ctx.postgres.models.y,
          			as: 'alias',
          			attributes: ['country']
        		},{
          			model: ctx.postgres.models.z,
          			attributes: ['model']
        		}
      		],
      		attributes: [
        		'id', 'name', 'created_at'
      		]
    	})
    	ctx.body = res
	}
}

代码全部写完了,测试一下吧

npm run server
curl -XGET '127.0.0.1:7001/api/all_car_list

猜你喜欢

转载自blog.csdn.net/zhai_865327/article/details/106794727
今日推荐