Sequelize學習經驗(ORM框架)

什麼是ORM?

簡單的講就是對SQL查詢語句的封裝,讓咱們能夠用OOP的方式操做數據庫,優雅的生成安全、可維護的SQL代碼。直觀上,是一種Model和SQL的映射關係。javascript

模型經常使用類型定義


BOOLEAN  布爾類型
STRING 字符串類型,相似varchar
CHAR 定長字符串
TEXT 文本類型
INTEGER 整數類型
FLOAT
DOUBLE
DECIMAL
DATE
ENUM 枚舉類型
NOW 當前時間戳
UUID UUID類型列,其默認值能夠爲UUIDV1或UUIDV4
UUIDV1  v1:是基於時間的uuid,經過計算當前時間戳、隨機數和機器MAC地址獲得。
UUIDV2  v4:根據隨機數,或者僞隨機數生成UUID
  uuid.v4().replace(/\-/g, '') //去除‘-’

更多看官方文檔說明,或者這個老哥的博客html

模型字段驗證


id: {
        type: Sequelize.UUIDV4,
        primaryKey: true, // 主鍵
        allowNull: false,   //不爲空
        // autoIncrement: true, //自增
        unique: true,
        defaultValue: NOW, //默認值的設置
        comment:'', // 說明
        validate: {
            is: ["^[a-z]+$",'i'],     // 只容許字母
            is: /^[a-z]+$/i,          // 只容許字母
            not: ["[a-z]",'i'],       // 不能使用字母
            isEmail: true,            // 檢測郵箱格式 (foo@bar.com)
            isUrl: true,              // 檢查Url格式 (http://foo.com)
            isIP: true,               // 檢查 IPv4 或 IPv6 格式
            isIPv4: true,             // 檢查 IPv4
            isIPv6: true,             // 檢查 IPv6
            isAlpha: true,            // 不能使用字母
            isAlphanumeric: true,     // 只容許字母數字字符
            isNumeric: true,          // 只能使用數字
            isInt: true,              // 只能是整數
            isFloat: true,            // 只能是浮點數
            isDecimal: true,          // 檢查數字
            isLowercase: true,        // 檢查小寫字母
            isUppercase: true,        // 檢查大寫字母
            notNull: true,            // 不容許null
            isNull: true,             // 只能爲null
            notEmpty: true,           // 不能空字符串
            equals: 'specific value', // 只能使用指定值
            contains: 'foo',          // 必須包含子字符串
            notIn: [['foo', 'bar']],  // 不能是數組中的任意一個值
            isIn: [['foo', 'bar']],   // 只能是數組中的任意一個值
            notContains: 'bar',       // 不能包含子字符串
            len: [2, 10],              // 值的長度必在 2 和 10 之間
            isUUID: 4,                // 只能是UUID
            isDate: true,             // 只能是日期字符串
            isAfter: "2011-11-05",    // 只能使用指定日期以後的時間
            isBefore: "2011-11-05",   // 只能使用指定日期以前的時間
            max: 23,                  // 容許的最大值
            min: 23,                  // 容許的最小值
            isArray: true,            // 不能使用數組
            isCreditCard: true,       // 檢查是有效的信用卡

            // 也能夠自定義驗證:
            isEven: function(value) {
                if(parseInt(value) % 2 != 0) {
                throw new Error('Only even values are allowed!')
                // we also are in the model's context here, so this.otherField
                // would get the value of otherField if it existed
                }
            }
        },
        // 假設暱稱後要加上 id 值
        get() {
            const id = this.getDataValue('id');
            return this.getDataValue('nickName') + '-' + id;
        },
        // set 假設數據庫中存儲的郵箱都要是大寫的,能夠在此處改寫
        set(val) {
            this.setDataValue('email', val.toUpperCase());
        },
    },

詳情看官方文檔java

模型使用

查詢

  • findById

await Role.findById(1);node

  • findByPk
await Role.findByPk(123)
  • findOne
await Role.findOne({
        where: {
            level: 1,
        },
        
        attributes: ['id', 'role_id']
    });
  • findAndCountAll
await Role.findAndCountAll({
        where: {
            level: 1,
        }
    });
  • all()是 findAll 的別名
await Role.all({
        where: {
            level: {
                [Op.gt]: 1,

                // [Op.and]: {a: 5},           // AND (a = 5)
                // [Op.or]: [{a: 5}, {a: 6}],  // (a = 5 OR a = 6)
                // [Op.gt]: 6,                // id > 6
                // [Op.gte]: 6,               // id >= 6
                // [Op.lt]: 10,               // id < 10
                // [Op.lte]: 10,              // id <= 10
                // [Op.ne]: 20,               // id != 20
                // [Op.between]: [6, 10],     // BETWEEN 6 AND 10
                // [Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
                // [Op.in]: [1, 2],           // IN [1, 2]
                // [Op.notIn]: [1, 2],        // NOT IN [1, 2]
                // [Op.like]: '%hat',         // LIKE '%hat'
                // [Op.notLike]: '%hat',      // NOT LIKE '%hat'
                
                $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'         // ILIKE '%hat' (case insensitive) (PG only)
                $notILike: '%hat'      // NOT ILIKE '%hat'  (PG only)
                $like: { $any: ['cat', 'hat']}
                                       // LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike
                $overlap: [1, 2]       // && [1, 2] (PG array overlap operator)
                $contains: [1, 2]      // @> [1, 2] (PG array contains operator)
                $contained: [1, 2]     // <@ [1, 2] (PG array contained by operator)
                $any: [2,3]            // ANY ARRAY[2, 3]::INTEGER (PG only)
                
                $col: 'user.organization_id' // = "user"."organization_id", with dialect specific column identifiers, PG in this example
            }
        },
        limit: 3,
        // 注意 raw, 默認爲 false, 這時候 Sequelize 會爲搜索出的每一條數據生成一個 Role 實例,用於更新,刪除等操做
        // 但當咱們只想搜索出數據用於顯示,並不想操做它,這個時候設置 raw: true 就會直接返回數據,而不會生成實例
        raw: true,
    });
  • count

await Role.count();mysql

  • findAndCountAll
await Role.findAndCountAll({
     where: {
        title: {
          [Op.like]: 'foo%'
        }
     },
     offset: 10,
     limit: 2
  })
  • findAndCountAll
await Role.findAndCountAll({
     where: {
        title: {
          [Op.like]: 'foo%'
        }
     },
     offset: 10,
     limit: 2
  })
  • 查詢結果重命名
const user = await User.findOne({
    where:{
        id:userId
    },
    attributes:[['id','user_id'],['username','user_name']]
})
  • order
await Role.findAll({
    'order': "id DESC"
});

Role.findAll({
  order: [
    // 將轉義用戶名,並根據有效的方向參數列表驗證DESC
    ['title', 'DESC'],

    // 將按最大值排序(age)
    sequelize.fn('max', sequelize.col('age')),

    // 將按最大順序(age) DESC
    [sequelize.fn('max', sequelize.col('age')), 'DESC'],

    // 將按 otherfunction 排序(`col1`, 12, 'lalala') DESC
    [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],

    // 將使用模型名稱做爲關聯的名稱排序關聯模型的 created_at。
    [Task, 'createdAt', 'DESC'],

    // Will order through an associated model's created_at using the model names as the associations' names.
    [Task, Project, 'createdAt', 'DESC'],

    // 將使用關聯的名稱由關聯模型的created_at排序。
    ['Task', 'createdAt', 'DESC'],

    // Will order by a nested associated model's created_at using the names of the associations.
    ['Task', 'Project', 'createdAt', 'DESC'],

    // Will order by an associated model's created_at using an association object. (優選方法)
    [Subtask.associations.Task, 'createdAt', 'DESC'],

    // Will order by a nested associated model's created_at using association objects. (優選方法)
    [Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],

    // Will order by an associated model's created_at using a simple association object.
    [{model: Task, as: 'Task'}, 'createdAt', 'DESC'],

    // 嵌套關聯模型的 created_at 簡單關聯對象排序
    [{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
  ]
  
  // 將按年齡最大值降序排列
  order: sequelize.literal('max(age) DESC')

  // 按最年齡大值升序排列,當省略排序條件時默認是升序排列
  order: sequelize.fn('max', sequelize.col('age'))

  // 按升序排列是省略排序條件的默認順序
  order: sequelize.col('age')
})
  • limit
// 獲取10個實例/行
Project.findAll({ limit: 10 })
  • offset
// 跳過8個實例/行
Project.findAll({ offset: 8 })
  • max
await Role.max('level', {
        where: {
            id: {
                [Op.gt]: 5,
            }
        }
    });
  • sum
await Role.sum('level');
  • COUNT,AVG,MAX,MIN,SUM等函數
//  可使用sequelize.fn來執行這些函數
Role.findAll({
    attributes: [[sequelize.fn('COUNT', sequelize.col('*')), 'total_count']],
});
// SELECT COUNT(*) AS `total_count` ...

Role.findAll({
    attributes: [[sequelize.fn('COUNT', 1), 'total_count']],
});
// SELECT COUNT(1) AS `total_count` ...
  • OR寫法
Role.findAll({
    where: {
      [Op.or]: [{ id: 2 }, { id: 3 }],
    },
});
// ... where `role`.`id` = 2 OR `role`.`id` = 3
  
// 另外一種寫法
Role.findAll({
    where: {
      id: {
        [Op.or]: [2, 3],
      },
    },
});
// ... where `role`.`id` = 2 OR `role`.`id` = 3
 
// 不一樣字段的寫法
Role.findAll({
    where: {
      [Op.or]: [{ id: 2 }, { level: 3 }],
    },
});
// ... where `role`.`id` = 2 OR `role`.`level` = 3

Role.findAll({
    where: {
      [Op.or]: [{ id: 2 }, { level: 3 }],
      [Op.and]: [{ role_id: { [Op.ne]: 10001 } }],
    },
});
// ... where (`role`.`id` = 2 OR `role`.`level` = 3) AND (`role`.`role_id` != 10001)
  • findOrCreate
await Role.findOrCreate({
        where: {
            role_name: 'alex'
        },
        defaults: {
            role_id: 5,
            role_name: 'alex',
            level: 15,
        },
    });

新建

  • 建立一個沒有寫入數據庫的實例,用save命令存入數據庫中
const role = Role.build({
    role_id: 1,
    role_name: 'name-1'
});
await role.save(); // 在須要的時候調用 save 命令,能夠將數據存入數據庫中
  • 直接使用create命令直接建立一條寫入數據庫的數據
await Role.create({
    role_id: 2,
    role_name: 'name-2',
});
  • 批量建立實例
const l = [];
for (let i = 0; i < 5; i++) {
    l.push({
        role_id: 1000 + i,
        role_name: `bulkname-${i}`,
        level: i + 5,
    });
}

const result = await Role.bulkCreate(l, {
    // 這樣建立語句中只有 role_id 和 role_name,會忽略 level
    fields: ['role_id', 'role_name']
});

更新

  • 查詢同時更新
await Role.update({
    level: 4
}, {
    where: {}
});
  • 先查詢,後更新
const role = await Role.findOne({
    where: {}
});
await role.update({
    // 注意這個role不是原型Role
    level: 5
});

增減

  • 增長值使用increment, 減小值使用decrement,用法相同
// level加5
const role = await Role.findById(1);
await role.increment('level', {
    by: 5
});

刪除

  • 批量刪除
await Role.destroy({
    where: {
        id: 1
    }
});

sequelize.query(原生sql)

默認狀況下,函數將返回兩個參數 - 一個結果數組,以及一個包含元數據(受影響的行等)的對象。 請注意,因爲這是一個原始查詢,因此元數據(屬性名稱等)是具體的方言。 某些方言返回元數據 "within" 結果對象(做爲數組上的屬性)。 可是,將永遠返回兩個參數,但對於MSSQL和MySQL,它將是對同一對象的兩個引用。sql

const result1 =  await mysql.query('SELECT id, img_url, url from carousel where status = 1');
 //返回值是兩個相同元素組成的數組
 "result1": [
        [
            {
                "id": 1,
                "url": "/ClassDetail?id=4"
            },
            {
                "id": 4,
                "url": "/ClassDetail?id=2"
            }
        ],
        [
            {
                "id": 1,
                "url": "/ClassDetail?id=4"
            },
            {
                "id": 4,
                "url": "/ClassDetail?id=2"
            }
        ]
    ]

解決辦法有兩種數據庫

  1. 傳遞一個查詢類型來告訴後續如何格式化結果
const result= await mysql.query('SELECT id, img_url, url from carousel where status = 1', {
    replacements: {},
    type: mysql.QueryTypes.SELECT
  });
  1. 傳遞模型
const Carousel = require('../../models/Carousel');

const result2 = await mysql.query('SELECT id, img_url, url from carousel where status = 1',{ model: Carousel });

替換

查詢中的替換能夠經過兩種不一樣的方式完成:使用命名參數(以:開頭),或者由?表示的未命名參數。 替換在options對象中傳遞。npm

  • ?

若是傳遞一個數組, ? 將按照它們在數組中出現的順序被替換segmentfault

const banner2 = await mysql.query('SELECT id, img_url, url from carousel where id = ?', {
    replacements: [1,4,5,6],
    type: mysql.QueryTypes.SELECT,
});
// 返回的結果只有數組第一個元素下標對應的數據
const banner3 = await mysql.query('SELECT id, img_url, url from carousel where id in (:[1,4,5,6])', {
    replacements: {[1,4,5,6]},
    type: mysql.QueryTypes.SELECT,
});
// 返回結果爲符合數組的數據

// 拓展:模糊查詢
sequelize.query('SELECT * FROM users WHERE name LIKE :search_name ',
  { replacements: { search_name: 'ben%'  }, type: sequelize.QueryTypes.SELECT }
).then(projects => {
  console.log(projects)
})

注意:
type: mysql.QueryTypes.SELECT
進行不一樣的原生操做是, QueryTypes的取值也不相同,這裏誤覺得都是select坑死

參考文檔:
官方文檔
Sequelize 中文文檔 v4 - Raw queries - 原始查詢
alex_my https://blog.csdn.net/alex_my...api

查詢結果

若是不想讓它自動包裝,能夠在查詢的時候,設置一個參數就會返回查詢的源數據

Model.findAll({
 raw: true, // 設置爲 true,便可返回源數據
 //Other parameters
});

也能夠在初始化 Sequelize 的時候直接全局設置:

const sequelize = new Sequelize('connectionUri', {
 define: {
  raw: true  // 設置爲 true,便可返回源數據
 } 
});
相關文章
相關標籤/搜索