Sequelize手記 - (一)

最近開始接觸數據庫,如今廣泛用的都是Mysql數據庫,簡單的瞭解了一下sql語句,沒有太深刻的學習,而後就開始找相關的ORM框架,而後鎖定了Sequelize,我的感受很強大,搜索了一些文檔,可是很讓人費解,講的每一部分都是那麼的官方,不太容易理解,記錄一下學習路程。本文檔以koa+Sequelize進行編碼測試。javascript

準備工做

在嘗試使用Sequelize以前先確認是否安裝了Mysql數據庫,安裝node,這裏使用的Mysql 5.6版本。前端

首先要建立一個項目執行命令以下:java

mkdir 文件夾名稱
cd 文件夾名稱
npm init -y     // 直接略過全部問答,所有采用默認答案

在開始以前首先要安裝相關依賴:node

//  安裝 sequelize koa mysql2
npm install --save-dev sequelize koa mysql2

注意:這裏是mysql2mysql

創建鏈接

建立main.js文件,分別引入koasequelize創建與mqsql數據庫鏈接。在實例化Sequelize時須要配置一些參數,第一個接收的參數時數據庫的名稱,第二個是用戶名,第三個是密碼,第四項是鏈接mysql的相關配置。sql

const Koa = require("koa");
const Sequelize = require("sequelize");
const app = new Koa();
const mysqlConfig ={
    host: 'localhost',  //  接數據庫的主機
    port: '3306',       //  接數據庫的端口
    protocol: 'tcp',    //  鏈接數據庫使用的協議
    dialect: 'mysql',   //  使用mysql
    pool: { 
        max: 5,         //  最大鏈接數量
        min: 0,         //  最小鏈接數量
        idle: 10000     //  鏈接空置時間(毫秒),超時後將釋放鏈接
    },
    retry: {        //  設置自動查詢時的重試標誌
        max: 3          //  設置重試次數
    },
    omitNull: false,    //  null 是否經過SQL語句查詢
    timezone: '+08:00'  //  解決時差 - 默認存儲時間存在8小時偏差
};
const sequelize = new Sequelize('aarontest', 'root', '123456',mysqlConfig );
app.listen(3000);

經過上述代碼就已經與Mysql創建了鏈接。這個地方沒有什麼能夠講的,按照上述進行配置就能夠了。test數據庫不必定要存在不存在也是能夠正常創建鏈接的。配置參數還有不少,這裏只說了一些經常使用的,就很少贅述了。若是以爲上述方法比較麻煩能夠經過也提供了其餘方法進行鏈接:數據庫

const sequelize = new Sequelize('mysql://root:123456@localhost:3306/aarontest', {
    ...mysqlConfig
});

這種鏈接方式與mongoose鏈接方法差很少,以致於具體使用哪一種方式仍是要根據我的的編碼習慣來決定。npm

創建model

sequelize是經過define方法創建模型的,Model至關於數據庫中的表,該對象不能經過構造函數實例化,而只能經過sequelize.define()sequelize.import()方法建立。json

const AaronTest = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
})

經過上面的方法建立好model以後,使用Navicat(數據庫可視化工具)仍然沒法看到咱們所建立的表,尷尬。。。sequelize提供了一個sync()方法能夠同步模型到數據庫。使用該方法的時候必定要注意所鏈接的數據庫必定要存在不然會報錯。後端

define方法接收三個參數,第一個參數爲表名稱,第二個爲所須要建立的數據庫字段,第三個參數是相關表配置。

const AaronTest = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
});
AaronTest.sync();

這樣model所建立的字段就被同步到了數據庫中,一樣相對應的表也被建立好了,須要注意經過這種方式同步的表會在表名稱後面添加一個s做爲複數。同步數據庫是會默認添加兩個字段createdAtupdatedAt有的時候可能不須要這兩個字段。這個時候須要在第三個字段中添加timestampsfalse默認爲true

const AaronTest = sequelize.define('project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
},{
  timestamps: false
})

上面能夠看出經過Sequelize.STRING設置當前字段的類型,Sequelize提供了不少數據類型供咱們進行使用:

類型 說明
STRING 將字段指定爲變長字符串類型,默認長度爲 255。Sequelize.STRING(64)
CHAR 將字段指定爲定長字符串類型,默認長度爲 255。Sequelize.CHAR(64)
TEXT 將字段指定爲(無)有限長度的文本列。可用長度:tiny, medium, long,Sequelize.TEXT('tiny')
INTEGER 32位整型,可用屬性:UNSIGNED,ZEROFILL,Sequelize.INTEGER('UNSIGNED')
BOOLEAN 小數,接受一個或兩個參數表示精度,Sequelize.BOOLEAN()
TIME 指定爲時間類型列,Sequelize.TIME()
DATE 指定爲日期時間類型列,Sequelize.DATE()
DATEONLY 指定爲日期類型列,Sequelize.DATEONLY()
HSTORE 指定爲鍵/值類型列,僅Postgres適用,Sequelize.HSTORE()
JSON 指定爲JSON字符串類型列,僅Postgres適用,Sequelize.JSON()
JSONB 指定爲預處理的JSON數據列,僅Postgres適用,Sequelize.JSONB()
NOW 一個表示當前時間戳的默認值,Sequelize.NOW()
UUID UUID類型列,其默認值能夠爲UUIDV1或UUIDV4,Sequelize.UUID()
ENUM 枚舉類型,Sequelize.ENUM()
ARRAY 數組類型,僅Postgres適用,Sequelize.ARRAY()

設定model的時候在添加第三個參數能夠對當前model進行二次設置,以使數據庫更增強壯,經常使用字段以下

  • timestamps:不要添加時間戳屬性 (updatedAt, createdAt)
  • paranoid:paranoid 屬性只在啓用 timestamps 時適用,不從數據庫中刪除數據,而只是增長一個 deletedAt 標識當前時間,咱們常說的邏輯刪除
  • underscored: 不使用駝峯式命令規則,這樣會在使用下劃線分隔,updatedAt的字段名會是 updated_at
  • freezeTableName:禁止修改表名. 默認狀況下sequelize會自動使用傳入的模型名(define的第一個參數)作爲表名,若是你不想使用這種方式你須要進行如下設置
  • tableName:定義表名
  • createdAt:不想使用 createdAt
  • updatedAt:想 updatedAt 的實際名爲'***'
  • deletedAt: 要將 deletedAt 設置爲 destroyTime (注意要啓用paranoid)

當建立model的時候可能須要有些附加屬性,好比主鍵,自增,不能爲null,默認值等等,能夠在建立model的時候進行手動設置。

  • autoIncrement:是否自增
  • references:經過references選項能夠建立外鍵
  • allowNull:設置 allowNull 選項爲 false 後,會爲列添加 NOT NULL 非空限制
  • defaultValue:設置默認值
  • type:字段類型
  • unique:添加惟一(unique)約束後插入重複值會報錯,unique屬性能夠是boolean 或 string類型
  • primaryKey:設置爲主鍵
  • comment:字段描述
  • field:指定數據庫中的字段名

除了這些之外Sequelize在定義model的時候,還提供了一個validate屬性,能夠爲添加的字段進行校驗處理:

字段 說明 值類型
is 存儲值必須知足正則 正則
not 除正則以外的值 布爾
isEmail 是否爲郵箱 布爾
isUrl 檢查Url格式 布爾
isIP 檢查 IPv4 或 IPv6 格式 布爾
isIPv4 檢查 IPv4 布爾
isIPv6 檢查 IPv6 布爾
isAlpha 不能使用字母 布爾
isAlphanumeric 只容許字母數字字符 布爾
isNumeric 只能使用數字 布爾
isInt 只能是整數 布爾
isFloat 只能是浮點數 布爾
isDecimal 檢查數字 布爾
isLowercase 檢查小寫字母 布爾
isUppercase 檢查大寫字母 布爾
notNull 不容許null 布爾
isNull 只能爲null 布爾
notEmpty 不能空字符串 布爾
equals 只能使用指定值 字符串
contains 必須包含子字符串 字符串
notIn 不能是數組中的任意一個值 數組
isIn 只能是數組中的任意一個值 數組
notContains 不能包含子字符串 字符串
len 值的長度必在 2 和 10 之間 數組
isUUID 只能是UUID 數字
isDate 只能是日期字符串 布爾
isAfter 只能使用指定日期以後的時間 字符串
isBefore: 只能使用指定日期以前的時間 字符串
max 容許的最大值 數字
min 容許的最小值 數字
isArray 不能使用數組 布爾
isCreditCard 檢查是有效的信用卡 布爾

除了上面的校驗方法之外還提供了自定義校驗方法,使用isEven去定義一個函數,其函數的第一個參數就是所存如的值:

const AaronTest = sequelize.define('project', {
    title: Sequelize.STRING,
    description: {
    type:Sequelize.TEXT,
    validate:{
        isEven: function(value) {
            if(parseInt(value) % 2 != 0) {
                throw new Error('Only even values are allowed!')
            }
        }
    }
    }
},{
    timestamps: false
})

上面說過使用sequelize.import()也能夠建立model這個方法實際上是模型導入,經過文件導入模型定義。檢查模型是否已經定義。被導入的模型會被緩存,因此屢次導入並不會重複加載,path表示要導入文件的路徑,若是使用相對路徑會自動轉換爲絕對路徑。

const AaronTest = sequelize.import('../model/user.js');

user.js

module.exports = function(sequelize, DataTypes) {
  return sequelize.define("project", {
    name: DataTypes.STRING,
    description: DataTypes.TEXT
  })
}

CRUD

CRUD:是指在作計算處理時的增長(Create)、讀取(Read)、更新(Update)和刪除(Delete)幾個單詞的首字母簡寫。crud主要被用在描述軟件系統中數據庫或者持久層的基本操做功能。

建立

建立數據的方法有不少種,這裏簡單的介紹一些經常使用的:

第一種:

先建立數據實例,而後調用實例的save方法,完成數據存儲。

const Aaron = AaronTest.build({
  'title': `後端 | ${Math.random()}`,
  'description': '技術部'
});
Aaron.save().then((result) => {
    //  成功
    console.log(result)
}).catch((error) => {
    //  失敗
    console.log(error)
})

第二種:

經過靜態create方法

const user = AaronTest.create({
  'title': `前端 | ${Math.random()}`,
  'description': '網絡部'
}).then(function(result) {
    //  成功
    console.log(result)
}).catch(function(error) {
    //  失敗
    console.log(error)
});

讀取

經過findAll方法讀取數據。

AaronTest.findAll({
  where: {
    description: '網絡部'
  },
  limit: 10,
  offset: 0
}).then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

經過上述方法建立的數據能夠直接做爲返回結果返回給前臺,可是若是對查詢到的結果進行數據操做時不行的,由於查詢到的結果是sequelize處理過的模型,若是想對其操做須要在參數中添加row:true屬性。

AaronTest.findAll({
  where: {
    description: '網絡部'
  },
  limit: 10,    //  查詢多少條
  offset: 0,    //  查詢開始位置
  raw:true
}).then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

添加row:true返回的則是一個沒有被包裝過的數組了。在項目過程當中須要查詢一下當前所查詢的數據共有多少條返回給前端。

AaronTest.count({
  where:{
    description: '網絡部'
  }
}).then().then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

使用這種方法是確實能夠查詢到想要的結果,可是不管是先查詢列表仍是先查詢總數,都須要對數據庫進行兩次查詢很繁瑣。sequelize提供了一個很方便的方法。

AaronTest.findAndCountAll({
  where: {
    description: '網絡部'
  },
  limit: 10,
  offset: 0,
  raw:true,
  attributes:["id", "title"]    //  須要查詢出的字段
}).then(function(result) {
  // success
  console.log(result)
}).catch(function(error) {
  // error
  console.log(error)
});

經過上面的方法則能夠查詢到總數以及條件範圍內的數據,一箭雙鵰。查詢結果返回的是一個json對象,其包括controws兩個屬性,分別是總數和數據。很舒服有沒有。

以上方式是查詢列表,查詢單條數據使用其餘方法:

AaronTest.findOne({
  where:{
    id:6
  },
  raw:true,
  attributes:["id", "title"] 
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

更新

修改數據能夠直接調用靜態的update方法,經過where條件查詢,對其搜索到的數據進行查詢,並對查詢到的數據進行更改。

AaronTest.update({
  description: '前端部',
  title:`前端 | ${Math.random()}`
},{
  where:{
    description: "網絡部"
  }
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

該方法修改全部查詢的到的數據,返回結果爲數組形式,數據只有一個值,也就是數組的第0項,則是N條數據修改爲功。

刪除

刪除操做經過destroy方法,一樣也是經過where條件查詢,對所查詢數據進行刪除。

AaronTest.destroy({
  where: {
    description: "UI部",
  }
}).then(function(result) {
  console.log(result)
}).catch(function(error) {
  console.log(error)
});

當刪除成功後,返回結果爲Number,刪除多少條數據,若是沒有刪除則會返回0。此方法屬於物理刪除,刪除後沒法進行恢復。

查詢參數

CRUD操做過程當中,都少不了的就是查詢,細心的應該能夠看的出,上面的例子中查詢的時候多多少少的對其進行了一些小的改動。Sequelize中有兩種查詢:使用Model(模型)中的方法查詢和使用sequelize.query()進行基於SQL語句的原始查詢。上面用到的是Model查詢方式,接下來就詳細的介紹一些經常使用的參數以及其表明的意義。

attributes - 屬性與查詢字段

查詢時,若是隻須要查詢模型的部分屬性,能夠在經過在查詢選項中指定attributes實現。該選項是一個數組參數,在數組中指定要查詢的屬性便可,這個字段在上面進行查詢的時候已經使用過了。

AaronTest.findOne({
  where:{
    id:6
  },
  raw:true,
  attributes:["id", "title", "description"]
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

查詢屬性(字段)能夠經過傳入一個嵌套數據進行重命名,這裏須要強調一下重命名所指的是對查詢出的數據鍵值進行重命名處理,而不是更改數據表中的字段名稱。

AaronTest.findOne({
  where:{
    id:2
  },
  attributes:["id", ["title","t"]],
  raw:true
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})
//  注意這裏t ↓
//  { id: 2, t: '前端 | 0.8765218593370694' }

經過sequelize.fn方法能夠進行聚合查詢,我的以爲這個方法不太經常使用,可是仍是簡單的介紹一下,這種查詢方式是向當前查詢內容中添加一個新的屬性,而且查詢的是列表仍是查詢單條數據。

應用場景:

好比有不少商品,每一個商品都有本身的分類,根據id進行查詢了一個商品,可是與其同類的商品有多少?就可使用這個方法添加進去。下面例子中的count則是添加進去屬性的鍵。

AaronTest.findAll({
    where:{
        id:2
    },
    attributes: [
        "id", 
        ["title","t"],
        [sequelize.fn('COUNT', sequelize.col('id')), 'count']
    ],
    raw:true
}).then((result) => {
    console.log(result)
}).catch((error) => {
    console.log(error)
})

可能有一種狀況,當前所須要查詢的表字段太多,可是隻有一兩個數據不想要,在attributes數組中添加很長的字段名稱,這樣會顯得代碼很臃腫。attributes不光能夠爲數組,還能夠爲對象在對象存在exclude這個屬性,這個屬性就是剔除掉那些不想要的屬性。

AaronTest.findOne({
  where:{
    id:2
  },
  attributes:{
    exclude: ['id'] 
  },
  raw:true
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

where - 指定篩選條件

上面的那麼多例子中where出現的次數最多了,除了增長數據不須要,其餘的都須要用到where條件,能夠指定一個where選項以指定篩選條件,where是一個包含屬性/值對對象,sequelize會根據此對象生產查詢語句的篩選條件。

where的基礎用法也就向上面那樣,針對某些特定的條件進行查詢處理。

AaronTest.findOne({
  where:{
    id:2
  },
  attributes:{
    exclude: ['id'] 
  },
  raw:true
}).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

就像上面那樣簡單的查詢沒法知足全部的業務需求,Sequelize還提供了操做符以知足更多的查詢條件,經常使用的操做符以下:

$and: {a: 5}                    // AND (a = 5)
$or: [{a: 5}, {a: 6}]           // (a = 5 OR a = 6)
$gt: 6,                         // > 6
$gte: 6,                        // >= 6
$lt: 10,                        // < 10
$lte: 10,                       // <= 10
$ne: 20,                        // != 20
$not: true,                     // IS NOT TRUE
$between: [6, 10],              // BETWEEN 6 AND 10
$notBetween: [11, 15],          // NOT BETWEEN 11 AND 15
$in: [1, 2],                    // IN [1, 2]
$notIn: [1, 2],                 // NOT IN [1, 2]
$like: '%hat',                  // LIKE '%hat'
$notLike: '%hat'                // NOT LIKE '%hat'
$iLike: '%hat'                  // 包含'%hat' (case insensitive) (PG only)
$notILike: '%hat'               // 不包含'%hat'  (PG only)
$like: { $any: ['cat', 'hat']}  // 像任何數組['cat', 'hat'] -也適用於iLike和notLike

limit/offset - 分頁與限制返回結果數

在進行列表查詢時,不能把查詢道德全部數據所有返回出去,須要對數據進行分頁處理。

// 獲取 10 條數據(實例)
AaronTest.findAll({ limit: 10 })
// 跳過 8 條數據(實例)
AaronTest.findAll({ offset: 8 })
// 跳過 5 條數據並獲取其後的 5 條數據(實例)
AaronTest.findAll({ offset: 5, limit: 5 })

查詢排序

order選項用於查詢結果的排序數據。排序時應該傳入一個包含屬性-排序方向的元組/數組,以保證正確的轉義:

AaronTest.findAll({
  order: [
    // 轉義 username 並對查詢結果按 DESC 方向排序
    ['username', 'DESC'],
    // 按 max(age) 排序
    sequelize.fn('max', sequelize.col('age')),
    // 按 max(age) DESC 排序
    [sequelize.fn('max', sequelize.col('age')), 'DESC'],
    // 按 otherfunction(`col1`, 12, 'lalala') DESC 排序
    [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
    // 按相關聯的User 模型的 name 屬性排序
    [User, 'name', 'DESC'],
    // 按相關聯的User 模型的 name 屬性排序並將模型起別名爲 Friend
    [{model: User, as: 'Friend'}, 'name', 'DESC'],
    // 按相關聯的User 模型的嵌套關聯的 Company 模型的 name 屬性排序
    [User, Company, 'name', 'DESC'],
  ]
  // 如下全部聲明方式都會視爲字面量,應該當心使用
  order: 'convert(user_name using gbk)'
  order: 'username DESC'
  order: sequelize.literal('convert(user_name using gbk)')
})

上面說的這些對於SQL語句瞭解一些,都是很容理解,有些API不經常使用也就沒些,詳細能夠查看中文文檔。

SQL語句查詢

原始查詢中有兩種替換查詢參數的方法,以:開頭的參數的形式替換或以不命名以?替換。在選項對象中傳遞參數:

  • 若是傳遞一個數組,? 會按數組的順序被依次替換
  • 巢傳遞一個對象,:key將會用對象的鍵替換。若是對象中未找到指定鍵,則會引起異常(反之亦然)
//  這裏是sequelize,並非model
sequelize.query('SELECT * FROM projects WHERE id = ?',
  { replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)
})

總結

以上是對sequelizeapi進行了整理,雖然不太全面,熟練掌握上述API能夠作一個項目了,有關sequelize的更多的用法我也在繼續爬坑中,可能文章中有些許錯誤,你們能夠在下方留言,我會盡快作出改正。感謝你們的閱讀。

相關文章
相關標籤/搜索