[cookbook] Sequelize #2 Association (Nodejs에서도 ORM 사용해보자)

Sequelize #2 Association Introduction

이번 시간에는 Relation (Association) 에 대해서 알아보겠습니다. association에 관련된 sequelize의 메뉴얼 정말 짱나는군요. ;;

orm

전제항목

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

//
// User Model
//
var User = sequelize.define('User', {
  name: Sequelize.STRING
});

Project, User 2개의 Model 을 가지고 3가지의 association 대해서 설명하겠습니다.

One-To-One associations

1:1 관계에 대해 설명합니다.

Syntax

//
// Parent와 Child의 관계를 정의합니다.
// options 값은 Object 이며,
// - as : 동적으로 생성되는 setter 메소드명을 (아래참고) 지정합니다.
// - foreignKey : 기본으로 Parent_ModelName + 'Id' 이런 문자열인데, 이를 지정할 수 있습니다.
//
Parent_ModelName.hasOne(Child_ModelName, options);

//
// 각각 생성한 Builder Instance 를 저장합니다.
//
Parent_BI_ModelName.save();
Child_ModelName.save();

//
// Parent와 Child 의 FK 를 업데이트합니다.
// setUser 는 User Model을 지정합니다. 사용시에는 Child_ModelName 으로 대체합니다.
//
Parent_BI_ModelName.setUser(Child_ModelName);

Example

$ cat hasOne.js

var Sequelize = require('sequelize');
var sequelize = new Sequelize('dbname', 'userid', 'password');

//
// 2개의 Model 정의
//
var Project = sequelize.define('Project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
});
var User = sequelize.define('User', {
  name: Sequelize.STRING
});

//
// 1:1 관계정의
//
Project.hasOne(User);

//
// force:1 설정으로 테이블을 모두 지우고 처리합니다.
//
sequelize.sync({force: 1}).on('success', function() {
  //
  // 2개의 build instance 생성합니다.
  //
  var project = Project.build({
    title: 'my awesome project',
    description: 'woot woot. this will make me a rich man'
  });
  var user = User.build({
    name: 'nanha',
  });

  //
  // 2개의 build instance 를 저장합니다.
  //
  project.save();
  user.save();

  //
  // Projects 테이블의 id값을 Users 의 ProjectId 값으로 FK를 업데이트합니다.
  //
  project.setUser(user);
});


$ node hasOne.js

Executing: DROP TABLE IF EXISTS `Projects`;
Executing: DROP TABLE IF EXISTS `Users`;
Executing: CREATE TABLE IF NOT EXISTS `Projects` (`title` VARCHAR(255), `description` TEXT, `id` INTEGER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing: CREATE TABLE IF NOT EXISTS `Users` (`name` VARCHAR(255), `id` INTEGER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `ProjectId` INTEGER, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing: INSERT INTO `Projects` (`title`,`description`,`id`,`createdAt`,`updatedAt`) VALUES ('my awesome project','woot woot. this will make me a rich man',NULL,'2012-08-07 22:04:45','2012-08-07 22:04:45');
Executing: INSERT INTO `Users` (`name`,`id`,`createdAt`,`updatedAt`,`ProjectId`) VALUES ('nanha',NULL,'2012-08-07 22:04:45','2012-08-07 22:04:45',NULL);
Executing: SELECT * FROM `Users` WHERE `ProjectId` IS NULL LIMIT 1;
Executing: UPDATE `Users` SET `name`='nanha',`id`=1,`createdAt`='2012-08-07 22:04:45',`updatedAt`='2012-08-07 22:04:45',`ProjectId`=NULL WHERE `id`=1
Executing: UPDATE `Users` SET `name`='nanha',`id`=1,`createdAt`='2012-08-07 22:04:45',`updatedAt`='2012-08-07 22:04:45',`ProjectId`=1 WHERE `id`=1

mysql data

mysql> select * from Projects\\G;
*************************** 1. row ***************************
      title: my awesome project
description: woot woot. this will make me a rich man
         id: 1
createdAt: 2012-08-07 22:04:45
  updatedAt: 2012-08-07 22:04:45
1 row in set (0.01 sec)

mysql> select * from Users;
+-------+----+---------------------+---------------------+-----------+
| name  | id | createdAt           | updatedAt           | ProjectId |
+-------+----+---------------------+---------------------+-----------+
| nanha |  1 | 2012-08-07 22:04:45 | 2012-08-07 22:04:45 |         1 |
+-------+----+---------------------+---------------------+-----------+
1 row in set (0.00 sec)

One-To-Many associations

1:n 관계에 대해 설명합니다.

Syntax

//
// Parent와 Child의 관계를 정의합니다.
// options 값은 Object 이며,
// - as : 동적으로 생성되는 setter 메소드명을 (아래참고) 지정합니다.
// - foreignKey : 기본으로 Parent_ModelName + 'Id' 이런 문자열인데, 이를 지정할 수 있습니다.
//
Parent_ModelName.hasMany(Child_ModelName, options);

//
// 각각 생성한 Builder Instance 를 저장합니다.
//
Parent_BI_ModelName.save();
Child_ModelName1.save();
Child_ModelName2.save();
//
// Parent와 Child 의 FK 를 업데이트합니다.
// setter 명은 복수로 해야하며
// 값은 배열로 넣어줍니다.
//
Parent_BI_ModelName.setUsers([Child_ModelName1, Child_ModelName2]);

Example

var Sequelize = require('sequelize');
var sequelize = new Sequelize('dbname', 'userid', 'password');

//
// 2개의 Model 정의
//
var Project = sequelize.define('Project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
});
var User = sequelize.define('User', {
  name: Sequelize.STRING
});

//
// 1:n 관계정의
// as 를 사용하여 foo 라고 setter명을 변경합니다.
// foreignKey도 변경했어요. 원래 ProjectId로 생성될 것을 project_id 으로 변경했어요.
//
Project.hasMany(User, {as:'foo', foreignKey: 'project_id'});

//
// force:1 설정으로 테이블을 모두 지우고 처리합니다.
//
sequelize.sync({force: 1}).on('success', function() {
  //
  // 3개의 build instance 생성합니다.
  //
  var project = Project.build({
    title: 'my awesome project',
    description: 'woot woot. this will make me a rich man'
  });
  var user = User.build({
    name: 'nanha',
  });
  var user2 = User.build({
    name: 'nanha2',
  });

  //
  // 3개의 build instance 를 저장합니다.
  //
  project.save();
  user.save();
  user2.save();

  //
  // Projects 테이블의 id값을 Users 의 project_id 값으로 FK를 업데이트합니다.
  // setter 명이 setFoo 으로 변경되었습니다.
  // 복수, 단수 구분을 할 필요없습니다.
  //
  project.setFoo([user, user2]);
});


$ node hasMany.js
Executing: DROP TABLE IF EXISTS `Projects`;
Executing: DROP TABLE IF EXISTS `Users`;
Executing: CREATE TABLE IF NOT EXISTS `Projects` (`title` VARCHAR(255), `description` TEXT, `id` INTEGER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing: CREATE TABLE IF NOT EXISTS `Users` (`name` VARCHAR(255), `id` INTEGER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `project_id` INTEGER, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing: INSERT INTO `Projects` (`title`,`description`,`id`,`createdAt`,`updatedAt`) VALUES ('my awesome project','woot woot. this will make me a rich man',NULL,'2012-08-07 22:06:07','2012-08-07 22:06:07');
Executing: INSERT INTO `Users` (`name`,`id`,`createdAt`,`updatedAt`,`project_id`) VALUES ('nanha',NULL,'2012-08-07 22:06:07','2012-08-07 22:06:07',NULL);
Executing: INSERT INTO `Users` (`name`,`id`,`createdAt`,`updatedAt`,`project_id`) VALUES ('nanha2',NULL,'2012-08-07 22:06:07','2012-08-07 22:06:07',NULL);
Executing: SELECT * FROM `Users` WHERE `project_id` IS NULL;
Executing: UPDATE `Users` SET `name`='nanha',`id`=1,`createdAt`='2012-08-07 22:06:07',`updatedAt`='2012-08-07 22:06:07',`project_id`=NULL WHERE `id`=1
Executing: UPDATE `Users` SET `name`='nanha2',`id`=2,`createdAt`='2012-08-07 22:06:07',`updatedAt`='2012-08-07 22:06:07',`project_id`=NULL WHERE `id`=2
Executing: UPDATE `Users` SET `name`='nanha',`id`=1,`createdAt`='2012-08-07 22:06:07',`updatedAt`='2012-08-07 22:06:07',`project_id`=1 WHERE `id`=1
Executing: UPDATE `Users` SET `name`='nanha2',`id`=2,`createdAt`='2012-08-07 22:06:07',`updatedAt`='2012-08-07 22:06:07',`project_id`=1 WHERE `id`=2

mysql data

mysql> select * from Projects\\G;
*************************** 1. row ***************************
      title: my awesome project
description: woot woot. this will make me a rich man
         id: 1
createdAt: 2012-08-07 22:06:07
  updatedAt: 2012-08-07 22:06:07
1 row in set (0.00 sec)

mysql> select * from Users;
+--------+----+---------------------+---------------------+------------+
| name   | id | createdAt           | updatedAt           | project_id |
+--------+----+---------------------+---------------------+------------+
| nanha  |  1 | 2012-08-07 22:06:07 | 2012-08-07 22:06:07 |          1 |
| nanha2 |  2 | 2012-08-07 22:06:07 | 2012-08-07 22:06:07 |          1 |
+--------+----+---------------------+---------------------+------------+
2 rows in set (0.00 sec)

Many-To-Many associations

n:m 관계에 대해 설명합니다.

Syntax

//
// Parent와 Child의 관계를 정의합니다.
// 서로 hasMany 로 관계를 설정합니다.
// options 값은 Object 이며,
// - as : 동적으로 생성되는 setter 메소드명을 (아래참고) 지정합니다.
// - foreignKey : 기본으로 Parent_ModelName + 'Id' 이런 문자열인데, 이를 지정할 수 있습니다.
//
Parent_ModelName.hasMany(Child_ModelName, options);
Child_ModelName.hasMany(Parent_ModelName, options);

//
// 각각 생성한 Builder Instance 를 저장합니다.
//
Parent_BI_ModelName.save();
Child_ModelName.save();
//
// Parent PK 와 Child 의 PK 를 ParentsChilds 테이블에 입력합니다.
// setter 명은 복수로 해야하며
// 값은 배열로 넣어줍니다.
//
Parent_BI_ModelName.setUsers([Child_ModelName]);

Example

var Sequelize = require('sequelize');
var sequelize = new Sequelize('dbname', 'userid', 'password');

//
// 2개의 Model 정의
//
var Project = sequelize.define('Project', {
  title: Sequelize.STRING,
  description: Sequelize.TEXT
});
var User = sequelize.define('User', {
  name: Sequelize.STRING
});

//
// n:m 관계정의
//
Project.hasMany(User);
User.hasMany(Project);

//
// force:1 설정으로 테이블을 모두 지우고 처리합니다.
//
sequelize.sync({force: 1}).on('success', function() {
  //
  // 2개의 build instance 생성합니다.
  //
  var project = Project.build({
    title: 'my awesome project',
    description: 'woot woot. this will make me a rich man'
  });
  var user = User.build({
    name: 'nanha',
  });

  //
  // 2개의 build instance 를 저장합니다.
  //
  project.save();
  user.save();

  //
  // Projects 테이블의 id값과 Users 의 id값을 ProjectsUsers 테이블에 입력합니다.
  //
  project.setUsers([user]);
});


$ node ManyToMany
Executing: CREATE TABLE IF NOT EXISTS `ProjectsUsers` (`UserId` INTEGER , `ProjectId` INTEGER , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`UserId`, `ProjectId`)) ENGINE=InnoDB;
Executing: DROP TABLE IF EXISTS `Projects`;
Executing: DROP TABLE IF EXISTS `Users`;
Executing: DROP TABLE IF EXISTS `ProjectsUsers`;
Executing: CREATE TABLE IF NOT EXISTS `Projects` (`title` VARCHAR(255), `description` TEXT, `id` INTEGER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing: CREATE TABLE IF NOT EXISTS `Users` (`name` VARCHAR(255), `id` INTEGER NOT NULL auto_increment , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing: CREATE TABLE IF NOT EXISTS `ProjectsUsers` (`UserId` INTEGER , `ProjectId` INTEGER , `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`UserId`, `ProjectId`)) ENGINE=InnoDB;
Executing: INSERT INTO `Projects` (`title`,`description`,`id`,`createdAt`,`updatedAt`) VALUES ('my awesome project','woot woot. this will make me a rich man',NULL,'2012-08-07 22:06:59','2012-08-07 22:06:5
9');
Executing: INSERT INTO `Users` (`name`,`id`,`createdAt`,`updatedAt`) VALUES ('nanha',NULL,'2012-08-07 22:06:59','2012-08-07 22:06:59');
Executing: SELECT `Users`.* FROM `Users`, `ProjectsUsers` WHERE `ProjectsUsers`.`ProjectId` IS NULL AND `ProjectsUsers`.`UserId`=`Users`.`id`;
Executing: INSERT INTO `ProjectsUsers` (`ProjectId`,`UserId`,`createdAt`,`updatedAt`) VALUES (1,1,'2012-08-07 22:06:59','2012-08-07 22:06:59');

mysql data

mysql> select * from Projects\\G;
*************************** 1. row ***************************
      title: my awesome project
description: woot woot. this will make me a rich man
         id: 1
createdAt: 2012-08-07 22:06:59
  updatedAt: 2012-08-07 22:06:59
1 row in set (0.01 sec)

mysql> select * from Users;
+-------+----+---------------------+---------------------+
| name  | id | createdAt           | updatedAt           |
+-------+----+---------------------+---------------------+
| nanha |  1 | 2012-08-07 22:06:59 | 2012-08-07 22:06:59 |
+-------+----+---------------------+---------------------+
1 row in set (0.00 sec)

mysql> select * from ProjectsUsers;
+--------+-----------+---------------------+---------------------+
| UserId | ProjectId | createdAt           | updatedAt           |
+--------+-----------+---------------------+---------------------+
|      1 |         1 | 2012-08-07 22:06:59 | 2012-08-07 22:06:59 |
+--------+-----------+---------------------+---------------------+
1 row in set (0.00 sec)

Conclusion

3개의 관계를 정의하는 방법에 대해서 소개해드렸습니다. rails 만큼 편하지는 않으나, 어느정도 wrapper 를 만들어 놓으면 rails와 비슷하게 사용할 수 있을것입니다. 다음 시간에는 Association 에 관련된 입력외에 Read, Delete 에 관해서 알아보겠습니다. 3편에서 시간이 되면 migration 부분도 포함하겠습니다. 감사합니다. 좋은 하루 되세요. ~ :-)

추천 관련글