首页
/ Drizzle ORM 中 $onUpdate 与 SQL 表达式结合使用的注意事项

Drizzle ORM 中 $onUpdate 与 SQL 表达式结合使用的注意事项

2025-05-06 14:07:17作者:魏侃纯Zoe

概述

在使用 Drizzle ORM 进行数据库操作时,开发者经常会遇到需要在记录更新时自动设置时间戳的需求。虽然 Drizzle 提供了 $onUpdate$onUpdateFn 这样的便捷方法,但在与 SQL 表达式结合使用时存在一些需要注意的技术细节。

问题现象

许多开发者尝试使用以下方式定义时间戳字段:

updatedAt: timestamp('updated_at')
  .notNull()
  .$onUpdateFn(() => sql`now()`)

但在实际执行更新操作时,会抛出 TypeError: value.toISOString is not a function 的错误。这是因为 Drizzle ORM 在运行时处理这些带有 $ 前缀的方法时,期望返回的是 JavaScript 的 Date 对象,而不是原始的 SQL 表达式。

技术原理

Drizzle ORM 中的 $onUpdate$onUpdateFn 是纯粹的运行时功能,它们不会影响数据库的实际 schema。这些方法:

  1. 只在 JavaScript 运行时环境中生效
  2. 不能直接嵌入 SQL 表达式
  3. 期望返回的是 JavaScript 可处理的值(如 Date 对象)

解决方案

方案一:使用字符串模式

updatedAt: timestamp('updated_at', {
  withTimezone: true,
  mode: 'string'  // 关键设置
})
.defaultNow()
.notNull()
.$onUpdate(() => sql`now()`)

这种方法通过将字段模式设置为 string,绕过了 Date 对象的类型检查,但返回的是字符串而非 Date 对象。

方案二:自定义时间处理函数

function getCurrentTimeInTimeZoneISO(timeZone: string) {
  const now = Date.now();
  const zonedDate = toZonedTime(now, timeZone);
  zonedDate.toISOString = () => format(zonedDate, "yyyy-MM-dd'T'HH:mm:ss'Z'", { timeZone })
  return zonedDate
}

// 使用
updatedAt: timestamp("updated_at")
.$onUpdate(() => getCurrentTimeInTimeZoneISO('America/Sao_Paulo'))

这种方法通过自定义 Date 对象的 toISOString 方法,提供了更灵活的时间处理能力。

最佳实践

  1. 对于简单场景,直接返回 new Date()
  2. 需要数据库服务器时间时,考虑在应用层处理
  3. 需要时区支持时,使用专门的时区处理库
  4. 文档中的示例需要谨慎对待,部分示例可能不适用于所有场景

总结

理解 Drizzle ORM 中运行时方法与数据库 schema 的区别至关重要。虽然 $onUpdate 系列方法提供了便利,但它们与直接 SQL 表达式的结合存在限制。开发者应根据实际需求选择合适的实现方式,平衡便利性与功能需求。

登录后查看全文
热门项目推荐
相关项目推荐