当前位置: 首页 > 文档资料 > Sequelize ORM 实践 >

3.1 属于

优质
小牛编辑
137浏览
2023-12-01

声明属于关系

声明是属于关系的方法是 belongsTo

Book.belongsTo(User);

Book 是主体发起模型,而 User 是目标模型。这样声明会在 Book 上面增加 setUser / createUser / User 俩个方法与一个属性,通过 options 里面的 as: 'Some',来让名称变成 setSome / createUser / Some。且在 Book 表里面会自动增加一个 UserId 的字段来记录 User 的 id 值,通过设置 options 里面的 foreignKey: 'user_id',来修改自动增加字段的名称。

倘若你十分讨厌驼峰原则,喜欢下划线分割的配置,并且期望所有表都这样,可以在初始化数据库连接的时候配置 define.underscored,它会默成为你所有 define 的 默认 options。

const sequelize = new Sequelize('nodelover', 'root', '', {
  host: 'localhost',
  dialect: 'mysql',
  define: {
      underscored: true,
  }    ,
  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },
});

小栗子

接下来,同样通过一个例子来说明属于关系。就着之前的 User 与 Book 模型定义做一些修改。

接口是支持继承的,没必要把 UserAttributes 里面的属性再写一次,直接继承一下就好了。并且为加强一下记忆,再次阐述一下属性接口与实例接口的区别attr 里面声明的是 create 创建的时候需要传递的属性,而 instance 接口里面的都是 sequelize 动态给类添加的方法与属性。

1. 新建 user.ts

import Sequelize from 'sequelize';

export interface UserAttributes {
  email: string;
  name: string;
}

export interface UserInstance extends Sequelize.Instance<UserAttributes>, UserAttributes {
  id: number;
  createdAt: Date;
  updatedAt: Date;
  say(): void;

}

export default function UserDefine(sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes): Sequelize.Model<UserInstance, UserAttributes>  {
    const S = dataTypes;
    const User = sequelize.define<UserInstance, UserAttributes>
    ('User', {
        email: S.STRING,
        name: S.STRING
    });

    (User as any).prototype.say = function(this: UserInstance) {
        console.log('name ' + this.name);
    };

    (User as any).associate = function(models){

    }
    return User;
}

在模型里面,可以在静态 associate 方法来定义模型之间的关系,这是官方所推荐的,假如使用官方提供的 migrate 工具会自动执行该方法。

prototype 上面加了一个 say 实例方法,所以在 instace 接口里面声明一下 say 方法。

2.book.ts

import Sequelize from 'sequelize';
import {UserInstance, UserAttributes} from './User'

export interface BookAttributes {
  status: "inSale" | "noSale";
  description: string;
  title: string;
  author: string;
}


export interface BookInstance extends Sequelize.Instance<BookAttributes>, BookAttributes{
  id: number;
  createdAt: Date;
  updatedAt: Date;

  setUser: Sequelize.BelongsToSetAssociationMixin<UserInstance, number>;
  createUser: Sequelize.BelongsToCreateAssociationMixin<UserAttributes>;
  User: Sequelize.BelongsToGetAssociationMixin<UserInstance>
}

export default function BookDefine(sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes): Sequelize.Model<BookInstance, BookAttributes> {
  const S = dataTypes;
  const Book =  sequelize.define<BookInstance, BookAttributes>('Book', {
    id: {
        type: S.INTEGER,
        autoIncrement: true,
        primaryKey: true,
        unique: true
    },
    description: S.TEXT,
    status: {
        type: S.ENUM,
        values: ['inSale', 'noSale'],
        validate: {
            isIn: {
                args: [['inSale', 'noSale']],
                msg: "status field must be inSale or noSale"
            }
        }
    },
    title:{
        type: S.STRING,
        allowNull: false,
        get(this: BookInstance) {
            return this.getDataValue('author') + ' - ' + this.getDataValue('title');
        }
    },
    author: {
        type: S.STRING,
        allowNull: false,
        set(val: string){
            this.setDataValue('author', val.toLowerCase());
        }
    }},{
        comment: "图书表", // 表注释
        indexes: [  // 表索引
            {
                fields: ['id']
            }
        ],
        classMethods: {
            // associate: function(models){} 第一种
        }
    });
    // 第二种
    (Book as any).associate = function(this: typeof Book, models: any){
        this.belongsTo(models.User);
    }

    return Book;
}

特别注意 第一种写法在 v4 版本将会被移除。具体可以查看官方的如何升级到 v4版本?文档。

在 define 方法的 options 参数里是有 classMethods 配置项的,可以指定模型的静态方法,所以可以有俩种写法。这里用的是直接写,也就是第二种写法。在 associate 静态方法内, this 指的就是 Book,而传递的 models 里面有所有 define 过的模型,这里定义的关系就是, Book 模型属于 User,所以会在 Book 实例上面添加 setUser / createUser / User

sequelize 动态添加了这些方法,但是 typescript 是不知道的,所以需要自己去声明,所以就有了。

  setUser: Sequelize.BelongsToSetAssociationMixin<UserInstance, number>;
  createUser: Sequelize.BelongsToCreateAssociationMixin<UserAttributes>;
  User: Sequelize.BelongsToGetAssociationMixin<UserInstance>

这些 Mixin 是由定义库提供的一些接口。

3.index.ts

import Sequelize from 'sequelize';

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

  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },
});

import UserDefined , { UserAttributes, UserInstance  }  from './user';
import BookDefined, {BookAttributes, BookInstance} from './book';

async function main() {
  try {
    const User = sequelize.import<UserInstance, UserAttributes>('./user');
    const Book = sequelize.import<BookInstance, BookAttributes>('./book');

    Object.keys(sequelize.models).forEach(modelName => {
      const model = (sequelize.models[modelName] as any)
      if('associate' in model){
        model.associate(sequelize.models)
      }
    })

    await sequelize.sync();

    let book = await Book.create({author:'alice', description: 'typescript hand book', status: 'inSale',title:'ts leaning'});
    let user = await User.create({email:'belovedyogurt@gmail.com',name: 'yugo'})
    book.setUser(user);
    await book.save();
  }catch(e){
    // console.log(e)
    if (e instanceof Sequelize.ValidationError) {
      console.log(e.message);
    }
  }
  process.exit(0)

sequelize.models 对象上面缓存了所有定义过的模型,通过以下几行代码,实现了自动调用 associate 方法,与同步数据库表。

Object.keys(sequelize.models).forEach(modelName => {
 const model = (sequelize.models[modelName] as any)
 if('associate' in model){
   model.associate(sequelize.models)
 }
})

await sequelize.sync();

运行以上代码,在数据库里面可以看到2张表都已经有了数据。

·>_<·! Nice Work!