[Sequelize ORM v6 번역 + 정리] 9. Associations-1

2021. 7. 15. 14:51

Associations

Sequelize 지원하는 표준 associations :  One-To-One(일대일), One-To-Many(일대다), Many-To-Many(다대다)

이를 위해. Sequelize는 결합하여 생성해야 하는 4가지 유형의 association을 제공한다.

  • HasOne association
  • BelongsTo association
  • HasMany association
  • BelongsToMany association

이 메뉴얼에서는 위 4가지 유형의 association을 정의하는 방법을 설명하고, 그 유형을 세 가지 표준 association 유형( 일대일 , 일대다, 다대다)과 결합하여 정의하는 방법을 설명한다. 


Defining the Sequelize associations

네 가지 유형은 매우 유사한 방식으로 정의된다.

A와 B라는 두 가지 모델이 있다고 가정했을 때, Sequelize로 둘을 연결하려면 함수 호출만 하면 된다.

const A = sequelize.define('A', /* ... */);
const B = sequelize.define('B', /* ... */);
A.hasOne(B);  // A HasOne B
A.belongsTo(B);  // A BelongsTo B
A.hasMany(B); // A HasMany B
A.belongsToMany(B, { through: 'C' }); // A BelongsToMany B through the junction table C

위 4가지 유형은 모두 두번째 매개변수로 Option 객체를 받아들인다. (belongsToMany만 옵션 필수).

위 코드에서 A는 souce 모델, B는 target 모델이라고 한다 .

  • A.hasOne(B) association :  A와 B 사이에 일대일 관계가 존재하며, FK target 모델 B에 정의되어 있음을 나타낸다. 
  • A.belongsTo(B) association : A와 B 사이에 일대일 관계가 존재하며, FK source 모델 A에 정의되어 있음을 나타낸다. 
  • A.hasMany(B) association : A와 B 사이에 일대다 관계가 존재하며, FK target 모델 B 정의되어 있음을 나타낸다. 
  • A.belongsToMany(B, { through: 'C' }) association : A와 B 사이에 다대다 관계가 존재하며, junction(접합) 테이블을 C로 사용함을 말한다. FK target 모델 B 정의되어 있음을 나타낸다.
  • 제일 위 세 개는 Sequelize가 자동으로 적절한 모델에 FK를 추가하도록 한다 (FK가 존재하지 않으면).
  • junction table : A와 B의 FK가 있어 데이터 데이블의 기본기를 참조해 둘 이상의 테이블을 함께 매핑하는 것.

 

Note : 위의 예 중 belongsToMany에서 string( 'C')가 through 옵션에 전달되었다. 이 경우 Sequelize는 이 이름을 가진 모델을 자동으로 생성한다. 만약 이미 모델을 정의한 경우에는 모델을 직접 전달할 수도 있다.


Creating the standard relationships

일반적으로 Sequelize 연관은 쌍으로 정의된다. 요약해서 말하자면:

  • 일대일 관계 - hasOne, belongsTo association이 함께 사용
  • 일대다 관계 - hasMany, belongsTo association이 함께 사용
  • 다대다 관계 - 두 개의 belongsToMany association 함께 사용

One-To-One relationships

1. Philosophy

먼저 Foo와 Bar이라는 두 가지 모델이 있다고 가정했을 때, 그 사이에 일대일 관계를 구축하고자 한다. 이는 관계형 데이터베이스에서 테이블 중 하나에 FK를 설정해 수행할 수 있다. 이때 FK가 어느 테이블에 있어야 할까?  즉, 우리가 원하는 게 Foo가 bar_id 열을 얻는 것일까 아니면, Bar이 foo_id 열을 얻는 것일까?

 

원칙적으로 두 옵션 모두 유효한 방법으로 Foo와 Bar 간의 일대일 관계를 설정할 수 있다. 그러나 우리가 "Foo와 Bar 사이에 일대일 관계가 있습니다" 와 같은 것을 말할 때 관계가 필수인지 선택인지가 불분명합니다 .즉, 'Bar 없이 Foo가 존재할 수 있냐' 혹은 'Foo 없이 Bar가 존재할 수 있냐'는 질문을 통해 FK 열이 있어야 하는 위치를 쉽게 파악할 수 있다.

 

2. Goal

Foo와 Bar이라는 두 가지 모델이 있다고 가정했을 때, Bar이 foo_id 열을 얻을 수 있도록 일대일 관계 설정

 

3. Implementation

Main setup :

Foo.hasOne(Bar);
Bar.belongsTo(Foo);

옵션이 전달되지 않았으므로 Sequelize는 모델 이름에서 수행할 작업을 유추합니다.

이 경우 Sequelize는 Bar에 foo_id열을 에 추가해야 한다고 생각한다.

 

이런 식으로 위 코드 이후에 Bar.sync()를 호출 하면 다음 SQL이 생성된다(예: PostgreSQL에서).

CREATE TABLE IF NOT EXISTS "foos" (
/* ... */
);
CREATE TABLE IF NOT EXISTS "bars" (
  /* ... */
  "fooId" INTEGER REFERENCES "foos" ("id") ON DELETE SET NULL ON UPDATE CASCADE
  /* ... */
);

 

4. Options

Association 호출의 두 번째 매개변수로 다양한 옵션을 전달할 수 있다.

 

- onDelete and onUpdate

Ex) ON DELETE와 ON UPDATE 동작 구성 :

Foo.hasOne(Bar, {
  onDelete: 'RESTRICT',
  onUpdate: 'RESTRICT'
});
Bar.belongsTo(Foo);

가능한 선택 사항 : RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL.

일대일 Association 기본값 : SET NULL - ON DELETE및, CASCADE - ON UPDATE

 

5. Customizing the foreign key

hasOne및 belongsTo호출 - foo_id FK가 생성되고 호출되어야 한다고 추론

myFoo_id같이 다른 이름을 사용하려면 :

// Option 1
Foo.hasOne(Bar, {
  foreignKey: 'myFooId'
});
Bar.belongsTo(Foo);

// Option 2
Foo.hasOne(Bar, {
  foreignKey: {
    name: 'myFooId'
  }
});
Bar.belongsTo(Foo);

// Option 3
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
  foreignKey: 'myFooId'
});

// Option 4
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
  foreignKey: {
    name: 'myFooId'
  }
});

foreignKey옵션 -  string/object 허용

Object를 받을 때 이 Object는 열에 대한 정의로 사용된다. (표준 sequelize.define호출에서와 마찬가지)

따라서, type, allowNull, defaultValue 등의 옵션을 사용할 수 있다.

 

Ex) UUID를 기본값(INTEGER) 대신 FK 데이터 형식으로 사용 :

const { DataTypes } = require("Sequelize");

Foo.hasOne(Bar, {
  foreignKey: {
    // name: 'myFooId'
    type: DataTypes.UUID
  }
});
Bar.belongsTo(Foo);

 

6. Mandatory versus optional associations

Association은  기본적으로 선택 사항이다.

이 예에서 는 fooId = null이어도 되며, Foo 없이 하나의 Bar가 존재할 수 있다.

allowNull로 FK 옵션을 지정할 수 있다 .

Foo.hasOne(Bar, {
  foreignKey: {
    allowNull: false
  }
});
// "fooId" INTEGER NOT NULL REFERENCES "foos" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT

One-To-Many relationships

 

1. Philosophy

일대다 연결 - 하나의 소스를 여러 대상과 연결, 모든 대상은 이 단일 소스에만 연결

즉, 외래 키를 배치할 위치를 선택해야 했던 일대일 연결과 달리 일대다 연결에는 하나의 옵션만 있습니다. 예를 들어, 하나의 Foo에 많은 Bar가 있는 경우(이렇게 하면 각 Bar가 하나의 Foo에 속함) 테이블에 fooId열 이 있어야만 합리적인 구현이 가능 Bar합니다. 하나의 Foo에 많은 Bar가 있기 때문에 그 반대는 불가능합니다.

 

2. Goal

Sequelize에게 모델 Team과 사이에 일대다 관계가 있음을 전달. (한 팀에는 많은 플레이어가 있고 각 플레이어는 단일 팀에 속함)

 

3. Implementation

주요 방법

Team.hasMany(Player);
Player.belongsTo(Team);

 

Ex) PostgreSQL에서는 위 코드에 대해 다음 SQL sync()를 생성한다.

CREATE TABLE IF NOT EXISTS "Teams" (
  /* ... */
);
CREATE TABLE IF NOT EXISTS "Players" (
  /* ... */
  "TeamId" INTEGER REFERENCES "Teams" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
  /* ... */
);

 

4. Options

적용되는 옵션은 일대일의 경우와 동일하다.

Ex) FK의 이름을 변경하고 관계가 필수인지 확인하려면

Team.hasMany(Player, { foreignKey: 'clubId' });
Player.belongsTo(Team);

ON DELETE 기본값 - SET NULL

ON UPDATE기본값 - CASCADE


Many-To-Many relationships

 

1. Philosophy

다대다 연결은 하나의 소스를 여러 대상과 연결한다. 각 대상은 차례로 첫 번째 소스를 제외한 다른 소스에 연결할 수 있다.

다른 관계와 마찬가지로 테이블 중 하나에 하나의 FK를 추가하여 나타낼 수 없습니다.

junction model이라는 개념이 사용된다. (이 개념을 정리한 url : https:// doplinblue.tistory.com/15)

 

[Sequelize - 개념 공부] Junction table

Junction table ? : A와 B의 FK가 있어 데이터 데이블의 기본기를 참조해 둘 이상의 테이블을 함께 매핑하는 것. associative entity에 나오는 용어로 DB 테이블 Association에서 BelongsToMany association 공부..

doplinblue.tistory.com

 

2. Goal

Sequelize에게 Movie와 Actor 사이에 다대다 연결이 있음을 전달. (한 배우가 여러 영화에 참여했을 수 있으며, 한 영화에는 많은 배우가 제작에 참여했다.)

junction 테이블인 ActorMovies가 호출된다. 여기에는 FK movieId와 actorId가 있다.

 

3. Implementation

주요 방법

const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });

 

belongsToMany에서 through옵션에 string이 주어져서 Sequelize는 junction 모델 역할을 할 ActorMovies 모델을 자동으로 생성합니다.

Ex) PostgreSQL :

CREATE TABLE IF NOT EXISTS "ActorMovies" (
  "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
  "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
  "MovieId" INTEGER REFERENCES "Movies" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
  "ActorId" INTEGER REFERENCES "Actors" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
  PRIMARY KEY ("MovieId","ActorId")
);

문자열 대신 모델을 직접 전달하는 것도 지원되며, 이 경우 주어진 모델이 접합 모델로 사용된다. (모델 자동 생성 X).

const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
const ActorMovies = sequelize.define('ActorMovies', {
  MovieId: {
    type: DataTypes.INTEGER,
    references: {
      model: Movie, // 'Movies' would also work
      key: 'id'
    }
  },
  ActorId: {
    type: DataTypes.INTEGER,
    references: {
      model: Actor, // 'Actors' would also work
      key: 'id'
    }
  }
});
Movie.belongsToMany(Actor, { through: ActorMovies });
Actor.belongsToMany(Movie, { through: ActorMovies });

위의 결과는 PostgreSQL에서 다음 SQL을 생성한다.

CREATE TABLE IF NOT EXISTS "ActorMovies" (
  "MovieId" INTEGER NOT NULL REFERENCES "Movies" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
  "ActorId" INTEGER NOT NULL REFERENCES "Actors" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
  "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
  "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
  UNIQUE ("MovieId", "ActorId"),     -- Note: Sequelize generated this UNIQUE constraint but
  PRIMARY KEY ("MovieId","ActorId")  -- it is irrelevant since it's also a PRIMARY KEY
);

 

4. Options

일대일 및 일대다 관계와 달리 많은 다대다 관계에서 ON UPDATE와 ON DELETE의 deflaut는 CASCADE이다.

 

Belongs-To-Many는 모델을 통해 Unique Key를 생성합니다. Unique Key 이름은 uniqueKey 옵션을 사용하여 재정의할 수 있습니고, 생성을 방지하려면 unique: false 옵션을 사용하면 된다 .

Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })

 

 

 

원본 링크 : https://sequelize.org/master/manual/assocs.html

'Server > Sequelize' 카테고리의 다른 글

[Sequelize - 개념 공부] Junction table  (3) 2021.07.15

BELATED ARTICLES

more