[cookbook] Sequelize #1 개념과 CRUD (Nodejs에서도 ORM 사용해보자)

Sequelize #1 Introduction

안녕하세요. 이번 시간에는 Node.js 에서도 Rails와 비슷한(ㅋ) ORM 모듈인 sequelize에 대해서 알아보겠습니다.

orm

Installation

npm install sequelize

Usage

basic

var sequelize = new Sequelize('database', 'username'[, 'password'])

more

var sequelize = new Sequelize('database', 'username', 'password', {
  // host 지정
  host: "my.server.tld",

  // port 지정
  port: 12345
})

// All options at once:
var sequelize = new Sequelize('database', 'username', 'password', {
  // custom host; default: localhost
  host: 'my.server.tld',

  // custom port; default: 3306
  port: 12345,

  // disable logging; default: console.log
  logging: false,

  // max concurrent database requests; default: 50
  maxConcurrentQueries: 100,

  // the sql dialect of the database
  // - default is 'mysql'
  // - currently supported: 'mysql', 'sqlite', 'postgres'
  dialect: 'mysql',

  // the storage engine for sqlite
  // - default ':memory:'
  storage: 'path/to/database.sqlite',

  // specify options, which are used when sequelize.define is called
  // the following example is basically the same as:
  // sequelize.define(name, attributes, { timestamps: false })
  // so defining the timestamps for each model will be not necessary
  define: { timestamps: false },

  // similiar for sync: you can define this to always force sync for models
  sync: { force: true }

  // use pooling in order to reduce db connection overload and to increase speed
  // currently only for mysql
  pool: { maxConnections: 5, maxIdleTime: 30}
})

Sequelize Instance info

Sequelize 인스턴스 정보 한번 참고하시고요.

$ cat example1.js                             
var Sequelize = require('sequelize');                      
var sequelize = new Sequelize('database', 'user', 'password')
console.log(sequelize);                                    


$ node example1.js                             
{ options:                                               
   { dialect: 'mysql',                                   
     host: 'localhost',                                  
     port: 3306,                                         
     protocol: 'tcp',                                    
     define: {},                                         
     query: {},                                          
     sync: {},                                           
     logging: [Function],                                
     omitNull: false },                                  
config:                                                
   { database: 'database',                               
     username: 'user',                                   
     password: 'password',                                 
     host: 'localhost',                                  
     port: 3306,                                         
     pool: undefined,                                    
     protocol: 'tcp' },                                  
daoFactoryManager: { daos: [], sequelize: [Circular] },
  connectorManager:                                      
   { sequelize: [Circular],                              
     client: null,                                       
     config:                                             
      { database: 'database',                            
        username: 'root',                                
        password: 'password',                              
        host: 'localhost',                               
        port: 3306,                                      
        pool: undefined,                                 
        protocol: 'tcp' },                               
     disconnectTimeoutId: null,                          
     queue: [],                                          
     activeQueue: [],                                    
     maxConcurrentQueries: 50,                           
     poolCfg: undefined },                               
  importCache: {} }                                      

Models

Syntax

var ModelName = sequelize.define('ModelName #2', {
  field1: Sequelize.STRING,
  filed2: Sequelize.TEXT
});

Example

var Project = sequelize.define('Project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
})

var Task = sequelize.define('Task', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT,
  deadline: Sequelize.DATE
})

supported datatypes

model instance

model instance 정보 한번 참고하시고요.

sequelize $ cat example1.js

var sequelize = new Sequelize('database', 'user', 'password')

var Project = sequelize.define('Project', {                
  title: Sequelize.STRING,                                 
  description: Sequelize.TEXT                              
});                                                        
console.log(Project);                                      


sequelize $ node example1.js                               
{ options:                                                 
   { timestamps: true,                                     
     instanceMethods: {},                                  
     classMethods: {},                                     
     validate: {},                                         
     freezeTableName: false,                               
     underscored: false,                                   
     syncOnAssociation: true,                              
     paranoid: false,                                      
     omitNull: false },                                    
name: 'Project',                                         
  tableName: 'Projects',                                   
  rawAttributes:                                           
   { title: 'VARCHAR(255)',                                
     description: 'TEXT',                                  
     id:                                                   
      { type: 'INTEGER',                                   
        allowNull: false,                                  
        primaryKey: true,                                  
        autoIncrement: true },                             
     createdAt: { type: 'DATETIME', allowNull: false },    
     updatedAt: { type: 'DATETIME', allowNull: false } },  
  daoFactoryManager:                                       
   { daos: [ [Circular] ],                                 
     sequelize:                                            
      { options: [Object],                                 
        config: [Object],                                  
        daoFactoryManager: [Circular],                     
        connectorManager: [Object],                        
        importCache: {},                                   
        queryInterface: [Object] } },                      
  associations: {},                                        
  validate: {},                                            
  autoIncrementField: 'id' }                               

Validate

model 을 정의하고, INSERT 되기 전에 Validate 설정을 다양하게 할 수 있습니다.

var ValidateMe = sequelize.define('Foo', {
  foo: {
    type: Sequelize.STRING,

    //
    // validate property 정의!!
    // 옵션이 굉장히 많은데, 참고하세요.
    //
    validate: {
      is: ["[a-z]",'i'],        // will only allow letters
      not: ["[a-z]",'i'],       // will not allow letters
      isEmail: true,            // checks for email format (foo@bar.com)
      isUrl: true,              // checks for url format (http://foo.com)
      isIP: true,               // checks for IPv4 format (129.89.23.1)
      isAlpha: true,            // will only allow letters
      isAlphanumeric: true      // will only allow alphanumeric characters, so "_abc" will fail
      isNumeric: true           // will only allow numbers
      isInt: true,              // checks for valid integers
      isFloat: true,            // checks for valid floating point numbers
      isDecimal: true,          // checks for any numbers
      isLowercase: true,        // checks for lowercase
      isUppercase: true,        // checks for uppercase
      notNull: true,            // won't allow null
      isNull: true,             // only allows null
      notEmpty: true,           // don't allow empty strings
      equals: 'specific value', // only allow a specific value
      contains: 'foo',          // force specific substrings
      notIn: 'foo',             // force specific substrings
      isIn: "foo",              // force specific substrings
      notContains: 'bar',       // don't allow specific substrings
      len: [2,10],              // only allow values with length between 2 and 10
      isUUID: 4,                // only allow uuids
      isDate: true,             // only allow date strings
      isAfter: "2011-11-05",    // only allow date strings after a specific date
      isBefore: "2011-11-05",   // only allow date strings before a specific date
      max: 23,                  // only allow values <= 23
      min: 23,                  // only allow values >= 23
      isArray: true,            // only allow arrays
      isCreditCard: true,       // check for valid credit card numbers

      // custom validations are also possible:
      isEven: function(value) {
        if(parseInt(value) % 2 != 0)
          throw new Error('Only even values are allowed!')
      }
    }
  }
})

CREATE TABLE

자 이제 CRUD 시작해보죠. 먼저 테이블먼저 생성합니다.

Syntax

// 기존에 테이블에 존재하면 생성하지 않습니다.
ModelName.sync()

// 기존에 테이블에 존재하더라도, 지우고 다시 생성합니다.
ModelName.sync({force: true})

Example

// create my nice tables:
Project.sync() // will emit success or failure event
Task.sync() // will emit success or failure event

// force the creation!
Project.sync({force: true}) // this will drop the table first and re-create it afterwards

// drop the tables:
Project.drop() // will emit success or failure event
Task.drop() // will emit success or failure event

// event handling:
Project.[sync|drop]().success(function() {
  // ok ... everything is nice!
}).error(function(error) {
  // oooh, did you entered wrong database credentials?
})

sync 명령을 실행시킨 결과

sequelize $ node example1.js
Executing: CREATE TABLE IF NOT EXISTS `Projects` (`title` VARCHAR(255), `description` TEXT, `id` INTEG
ER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY
 (`id`)) ENGINE=InnoDB;                                                                               

mysql> use test               
Database changed              
mysql> show tables;           
+-----------------+           
| Tables_in_test  |           
+-----------------+           
| Projects        |           
+-----------------+           
7 rows in set (0.00 sec)      

mysql> desc Projects;                                                 
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| title       | varchar(255) | YES  |     | NULL    |                |
| description | text         | YES  |     | NULL    |                |
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| createdAt   | datetime     | NO   |     | NULL    |                |
| updatedAt   | datetime     | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)                                              

mysql packet

##                                                                                                   
T 127.0.0.1:49755 -> 127.0.0.1:3306 [AP]                                                             
.....CREATE TABLE IF NOT EXISTS `Projects` (`title` VARCHAR(255), `description` TEXT, `id` INTEGER NO
T NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`i
d`)) ENGINE=InnoDB;                                                                                  
##                                                                                                   
T 127.0.0.1:3306 -> 127.0.0.1:49755 [AP]                                                             
...........                                                                                          

INSERT (Building an instance) #1

model을 정의하고, 데이타를 입력하기 위해서는 build instance 를 생성해야 합니다. 귀찮게 생각마시고, ORM 을 익힌다고 생각하세요. ㅋ. 저도 지금 짜증납니다.

Syntax

//
// Project 변수는 위에서 지정한 ModelName 입니다. !!!
// Model 에서 지정한 field 값을 지정합니다.
//
var BI_ModelName = ModelName.build({                           
  title       : '제목',                          
  description : '설명'
});                                                     

//
// BI_ModelName 을 이제 테이블에 INSERT 합니다.
// save 메소드를 사용합니다.
//                                            
BI_ModelName.save().success(function() {                     
  console.log('create ====');                           
  console.log(arguments);                               
});

example

$ cat example.js

//
// Project 변수는 위에서 지정한 ModelName 입니다. !!!
// Model 에서 지정한 field 값을 지정합니다.
//
var project = Project.build({                           
  title       : '제목',                          
  description : '설명'
});                                                     

project.save().success(function() {                     
  console.log('create ====');                           
  console.log(arguments);                               
})


$ node example.js                                                                          
Executing: INSERT INTO `Projects` (`title`,`description`,`id`,`createdAt`,`updatedAt`) VALUES ('제목','설명',NULL,'2012-08-07 07:04:14','2012-08-07 07:04:14');                                                                                                                                                        

mysql data

데이타 확인합니다.

mysql> select * from Projects \G;                             
*************************** 1. row ***************************
      title: 제목                               
description: 설명          
         id: 1                                                
createdAt: 2012-08-07 07:04:14                              
  updatedAt: 2012-08-07 07:04:14                              
1 row in set (0.00 sec)                                       

ERROR:                                                        
No query specified                                            

mysql packet

##                                                                                                   
T 127.0.0.1:49791 -> 127.0.0.1:3306 [AP]                                                             
.....INSERT INTO `Projects` (`title`,`description`,`id`,`createdAt`,`updatedAt`) VALUES ('제목','내용',NULL,'2012-08-07 07:04:14','2012-08-07 07:04:14');
##                                                                                                   
T 127.0.0.1:3306 -> 127.0.0.1:49791 [AP]                                                             
...........                                                                                          

INSERT #2

build 에 데이타를 매핑시킨 후, save 메소드를 사용하는 방법 말고, ModelName에 대해서 create 메소드를 사용하여 바로 데이타를 입력할 수 있습니다.

Syntax

var ModelName = sequelize.define('ModelName', {                                   
  title         : Sequelize.STRING,                                                 
  description   : Sequelize.TEXT                                              
});    

ModelName.create({ title: 'foo', description: 'bar' }).success(function(task) { 
  console.log(arguments);                                                  
});                                                                                                                                            

mysql packet

##                                                                                                   
T 127.0.0.1:49821 -> 127.0.0.1:3306 [AP]                                                             
.....INSERT INTO `Projects` (`title`,`description`,`id`,`createdAt`,`updatedAt`) VALUES ('foo','bar', NULL,'2012-08-07 07:11:06','2012-08-07 07:11:06');                                                   
##                                                                                                  

READ

이제 테이블을 만들고, 데이타를 입력시켰으니, 데이타를 읽어와 봅시다.

Syntax

//
// id 값이 123 인것을 찾습니다.
//
ModelName.find(123).success(function(project) {
  // project will be an instance of Project and stores the content of the table entry
  // with id 123. if such an entry is not defined you will get null
})

//
// where property 사용하여
// title 필드에서 검색합니다.
    //
Project.find({ where: {title: 'aProject'} }).success(function(project) {
  // project will be the first entry of the Projects table with the title 'aProject' || null
})

//
// 원하는 필드만 추출하고,
// name 필드를 title 으로 alias 처리합니다.
//
Project.find({
  where: {title: 'aProject'},
  attributes: ['id', ['name', 'title']]
}).success(function(project) {
  // project will be the first entry of the Projects table with the title 'aProject' || null
  // project.title will contain the name of the project
})

//
// 전체 데이타를 가져옵니다.
//
Project.findAll().success(function(projects) {
  // projects will be an array of all Project instances
})

// also possible:
Project.all().success(function(projects) {
  // projects will be an array of all Project instances
})

//     
// 문자열 치환하여 등호 검색합니다.
//
Project.findAll({where: ["id > ?", 25]}).success(function(projects) {
  // projects will be an array of Projects having a greater id than 25
})

//
// IN 조건을 사용합니다.
//
Project.findAll({where: { id: [1,2,3] }}).success(function(projects) {
  // projects will be an array of Projects having the id 1, 2 or 3
  // this is actually doing an IN query
})

//
// where 조건의 매칭 조건을 아래와 같이도 사용할 수 있습니다.
// 
Project.findAll({where: "name = 'A Project'"}).success(function(projects) {
  // the difference between this and the usage of hashes (objects) is, that string usage
  // is not sql injection safe. so make sure you know what you are doing!
})

//
// order by 설정합니다.
//
Project.findAll({order: 'title DESC'})

//
// limit 설정합니다.
//
Project.findAll({limit: 10})

//
// limit offset 설정도 있습니다.
//
Project.findAll({offset: 10, limit: 2})

//
// 갯수는 몇개?
//    
Project.count().success(function(c) {
  console.log("There are " + c + " projects!")
})

//
// min, max 함수도 사용할 수 있네요. 
//    
Project.min('age').success(function(min) {
})
Project.max('age').success(function(max) {
})

instance 정보 확인하세요.

$ cat example1.js

Project.find(1).success(function(project) {                                          
  console.log(project);                                                              
})                                                                                   


$ node example1.js                                                

Executing: SELECT * FROM `Projects` WHERE `id`=1 LIMIT 1;                   
{ attributes: [ 'title', 'description', 'id', 'createdAt', 'updatedAt' ],   
  validators: {},                                                           
  __factory:                                                                
   { options:                                                               
      { timestamps: true,                                                   
        instanceMethods: {},                                                
        classMethods: {},                                                   
        validate: {},                                                       
        freezeTableName: false,                                             
        underscored: false,                                                 
        syncOnAssociation: true,                                            
        paranoid: false,                                                    
        omitNull: false,                                                    
        title: 'VARCHAR(255)',                                              
        description: 'TEXT',                                                
        id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',                  
        createdAt: 'DATETIME NOT NULL',                                     
        updatedAt: 'DATETIME NOT NULL',                                     
        hasPrimaryKeys: false },                                            
   name: 'Project',                                                       
     tableName: 'Projects',                                                 
     rawAttributes:                                                         
      { title: 'VARCHAR(255)',                                              
        description: 'TEXT',                                                
        id: [Object],                                                       
        createdAt: [Object],                                                
        updatedAt: [Object] },                                              
     daoFactoryManager: { daos: [Object], sequelize: [Object] },            
     associations: {},                                                      
     validate: {},                                                          
     autoIncrementField: 'id' },                                            
  __options:                                                                
   { underscored: false,                                                    
     hasPrimaryKeys: false,                                                 
     timestamps: true,                                   
     paranoid: false,                                    
     instanceMethods: {},                                
     classMethods: {},                                   
     validate: {},                                       
     freezeTableName: false,                             
     syncOnAssociation: true,                            
     omitNull: false,                                    
     title: 'VARCHAR(255)',                              
     description: 'TEXT',                                
     id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',  
     createdAt: 'DATETIME NOT NULL',                     
     updatedAt: 'DATETIME NOT NULL' },                   
  title: 'my awesome project',                           
  description: 'woot woot. this will make me a rich man',
  id: 1,                                                 
  createdAt: Tue Aug 07 2012 07:04:14 GMT+0900 (KST),    
  updatedAt: Tue Aug 07 2012 07:04:14 GMT+0900 (KST),    
  isNewRecord: false }                                        

Update

자 이제 다 왔네요. C, R 했으니, Update 입니다.

Syntax

// way 1
BI_ModelName.title = 'a very different title now'
BI_ModelName.save().success(function() {})

// way 2
BI_ModelName.updateAttributes({
  title: 'a very different title now'
}).success(function() {})

example

$ cat example1.js

//
// Model 로부터 데이타를 가져와야 합니다.
// -> 이 부분 좀 이해 안가는데, 이렇게 해야 합니다.
// -> build instance 에 updateAttributes 실행시키면 INSERT 되어집니다.
// 
ModelName.find(1).success(function(task) {
  //
  // 이때, 수정이 가능합니다.
  //
  task.updateAttributes({              
    description  : "hello",               
    title        : "a very different title now"
  }).success(function() {})            
});                                    



$ node example1.js                                                                          

Executing: SELECT * FROM `Projects` WHERE `id`=1 LIMIT 1;                                             
Executing: UPDATE `Projects` SET `title`='a very different title now',`description`='hello',`id`=1,`cr
eatedAt`='2012-08-07 07:04:14',`updatedAt`='2012-08-07 07:29:31' WHERE `id`=1                         

mysql packet

T 127.0.0.1:49995 -> 127.0.0.1:3306 [AP]                                                             
.....UPDATE `Projects` SET `title`='a very different title now',`description`='hello',`id`=1,`created
At`='2012-08-07 07:04:14',`updatedAt`='2012-08-07 07:29:31' WHERE `id`=1                             
##                                                                                                   
T 127.0.0.1:3306 -> 127.0.0.1:49995 [AP]                                                             
0..........(Rows matched: 1  Changed: 1  Warnings: 0                                                 
##                                                                                                   

mysql data

mysql> select * from Projects where id=1\G;                           
*************************** 1. row ***************************        
      title: a very different title now                               
description: hello                                                    
         id: 1                                                        
createdAt: 2012-08-07 07:04:14                                      
  updatedAt: 2012-08-07 07:29:31                                      
1 row in set (0.00 sec)                                               

ERROR:                                                                
No query specified                                                    

troubleshotting

무조건 find 해야한다. build하고 update 할 수 없을까?

// a 라는 인스턴스는 build, find 인스턴스와 동일합니다.
a = Task.build();                     

// id 값을 주면 update 될것으로 생각되었으나
// id 값은 받아주지 않네요. 
// lib/dao.js 소스 확인
a.updateAttributes({                  
  id:1,                               
  description:"hello",                
  title: 'a very different title now' 
}).success(function() {})             

Delete

다 왔네요. D 입니다.

Syntax

$ cat example1.js                                 
var BI_ModelName = ModelName.build({
  //
  // 원하는 필드명과 값을 지정합니다.
  //
  id: 1                        
});

//
// update와는 다르게 바로 delete 할 수 있습니다.                            
//
project.destroy();              



$ node example1.js                               

Executing: DELETE FROM `Projects` WHERE `id`=1 LIMIT 1     

Syntax #2

$ cat example2.js                               

//
// 물론, find 한후에도 가능합니다.
//
Task.find(1).success(function(task) {  
  task.destroy();
});   


$ node example2.js                               
Executing: SELECT * FROM `Projects` WHERE `id`=1 LIMIT 1;  
Executing: DELETE FROM `Projects` WHERE `id`=1 LIMIT 1     

Mysql packet

##                                                       
T 127.0.0.1:50003 -> 127.0.0.1:3306 [AP]                 
,....DELETE FROM `Projects` WHERE `id`=1 LIMIT 1         
##                                                       

Conclusion

다음 시간에는 Association에 대해서 알아보겠습니다. 오늘은 좀 선선하군요. 좋은 하루 되세요. :-)

추천 관련글