ORM چیست؟ چطور برنامهها با پایگاه داده حرف میزنند، به زبان ساده
ORM (نگاشت شیء به رابطه) یک کتابخانه کدنویسی است که بین یک برنامه و یک پایگاه داده رابطهای قرار میگیرد. بهجای نوشتن کوئریهای SQL، توسعهدهندگان دادهها را بهصورت شیءهای عادی در زبان برنامهنویسی خود کار میکنند — مثلاً db.booking.create(...) یا Booking.find(...) — و ORM این دستورات را به SQL تبدیل میکند، اجرایش میکند و نتایج را دوباره به شیء تبدیل میکند. نمونههای معروف عبارتاند از Prisma و Drizzle برای TypeScript، SQLAlchemy برای Python و Entity Framework Core برای C#.
چرا ORM وقتی با هوش مصنوعی میسازید مهم است
وقتی یک سازنده برنامه با هوش مصنوعی یک پایگاه داده به برنامه شما اضافه میکند، معمولاً یک ORM هم برای ارتباط با آن اضافه میکند. لازم نیست خودتان کد ORM بنویسید، اما دانستن وجودش کمک میکند:
- مدل داده شما در کد زندگی میکند. فایل schema ORM اغلب روشنترین توصیف دادههای برنامه شماست: کدام جدولها وجود دارند، هر کدام چه فیلدهایی دارد و چطور به هم وصلاند. خواندن آن راه سریعی است برای بررسی اینکه هوش مصنوعی درست فهمیده است.
- تغییرات پایگاه داده از طریق مهاجرتها انجام میشوند. وقتی میگویید «شماره تلفن به مشتریها اضافه کن»، هوش مصنوعی مدل را بهروز میکند و یک مهاجرت میسازد. دانستن این موضوع کمک میکند بفهمید چرا یک تغییر ممکن است قبل از کار کردن در محیط واقعی باید اعمال شود.
- امنیت بهصورت پیشفرض. ORMها کوئریها را طوری میسازند که در برابر SQL injection محافظت شوند؛ یکی از رایجترین حفرههای امنیتی در برنامههایی که کوئری را دستی میسازند.
- تلههای سرعت. ORMها بهراحتی باعث میشوند صدها کوئری کوچک اجرا شود در حالی که یک کوئری کافی بود. این یکی از دلایل رایج کندی صفحات در برنامههای ساختهشده با هوش مصنوعی است.
یک تشبیه روزمره
تصور کنید یک گردشگر بریتانیایی و یک فروشنده ژاپنی که زبان مشترکی ندارند، با یک مترجم حرف میزنند.
گردشگر (برنامه شما) میگوید: «سه تا از ارزانترین چایهایتان را میخواهم.» مترجم (ORM) این جمله را به ژاپنی دقیق (SQL) تبدیل میکند، به فروشنده (پایگاه داده) میدهد و پاسخ را دوباره به انگلیسی برمیگرداند.
گردشگر هرگز ژاپنی یاد نمیگیرد و برای بیشتر گفتوگوها این مشکلی نیست. اما در یک مذاکره پیچیده، بهتر است مستقیم حرف بزند؛ به همین دلیل بیشتر ORMها اجازه میدهند توسعهدهنده در صورت نیاز SQL خام بنویسد.
ORM چگونه کار میکند
پایگاههای دادهای مثل Postgres دادهها را در جدولهایی از سطر و ستون ذخیره میکنند و با SQL پرسوجو میشوند. زبانهای برنامهنویسی با شیء کار میکنند: یک رزرو با یک date، یک customer و یک status. ORM این دو را به هم نگاشت میکند:
| سمت پایگاه داده | سمت ORM / کد |
|---|---|
جدول bookings | مدل Booking |
| سطر | یک شیء رزرو |
ستون starts_at | فیلد startsAt |
کلید خارجی customer_id | رابطه booking.customer |
SELECT، INSERT، UPDATE، DELETE | متدهای find، create، update، delete |
این چهار عمل همان عملیات CRUD هستند که تقریباً هر برنامهای روی آن ساخته میشود.
یک مثال کامل: رزروها با Prisma
در اینجا یک schema ساده Prisma برای یک برنامه رزرو آرایشگاه آمده است. این schema دو جدول و نحوه اتصالشان را توصیف میکند:
model Customer {
id Int @id @default(autoincrement())
name String
email String @unique
bookings Booking[]
}
model Booking {
id Int @id @default(autoincrement())
startsAt DateTime
service String
customer Customer @relation(fields: [customerId], references: [id])
customerId Int
}
به زبان ساده: هر مشتری یک نام، یک ایمیل یکتا و چند رزرو دارد؛ هر رزرو یک زمان شروع و یک خدمت دارد و متعلق به یک مشتری است.
حالا برنامه میخواهد «رزروهای پیشرو لنا، از نزدیکترین» را بیاورد:
const bookings = await db.booking.findMany({
where: {
customer: { email: "lena@example.com" },
startsAt: { gte: new Date() },
},
orderBy: { startsAt: "asc" },
});
ORM این را تقریباً به SQL زیر تبدیل میکند، اجرا میکند و فهرستی از شیءهای رزرو برمیگرداند:
SELECT b.* FROM "Booking" b
JOIN "Customer" c ON c.id = b."customerId"
WHERE c.email = $1 AND b."startsAt" >= $2
ORDER BY b."startsAt" ASC;
$1 و $2 جاینگهدار هستند: ایمیل و تاریخ جدا از متن کوئری ارسال میشوند و همین کار از SQL injection جلوگیری میکند.
مهاجرتها
اگر بعد از آن بگویید «به مشتریها اجازه بده شماره تلفن اضافه کنند»، هوش مصنوعی فیلد phone String? را به مدل Customer اضافه میکند (علامت ? یعنی فیلد اختیاری است) و یک مهاجرت میسازد — یک اسکریپت کوتاه SQL مثل ALTER TABLE "Customer" ADD COLUMN "phone" TEXT;. مهاجرتها به ترتیب ذخیره میشوند، پس هر نسخه از پایگاه داده را میتوان به همان ساختار رساند و تاریخچه تغییرات حفظ میشود.
ORMهای رایج بر اساس زبان
| زبان | ORMهای رایج |
|---|---|
| TypeScript / JavaScript | Prisma، Drizzle، TypeORM، Sequelize |
| Python | SQLAlchemy، ORM جنگو، SQLModel |
| C# | Entity Framework Core |
| Java | Hibernate (اغلب همراه با Spring Data JPA) |
| Ruby | Active Record |
ORMها از نظر میزان پنهان کردن SQL متفاوتاند. Prisma زبان schema و سبک کوئری خاص خودش را دارد؛ Drizzle عمداً به ساختار SQL نزدیک میماند و در عین حال همهچیز را تایپشده نگه میدارد. ابزارهای سبکتری به نام query builder، مثل Knex یا Kysely برای TypeScript، هم وجود دارند که SQL را در کد میسازند، بدون اینکه سطرها را به شیء کامل نگاشت کنند.
واژههای کلیدی
| واژه | معنی |
|---|---|
| مدل (Model) | تعریف کد یک جدول و فیلدهایش. |
| Schema | توصیف کامل همه جدولها، فیلدها و ارتباطها. |
| رابطه (Relation) | اتصال بین مدلها، مثل تعلق یک رزرو به یک مشتری. |
| مهاجرت (Migration) | اسکریپت نسخهبندیشدهای که ساختار پایگاه داده را تغییر میدهد. |
| Query builder | ابزار سبکتری برای ساختن SQL در کد، بدون نگاشت کامل به شیء. |
| مشکل N+1 | اجرای یک کوئری برای یک فهرست و سپس یک کوئری دیگر برای هر آیتم — در مقیاس بزرگ کند. |
| SQL خام (Raw SQL) | SQL نوشتهشده با دست، وقتی ORM مانع میشود. |
برداشتهای غلط رایج
- «ORM یک پایگاه داده است.» نیست. یک مترجم است. هنوز به پایگاه دادهای مثل Postgres نیاز دارید.
- «با ORM لازم نیست دادههایتان را بفهمید.» جدولهای بد طراحیشده از طریق ORM هم دردسرساز هستند. یک مدل داده خوب از ابزار مهمتر است.
- «ORMها همیشه کند هستند.» کوئریهای روزمره مشکلی ندارند. کندی معمولاً از الگوهایی مثل کوئریهای N+1 یا نبودن ایندکس میآید که قابل رفع هستند.
- «ORM یعنی هیچ ریسک SQL injection وجود ندارد، هرگز.» متدهای استاندارد ORM امناند، اما SQL خامی که ورودی کاربر در آن چسبانده شده باشد همچنان خطرناک است.
- «ویرایش فایل schema، پایگاه داده زنده را تغییر میدهد.» فقط وقتی یک مهاجرت ساخته و روی همان پایگاه داده اعمال شود.
از سازنده هوش مصنوعی خود چه بپرسید
- «schema پایگاه داده را نشان بده و هر جدول و ارتباط را به زبان ساده توضیح بده.»
- «این پروژه از کدام ORM استفاده میکند و مهاجرتها کجا هستند؟»
- «قبل از تغییر هر جدول، بگو آیا دادههای موجود تحت تأثیر قرار میگیرند، و هرگز ستونی را که داده دارد بدون پرسیدن از من حذف نکن.»
- «کندترین صفحات را برای کوئریهای N+1 بررسی کن و جایی که لیستها فیلتر یا مرتب میشوند، ایندکس اضافه کن.»
- «مطمئن شو هر کوئری که از ورودی کاربر استفاده میکند از طریق ORM یا پارامتر انجام میشود — هیچ SQLی که با رشته ساخته شده باشد وجود نداشته باشد.»
ORMها در Mythex
وقتی برنامه شما نیاز به ذخیره داده دارد، Mythex یک پایگاه داده اختصاصی Postgres به پروژه اضافه میکند — در گفتوگو درخواست کنید یا از دستور /database استفاده کنید — و اطلاعات اتصال آن را به فضای کار و برنامه منتشرشده تزریق میکند (Database). سپس عامل کدی را مینویسد که دادههای شما را میخواند و ذخیره میکند. اگر ترجیحی دارید — «از Prisma استفاده کن»، «از SQLAlchemy استفاده کن» یا «SQL ساده، بدون ORM» — همین را در گفتوگو بگویید. هر وقت خواستید ساختار را بررسی کنید، از آن بخواهید schema را برایتان توضیح دهد؛ میتوانید فایلها را در ویرایشگر کد هم بخوانید. برای تصویر بزرگتر، ببینید پایگاه داده چیست و SQL در برابر NoSQL.
پرسشها
ORM به زبان ساده چیست؟
ORM (نگاشت شیء به رابطه) یک کتابخانه کدنویسی است که بین برنامه شما و یک پایگاه داده رابطهای قرار میگیرد. به توسعهدهنده اجازه میدهد دادهها را بهصورت شیءهای عادی در زبان برنامهنویسیاش بخواند و ذخیره کند، و کوئریهای SQL را پشت صحنه برای او مینویسد.
چند نمونه از ORMها کداماند؟
ORMهای رایج عبارتاند از Prisma و Drizzle برای TypeScript و JavaScript، SQLAlchemy و ORM جنگو برای Python، Entity Framework Core برای C#، Hibernate برای Java، و Active Record در Ruby on Rails.
آیا ORM از نوشتن SQL بهتر است؟
بستگی به کار دارد. ORM باعث میشود خواندن و نوشتنهای روزمره سریعتر نوشته شوند، امنتر باشند و تغییردادنشان آسانتر شود. در مقابل، SQL نوشتهشده با دست کنترل کامل برای گزارشهای پیچیده یا کوئریهای حساس به کارایی میدهد. بیشتر ORMها اجازه میدهند در صورت نیاز به SQL خام برگردید.
مهاجرت (migration) چیست؟
مهاجرت یک اسکریپت کوچک و نسخهبندیشده است که ساختار پایگاه داده را تغییر میدهد، مثلاً یک جدول یا ستون اضافه میکند. ORMها میتوانند از مدل داده شما مهاجرت بسازند، تا هر نسخه از پایگاه داده — روی لپتاپ، در آزمایش و در محیط واقعی — به یک ساختار یکسان برسد.