Nodejs之ORM框架

概述

寫這篇blog的原因,想找個node的ORM框架用用,確很難找到一篇對比分析這些ORM框架的文章,唯一找到了一篇,居然是通過star數(shù)來論英雄,我覺著很難服眾,于是就找?guī)讉€看看。后來又不想分析,因?yàn)槲野l(fā)現(xiàn)node這種野蠻生長,滋生這些ORM輪子比比皆是,遠(yuǎn)比我想象的多;后來又覺著可以寫,作為一個java出身業(yè)余研究node的就想通過java的ORM框架來洞悉node這群ORM框架的是非曲直,于是挑了幾個框架小扯一篇。

ORM框架

ORM框架:Object Relational Mapping,對象-關(guān)系-映射,所以說ORM框架就是用面向?qū)ο蟮姆绞胶湍壳暗年P(guān)系型數(shù)據(jù)庫做匹配,java開發(fā)者目前主流的hibernate、mybatis很熟悉了,JDBC原始驅(qū)動的方式想必也不在成為主流了。下面介紹幾款node的ORM框架,介紹之前先介紹ORM的兩種模式:

Active Record 模式:活動記錄模式,領(lǐng)域模型模式一個模型類對應(yīng)關(guān)系型數(shù)據(jù)庫中的一個表,模型類的一個實(shí)例對應(yīng)表中的一行記錄。這個不難理解,比較簡單,但是不夠靈活,再看另一種模式,比較一下

Data Mapper 模式:數(shù)據(jù)映射模式,領(lǐng)域模型對象和數(shù)據(jù)表是松耦合關(guān)系,只進(jìn)行業(yè)務(wù)邏輯的處理,和數(shù)據(jù)層解耦。需要一個實(shí)體管理器來將模型和持久化層做對應(yīng),這樣一來,靈活性就高,當(dāng)然復(fù)雜性也增加了。

所以說,Data Mapper模式對業(yè)務(wù)代碼干預(yù)少,Active Record模式直接在對象上CRUD,代碼編寫也更方便,這就像hibernate和mybatis兩種框架,如果想深入研究,可以了解一下貧血與充血領(lǐng)域?qū)ο蟮钠胶?/a>。

有這么一句話很認(rèn)同,ActiveRecord更加適合快速開發(fā)成型的短期簡單項目,而DataMapper更加適合長線開發(fā),保持業(yè)務(wù)邏輯與數(shù)據(jù)存儲獨(dú)立的復(fù)雜項目。除此之外,技術(shù)選型還要考慮其他因素,比如項目歷史背景等等。

TypeORM

TypeORM 是一個 ORM 框架,詳細(xì)介紹見 TypeORM 官方介紹,TypeORM 也借鑒了hibernate,所以你會發(fā)現(xiàn)它特別熟悉,尤其是裝飾類的方式。

閑話少說,直接用CLI 命令快速構(gòu)建項目

npm install typeorm -g

創(chuàng)建項目

typeorm init --name MyProject --database mysql

name 是項目的名稱,database 是將使用的數(shù)據(jù)庫,TypeORM 支持多種數(shù)據(jù)庫。

生成文檔結(jié)構(gòu)

MyProject
├── src              // TypeScript 代碼
│   ├── entity       // 存儲實(shí)體(數(shù)據(jù)庫模型)的位置
│   │   └── User.ts  // 示例 entity
│   ├── migration    // 存儲遷移的目錄
│   └── index.ts     // 程序執(zhí)行主文件
├── .gitignore       // gitignore文件
├── ormconfig.json   // ORM和數(shù)據(jù)庫連接配置
├── package.json     // node module 依賴
├── README.md        // 簡單的 readme 文件
└── tsconfig.json    // TypeScript 編譯選項

修改 ormconfig.json 數(shù)據(jù)庫配置文件,直接運(yùn)行就可以了

npm start

看一下實(shí)體model,user類

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
?    @PrimaryGeneratedColumn()
?    id: number;

?    @Column()
?    firstName: string;

?    @Column()
?    lastName: string;

?    @Column()
?    age: number;
}

CRUD操作:邏輯層

import "reflect-metadata";
import {createConnection} from "typeorm";
import {User} from "./entity/User";

createConnection().then(async connection => {
?    console.log("Inserting a new user into the database...");
?    const user = new User();
?    user.firstName = "Timber";
?    user.lastName = "Saw";
?    user.age = 25;
?    await connection.manager.save(user);
?    console.log("Saved a new user with id: " + user.id);
?    console.log("Loading users from the database...");
?    const users = await connection.manager.find(User);
?    console.log("Loaded users: ", users);
?    console.log("Here you can setup and run express/koa/any other framework.");
}).catch(error => console.log(error));

所以,TypeORM的方式很像hibernate的方式,雖然es6中就已經(jīng)有裝飾器類似java的注解的功能了,但是還是和裝飾器有所區(qū)別,因?yàn)門ypeORM采用的是TypeScript 的方式,TypeScript 是 JavaScript 的一個超集,TypeScript 采用類型注解方式,雖然支持es6的標(biāo)準(zhǔn),但是有些語法還是需要了解,這也或多或少增加了一些選擇難度。

Sequelize

這個被star數(shù)最多了一個ORM框架,官方居然不給中文文檔,找個CLI命令快速構(gòu)建也沒有,也沒找到個合適輪子,只能自己搭了,也不是少了輪子就不能活了。不過Sequelize的官網(wǎng)文檔看著很順眼,不得不稱贊一下,需要注意的一點(diǎn)Sequelize v5版本發(fā)生了比較大的變化,這里我以最新版本v5版本為主,老版本可以自己看看下官方文檔。Sequelize v5

安裝npm包

$ npm install --save sequelize
$ npm install --save mysql2

數(shù)據(jù)庫的配置文件config.js

module.exports = {
?    database: {
?        dbName: 'TEST',
?        host: 'localhost',
?        port: 3306,
?        user: 'root',
?        password: '123456'
?    }
}

構(gòu)建數(shù)據(jù)庫訪問公共文件db.js

const Sequelize = require('sequelize')
const {
?    dbName,
?    host,
?    port,
?    user,
?    password
} = require('../config').database

const sequelize = new Sequelize(dbName, user, password, {
?    dialect: 'mysql',
?    host,
?    port,
?    logging: true,
?    timezone: '+08:00',
?    define: {
?        // create_time && update_time
?        timestamps: true,
?        // delete_time
?        paranoid: true,
?        createdAt: 'created_at',
?        updatedAt: 'updated_at',
?        deletedAt: 'deleted_at',
?        // 把駝峰命名轉(zhuǎn)換為下劃線
?        underscored: true,
?        scopes: {
?            bh: {
?                attributes: {
?                    exclude: ['password', 'updated_at', 'deleted_at', 'created_at']
?                }
?            },
?            iv: {
?                attributes: {
?                    exclude: ['content', 'password', 'updated_at', 'deleted_at']
?                }
?            }
?        }
?    }
})
// 創(chuàng)建模型
sequelize.sync({
?    force: false
})
module.exports = {
?    sequelize
}

model

const {Sequelize, Model} = require('sequelize')
const {db} = require('../../db')

class User extends Model {}
User.init({
    // attributes
    firstName: {
        type: Sequelize.STRING,
        allowNull: false
    },
    lastName: {
        type: Sequelize.STRING
        // allowNull defaults to true
    }
}, {
    db,
    modelName: 'user'
    // options
});

還有一種寫法,兼容老版本,不推薦

const User = db.define('user', {
    // attributes
    firstName: {
        type: Sequelize.STRING,
        allowNull: false
    },
    lastName: {
        type: Sequelize.STRING
        // allowNull defaults to true
    }
}, {
    // options
});

這種實(shí)際上是sequelize.define內(nèi)部調(diào)用了model.init,但是老版本是沒有第一種寫法的。

此外需要知道的是,sequelize還默認(rèn)為每個模型定義字段id(主鍵)、createdat和updatedat,也可以進(jìn)行設(shè)置。

我們的db.js文件里面配置了,不自動創(chuàng)建模型,也就是自動創(chuàng)建數(shù)據(jù)表,關(guān)閉是有原因的,因?yàn)槿绻泶嬖跁萪rop然后再創(chuàng)建,這種操作本身就很可怕的

// 創(chuàng)建模型
sequelize.sync({
    force: false
})

單個模型也可以配置,切記這種操作很危險,尤其是生成環(huán)境

// Note: using `force: true` will drop the table if it already exists
User.sync({ force: true }).then(() => {
    // Now the `users` table in the database corresponds to the model definition
    return User.create({
        firstName: 'John',
        lastName: 'Hancock'
    });
});

CRUD操作:然后看一下邏輯層,就非常簡單了,直接使用ES7 async/await即可

// Find all users
User.findAll().then(users => {
    console.log("All users:", JSON.stringify(users, null, 4));
});
// Create a new user
User.create({ firstName: "Jane", lastName: "Doe" }).then(jane => {
    console.log("Jane's auto-generated ID:", jane.id);
});
// Delete everyone named "Jane"
User.destroy({
    where: {
        firstName: "Jane"
    }
}).then(() => {
    console.log("Done");
});
// Change everyone without a last name to "Doe"
User.update({ lastName: "Doe" }, {
    where: {
        lastName: null
    }
}).then(() => {
    console.log("Done");
});

由此來看,沒有typeorm裝飾類的方式看著順眼,但是整體構(gòu)造也容易上手,操作簡單,容易理解,看官網(wǎng)文檔,功能覆蓋強(qiáng)大,typeorm用戶反饋使用問題比Sequelize要多,后期用到再做比較。

ORM2

ORM2貌似沒有正了八經(jīng)的官網(wǎng),所以看起來就特別麻煩,但是可以看一下github介紹node-orm2,只支持四種數(shù)據(jù)庫MySQL、PostgreSQL、Amazon Redshift、SQLite,這個我沒寫demo,直接分析一下

安裝

npm install orm

數(shù)據(jù)庫連接

var orm = require("orm");
orm.connect("mysql://username:password@host/database", 
    function (err, db) {
    // ...里面一些參數(shù)不詳細(xì)寫了
});

model

var Person = db.define('person', {
    name: String,
    surname: String,
    age: String,
    male: boolean
}, {
    identityCache : true
});

CRUD操作

Person.create([
    {
        name: "John",
        surname: "Doe",
        age: 25,
        male: true
    },
    {
        name: "Liza",
        surname: "Kollan",
        age: 19,
        male: false
    }
], function (err, items) {
    // err - description of the error or null
    // items - array of inserted items
});
Person.get(1, function (err, John) {
    John.name = "Joe";
    John.surname = "Doe";
    John.save(function (err) {
        console.log("saved!");
    });//保存
    Person.find({ surname: "Doe" }).remove(function (err) {
      // Does gone..
    });//刪除
});
Person.find({
    name: "admin"})
    .limit(3)
    .offset(2)//跳過
    .only("name", "age")//返回字段
    .run(function(err, data) {
    
    });

所以,準(zhǔn)確應(yīng)該是node-orm2,寫法和sequelize類似,但是文檔確實(shí)不行,數(shù)據(jù)庫支持也少,很難想象后續(xù)的可維護(hù)性。

其它

  • bookshelf(這個用的也挺多)

  • persistencejs

  • waterline

  • mongoose

  • node-mysql

  • knex

    。。。

blog鏈接:https://dumplingbao.github.io/2019/08/29/node-orm/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,033評論 0 11
  • 這部分主要是開源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,572評論 0 2
  • 1.JVM 堆內(nèi)存和非堆內(nèi)存 堆和非堆內(nèi)存按照官方的說法:“Java 虛擬機(jī)具有一個堆(Heap),堆是運(yùn)行時數(shù)據(jù)...
    yanzhu728閱讀 1,014評論 0 0
  • 背景 和同事一起有一個公司內(nèi)部平臺的項目,平臺需要對于用戶上傳的圖片,視頻等資源進(jìn)行管理和存儲。 在項目一期,由于...
    中v中閱讀 7,440評論 0 8
  • 如果你覺得Sequelize的文檔有點(diǎn)多、雜,不方便看,可以看看這篇。 在使用NodeJS來關(guān)系型操作數(shù)據(jù)庫時,為...
    布拉德皮蛋_qzy閱讀 2,549評論 2 22

友情鏈接更多精彩內(nèi)容