Sequelize.js是一款針對nodejs的ORM框架。javascript
使用nodejs鏈接過數據庫的人確定對數據庫不陌生了。若是是直接連接,須要本身創建並管理鏈接,還須要手動編寫sql語句。簡單的項目到是無所謂,但是一旦項目設計的東西比較複雜,表比較多的時候整個sql的編寫就很是的消耗精力。前端
在Java、c#等語言中已經有輕量的數據庫框架或者解決方案了。在nodejs中我推薦Sequelize。它是一個很成熟的框架,在速度和性能上也很是有優點。而其中最關鍵的地方就在於,平常開發只須要管理對象的建立、查詢方法的調用等便可,極少須要編寫sql語句。這一個好處就是省去了複雜的sql語句維護,同時也避免了因sql而引發的沒必要要的bug。java
Sequelize是針對node.js和io.js提供的ORM框架。具體就是突出一個支持普遍,配置和查詢方法統一。它支持的數據庫包括:PostgreSQL、 MySQL、MariaDB、 SQLite 和 MSSQL。node
本文中測試以及API展現地址:github地址mysql
Sequelize的調用突出一個簡單快捷。具體狀況能夠感覺一下下面的代碼。若是有過開發經驗的能夠略過。git
Table1.findById(23); //select a,b,c,d from table1 where id=23; Table1.findAll({ where:{a:"test",b:76} }); //select a,b,c,d from table1 where a="test" and "b=76;
在單表查詢的時候只須要簡單的配置便可完成查詢。是否是很是的簡單方便呢?github
Sequelize的鏈接須要傳入參數,而且能夠配置開啓線程池、讀寫分庫等操做。sql
簡單的寫法是這樣的:new Sequelize("表名","用戶名","密碼",配置)
數據庫
正常使用中不多使用到全部的參數,這裏提供一個經常使用的模板,只須要修改本身使用的值便可。c#
const sequelize = new Sequelize('database', 'username', 'password', { host: 'localhost', //數據庫地址,默認本機 port:'3306', dialect: 'mysql', pool: { //鏈接池設置 max: 5, //最大鏈接數 min: 0, //最小鏈接數 idle: 10000 }, });
下面是詳細的配置參數。
const sequelize = new Sequelize('database', 'username', 'password', { // 數據庫類型,支持: 'mysql', 'sqlite', 'postgres', 'mssql' dialect: 'mysql', // 自定義連接地址,能夠是ip或者域名,默認本機:localhost host: 'my.server.tld', // 自定義端口,默認3306 port: 12345, // postgres使用的參數,鏈接類型,默認:tcp protocol: null, // 是否開始日誌,默認是用console.log // 建議開啓,方便對照生成的sql語句 logging: true, // 默認是空 // 支持: 'mysql', 'postgres', 'mssql' dialectOptions: { socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock', supportBigNumbers: true, bigNumberStrings: true }, // sqlite的存儲位置,僅sqlite有用 // - 默認 ':memory:' storage: 'path/to/database.sqlite', // 是否將undefined轉化爲NULL // - 默認: false omitNull: true, // pg中開啓ssl支持 // - 默認: false native: true, // 數據庫默認參數,全局參數 define: { underscored: false freezeTableName: false, charset: 'utf8', dialectOptions: { collate: 'utf8_general_ci' }, timestamps: true }, // 是否同步 sync: { force: true }, // 鏈接池配置 pool: { max: 5, idle: 30000, acquire: 60000, }, isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ })
在使用以前必定要先建立模型對象。就是數據庫中表的名稱、使用到的字段、字段類型等。
這裏有一個推薦的開發方式。先在nodejs中將對象建立出來,而後調用Sequelize的同步方法,將數據庫自動建立出來。這樣就避免了既要寫代碼建表,又要手工建立數據庫中的表的操做。只須要單獨考慮代碼中的對象類型等屬性就行了。
若是數據庫中已經建好了表,而且不能刪除,這個時候就不能自動建立了,由於建立的時候會刪除掉舊的數據。
下面是簡單的對象建立多數狀況下這樣就能夠了。
const users = db.define('t_user'/*自定義表名*/, { id: { type: Sequelize.INTEGER, primaryKey: true, //主鍵 autoIncrement: true, //自增 comment: "自增id" //註釋:只在代碼中有效 }, //用戶名 username: { type: Sequelize.STRING, validate:{ isEmail: true, //類型檢測,是不是郵箱格式 } }, //密碼 pwd: { type: Sequelize.STRING(10), allowNull: false,//不容許爲null }, //狀態 status: { type: Sequelize.INTEGER, defaultValue: 0,//默認值是0 }, //暱稱 nickname: { type: Sequelize.STRING }, //token token: { type: Sequelize.UUID }, create_time: { type: Sequelize.DATE, defaultValue: Sequelize.NOW } }, { //使用自定義表名 freezeTableName: true, //去掉默認的添加時間和更新時間 timestamps: false, indexes:[ //普通索引,默認BTREE { unique: true, fields: ['pid'] }, ] }); //同步:沒有就新建,有就不變 // users.sync(); //先刪除後同步 users.sync({ force: true });
前段將了對象的建立,裏面用到了對象的各類類型。這裏再介紹一下類型的具體使用方式。
Sequelize.STRING //字符串,長度默認255,VARCHAR(255) Sequelize.STRING(1234) //設定長度的字符串,VARCHAR(1234) Sequelize.STRING.BINARY //定義類型VARCHAR BINARY Sequelize.TEXT //長字符串,文本 TEXT Sequelize.TEXT('tiny') //小文本字符串,TINYTEXT Sequelize.INTEGER //int數字,int Sequelize.BIGINT //更大的數字,BIGINT Sequelize.BIGINT(11) //設定長度的數字,BIGINT(11) Sequelize.FLOAT //浮點類型,FLOAT Sequelize.FLOAT(11) //設定長度的浮點,FLOAT(11) Sequelize.FLOAT(11, 12) //設定長度和小數位數的浮點,FLOAT(11,12) Sequelize.REAL //REAL PostgreSQL only. Sequelize.REAL(11) // REAL(11) PostgreSQL only. Sequelize.REAL(11, 12) // REAL(11,12) PostgreSQL only. Sequelize.DOUBLE // DOUBLE Sequelize.DOUBLE(11) // DOUBLE(11) Sequelize.DOUBLE(11, 12) // DOUBLE(11,12) Sequelize.DECIMAL // DECIMAL Sequelize.DECIMAL(10, 2) // DECIMAL(10,2) Sequelize.DATE // 日期類型,DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres Sequelize.DATE(6) // mysql 5.6.4+支持,分秒精度爲6位 Sequelize.DATEONLY // 僅日期部分 Sequelize.BOOLEAN // int類型,長度爲1,TINYINT(1) Sequelize.ENUM('value 1', 'value 2') // 枚舉類型 Sequelize.ARRAY(Sequelize.TEXT) //PostgreSQL only. Sequelize.ARRAY(Sequelize.ENUM) // PostgreSQL only. Sequelize.JSON // JSON column. PostgreSQL, SQLite and MySQL only. Sequelize.JSONB // JSONB column. PostgreSQL only. Sequelize.BLOB // BLOB (bytea for PostgreSQL) Sequelize.BLOB('tiny') // TINYBLOB (bytea for PostgreSQL. Other options are medium and long) Sequelize.UUID // PostgreSQL和SQLite的數據類型是UUID, MySQL是CHAR(36)類型 Sequelize.CIDR // PostgreSQL中的CIDR類型 Sequelize.INET // PostgreSQL中的INET類型 Sequelize.MACADDR // PostgreSQL中的MACADDR類型 Sequelize.RANGE(Sequelize.INTEGER) //PostgreSQL only. Sequelize.RANGE(Sequelize.BIGINT) // PostgreSQL only. Sequelize.RANGE(Sequelize.DATE) //PostgreSQL only. Sequelize.RANGE(Sequelize.DATEONLY) //PostgreSQL only. Sequelize.RANGE(Sequelize.DECIMAL) //PostgreSQL only. Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // PostgreSQL only. Sequelize.GEOMETRY //PostgreSQL (with PostGIS) or MySQL only. Sequelize.GEOMETRY('POINT') // PostgreSQL (with PostGIS) or MySQL only. Sequelize.GEOMETRY('POINT', 4326)// PostgreSQL (with PostGIS) or MySQL only.
上面能夠看到使用validate字段去驗證字段的值是否符合標準,這樣就能夠在入庫以前就能知道數據是否符合規則。不然貿然將陌生的數據存入數據庫就好像將陌生人帶到家裏同樣,是否安全全靠緣分啊。
Sequelize內置支持的驗證仍是很是的多的,若是這些都不滿意,還能夠本身定義一個。
validate: { is: ["^[a-z]+$",'i'], // 全匹配字母 is: /^[a-z]+$/i, // 全匹配字母,用規則表達式寫法 not: ["[a-z]",'i'], // 不能包含字母 isEmail: true, // 檢查郵件格式 isUrl: true, // 是不是合法網址 isIP: true, // 是不是合法IP地址 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], // 長度範圍 isUUID: 4, // 是不是合法 uuids 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('請輸入偶數!') } }
Sequelize的API基本覆蓋了經常使用的使用方式,其中單表查詢經常使用的有一下幾種。複雜的能夠參考更多的API。
查詢用的參數廣泛通用,只有部分API的有特殊參數。這裏展現一次經常使用參數,下面就略過了。
let list = await model.findAll({ where:{ id:{$gt:10},//id大於10的 name:"test" //name等於test }, order:[ "id", //根據id排序 ["id","desc"]//根據id倒序 ], limit:10,//返回個數 offset:20,//起始位置,跳過數量 attributes:["attr1","attr2"], //返回的字段 }); //select attr1,attr2 from model where ......
這裏默認數據的主鍵是id,查詢的時候直接經過id查詢數據。這裏推薦在新建數據庫的時候能夠添加id做爲惟一主鍵。
let model = await model.findById(12); //select a,b,c from model where id=12;
根據條件查詢記錄,這裏的條件必定要填寫,否則就是返回第一條數據了。
let model = await model.findOne({ where:{id:12} }); //select a,b,c from model where id=12;
分頁查詢恐怕是另一個經常使用方法了。任何一個列表都有須要分頁的時候。
這個方法會同時執行2跳語句。
let data = await model.findAndCount({ limit:10,//每頁10條 offset:0*10,//第x頁*每頁個數 where:{} }); let list = data.rows; let count = data.count; //select count(*) from model where ...; //select a,b,c from model where .... limit 0,10;
添加就很是的自在了。簡單的只須要傳入model對象便可。這裏要保證model對象的屬性和字段名要一致。若是不一致就會出錯。也能夠傳入配置參數來增長條件等。
let model= { name:"test", token:"adwadfv2324" } await model.create(model); //insert into model (name,token) values("test","adwadfv2324");
opts.default 默認值對象
這個方法首先會查詢數據庫,若是沒有結果就會返回參數中的default對象。這個比較適合返回默認對象之類的場景。
這個方法用到的狀況也比較多。一般用於自動建立不存在的數據。直接就返回了默認值。
根據主鍵或者惟一約束鍵匹配
經常使用於編輯的時候添加或者更新統一操做。
就是最經常使用的更新方法,能夠傳入要更新的model對象,同時用配置參數有條件的區別要更新的對象。
刪除有2種狀況,一種是物理刪除。刪除就從表中不存在了。另一種就是設置paranoid,這個是虛擬刪除,默認一個字段表示數據是否刪除,查詢的時候去掉這個條件便可查詢到刪除的數據。
恢復多個實例,當啓用paranoid時就可使用這個方法將曾今刪除的數據恢復了。
Sequelize中的事務比較簡單。可是若是有多個事務的話寫出來的代碼會很是的難看。這也算是Sequelize優化的比較差的地方了。
須要記得transaction參數要一致傳遞就能夠了。其餘就是一個正常的Promise調用。
//調用Sequelize初始化以後的sequelize對象 return sequelize.transaction(function (t) { //返回最終的Promise return User.create({ firstName: 'Abraham', lastName: 'Lincoln' }, {transaction: t}).then(function (user) { return user.setShooter({ firstName: 'John', lastName: 'Boothe' }, {transaction: t}); }); }).then(function (result) { //主動調用commit提交結果 return t.commit(); }).catch(function (err) { //主動回滾操做 return t.rollback(); });
外鍵可能算是Sequelize中的一個難點了。這裏涉及的東西稍微多一點,咱們來慢慢捋一遍。
外鍵的定製做用----三種約束模式:
在Sequelize中使用外鍵須要提早檢查一下下面的這些選項,裏面有一條出錯就會致使設置失敗。
默認狀況下,主鍵使用的是主表的id字段,外鍵是使用的按照table+字段的方式創建的外鍵。通常狀況下須要手動指定。
//主表指定關係 test1.hasMany(test2, { foreignKey: "pid",//外鍵名稱 }); //子表指定關係 test2.belongsTo(test1, { foreignKey: "pid",//外鍵名稱 });
默認就會在子表中添加一條外鍵記錄,指向的就是主表的id。通常狀況下這樣就可以知足正常的使用了。好比一個主表記錄商品信息,一個子表記錄多個評論消息。
若是主表使用的主鍵id並不能知足正常的使用,還能夠指定某一個固定的字段做爲主表中的約束關係。
tips:主表中若是不是使用id做爲主要關係,自定義的字段必須添加索引等條件,做爲依賴中的關係。
test1.hasMany(test2, { foreignKey: "pid",//外鍵字段名 sourceKey: "pid",//主鍵字段名 }); test2.belongsTo(test1, { foreignKey: "pid",//關聯名 targetKey:"pid"//自定義外鍵字段 }); //等待主鍵創建成功再創建子表的外鍵關係 setTimeout(() => { test2.sync({ force: true }); }, 2500);
實際使用的時候我仍是傾向於這種關係。即表中關係已定的狀況下僅僅指定外鍵關係。同步的時候僅僅同步表內容,不一樣步這個外鍵關係。
真正的創建可使用手動建表的時候添加。或者也能夠在自動建表結束後異步再執行一次外鍵關係的添加。
test1.hasMany(test2, { foreignKey: "pid", sourceKey: "pid", constraints: false //不一樣步創建外鍵關係 }); test2.belongsTo(test1, { foreignKey: "pid", targetKey:"pid", constraints: false //不一樣步創建外鍵關係 });
實際的操做部分你們能夠看github中的test.js。github地址
Sequelize在查詢結果返回以後會返回一個它自定義的對象。這個對象是支持繼續操做的,其中具體的值存放在datavalues中。不過能夠放心的是在轉化爲字符串的時候是不會帶有任何Sequelize的屬性的。
//根據條件查詢一條數據 let model = await test1.findOne({ where:{ id:5, name:"test" } }); //修改其中的name字段的值 model.name="更新"; //保存,會自動update數據庫中的值 model.save();
正常的使用過程當中不多會說只須要查詢一個表就能結果問題的。這裏再說一下2個表查詢的時候是怎麼使用的。
這裏的查詢默認已經作好了外鍵的的關係。不過在使用的時候不作也是能夠的,就是在查詢的時候性能稍微很差而已。
//查詢主表list的數據 //一條list中的數據對應多條item中的數據 let data = await models.List.findAll({ where:{id:5},//條件,這裏jiashe只需查詢一條 include: [{ model: models.Item, as:"items",//返回的對象修改爲一個固定的名稱 }] }); let list1=data[0];//返回的第一條數據就是要查詢的數據 let list2=list1.items;//返回子表數據,items是自定義的名稱
上面的介紹已經解決了大多數狀況下的查詢等操做。並且我也相信,真的遇到了瓶頸,解決方案極可能也並非在Sequelize方面,或者說主要不是Sequelize的問題。好比大數據量的時候分表操做,就涉及到了更多的知識點。
nodejs在作後端方面還處於發展階段。但願有更多的前端可以接觸並瞭解它。不只僅在開發過程當中對本身是一個加強,在長期的職業規劃中也是一個很好的加強本身的武器。