NodeJS – Lưu trữ dữ liệu trên MySQL với Sequelize


Được đăng vào ngày 11/10/2016 | 11 bình luận
Đánh giá bài viết

Ngoài SQLite ra thì còn có rất nhiều các hệ quản trị cơ sở dữ liệu nổi tiếng và mạnh mẽ, và ứng với mỗi hệ quản trị CSDL thì lại có vài module được dùng để tương tác với CSDL đó từ Node. Trong số đó có một số module cung cấp các hàm ở level cao và cung cấp khả năng sử dụng chỉ một model cho nhiều CSDL khác nhau.

Module Sequelize (http://sequelize.com/) có thể kết nối tới 3 hệ quản trị cơ sở dữ liệu là SQLite3, MySQL và PostgreSQL. Các thao tác với CSDL sẽ được thực hiện theo ORM (Object Relation Mapping).

MySQL

MySQL là một hệ quản trị cơ sở dữ liệu SQL rất mạnh và miễn phí. Nếu bạn đã cài MySQL trong máy rồi thì bạn chỉ cần tạo một CSDL có tên là notes (không tạo bảng) rồi có thể bỏ qua phần này và kéo xuống phần tạo model phía dưới. Nếu chưa thì bạn lên trang chủ của MySQL và tải bản Community về tại địa chỉ http://dev.mysql.com/downloads/. MySQL là một hệ quản trị cơ sở dữ liệu, phần lõi chịu trách nhiệm lưu trữ, đọc/ghi dữ liệu… tất cả đều được thực hiện qua dòng lệnh, do đó nếu muốn bạn có thể tải thêm phần mềm MySQL Workbench về, đây là phần mềm cho phép bạn làm việc với MySQL thông qua giao diện GUI, nghĩa là thay vì dùng dòng lệnh thì bạn chỉ cần thực hiện các thao tác click chuột đơn giản.

capture

Quá trình cài đặt MySQL rất đơn giản, bạn chỉ cần lưu ý một số thứ như tên tải khoản truy cập, thường là root, mật khẩu, chế độ cài (Developement, Deploy…)…

Sau khi đã cài đặt xong, chúng ta có thẻ bắt đầu truy cập server của MySQL để tạo CSDL. Ở đây mình dùng MySQL Workbench cho nhanh. Khi mở MySQL Workbench lên, bạn sẽ phải tạo những cái gọi là Connections, nếu bạn chưa từng làm việc với Connection thì có thể hình dung đây giống như là các nút để chúng ta thực hiện kết nối tới server vậy. Để tạo một connection thì chúng ta click vào nút dấu + phía trên bên trái màn hình, sau đó điền các thông tin như tên connection, host là 127.0.0.1, port là 3306, username là tên đăng nhập mà bạn tạo khi cài MySQL, vậy là xong.

capture

Sau đó bạn click Ok để tạo, bây giờ bên màn hình sẽ xuất hiện một nút Connection và chúng ta có thể click vào để đăng nhập vào server. Khi đăng nhập thì MySQL Workbench sẽ hỏi password, bạn nhập password lúc cài đặt MySQL là xong.

capture

Tiếp theo chúng ta phải tạo một CSDL để lưu các ghi chú cho ứng dụng Notes. Bạn click vào nút có hình trụ với dấu + để tạo, trong này chúng ta cần cung cấp nên CSDL và collation là tập kí tự, chúng ta sẽ chọn là uttf8_unicode_ciỞ đây chúng ta không tạo bảng vì module Sequelize sẽ tự động tạo các bảng đó cho chúng ta. Vậy là quá trình tạo CSDL đã xong, bây giờ chúng ta sẽ tạo mdel.

capture

Tạo model

Chúng ta tạo một thư mục mới có tên models-sequelize trong thư mục gốc của project, trong thư mục này chúng ta tạo một file có tên notes.js với nội dung như sau:

var Sequelize = require('sequelize');
var Note = undefined;
module.exports.connect = function(params, callback) {
    var sequlz = new Sequelize(
        params.dbname, 
        params.username, 
        params.password,
        params.params
    );
    Note = sequlz.define('Note', {
        notekey: {
            type: Sequelize.STRING,
            primaryKey: true,
            unique: true
        },
        title: Sequelize.STRING,
        body: Sequelize.TEXT
    });
    Note.sync().then(function() {
        callback();
    }).error(function(err) {
        callback(err);
    });
}
exports.disconnect = function(callback) {
    callback();
}

exports.create = function(key, title, body, callback) {
    Note.create({
            notekey: key,
            title: title,
            body: body
        }).then(function(note) {
            callback();
        }).error(function(err) {
            callback(err);
    });
}

exports.update = function(key, title, body, callback) {
    Note.find({ where:{ notekey: key} }).then(function(note) {
        if(!note) {
            callback(new Error("No note found for key " + key));
        } else {
            note.updateAttributes({
                title: title,
                body: body
            }).then(function() { 
                callback();
            }).error(function(err) {
                callback(err);
            });
        }
    }).error(function(err) {
        callback(err);
    });
}

exports.read = function(key, callback) {
    Note.find({ where:{ notekey: key} }).then(function(note) {
        if(!note) {
            callback("Nothing found for " + key);
        } else {
            callback(null, {
                notekey: note.notekey,
                title: note.title,
                body: note.body
            });
        }
    });
}

exports.destroy = function(key, callback) {
    Note.find({ where:{ notekey: key} }).then(function(note) {
        note.destroy().then(function() {
        callback();
    }).error(function(err) {
        callback(err);
    });
    });
}

exports.titles = function(callback) { 
    Note.findAll().then(function(notes) { 
        var noteList = []; 
        notes.forEach(function(note) { 
            noteList.push({
                key: note.notekey,
                title: note.title
            }); 
        });
        callback(null, noteList);
    });
}

Cũng giống như các phần trước, file này sẽ lưu trữ phần model của ứng dụng.

var Sequelize = require('sequelize');

Chúng ta sẽ cần dùng đến module sequelize.

var Note = undefined;
module.exports.connect = function(params, callback) {
    var sequlz = new Sequelize(
        params.dbname, 
        params.username, 
        params.password,
        params.params
    );
    Note = sequlz.define('Note', {
        notekey: {
            type: Sequelize.STRING,
            primaryKey: true,
            unique: true
        },
        title: Sequelize.STRING,
        body: Sequelize.TEXT
    });
    Note.sync().then(function() {
        callback();
    }).error(function(err) {
        callback(err);
    });
}
exports.disconnect = function(callback) {
    callback();
}

Ở đây hàm connect() không nhận vào một chuỗi thông thường như các phần trước mà sẽ nhận vào một tập các tham số, gồm có tên CSDL (dbname), tên đăng nhập (username), mật khẩu (password), địa chỉ MySQL server và tên loại server (nằm trong tham số params).

Như đã nói ở trên, chúng ta không tạo bảng một cách trực tiếp mà sequelize sẽ tạo các bảng đó cho chúng ta, bằng cách dùng phương thức define(), tham số đầu tiên là tên bảng, ở đây mình đặt là Note, sequelize sẽ tạo bảng với tên tương ứng và thêm kí tự vào sau cùng, tham số thứ hai là một đối tượng chứa các trường tương ứng trong bảng, ở đây bao gồm các trường notekey, titlebody giống như với các model khác, ngoài ra sequelize sẽ tự động chèn thêm 2 trường nữa là createdAtupdatedAt có kiểu dữ liệu datetime để chúng ta có thể biết được các bản ghi được tạo khi nào và được sửa đổi khi nào. Phương thức sync().then() sẽ thực hiện việc tạo thật sự trên CSDL.

exports.create = function(key, title, body, callback) {
    Note.create({
            notekey: key,
            title: title,
            body: body
        }).then(function(note) {
            callback();
        }).error(function(err) {
            callback(err);
    });
}

Hàm create() sẽ thực hiện tạo các bản ghi mới. Để chèn một bản ghi thì chúng ta chỉ cần gọi phương thức Sequelize.create() và đưa các tham số tương ứng vào, sau đó gọi hàm then(), ngoài ra hàm còn có hàm error() có chức năng kiểm tra xem có lỗi hay không.

exports.update = function(key, title, body, callback) {
    Note.find({ where:{ notekey: key} }).then(function(note) {
        if(!note) {
            callback(new Error("No note found for key " + key));
        } else {
            note.updateAttributes({
                title: title,
                body: body
            }).then(function() { 
                callback();
            }).error(function(err) {
                callback(err);
            });
        }
    }).error(function(err) {
        callback(err);
    });
}

Hàm update() sẽ cập nhật các ghi chú. Ở đây thao tác cập nhật hơi khác chút xíu. Đầu tiên chúng ta phải tìm bản ghi đó bằng phương thức find(), phương thức này nhận vào câu truy vấn, bạn có thể xem cấu trúc truy vấn theo đoạn code trên. Sau đó gọi hàm then(), hàm này nhận vào một hàm callback và hàm callback này nhận vào dữ liệu trả về. Chúng ta có thể kiểm tra xem dữ liệu trả về này có rỗng hay không, nếu không rỗng thì chúng ta thực hiện cập nhật mới dữ liệu này bằng cách gọi hàm updateAttributes().

exports.read = function(key, callback) {
    Note.find({ where:{ notekey: key} }).then(function(note) {
        if(!note) {
            callback("Nothing found for " + key);
        } else {
            callback(null, {
                notekey: note.notekey,
                title: note.title,
                body: note.body
            });
        }
    });
}

Hàm read() được dùng để đọc một ghi chú và cũng tương tự như hàm update().

exports.destroy = function(key, callback) {
    Note.find({ where:{ notekey: key} }).then(function(note) {
        note.destroy().then(function() {
        callback();
    }).error(function(err) {
        callback(err);
    });
    });
}

Hàm destroy() được dùng để xóa một ghi chú, tương tự chúng ta cũng tìm một ghi chú trước rồi nếu thấy thì chúng ta xóa ghi chú đó bằng hàm destroy().

exports.titles = function(callback) { 
    Note.findAll().then(function(notes) { 
        var noteList = []; 
        notes.forEach(function(note) { 
            noteList.push({
                key: note.notekey,
                title: note.title
            }); 
        });
        callback(null, noteList);
    });
}

Hàm titles() sẽ liệt kê toàn bộ ghi chú có trong CSDL, để lấy về toàn bộ ghi chú thì chúng ta dùng hàm findAll().

Cấu hình app.js

Trong file app.js chúng ta sửa lại như sau:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');
var notes = require('./routes/notes');
//var models = require('./models-fs/notes');
//var models = require('./models-sqlite3/notes');
//var models = require('./models-mongoose/notes');
var models = require('./models-sequelize/notes');
models.connect({
    dbname: "notes",
    username: "root",
    password: "<mật khẩu>",
    params: {
        host: "127.0.0.1",
        dialect: "mysql"
    }
},
function(err) {
    if(err)
        throw err;
});
notes.configure(models);
routes.configure(models);
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes.index);
app.use('/users', users);
app.get('/noteadd', notes.add);
app.post('/notesave', notes.save);
app.use('/noteview', notes.view);
app.use('/noteedit', notes.edit);
app.use('/notedestroy', notes.destroy);
app.post('/notedodestroy', notes.dodestroy);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;

Ở đây chúng ta lưu ý là tham số để kết nối tới CSDL là một tập các tham số như địa chỉ, username, mật khẩu đăng nhập… bạn điền cho chính xác là được.

Tiếp theo chúng ta khai báo module sequelize trong file package.json như sau:

{
    "name": "notes",
    "version": "0.0.0",
    "private": true,
    "scripts": {
    "start": "node ./bin/www"
    },
    "dependencies": {
        "body-parser": "~1.15.1",
        "cookie-parser": "~1.4.3",
        "debug": "~2.2.0",
        "ejs": "~2.4.1",
        "express": "~4.13.4",
        "morgan": "~1.7.0",
        "serve-favicon": "~2.3.0",
        "async": "*",
        "sqlite3": "*",
        "mongoose": "*",
        "sequelize": "*"
    }
}

Cuối cùng chúng ta chạy lệnh npm install để cài module này rồi chạy npm start để chạy server là xong.

Cứ mỗi lần chạy server, sequelize sẽ tạo mới bảng nếu bảng chưa tồn tại.

C:\NodeJS\notes>npm start

> notes@0.0.0 start C:\NodeJS\notes
> node ./bin/www

  notes:server Listening on port 3000 + 0ms
Executing (default): CREATE TABLE IF NOT EXISTS 'Notes' ('notekey' VARCHAR(255)
UNIQUE , 'title' VARCHAR(255), 'body' TEXT, 'createdAt' DATETIME NOT NULL, 'updatedAt'
DATETIME NOT NULL, UNIQUE 'Notes_notekey_unique' ('notekey'), PRIMARY KEY ('notekey')) 
ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM 'Notes'

Bạn có thể vào MySQL Workbench để kiểm tra dữ liệu.

capture

capture







Bình luận (11)

  1. Quyền

    Hi chủ thớt,
    Cho mình hỏi, mình thêm khúc
    notes.configure(models);
    routes.configure(models);
    trong file app.js thì báo lỗi :”
    notes.configure(models);
    ^
    TypeError: notes.configure is not a function

    Thớt cho mình hỏi mình có thiếu gì ko he? Thanks,

    1. Phở Code Admin

      Bạn kiểm tra trong file routes/notes.js xem đã code hàm configure() chưa, đoạn code đó như thế này:

      exports.configure = function(params) {
          notes = params;
      }
      

      Ngoài ra trước đó bạn phải có dòng require() đến file đó nữa:

      var notes = require('./routes/notes');
      
  2. Quyền

    Hi bác,
    Sau khi thêm đoạn code như bác nói e đã lưu được xuống database
    exports.configure = function(params) {
    notes = params;
    }
    Nhưng edit và delete thì vẫn chưa được, Home page vẫn ko thấy danh sách notes lấy từ DB lên,
    bác có thể cho e xin đoạn code cho routes.configure(models) không bác? của em báo lỗi
    “TypeError: routes.configure is not a function”
    Cảm ơn bác rất nhiều

    1. Phở Code Admin

      Ở đây đối tượng routes trong file app.js là lấy từ file routes/index.js đó bạn, đoạn code của nó dạng như thế này:

      var express = require('express');
      var router = express.Router();
      var notes = undefined;
      exports.configure = function(params) {
      	notes = params;
      }
      exports.index = router.get('/', function(req, res, next) {
          ...
      });
      

      Chủ yếu là trong file này phải có đoạn code exports.configure.

  3. Quyền

    Hi bác, cái file routes/index.js của e có nội dung thế này, thì sửa như thế nào bác?
    Vì em sửa như bác nói bị báo lỗi cũ.

    var express = require(‘express’);
    var router = express.Router();
    var notes = require(‘../models/notes’);

    /* GET home page. */

    router.get(‘/’, function(req, res, next) {
    res.render(‘index’, { title: ‘Notes’, notes: notes });
    });

    module.exports = router;

    E cài theo hướng dẫn của bác,e ko biết sao mà nó ko giống nữa.

      1. Quyền

        bỏ đi nó báo lỗi này bác ơi :
        D:\xampp\htdocs\express\node_modules\express\lib\router\index.js:458
        throw new TypeError(‘Router.use() requires middleware function but got a ‘
        + gettype(fn));
        ^

        TypeError: Router.use() requires middleware function but got a Object

  4. Quyền

    bỏ đi nó báo lỗi này bác ơi :
    D:\xampp\htdocs\express\node_modules\express\lib\router\index.js:458
    throw new TypeError(‘Router.use() requires middleware function but got a ‘
    + gettype(fn));
    ^

    TypeError: Router.use() requires middleware function but got a Object

  5. Phở Code Admin

    Đây là đoạn code trong file routes/index.js của mình:

    var express = require('express');
    var router = express.Router();
    var notes = undefined;
    exports.configure = function(params) {
    	notes = params;
    }
    /* GET home page. */
     
    exports.index = router.get('/', function(req, res, next) {	
    	notes.titles(function(err, titles) {		
    		if(err) {			
    			res.render('showerror', {
    				title: "Could not retrieve note keys from data store",
    				error: err,
    				user: req.user ? req.user : undefined
    			});
    		} else {						
    			res.render('index', { 
    				title: "Notes", 
    				notes: titles,
    				user: req.user ? req.user : undefined
    			});			
    		}
    	});
    });
    

    Bạn lưu ý ở bên file app.js thì dòng routing đển ‘/’ là:

    app.use('/', routes.index);
    

    Chứ không phải là:

    app.use('/', routes);
    

Trả lời


Lưu ý: bọc code trong cặp thẻ [code language="x"][/code] để highlight code.


Ví dụ:


[code language="cpp"]


    std::cout << "Hello world";


[/code]



Các ngôn ngữ được hỗ trợ gồm: actionscript3, bash, clojure, coldfusion, cpp, csharp, css, delphi, diff, erlang, fsharp, go, groovy, html, java, javafx, javascript, latex, matlab, objc, perl, php, powershell, python, r, ruby, scala, sql, text, vb, xml.

Thư điện tử của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *