node下的orm类sequelize的使用

做前端的,在node出现后开始做类似后台类的操作,对于怎么操作数据库刚开始还是很慌的,找到一个工具orm,屏蔽底层的具体实现,只通过相应的对象类操作完成数据库操作,这边选定sequelize。当然是用免费的mysql数据库咯。

首先肯定是根据需求在数据库建好相应的数据库以及数据表,然后用sequelize-auto将数据表生成基本的model对象,用于生成model的文件generate_model.js可以这么写

const SeqAuto = require('sequelize-auto')
const path = require('path')
const tables = process.argv.slice(2)

if (tables.length === 0) {
  throw new Error('No table specified\nUsage: node generate_model table1 table2 table3\n')
}

const auto = new SeqAuto('database', 'uaername', 'password', {
  host: '127.0.0.1',
  dialect: 'mysql',
  directory: path.resolve(__dirname, './generated'),
  additional: {
    timestamps: false
  },
  tables: tables
})

auto.run(err => {
  if (err) {
    throw err
  }

  console.log('\n\nProcess completed successfully.')
  console.log('You may need to add these Foreign Keys mannually:\n', auto.foreignKeys)
})

运行node generate_model table_name,即可在generate文件夹下,生成对应的model文件,这里以package资产包表为例

/* jshint indent: 1 */

module.exports = function(sequelize, DataTypes) {
	return sequelize.define('package', {
		id: {
			type: DataTypes.BIGINT,
			allowNull: false,
			primaryKey: true,
			autoIncrement: true
		},
		product_id: {
			type: DataTypes.BIGINT,
			allowNull: true
		},
		product_state: {
			type: DataTypes.INTEGER(6),
			allowNull: false
		},
		name: {
			type: DataTypes.STRING(100),
			allowNull: false,
			unique: true
		},
		dept: {
			type: DataTypes.CHAR(10),
			allowNull: false
		},
		type: {
			type: DataTypes.INTEGER(6),
			allowNull: false
		},
		pack_date: {
			type: DataTypes.DATE,
			allowNull: false
		},
		portfolio: {
			type: DataTypes.TEXT,
			allowNull: true
		},
		maker: {
			type: DataTypes.STRING(50),
			allowNull: true
		},
		maker_time: {
			type: DataTypes.DATE,
			allowNull: true
		},
		checker: {
			type: DataTypes.STRING(50),
			allowNull: true
		},
		checker_time: {
			type: DataTypes.DATE,
			allowNull: true
		},
		state: {
			type: DataTypes.INTEGER(6),
			allowNull: false
		},
		comment: {
			type: DataTypes.TEXT,
			allowNull: true
		},
		version: {
			type: DataTypes.INTEGER(11),
			allowNull: false
		}
	}, {
		tableName: 'package',
		timestamps: false
	});
};

这些只是单纯的数据结构,以及一些字段要求和主键类关系,但是一张表可能和另一张表存在关系等,需要对该model进行一些丰富,改良之后的样子暂时称之为schema大概是这样

import { recordStates, assetTypes, productSates, getEnumString, phaseStateTypes, getDateString } from '../enum'
import packageProps from '../props/package'
import _ from 'lodash'
import moment from 'moment'

// getter setter and pseudo properties are manually amend.
// DO NOT overwrite all these context!

export default (sequelize, DataTypes) => {
  const PackageInfo = sequelize.define('packageInfo', {
    id: {
      type: DataTypes.BIGINT,
      allowNull: false,
      primaryKey: true,
      autoIncrement: true
    },
    product_id: {
      type: DataTypes.BIGINT,
      allowNull: true
    },
    product_state: {
      type: DataTypes.INTEGER(6),
      allowNull: false,
      defaultValue: 0
    },
    name: {
      type: DataTypes.STRING(100),
      allowNull: false
    },
    dept: {
      type: DataTypes.CHAR(10),
      allowNull: false
    },
    type: {
      type: DataTypes.INTEGER(6),
      allowNull: false
    },
    pack_date: {
      type: DataTypes.DATE,
      allowNull: false,
      set (val) {
        this.setDataValue('pack_date', moment(val))
      }
    },
    portfolio: {
      type: DataTypes.CHAR(100),
      allowNull: true
    },
    maker: {
      type: DataTypes.CHAR(50),
      allowNull: true
    },
    maker_time: {
      type: DataTypes.DATE,
      allowNull: true
    },
    checker: {
      type: DataTypes.CHAR(50),
      allowNull: true
    },
    checker_time: {
      type: DataTypes.DATE,
      allowNull: true
    },
    state: {
      type: DataTypes.INTEGER(6),
      allowNull: false,
      defaultValue: 0
    },
    comment: {
      type: DataTypes.TEXT,
      allowNull: true
    },
    version: {
      type: DataTypes.INTEGER(11),
      allowNull: false,
      defaultValue: 1
    }
  }, {
    getterMethods: {
      _type () {
        return getEnumString(this, 'type', assetTypes)
      },
      _state () {
        return getEnumString(this, 'state', recordStates)
      },
      _product_state () {
        return getEnumString(this, 'product_state', productSates)
      },
      _maker_time () {
        return getDateString(this, 'maker_time', 'YYYY-MM-DD HH:mm:ss')
      },
      _checker_time () {
        return getDateString(this, 'checker_time')
      },
      _pack_date () {
        return getDateString(this, 'pack_date')
      }
    },
    tableName: 'abs_package',
    timestamps: false,
    defaultScope: {
      where: {
        state: { $not: -1 }
      }
    },
    scopes: {
      deleted: {
        where: {
          state: -1
        }
      }
    }
  })

  // set the plural for model
  PackageInfo.plural = PackageInfo.name + 's'

  // tell models to export this as audit item
  PackageInfo.isAuditItem = true
  PackageInfo.auditItemIdx = 1

  PackageInfo.propertyKeys = packageProps

  PackageInfo.associate = models => {
    // associate with ProductInfo
    PackageInfo.hasOne(models.productInfo, { foreignKey: 'package_id' })

    // associate with Package Items
    PackageInfo.hasMany(models.mortgageItem, {
      foreignKey: 'package_id',
      constraints: false
    })
  }

  PackageInfo.hooks = models => {

    PackageInfo.afterCreate(async (instance, options) => {
      let record = instance.toJSON()
      record.update_by = record.maker
      record.update_time = record.maker_time
      await models.packageInfoAudit.create(record, { transaction: options.transaction })
    })

    PackageInfo.afterUpdate(async (instance, options) => {
      let record = Object.assign(instance.toJSON(), options.audit)
      let prev = instance.previous('version')
      if ( !(prev === instance.toJSON().version) ) {
        if (!_.isEmpty(options.changes)) {
          _.merge(record, options.changes)
        }
        await models.packageInfoAudit.create(record, { transaction: options.transaction })
      } else {
        const prevInfo = await models.packageInfoAudit.findOne({
          where:{
            id: record.id,
            version: record.version,
            state: {$gte: 0}
          }
        })
        if (!_.isNil(prevInfo)) {
          await prevInfo.update({
            product_id: record.product_id,
            product_state: 1  
          }, { transaction: options.transaction })
        }
        
      }
    })
  }

  return PackageInfo
}

可以增加一些,数据库中的数据字典值的输出,定义表之间的关系,比如,基于一个资产包只能创建一个产品,一个资产包下面可以有多个资产项,甚至,可以提前配置好钩子,在该表create或者update之后或者之前等进行相关的操作。定义scope作为某些查询的默认条件等。

在理清各表之间的关系,建立好每个表对应的schema之后,就是将这些东西抛出来用啦~

import fs from 'fs'
import path from 'path'
import Sequelize from 'sequelize'
import config from '../config/db.json'
import log4js from 'log4js'
import modelProps from './props'

const logger = log4js.getLogger('models')
const dbConfig = config[process.env.NODE_ENV]

if (dbConfig === undefined) {
  throw new Error('Database config is missing for ' + process.env.NODE_ENV)
}

const models = {
  AuditItems: {},
  PackageItems: {},
  PackageItemsAudit: {}
}
const schemas = fs.readdirSync(path.resolve(__dirname, './schemas'))

const sequelize = new Sequelize(dbConfig.uri, {
  benchmark: true,
  pool: config.pool,
  logging: (msg, cost) => logger.debug(`${msg} costs ${cost}ms.`)
})

// const sequelize = new Sequelize('abs_es', 'root', '', {
//   host: 'localhost',
//   dialect: 'mysql',

//   benchmark: true,
//   pool: config.pool,
//   logging: (msg, cost) => logger.debug(`${msg} costs ${cost}ms.`)
// })

schemas.forEach( schema => {
  let schemaName = schema.split('.')[0]
  let model = sequelize.import(`./schemas/${schemaName}`)
  models[model.name] = model

  logger.info('[AES] Model', model.name, 'is mounted')

  if (model.isAuditItem) {
    models.AuditItems[model.auditItemIdx] = model
  } else if (model.isPackageItem) {
    models.PackageItems[model.packageItemIdx] = model
  } else if (model.isPackageItemAudit) {
    models.PackageItemsAudit[model.packageItemAuditIdx] = model
  }
})

Object.keys(models).forEach( name => {
  let model = models[name]

  if ('associate' in model) {
    logger.info('[AES] Association of model', model.name, 'is ready')
    model.associate(models)
  }

  if ('hooks' in model) {
    logger.info('[AES] Hooks of model', model.name, 'is ready')
    model.hooks(models)
  }
})

export const getTransaction = async () => {
  return await sequelize.transaction()
}

export const MAX =  column => {
  return sequelize.fn('MAX', sequelize.col(column))
}

export default models

大概展示一下,每种操作的基本用法

let orderInfo = await Models.packageInfo.scope(null).findById(package_id)

let packages = await Models.packageInfoAudit.findAll({
              where: {
                  id: ctx.state.records.related.id,
                  state: 1,
                  product_id: relatedProduct.id
              },
              order: [ ['pack_date', 'DESC'] ]
            })
            packages = JSON.parse(JSON.stringify(packages))

const record = await Models.productInfo.create(productData, {
      transaction: txn,
      include: [
        { model: docModel, audit: ctx.state.audits, as: docModel.alias },
        { model: ceModel, audit: ctx.state.audits, as: ceModel.alias },
        { model: sdModel, audit: ctx.state.audits, as: sdModel.alias }
      ]
    })

const newRecord = await ctx.state.records.product.update({
      state: ctx.state.changes.state,
      version: ctx.state.records.product.version + 1,
      maker: ctx.state.audits.update_by,
      maker_time: ctx.state.audits.update_time
    }, _.merge({
      changes:ctx.state.changes.product
    }, txno))
    
const record = await Models.packageInfo.findOne({
    where: {
      id: ctx.state.records.related.id
    },
    include: [{
      model: Models.productInfo,
      as: Models.productInfo.name,
      required: false
    }],
    lock: txnLocks.UPDATE
  })
    
  try {
    const deletedRecord = await ctx.state.records.package.update({
      state: ctx.state.changes.state,
      version: ctx.state.records.package.version + 1,
      maker: ctx.state.audits.update_by,
      maker_time: ctx.state.audits.update_time
    }, txno)

    await txn.commit()
    // await txn.rollback()
    return ctx.success('资产包删除成功,等待审核')
  } catch (err) {
    await txn.rollback()
    if (err.errors && err.errors.length > 0) {
      logger.info("failed error", err)
      return ctx.error(errMap[err.errors[0].message] || err.errors[0])
    }
    throw err
  }

猜你喜欢

转载自blog.csdn.net/banxia561/article/details/81111853