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

4.7/5 - (3 votes)

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

0 0 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

15 Comments
Inline Feedbacks
View all comments
Quyền
7 năm trước

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,

Quyền
7 năm trước

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

Quyền
7 năm trước

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.

Quyền
7 năm trước
Reply to  Phở Code

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

Quyền
7 năm trước

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

Quyền
7 năm trước

E đã sửa theo hướng dẫn của bác và đã chạy được,
Cảm ơn bác rất nhiều nhé! 🙂

Phương Nam
Phương Nam
7 năm trước

Cho mình hỏi tại sao lại cần

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

trong file routes/notes.js

vic
vic
7 năm trước

Cho mình hỏi:
Khi mình nhập thông tin và click submit, thì thông tin dc gui qua /notesave.
Mình thắc mắc là dựa vào đâu, logic nào mà phân này: models-sequelize/notes.js, lấy được dữ liệu từ form để save vao DB nhỉ?
Mình xem mà k hiểu nó dc kết nôi với nhau bằng cái gì.

Ad trả lời giúp mình với,
Thank,