手记

Drizzle ORM项目实战:新手入门教程

概述

Drizzle ORM 是一个轻量级且易于使用的ORM库,特别适合Node.js环境下的数据库应用开发。本文将详细介绍如何安装和配置Drizzle ORM,并通过实战项目演示其数据模型设计、基本CRUD操作以及查询与筛选功能。此外,还将探讨数据库迁移与版本控制的实现方法。通过Drizzle ORM项目实战,你将快速掌握这一强大工具。

Drizzle ORM简介与安装

什么是Drizzle ORM

Drizzle ORM 是一个轻量级、易于使用的 ORM(对象关系映射)库,主要用于 Node.js 环境。它主要依赖于 Postgres 数据库,但也可以与 SQLite 和 MySQL 等其他数据库兼容。Drizzle ORM 的设计理念是灵活性和简洁性,适合开发快速、高效的数据库应用。

Drizzle ORM 具有以下特点:

  • 类型安全: Drizzle ORM 与 TypeScript 集成良好,可以提供类型安全的编程体验。
  • 关系映射: 支持多种关系类型(一对一、一对多、多对多),可以帮助开发者更方便地管理数据之间的关系。
  • 易于使用: 通过简洁的 API,开发者可以快速完成基本的 CRUD 操作。
  • 查询构建器: 提供强大的查询构建器,支持复杂的查询语句,包括联接、子查询等。
  • 迁移与版本控制: 提供了一套完善的数据库迁移工具,支持版本控制和回退。

安装Drizzle ORM

要开始使用 Drizzle ORM,首先需要安装 Node.js 和 npm。安装完成后,可以使用 npm 安装 Drizzle ORM 及其依赖库:

npm install drizzle-orm @types/node pg

上述命令将安装 drizzle-orm 库、类型定义支持库 @types/node 以及 PostgreSQL 的客户端库 pg

环境配置

为了能正常运行 Drizzle ORM,你需要配置数据库连接。这里以 PostgreSQL 为例,说明如何配置数据库连接:

  1. 安装 PostgreSQL: 如果你还没有安装 PostgreSQL,可以从官方网站下载并安装。
  2. 创建数据库: 使用 PostgreSQL 命令行工具创建一个新的数据库。
createdb mydatabase
  1. 配置数据库连接: 在你的 Node.js 项目中,创建一个配置文件(例如 db.config.ts)来设置数据库连接信息:
import { PostgresJsDatabase, createConnection } from 'drizzle-orm/postgres-js';
import { users, posts, comments } from './models';

const database = new PostgresJsDatabase({
  database: 'mydatabase',
  schema: './src/schema',
});

export const db = createConnection({
  schema: database,
  connectionString: 'postgresql://user:password@localhost/mydatabase',
});

上述代码中,database 参数指定了数据库名称,connectionString 是数据库的连接字符串,格式为 postgresql://[user[:password]@][netloc][:port][/dbname]。请根据实际情况替换其中的 userpassworddbname 部分。

数据模型设计

数据库表设计

在设计数据库表之前,需要明确你的应用会处理哪些数据,这些数据之间有何联系。以下是一个简单的示例,我们假设有一个博客应用,需要管理用户和文章的数据:

  • 用户表: 包含用户的基本信息,如用户名、邮箱、密码等。
  • 文章表: 包含文章的标题、内容、发布日期等信息。
  • 评论表: 包含每篇文章下的评论信息。

定义数据模型

定义数据模型是 ORM 的核心部分。使用 Drizzle ORM,可以通过定义表的结构来创建数据模型。以下是如何定义上述博客应用中的数据模型:

import { pgTable, varchar, text, integer, timestamp, boolean, index, uniqueIndex, primaryKey } from 'drizzle-orm/pg-core';

// 定义用户表
export const users = pgTable('users', {
  id: integer('id').primaryKey().autoIncrement(),
  username: varchar('username', { length: 50 }).notNull().unique(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  password: varchar('password', { length: 255 }).notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().onUpdateNow().notNull(),
});

// 定义文章表
export const posts = pgTable('posts', {
  id: integer('id').primaryKey().autoIncrement(),
  title: varchar('title', { length: 100 }).notNull(),
  content: text('content').notNull(),
  userId: integer('user_id').notNull(),
  published: boolean('published').defaultTrue(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().onUpdateNow().notNull(),
});

// 定义评论表
export const comments = pgTable('comments', {
  id: integer('id').primaryKey().autoIncrement(),
  content: text('content').notNull(),
  postId: integer('post_id').notNull(),
  userId: integer('user_id').notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().onUpdateNow().notNull(),
});

关系建立与管理

上述定义的数据模型中的表之间存在关系,如用户与文章、文章与评论等。Drizzle ORM 通过外键和索引来管理这些关系。

定义外键

外键是用于表示两个表之间关系的字段。例如,在 posts 表中 userId 字段表示作者的 ID,而在 comments 表中 postIduserId 分别表示文章的 ID 和评论用户的 ID。在外键的定义中,可以指定外键需要引用的主键。

// 在 posts 表中定义 userId 为外键,引用 users 表的 id 主键
posts.userId.integer('user_id').references(() => users.id);

// 在 comments 表中定义 postId 和 userId 为外键
comments.postId.integer('post_id').references(() => posts.id);
comments.userId.integer('user_id').references(() => users.id);

创建索引

为了提高查询性能,通常会在某些字段上创建索引。例如,在 users 表中创建一个索引来加速根据用户名检索用户:

users.username.index('username_index');
users.email.index('email_index');
基本CRUD操作

创建数据

使用 Drizzle ORM,可以通过 insert 方法创建新的数据项。例如,插入一条用户记录:

import { db, users } from './db.config.ts';

// 创建一个新的用户
const newUser = await db.insert(users).values({
  username: 'john_doe',
  email: 'john@example.com',
  password: 'securepassword',
});

读取数据

读取数据通常使用 select 方法。例如,读取所有用户信息:

import { db, users } from './db.config.ts';

// 查询所有用户
const allUsers = await db.select().from(users);

条件查询

也可以根据条件查询数据。例如,查询某个特定用户的详细信息:

import { db, users } from './db.config.ts';

// 查询用户名为 'john_doe' 的用户
const user = await db.select().from(users).where(users.username.equals('john_doe'));

更新数据

更新数据使用 update 方法。例如,更新用户密码:

import { db, users } from './db.config.ts';

// 更新用户名为 'john_doe' 的用户密码
const updatedUser = await db.update(users)
  .set({ password: 'newsecurepassword' })
  .where(users.username.equals('john_doe'));

删除数据

删除数据使用 delete 方法。例如,删除用户名为 'john_doe' 的用户:

import { db, users } from './db.config.ts';

// 删除用户名为 'john_doe' 的用户
const deletedUser = await db.delete(users)
  .where(users.username.equals('john_doe'));
查询与筛选

基本查询操作

除了前面提到的 select 方法,Drizzle ORM 提供了丰富的查询构建器来构建复杂的查询语句。例如,获取所有已发布的文章:

import { db, posts } from './db.config.ts';

// 查询所有已发布的文章
const publishedPosts = await db.select().from(posts).where(posts.published.equals(true));

过滤与排序

过滤和排序查询结果是常见的需求。例如,获取最新的五篇文章,并按照创建时间降序排序:

import { db, posts } from './db.config.ts';

// 获取最新的五篇文章,按创建时间降序排序
const latestPosts = await db.select().from(posts)
  .orderBy(posts.createdAt, 'desc')
  .limit(5);

关联查询

关联查询是指查询多个表之间的数据。例如,查询所有用户及其创建的文章:

import { db, users, posts } from './db.config.ts';

// 查询所有用户及其创建的文章
const usersWithPosts = await db.select({
  username: users.username,
  posts: db.select().from(posts).where(posts.userId.equals(users.id)),
}).from(users);
迁移与数据库版本控制

理解数据库迁移

数据库迁移是一种管理数据库结构变化的方式。迁移文件包含了数据库结构的变更描述,可以自动执行这些变更,从而保持数据库结构与应用代码的一致性。Drizzle ORM 提供了一套完整的迁移工具来管理这些变更。

创建与执行迁移文件

创建迁移文件可以通过 drizzle-migrate 命令行工具完成。首先,安装 drizzle-migrate

npm install drizzle-migrate

创建迁移文件:

npx drizzle-migrate init migrations

上述命令会在 migrations 目录下生成一个初始的迁移文件。你可以根据需要添加更多的迁移文件,例如:

npx drizzle-migrate create create_users_table

这将创建一个新的迁移文件,内容如下:

import { migrate, createTable, varchar, integer } from 'drizzle-orm/pg-core';
import { users } from '../models/users';

export default migrate([
  createTable(users, (table) => ({
    id: integer('id').primaryKey().autoIncrement(),
    username: varchar('username', { length: 50 }).notNull().unique(),
    email: varchar('email', { length: 255 }).notNull().unique(),
    password: varchar('password', { length: 255 }).notNull(),
    createdAt: integer('created_at').defaultNow().notNull(),
    updatedAt: integer('updated_at').defaultNow().onUpdateNow().notNull(),
  })),
]);

执行迁移:

npx drizzle-migrate up

版本控制与回退

数据库迁移支持版本控制和回退操作。例如,如果你需要回退到之前的某个版本,可以使用以下命令:

npx drizzle-migrate down

这将回退到上一个版本。若要回到更早的版本,可以多次执行 down 命令。

实战项目案例

项目需求与规划

假设你现在正在开发一个博客应用,需要管理用户、文章和评论的数据。具体需求如下:

  1. 用户管理:支持用户注册、登录、修改个人信息。
  2. 文章管理:用户可以发布、查看、编辑和删除自己的文章。
  3. 评论管理:用户可以对文章发表评论,其他用户可以回复评论。

数据模型构建

根据上述需求,我们需要定义以下数据模型:

  • 用户表
  • 文章表
  • 评论表

以下是数据模型的定义:

import { pgTable, varchar, text, integer, timestamp, boolean, index, uniqueIndex, primaryKey } from 'drizzle-orm/pg-core';

// 定义用户表
export const users = pgTable('users', {
  id: integer('id').primaryKey().autoIncrement(),
  username: varchar('username', { length: 50 }).notNull().unique(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  password: varchar('password', { length: 255 }).notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().onUpdateNow().notNull(),
});

// 定义文章表
export const posts = pgTable('posts', {
  id: integer('id').primaryKey().autoIncrement(),
  title: varchar('title', { length: 100 }).notNull(),
  content: text('content').notNull(),
  userId: integer('user_id').notNull(),
  published: boolean('published').defaultTrue(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().onUpdateNow().notNull(),
});

// 定义评论表
export const comments = pgTable('comments', {
  id: integer('id').primaryKey().autoIncrement(),
  content: text('content').notNull(),
  postId: integer('post_id').notNull(),
  userId: integer('user_id').notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().onUpdateNow().notNull(),
});

// 定义外键
posts.userId.integer('user_id').references(() => users.id);
comments.postId.integer('post_id').references(() => posts.id);
comments.userId.integer('user_id').references(() => users.id);

功能实现与测试

在实现功能之前,先创建一些测试数据用于验证:

import { db, users, posts, comments } from './db.config.ts';

// 创建测试用户
const newUser = await db.insert(users).values({
  username: 'john_doe',
  email: 'john@example.com',
  password: 'securepassword',
});

// 创建测试文章
const newPost = await db.insert(posts).values({
  title: 'Hello World',
  content: 'This is my first blog post.',
  userId: newUser.id,
});

// 创建测试评论
const newComment = await db.insert(comments).values({
  content: 'Great post!',
  postId: newPost.id,
  userId: newUser.id,
});

// 验证数据是否创建成功
const createdUser = await db.select().from(users).where(users.id.equals(newUser.id));
const createdPost = await db.select().from(posts).where(posts.id.equals(newPost.id));
const createdComment = await db.select().from(comments).where(comments.id.equals(newComment.id));

console.log(createdUser);
console.log(createdPost);
console.log(createdComment);

完成数据创建后,可以实现用户、文章和评论的管理功能:

用户管理

注册用户
import { db, users } from './db.config.ts';

// 注册新用户
const registerUser = async (username: string, email: string, password: string) => {
  return await db.insert(users).values({
    username,
    email,
    password,
  });
};

const newUser = await registerUser('jane_doe', 'jane@example.com', 'securepassword');
登录用户
import { db, users } from './db.config.ts';

// 登录用户
const loginUser = async (email: string, password: string) => {
  return await db.select().from(users).where(users.email.equals(email) && users.password.equals(password));
};

const loggedUser = await loginUser('jane@example.com', 'securepassword');
修改用户信息
import { db, users } from './db.config.ts';

// 修改用户信息
const updateUser = async (userId: number, newData: { username?: string, email?: string, password?: string }) => {
  return await db.update(users)
    .set(newData)
    .where(users.id.equals(userId));
};

const updatedUser = await updateUser(newUser.id, { username: 'jane_doe_updated' });

文章管理

创建文章
import { db, posts, users } from './db.config.ts';

// 创建文章
const createPost = async (title: string, content: string, userId: number) => {
  return await db.insert(posts).values({
    title,
    content,
    userId,
  });
};

const newPost = await createPost('Hello World Again', 'This is my second blog post.', newUser.id);
获取文章列表
import { db, posts } from './db.config.ts';

// 获取所有文章
const getAllPosts = async () => {
  return await db.select().from(posts);
};

const postsList = await getAllPosts();
更新文章
import { db, posts } from './db.config.ts';

// 更新文章
const updatePost = async (postId: number, newData: { title?: string, content?: string }) => {
  return await db.update(posts)
    .set(newData)
    .where(posts.id.equals(postId));
};

const updatedPost = await updatePost(newPost.id, { content: 'Updated content.' });
删除文章
import { db, posts } from './db.config.ts';

// 删除文章
const deletePost = async (postId: number) => {
  return await db.delete(posts).where(posts.id.equals(postId));
};

const deletedPost = await deletePost(newPost.id);

评论管理

创建评论
import { db, posts, comments, users } from './db.config.ts';

// 创建评论
const createComment = async (postId: number, userId: number, content: string) => {
  return await db.insert(comments).values({
    postId,
    userId,
    content,
  });
};

const newComment = await createComment(newPost.id, newUser.id, 'Great post!');
获取评论列表
import { db, comments } from './db.config.ts';

// 获取所有评论
const getAllComments = async () => {
  return await db.select().from(comments);
};

const commentsList = await getAllComments();
更新评论
import { db, comments } from './db.config.ts';

// 更新评论
const updateComment = async (commentId: number, newData: { content?: string }) => {
  return await db.update(comments)
    .set(newData)
    .where(comments.id.equals(commentId));
};

const updatedComment = await updateComment(newComment.id, { content: 'Updated comment.' });
删除评论
import { db, comments } from './db.config.ts';

// 删除评论
const deleteComment = async (commentId: number) => {
  return await db.delete(comments).where(comments.id.equals(commentId));
};

const deletedComment = await deleteComment(newComment.id);

至此,我们已经完成了基本的数据模型定义和 CRUD 操作的实现。通过这种方式,你可以开发一个功能齐全的博客应用。如果你需要更复杂的查询操作或更高级的功能,可以参考 Drizzle ORM 的文档,并结合实际的应用需求进行扩展。

0人推荐
随时随地看视频
慕课网APP