在TypeScript+Sequelize中通过关联RefreshToken查询Account的正确方法
TypeScript + Sequelize 一对多关联实现方案
需求说明
需要为Account与RefreshToken模型建立一对多关联,实现两个核心查询:
- 查询指定Account关联的所有RefreshToken
- 通过RefreshToken(实例或token字符串)查询对应的Account
现有代码问题
initModels.ts
const Account = _Account.initModel(sequelize); const RefreshToken = _RefreshToken.initModel(sequelize); Account.hasMany(RefreshToken); RefreshToken.belongsTo(Account);
目标查询函数(当前写法错误)
export const getAccountByRefreshToken = (token: RefreshToken) => { // 此处写法错误,目标是通过RefreshToken获取对应的Account return Account.findOne({ where: { refreshToken: token } }); };
RefreshToken.ts(精简版)
import { DataTypes, Sequelize, Model, Optional } from "sequelize"; import sequelizeConnection from "../Database"; interface RefreshTokenAttributes { accountId: number; // 待关联Account模型 token: string; } export type RefreshTokenOptionalAttributes = "createdAt" | "updatedAt" | "deletedAt"; export type RefreshTokenCreationAttributes = Optional<RefreshTokenAttributes, RefreshTokenOptionalAttributes>; export class RefreshToken extends Model<RefreshTokenAttributes, RefreshTokenCreationAttributes> implements RefreshTokenAttributes { public accountId!: number; public token!: string; static initModel(sequelize: Sequelize): typeof RefreshToken { return RefreshToken.init( { accountId: { type: DataTypes.INTEGER, // 待关联用户模型 autoIncrement: true, primaryKey: true, }, token: { type: DataTypes.STRING, allowNull: false, }, }, ); } }
Account.ts(精简版)
import { DataTypes, Sequelize, Model, Optional } from "sequelize"; import sequelizeConnection from "../Database"; interface AccountAttributes { id: number; email: string; } export type AccountOptionalAttributes = "createdAt" | "updatedAt" | "deletedAt"; export type AccountCreationAttributes = Optional<AccountAttributes, AccountOptionalAttributes>; export class Account extends Model<AccountAttributes, AccountCreationAttributes> implements AccountAttributes { public id!: number; public email!: string; static initModel(sequelize: Sequelize): typeof Account { return Account.init( { id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, }, email: { type: DataTypes.STRING, allowNull: false, }, }, ); } }
修正步骤
1. 修复模型字段定义
RefreshToken.ts 修正
- 移除accountId的
autoIncrement和primaryKey配置,添加独立主键id - 为accountId添加外键关联配置,指定关联Account的id字段
- 确保init方法传入
sequelize参数 - 添加关联属性声明,让TypeScript识别关联关系
import { DataTypes, Sequelize, Model, Optional } from "sequelize"; import sequelizeConnection from "../Database"; import type { Account } from "./Account"; interface RefreshTokenAttributes { id: number; // 添加独立主键 accountId: number; token: string; } export type RefreshTokenOptionalAttributes = "createdAt" | "updatedAt" | "deletedAt"; export type RefreshTokenCreationAttributes = Optional<RefreshTokenAttributes, RefreshTokenOptionalAttributes | "id">; export class RefreshToken extends Model<RefreshTokenAttributes, RefreshTokenCreationAttributes> implements RefreshTokenAttributes { public id!: number; public accountId!: number; public token!: string; // 声明关联属性,TypeScript类型支持 public readonly account?: Account; static initModel(sequelize: Sequelize): typeof RefreshToken { return RefreshToken.init( { id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, }, accountId: { type: DataTypes.INTEGER, allowNull: false, references: { model: "accounts", // 对应Account模型的表名(默认复数) key: "id", }, onDelete: "CASCADE", // 可选:删除Account时同步删除关联的RefreshToken }, token: { type: DataTypes.STRING, allowNull: false, unique: true, // 建议token设为唯一 }, }, { sequelize, tableName: "refreshTokens", // 可选:显式指定表名 } ); } }
Account.ts 补充关联声明
添加关联属性声明,让TypeScript识别关联的RefreshToken集合:
import { DataTypes, Sequelize, Model, Optional } from "sequelize"; import sequelizeConnection from "../Database"; import type { RefreshToken } from "./RefreshToken"; interface AccountAttributes { id: number; email: string; } export type AccountOptionalAttributes = "createdAt" | "updatedAt" | "deletedAt"; export type AccountCreationAttributes = Optional<AccountAttributes, AccountOptionalAttributes>; export class Account extends Model<AccountAttributes, AccountCreationAttributes> implements AccountAttributes { public id!: number; public email!: string; // 声明关联属性,TypeScript类型支持 public readonly refreshTokens?: RefreshToken[]; static initModel(sequelize: Sequelize): typeof Account { return Account.init( { id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, }, email: { type: DataTypes.STRING, allowNull: false, unique: true, }, }, { sequelize, tableName: "accounts", } ); } }
2. 完善关联配置(initModels.ts)
显式指定关联的外键和别名,避免Sequelize自动推断出错:
import { Sequelize } from "sequelize"; import { Account as _Account } from "./Account"; import { RefreshToken as _RefreshToken } from "./RefreshToken"; export const initModels = (sequelize: Sequelize) => { const Account = _Account.initModel(sequelize); const RefreshToken = _RefreshToken.initModel(sequelize); // 一对多关联:一个Account对应多个RefreshToken Account.hasMany(RefreshToken, { foreignKey: "accountId", // 指定外键字段 as: "refreshTokens", // 关联别名,对应Account模型里的refreshTokens属性 }); // 多对一关联:一个RefreshToken属于一个Account RefreshToken.belongsTo(Account, { foreignKey: "accountId", as: "account", // 关联别名,对应RefreshToken模型里的account属性 }); return { Account, RefreshToken }; };
3. 修正查询函数
方式1:通过RefreshToken实例查询Account
import { RefreshToken } from "./RefreshToken"; export const getAccountByRefreshToken = async (tokenInstance: RefreshToken) => { // 使用Sequelize自动生成的关联方法获取Account return tokenInstance.getAccount(); };
方式2:通过token字符串查询对应的Account
import { RefreshToken } from "./RefreshToken"; export const getAccountByRefreshTokenString = async (token: string) => { // 先查询RefreshToken,再关联获取Account const refreshToken = await RefreshToken.findOne({ where: { token }, include: ["account"], // 关联查询Account }); return refreshToken?.account; };
查询Account关联的所有RefreshToken
import { Account } from "./Account"; export const getRefreshTokensByAccount = async (accountId: number) => { const account = await Account.findByPk(accountId, { include: ["refreshTokens"], // 关联查询所有RefreshToken }); return account?.refreshTokens; };
关键说明
Sequelize识别关联的逻辑:
- 通过
hasMany和belongsTo方法建立关联关系,指定外键后,Sequelize会自动处理表之间的关联查询逻辑 - 模型初始化时必须传入
sequelize实例,否则无法将模型与数据库连接绑定 - 外键字段配置
references可在数据库层面建立外键约束,保证数据一致性(可选但推荐)
- 通过
TypeScript类型支持:
- 在模型类中声明关联属性(如
public readonly account?: Account),让TypeScript能识别关联方法和属性 - 使用
as指定关联别名,保持模型属性与关联配置一致,避免类型报错
- 在模型类中声明关联属性(如
内容的提问来源于stack exchange,提问作者plutownium
相关产品推荐
相关产品推荐

