ORM(對象關係對映):讓資料庫操作更簡單的工具
更新日期: 2025 年 3 月 4 日
本文為 SQL 關聯資料庫 基本介紹系列文,第 8 篇:
- 關聯式資料庫與資料完整性:初學者指南
- SQL 觸發器(Triggers):自動執行的資料庫機制
- Django 信號(Signals) vs SQL 觸發器(Triggers):關係與差異解析
- 關聯式資料庫與交易(Transaction)機制入門
- 深入理解死結(Deadlock)與發生條件
- DCL(資料控制語言)入門:SQL 權限管理基礎
- SQL 儲存程序(Stored Procedure)入門
- ORM(對象關係對映):讓資料庫操作更簡單的工具 👈進度
- SQL 儲存程序 vs ORM:如何選擇最適合的數據庫操作方式?
- 關聯式資料庫 View(檢視)是什麼?完整指南
- 理解 Materialized View:初學者指南
在開發應用程式時,與資料庫的互動是不可避免的。
過去,開發者需要手寫 SQL 查詢來對資料庫進行增刪改查(CRUD)操作,這種方式靈活但開發效率較低,並且容易因為不同的資料庫語法差異導致程式碼難以維護。
ORM(Object-Relational Mapping,對象關係對映) 的出現,為開發者提供了一種更直覺、更高效的方式來與資料庫交互。
它允許開發者使用程式語言中的物件來操作資料庫,而不需要直接寫 SQL,大幅提升開發效率與可維護性。
本篇文章將詳細介紹 ORM 是什麼、如何運作,以及它的優缺點,幫助初學者理解 ORM 如何簡化資料庫開發。
什麼是 ORM(對象關係對映)?
ORM(Object-Relational Mapping,對象關係對映) 是一種將程式語言中的物件(Object)與資料庫中的關係型表格(Relational Table)對應起來的技術。
換句話說,ORM 讓開發者可以用物件的方法來操作資料,而不需要直接撰寫 SQL。例如:
- Python + Django ORM
- Java + Hibernate
- C# + Entity Framework
- JavaScript + Sequelize
透過 ORM,開發者可以在程式碼中像操作物件一樣操作資料庫,而 ORM 會自動轉換為 SQL 查詢,並執行相應的操作。
ORM 的起源與發明者
ORM(Object-Relational Mapping,對象關係對映)的概念,並不是由某個特定個人「發明」的,而是隨著關聯式資料庫與面向對象程式設計(OOP) 的發展,自然演進出來的一種技術。
然而,ORM 的思想可以追溯到 20 世紀 80-90 年代:
1980 年代:
關聯式資料庫(Relational Database,RDB)逐漸成為企業數據存儲的標準,但當時的程式語言(如 C、COBOL)並不具有面向對象的特性,開發者需要手動撰寫 SQL 來存取數據。
1990 年代:
物件導向程式設計(OOP)逐漸流行,如 Java、C++ 開始廣泛使用。
然而,關聯式資料庫仍然使用 SQL,而 OOP 語言則以「類別(Class)」和「物件(Object)」為核心,導致程式碼與資料庫之間存在「不匹配」的問題。
2000 年代初:
開發者開始尋找一種方式來將「關聯式資料庫」與「物件導向程式設計」結合,ORM 應運而生,提供了一種將資料表映射為程式語言中的物件的方法。
雖然 ORM 不是某個特定人「發明」的,但一些關鍵技術推動了 ORM 的發展:
- Hibernate(2001 年,Java):由 Gavin King 開發,這是最早的 ORM 框架之一,它解決了 Java 開發者手寫 SQL 的問題。
- Active Record(2003 年,Ruby on Rails):由 David Heinemeier Hansson(DHH) 提出的 ORM 設計模式,使得 ORM 變得更易用。
- Entity Framework(2008 年,.NET):微軟開發的 ORM,成為 C#/.NET 開發的標準。
ORM 的運作原理
sequenceDiagram participant 開發者 as 開發者(應用程式) participant ORM as ORM框架 participant DB as 資料庫 開發者->>ORM: 透過方法呼叫查詢/保存資料 ORM->>DB: 產生並執行 SQL DB-->>ORM: 回傳結果集(查詢) / 執行結果(保存) ORM-->>ORM: 依照映射設定,將欄位 <-> 物件屬性進行轉換 ORM-->>開發者: 回傳轉換後的物件或執行結果
將資料表對應為程式中的類別
在使用 ORM 時,通常會為資料庫中的每個表(Table)建立一個對應的類別(Class),類別中的屬性對應到資料表的欄位。例如,在 Django ORM 中:
from django.db import models
class User(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
這段程式碼表示:
User
類別對應到資料庫中的users
表。name
和email
是對應的欄位,ORM 會自動把這些轉換為 SQL。
物件操作 → 自動轉換成 SQL
ORM 允許開發者透過物件的方法來操作資料庫,而 ORM 會在底層自動生成並執行 SQL 查詢。例如:
# 建立新使用者
user = User(name="Alice", email="[email protected]")
user.save()
# ORM 會自動執行
# INSERT INTO users (name, email) VALUES ('Alice', '[email protected]');
# 查詢使用者
user = User.objects.get(id=1)
# ORM 會執行
# SELECT * FROM users WHERE id = 1;
# 更新使用者
user.name = "Alice Smith"
user.save()
# ORM 會執行
# UPDATE users SET name = 'Alice Smith' WHERE id = 1;
# 刪除使用者
user.delete() # ORM 會執行 DELETE FROM users WHERE id = 1;
開發者不需要寫 SQL,ORM 會根據程式碼的邏輯,自動產生並執行 SQL 查詢,簡化了資料庫操作。
--- title: ORM 底層運作邏輯 --- classDiagram direction TB %% 由上到下,符合邏輯層次 %% 應用層(開發者操作) class 開發者操作 { +User.save() 物件操作 +User.objects.get(id=1) 查詢操作 +User.delete() 刪除操作 } %% ORM 處理層 class ORM層 { +屬性映射: 物件欄位 ↔ 資料庫欄位 +關聯映射: 物件關聯 ↔ 外鍵 +SQL 生成器: 產生 SQL 指令 +執行 SQL 查詢 } %% SQL 轉換與資料庫層 class SQL處理層 { +解析 ORM 指令 +生成對應 SQL +執行 SQL 並回傳結果 } %% 資料庫層 class 資料庫 { +users 表 +posts 表 +儲存資料 +返回查詢結果 } %% 連結關係 開發者操作 --> ORM層 : "操作物件" ORM層 --> SQL處理層 : "轉換為 SQL 指令" SQL處理層 --> 資料庫 : "執行 SQL" 資料庫 --> SQL處理層 : "回傳資料" SQL處理層 --> ORM層 : "轉換回 ORM 物件" ORM層 --> 開發者操作 : "回傳物件或執行結果"
ORM 主要要解決的問題
ORM 的核心目標,是解決「關聯式資料庫」與「物件導向程式設計(OOP)」之間的「不匹配問題(Impedance Mismatch)」,具體來說,ORM 要解決以下幾個主要問題:
SQL 與程式語言的「語法不匹配」
問題:
- 關聯式資料庫使用 SQL 來存取數據,而 OOP 程式語言使用類別與物件。
- 開發者必須手寫 SQL 查詢,這導致程式碼可讀性低,且容易因為不同資料庫的 SQL 語法不同而產生相容性問題。
解決方案: ORM 允許開發者使用程式語言中的物件方法來操作資料庫,而不用直接撰寫 SQL。例如,在 Django ORM 中:
# 創建一個新的使用者
user = User(name="Alice", email="[email protected]")
user.save()
# ORM 自動生成
# INSERT INTO users (name, email) VALUES ('Alice', '[email protected]');
# 查詢使用者
user = User.objects.get(id=1)
# ORM 轉換為
# SELECT * FROM users WHERE id = 1;
ORM 會自動生成並執行 SQL 查詢,開發者可以完全不需要寫 SQL。
物件與關聯式資料表的「結構不匹配」
問題:
- 在 OOP 中,數據是以「類別(Class)」和「物件(Object)」的形式存在,例如:
class User:
def __init__(self, id, name, email):
self.id = id
self.name = name
self.email = email
- 但在關聯式資料庫中,數據是以「表格(Table)」的形式存儲:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
- 物件導向程式設計中的「繼承」 在 SQL 資料表中無法直接對應。
解決方案: ORM 透過映射機制(Mapping),將類別與資料表建立關聯,讓開發者可以像操作物件一樣存取資料庫:
class User(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
ORM 會自動處理類別與資料表之間的映射關係,開發者無需手動管理。
提高開發效率,減少重複的 SQL
問題:
- 每次查詢資料時,都要寫 SQL,增加了開發負擔:
SELECT * FROM users WHERE id = 1;
- 如果資料庫結構變更(例如欄位名稱改變),所有 SQL 查詢都需要修改,導致維護成本上升。
解決方案: ORM 將 SQL 操作封裝成 API,開發者可以用程式語言的方法來執行 SQL:
user = User.objects.get(id=1) # ORM 自動轉換 SQL
- 開發者不需要重複撰寫 SQL,ORM 會根據類別結構自動調整 SQL 查詢。
簡化交易控制與關聯查詢
問題:
- 傳統 SQL 需要手動控制交易:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
- 關聯式資料庫的多表關聯查詢(JOIN)語法複雜,例如:
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE users.id = 1;
- 這些操作需要開發者對 SQL 非常熟悉,且不同資料庫的 SQL 語法可能不同,影響可移植性。
解決方案: ORM 自動管理交易與關聯查詢,開發者可以透過簡單的方法處理:
# 透過 ORM 進行交易
with transaction.atomic():
sender.balance -= 100
receiver.balance += 100
sender.save()
receiver.save()
# 使用 ORM 進行關聯查詢
orders = Order.objects.filter(user__id=1) # ORM 自動產生 JOIN 查詢
ORM 簡化了關聯查詢與交易控制,使開發者不需要手動撰寫 SQL。
為什麼要使用 ORM?
ORM 最大優勢 在於它可以提升開發效率、增加可讀性、減少錯誤。以下是 ORM 的幾個主要優點:
提高開發效率
- 開發者不需要手寫 SQL 查詢,而是透過物件方法來操作資料庫,減少開發時間。
- 適用於大型應用程式,避免撰寫重複的 SQL 查詢。
可讀性與維護性高
- ORM 讓程式碼更結構化,開發者可以更直覺地理解資料庫操作,而不需要解析 SQL 查詢語句。
- 物件導向的方式使業務邏輯更清晰。
可移植性強
- 直接寫 SQL 可能會因為不同資料庫(MySQL、PostgreSQL、SQLite)的語法不同,導致程式碼難以跨資料庫使用。
- ORM 屏蔽了底層 SQL 的差異,讓程式碼更容易在不同資料庫之間轉換。
內建安全性
- ORM 內建 SQL 防注入攻擊機制,自動處理 SQL 參數,避免惡意 SQL 注入攻擊。
ORM 的缺點
雖然 ORM 有許多優勢,但它並不是適用於所有情境,以下是 ORM 的幾個常見缺點:
效能可能較低
- ORM 生成的 SQL 可能不是最優化的 SQL,在大規模數據查詢時,直接寫 SQL 可能會更高效。
- ORM 的查詢通常會自動加上許多額外的欄位,影響執行效率。
複雜查詢難以處理
- ORM 適合簡單的 CRUD 操作,但當查詢變得非常複雜時(如多表聯查、窗口函數),可能仍需要手寫 SQL。
- 例如:
users = User.objects.raw("SELECT id, name FROM users WHERE email LIKE '%example.com%'")
- 這種情境下,開發者仍然需要直接寫 SQL 查詢。
學習成本
- ORM 需要學習不同的框架語法與規則,對初學者來說,可能需要一些時間適應不同 ORM 的用法。
ORM 與 SQL 手寫查詢的比較
特性 | ORM | 手寫 SQL |
---|---|---|
開發效率 | ✅ 高,直接透過物件操作資料 | ❌ 低,每次都需手寫 SQL |
可讀性 | ✅ 高,程式碼更直覺 | ❌ 低,SQL 嵌入程式碼較難讀 |
效能 | ❌ 可能較低 | ✅ 高,可手動優化查詢 |
移植性 | ✅ 高,支援多種資料庫 | ❌ 低,不同資料庫 SQL 語法不同 |
安全性 | ✅ 自動防 SQL 注入 | ❌ 需要手動處理安全性 |
ORM 適用場景
✅ 適合
- 快速開發 Web 應用程式(如 Django、Flask、Spring Boot)
- 小型專案與原型開發
- 需要跨資料庫的應用
- CRUD 為主的應用
❌ 不適合
- 需要高效能的應用(如大規模數據分析、金融交易系統)
- 查詢邏輯非常複雜的應用(可能仍需手寫 SQL)
結論
ORM 透過物件來映射關聯式資料庫,使開發者可以不用寫 SQL,透過程式語言的方式進行資料庫操作。這使開發變得更快速、直覺,也提高了程式的可讀性與可維護性。
然而,ORM 並不是萬能的,當涉及到效能優化或複雜查詢時,仍然可能需要手寫 SQL。
因此,最好的方式通常是結合 ORM 與 SQL,在日常開發中使用 ORM 提高效率,而在高效能需求場景下直接使用 SQL。
如果你是初學者,推薦你先從 ORM 開始學習,熟悉基礎後,再學習 SQL 的最佳實踐! 🚀