初探 ORM
物件關聯對映(英語:Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程式設計技術,用於實現物件導向程式語言裡不同類型系統的資料之間的轉換。 從效果上說,它其實是建立了一個可在程式語言裡使用的「虛擬物件資料庫」。
簡單講就是,透過 ORM 可以幫我們把程式語言的「物件」跟資料庫的資料做一個對應關係,可以透過操縱物件,來改動資料庫的資料。
Sequelize
安裝:
$ npm install --save sequelize
依照我們所使用的資料庫,安裝對應的工具(這邊以 mysql2 做示範)
# One of the following:
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server
使用方式:
先在 mysql 中建立好一個需要連線的資料庫,接著在 Express 中做下列設定
const Sequelize = require('sequelize'); // 引入 sequelize
// 填入對應的資料庫帳號
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
});
上面這些設定完成之後,就可以跟資料庫建立連線了,接下來要做欄位的設定
欄位設定:
由於 ORM 是利用物件跟資料庫做連線,所以資料庫欄位中的設定(資料型態,是否可以為空....),也是寫在物件中的
const User = sequelize.define('user', {
// attributes
firstName: {
type: Sequelize.STRING, // 資料型態
allowNull: false // 是否可以為空
},
lastName: {
type: Sequelize.STRING
// allowNull defaults to true
}
}, {
// options
});
這裡為基本的型態設定,詳細設定方式,可以參考 官方文件
上面這些欄位設定好了之後,需要利用 sequelize.sync()
來讓 Sequelize 幫你去資料庫把欄位建立起來,這個指令會回傳一個 promise,所以要用 .then()
來接續後面的動作。
// Note: using `force: true` will drop the table if it already exists
sequelize.sync({ force: true }).then()
如果要新增資料的話,在 .then()
裡面用 <Table Name>.create()
,在裡面填上欄位名稱跟資料內容
sequelize.sync({ force: true }).then(() => {
// Now the `users` table in the database corresponds to the model definition
return User.create({
firstName: 'John',
lastName: 'Hancock'
});
});
如果要 select 所有資料,在 .then()
裡面用 <Table Name>.findAll()
,由於 findAll()
會回傳 promise,所以也要用 .then()
來接
sequelize.sync({ force: true }).then(() => {
User.findAll().then(users => {
console.log("All users:", JSON.stringify(users, null, 4));
});
}
如果要設定 select 的 where 條件,可以這樣寫:
sequelize.sync({ force: true }).then(() => {
User.findAll({ // 條件寫在 () 括號裡面用 {} 大括號包住
where: { // where 裡面的內容也是用物件包住
firstName: 'John'
}
}).then(users => {
console.log("All users:", JSON.stringify(users, null, 4));
});
}
上面這個寫法相當於 select * from User where firstName = John
- 如果想透過 id 來撈取資料,可以使用
findOne()
,再將 id 寫在 where 條件裡
sequelize.sync({ force: true }).then(() => {
User.findOne({
where: {
id: '1'
}
}).then(user => {
console.log(user.firstName);
});
}
- 撈取到指定資料之後,假如我們要將資料做更新,我們可以用
.then()
,並且在裡頭的 function 回傳一個user.update()
sequelize.sync({ force: true }).then(() => {
User.findOne({
where: {
id: '1'
}
}).then(user => {
user.update({ // 將要更新的資料內容寫在 () 括號裡面用 {} 大括號包住
lastName: 'hahaha'
})
}).then(() => {
console.log('update done');
})
}
- 要將資料做刪除,我們可以用
.then()
,並且在裡頭的 function 回傳一個user.destroy()
sequelize.sync({ force: true }).then(() => {
User.findOne({
where: {
id: '1'
}
}).then(user => {
user.destroy().then(() => {
console.log('destroy done');
})
})
}
Associations 資料庫關聯
關聯式資料庫,可以將兩個不同的 table 做關聯(ex. user.id 對應到 comment.userId)
當我們要利用 ORM 將兩個 table 做關聯的時候,需要用到一個指令:User.hasMany(Comment)
告訴 Sequelize 一個 user 會對應到好幾個 comment,這樣它就會利用 User.id 幫我們建立起關聯
const User = sequelize.define('user', {
// attributes
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING
}
});
const Comment = sequelize.define('comment', {
content: {
type: Sequelize.STRING
}
});
User.hasMany(Comment); 將兩個 table 做關聯
Comment.belongsTo(User);
當我們要在 comment 填入資料的時候,要記得也把 userId 一起新增進去
sequelize.sync().then(() => {
Comment.create({
userId: 2,
content: 'I am 2'
}).then(() => {
console.log('create done');
});
});
當我們在撈取資料的時候,就可以利用關聯的 id 來撈取 comment
下面程式碼示範:找出一個使用者叫做 John 並把他有發過的所有 comment 都撈出來
sequelize.sync().then(() => {
User.findOne({
where: {
firstName: 'John'
},
include: [comment] // 用一個陣列把要撈取的 table 一起放進來
}).then(user => {
console.log(JSON.stringify(user.comments, null, 4)) // 篩選我們要的資料
})
})
Sequelize CLI
為了讓實際開發時,程式碼更有結構,可以安裝 Sequelize CLI 這套工具,可以參考 官方文件 Migrations
安裝:
$ npm install --save sequelize-cli
$ npx sequelize-cli init
上面 init 會幫我們做好一些初始化設定,等它跑完之後,可以打開 config.json
這個檔案,裡面可以更改我們連到資料庫的設定(帳號密碼)。
使用方式:
建立 Model
$ npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string
$ npx sequelize-cli model:generate --name Comment --attributes content:string
建立好 Model 之後,可以在 Model 這個資料夾底下看到 Sequelize 幫你建立的檔案,這些檔案中的設定可以讓我們幾去做微調
'use strict';
module.exports = (sequelize, DataTypes) => {
const Comment = sequelize.define('Comment', {
content: DataTypes.STRING
}, {});
Comment.associate = function(models) {
// 這邊要自行加上關聯的設定
Comment.belongsTo(Models.User);
};
return Comment;
}
設定完這些檔案之後,資料庫裡面目前還不會有任何東西。
我們要透過 Migrations,依照我們在 Model 做的設定去實際去改動資料庫的內容
$ npx sequelize-cli db:migrate
接著到 migrations
這個資料夾底下,找到 Comment 的檔案去新增 UserId 這個關聯用的欄位,存擋之後。讓 Migrations 重跑一遍,把 UserId 加進資料庫當中
$ npx sequelize-cli db:migrate:undo
$ npx sequelize-cli db:migrate
這段指令跑完之後,資料庫就可以看到 User Comment 兩個 table
由於 Models 資料夾底下的 index.js 都幫我們把我們需要用到的內容打包成一個 db exports 出去了,所以在外頭利用 require 把 Models 底下的 index.js 引入,就可以來操縱這些資料。
操縱資料
回到 Models 資料夾外面,我們可以在 index.js 引入 Models 裡面的東西
const db = require('./models');
我們就可以透過 index.js 來操控資料:
const db = require('./models');
const User = db.User;
const Comment = db.Comment;
User.create({
firstName: 'John'
}).then(() => {
console.log('create done');
});