TypedSQL
TypedSQL 入门
要在 Prisma 项目中开始使用 TypedSQL,请遵循以下步骤:
-
确保已安装并更新
@prisma/client和prisma到至少5.19.0版本。npm install @prisma/client@latest
npm install -D prisma@latest -
将
typedSql预览功能标志添加到您的schema.prisma文件中generator client {
provider = "prisma-client" // or "prisma-client-js"
previewFeatures = ["typedSql"]
output = "../src/generated/prisma"
}将驱动程序适配器与 TypedSQL 一起使用如果您在无服务器或边缘环境中部署 Prisma,可以使用驱动程序适配器通过 JavaScript 数据库驱动程序进行连接。驱动程序适配器与 TypedSQL 兼容,但
@prisma/adapter-better-sqlite3除外。对于 SQLite 支持,请改用@prisma/adapter-libsql。所有其他驱动程序适配器都受支持。 -
在
prisma目录中创建一个sql目录。这是您将编写 SQL 查询的地方。mkdir -p prisma/sql自定义 SQL 文件夹位置从 Prisma 6.12.0 开始,您可以使用 Prisma 配置文件为 SQL 文件配置自定义位置。在项目根目录中创建
prisma.config.ts文件并指定typedSql.path选项prisma.config.tsimport 'dotenv/config'
import { defineConfig } from 'prisma/config'
export default defineConfig({
schema: './prisma/schema.prisma',
typedSql: {
path: './prisma/sql',
},
}) -
在
prisma/sql目录中创建一个新的.sql文件。例如,getUsersWithPosts.sql。请注意,文件名必须是有效的 JS 标识符,并且不能以$开头。 -
在新
.sql文件中编写 SQL 查询。例如prisma/sql/getUsersWithPosts.sqlSELECT u.id, u.name, COUNT(p.id) as "postCount"
FROM "User" u
LEFT JOIN "Post" p ON u.id = p."authorId"
GROUP BY u.id, u.name -
使用
sql标志生成 Prisma Client,以确保为您的 SQL 查询创建 TypeScript 函数和类型警告请确保在生成带有
sql标志的客户端之前应用任何待处理的迁移。prisma generate --sql如果您不想在每次更改后重新生成客户端,此命令也适用于现有的
--watch标志prisma generate --sql --watch -
现在您可以在 TypeScript 代码中导入和使用您的 SQL 查询了
import { PrismaClient } from './generated/prisma/client'
import { getUsersWithPosts } from './generated/prisma/sql'
const prisma = new PrismaClient()
const usersWithPostCounts = await prisma.$queryRawTyped(getUsersWithPosts())
console.log(usersWithPostCounts)
如果您不自定义生成器 output,则可以从 @prisma/client 和 @prisma/client/sql 导入。
将参数传递给 TypedSQL 查询
要将参数传递给 TypedSQL 查询,可以使用参数化查询。这允许您编写灵活且可重用的 SQL 语句,同时保持类型安全。以下是操作方法:
-
在 SQL 文件中,为您要传递的参数使用占位符。占位符的语法取决于您的数据库引擎
- PostgreSQL
- MySQL
- SQLite
对于 PostgreSQL,使用位置占位符
$1、$2等。prisma/sql/getUsersByAge.sqlSELECT id, name, age
FROM users
WHERE age > $1 AND age < $2对于 MySQL,使用位置占位符
?prisma/sql/getUsersByAge.sqlSELECT id, name, age
FROM users
WHERE age > ? AND age < ?在 SQLite 中,有许多不同的占位符可以使用。位置占位符(
$1、$2等)、通用占位符(?)和命名占位符(:minAge、:maxAge等)都可用。对于此示例,我们将使用命名占位符:minAge和:maxAgeprisma/sql/getUsersByAge.sqlSELECT id, name, age
FROM users
WHERE age > :minAge AND age < :maxAge注意有关如何在 SQL 文件中定义参数类型的信息,请参阅下文。
-
在 TypeScript 代码中使用生成的函数时,将参数作为附加参数传递给
$queryRawTyped
import { PrismaClient } from './generated/prisma/client'
import { getUsersByAge } from './generated/prisma/sql'
const prisma = new PrismaClient()
const minAge = 18
const maxAge = 30
const users = await prisma.$queryRawTyped(getUsersByAge(minAge, maxAge))
console.log(users)
通过使用参数化查询,您可以确保类型安全并防止 SQL 注入漏洞。TypedSQL 生成器将根据您的 SQL 查询为参数创建相应的 TypeScript 类型,为查询结果和输入参数提供全面的类型检查。
将数组参数传递给 TypedSQL
TypedSQL 支持将数组作为参数传递给 PostgreSQL。将 PostgreSQL 的 ANY 运算符与数组参数一起使用。
SELECT id, name, email
FROM users
WHERE id = ANY($1)
import { PrismaClient } from './generated/prisma/client'
import { getUsersByIds } from './generated/prisma/sql'
const prisma = new PrismaClient()
const userIds = [1, 2, 3]
const users = await prisma.$queryRawTyped(getUsersByIds(userIds))
console.log(users)
TypedSQL 将为数组参数生成相应的 TypeScript 类型,确保输入和查询结果的类型安全。
传递数组参数时,请注意数据库在单个查询中支持的最大占位符数量。对于非常大的数组,您可能需要将查询拆分为多个较小的查询。
在 SQL 文件中定义参数类型
TypedSQL 中的参数类型通过 SQL 文件中的特定注释来实现。这些注释的形式为
-- @param {Type} $N:alias optional description
其中 Type 是有效的数据库类型,N 是参数在查询中的位置,alias 是参数的可选别名,用于 TypeScript 类型中。
例如,如果您需要使用别名 name 和描述“用户的名称”来键入单个字符串参数,您将向 SQL 文件添加以下注释
-- @param {String} $1:name The name of the user
要指示参数可为空,请在别名后添加问号
-- @param {String} $1:name? The name of the user (optional)
当前接受的类型为 Int、BigInt、Float、Boolean、String、DateTime、Json、Bytes、null 和 Decimal。
以上述示例为例,SQL 文件将如下所示
-- @param {Int} $1:minAge
-- @param {Int} $2:maxAge
SELECT id, name, age
FROM users
WHERE age > $1 AND age < $2
无论数据库引擎如何,参数类型定义的格式都是相同的。
不支持对数组参数进行手动参数类型定义。对于这些参数,您需要依赖 TypedSQL 提供的类型推断。
示例
有关如何使用 TypedSQL 的实际示例,请参阅 Prisma 示例仓库中的 TypedSQL 示例。
TypedSQL 的限制
支持的数据库
TypedSQL 支持现代版本的 MySQL 和 PostgreSQL,无需任何额外配置。对于早于 8.0 的 MySQL 版本和所有 SQLite 版本,您需要手动在 SQL 文件中描述参数类型。在所有支持的 PostgreSQL 版本和 MySQL 8.0 及更高版本中,都会推断输入类型。
TypedSQL 不适用于 MongoDB,因为它专门为 SQL 数据库设计。
需要活动的数据库连接
TypedSQL 需要活动的数据库连接才能正常运行。这意味着在生成带有 --sql 标志的客户端时,您需要有一个正在运行的数据库实例供 Prisma 连接。TypedSQL 使用 prisma.config.ts(datasource.url)中定义的连接字符串来建立此连接。
带有动态列的动态 SQL 查询
TypedSQL 不原生支持构造带有动态添加列的 SQL 查询。当您需要创建列在运行时确定的查询时,必须使用 $queryRaw 和 $executeRaw 方法。这些方法允许执行原始 SQL,其中可以包括动态列选择。
使用动态列选择的查询示例
const columns = 'name, email, age'; // Columns determined at runtime
const result = await prisma.$queryRawUnsafe(
`SELECT ${columns} FROM Users WHERE active = true`
);
在此示例中,要选择的列是动态定义的并包含在 SQL 查询中。虽然这种方法提供了灵活性,但它需要仔细注意安全性,特别是要避免 SQL 注入漏洞。此外,使用原始 SQL 查询意味着放弃 TypedSQL 的类型安全和开发体验。