本文會列舉多個ORM框架,並介紹各自實現的思想、特點。
數據庫模式(Database Schema)
Schema是什麼?如圖所示。
Schema的表示法
對於熟悉SQL的DBA來說,SQL代碼就能當做“Schema表示法”了,如下:
CREATE TABLE user
(
user_id int PRIMARY KEY,
username varchar(15) NOT NULL,
email varchar(255) NOT NULL
)
不是所有人都熟悉SQL語句,對於技術水平薄弱的業務人員,可以使用「模式圖(schema diagram)」表示法,見《數據庫系統概念》2.4節。如書上的這幅圖所示。
【重點來了】對開發人員來說,面臨一些困難:
- 開發人員對SQL語句的掌握比較生疏,遠不如DBA
- 開發使用的語言是面向對象的,和SQL的思維模式差異大
- 目前非關係數據庫(NoSQL)遠不如關係數據庫成熟可靠,項目中還是採用RDBMS
因爲這些困難,開發人員需要一種新的“Schema表示法”。下面例舉幾個常用的ORM框架,看看這些框架的“表示法”長什麼樣。
例如SQLAlchemy ORM中的Declarative Mapping,其表示法如下:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
再如Rails中的Active Record,代碼如下:
class User < ApplicationRecord
end
上述代碼怎麼啥都沒有呢?這是因爲Rails採用「推斷式」方案,所以不需要開發人員編寫映射代碼,只要數據庫存在合適的表即可。表怎麼創建呢?在Rails開發中,一般是通過Migrations創建表,因此可以認爲Migration纔是Rails的”Schema表示法“,如下:
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :nickname
t.timestamps
end
end
end
【對比】SQLAlchemy ORM和Rails,前者採用「聲明式」方案、後者採用「推斷式」方案,區別類似編譯型語言和解釋型語言。
Rails以極高的開發效率著稱,採用的「推斷式」方案讓開發者不用寫映射代碼(ActiveRecord一行代碼都沒有,Migration是自動生成的不用手寫)。
Schema持久化
Schema在ORM中表示完畢後,數據庫中並不存在對應的Schema。我們要在數據庫中創建Schema對應的表。因爲ORM在內存中,數據庫在磁盤上,所以這一過程也叫做持久化。
項目是否需要Schema持久化呢:
- 如果數據庫早已存在,ORM是後來才引入的,那麼Schema已經存在於數據庫中,無需進行持久化
- 如果Schema一開始就是通過ORM表示法(而不是SQL表示法)描述的,那麼需要持久化
持久化到底該怎麼做呢?說的直白一點,無非就是要在RDBMS中創建表(用DDL語句創建)。在大多ORM框架中,開發人員只需要簡單幾行代碼,就能夠完成持久化了。ORM框架會根據Schema表示法構造一些DDL語句,並執行DDL語句,這個過程的示意圖如下。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0TiXezWP-1571311796341)(https://upload-images.jianshu.io/upload_images/14066340-354cbebf59dea63d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
舉個例子,看看SQLAlchemy中如何進行持久化的。在SQLAlchemy ORM中,通過調用Base.metadata.create_all
來創建表,代碼如下:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine) # Schema持久化
再舉個例子,看看Rails中如何進行持久化的。在app/db/migrate目錄下建立了一些Migration後,命令行執行$ rails db:migrate
就可以在數據庫創建表了。
CRUD操作、數據庫會話
數據驗證
確保存入數據庫的數據一定是合法的,例如:確保每個用戶都要有一個合法的郵箱地址。