Associations 관계

broccoli·2021년 6월 10일
0

sequelize

목록 보기
2/7
post-thumbnail

sequelizeOne To One, One To Many, Many to Many의 관계를 지원한다.
따라서 모델은 다음 4가지의 타입 형태를 갖는다.

  • A HasOne B
  • A BelongsTo B
  • A HasMany B
  • A BelongsToMany B

이때 A는 source 소스모델이다. B는 target 타겟모델이다. 소스는 함수를 호출하는쪽, 타겟은 인자로 전달되는 쪽

1. 정의

1-1. A HasOne B

A와 B가 1대1 관계에 있음을 뜻한다. 이때 외래키는 타겟 모델 B에 정의된다.

1-2. A belongsTo B

A와 B가 1대1 관계에 있음을 뜻하다. 이때 외래키는 소스 모델 A에 정의된다.

1-3. A hasMany B

A와 B가 1대다 관계임을 뜻한다. 이때 외래키는 타겟 모델 B에 정의된다.

1-4. 1대1과 1대다 관계에서의 외래키 설정

외래키설정시 외래키는 종속이 되는 모델에서 정의된다. 즉 종속되는 모델에 외래키가 정의된다는 의미이다. 종속되는 모델의 외래키는 종속시킨 모델의 기본키값이 되기 때문에 종속시킨 모델의 테이블+id 값 디폴트로 외래키명으로 생성된다.

아래에서 예를 보면 User모델과 Post모델의 1대다관계이다. User모델은 종속시킨 모델이고 Post모델은 종속되는 모델이다.

  • 따라서 외래키는 Post에 정의된다.
  • Post에서는 targetKey로 User의 id가 사용된다는 의미이다.
  • User에서는 sourceKey로서 id가 사용된다는 의미이다.
// Post 모델에서
db.Post.belongsTo(db.User, {
  onUpdate: 'CASCADE',
  onDelete: 'CASCADE',
  foreignKey: 'userId',
  targetKey: 'id'
})
// User 모델에서
db.User.hasMany(db.Post, {
  foreignKey: 'userId',
  sourceKey: 'id'
})

여기까지는 Sequelize가 자동으로 외래키를 소스모델과 타겟모델에 추가한다.(이미 존재하고 있지 않다면)

1-5. A belongsToMany (B, {through: C})

A와 B가 다대다의 관계음을 뜻한다. 이 대다다 관계는 C라는 접합테이블을 활용한다. 이 접합테이블 C는 A의 id, B의 id를 외래키로 갖는다. Sequelize는 자동으로 C라는 모델을 만들고 그곳에 외래키들을 정의한다.

// 자기 테이블을 스스로 관계시킬때 예
// Comment 모델
db.Comment.belongsToMany(db.Comment, {
  as: 'UpperComment',
  through: 'commentsinfo',
  foreignKey: {
    name: 'downcommentId'
  },
  sourceKey: 'id'
})
db.Comment.belongsToMany(db.Comment, {
  as: 'LowerComment',
  through: 'commentsinfo',
  foreignKey: {
    name: 'upcommentId'
  },
  sourceKey: 'id'
})
}
  
// 다른 테이블들을 관계시킬때 예
// Post 모델
db.Post.belongsToMany(db.User, {
  as: 'Liker',
  through: 'likes',
  foreignKey: 'postId',
  sourceKey: 'id'
})
// User 모델
db.User.belongsToMany(db.Post, {
  as: 'Liked',
  through: 'likes',
  foreignKey: 'userId',
  sourceKey: 'id'
})

다대다관계에서는 외래키를 설정할때 헷갈릴 수 있다.
보통 belongsTo 관계를 쓸때는 앞에 모델이 종속되는 모델이고 뒤에가 종속시키는 모델이었지만 belongsToMany는 다르다. 새로운 접합테이블에 관계되는 것이므로

hasMany, hasOne과 같은 방식으로 앞에서 종속하는 모델 뒤에가 종속시키는 모델이라고 생각하면 된다. 따라서 외래키명은 앞에모델의 id를 가리키면된다.

2. Options

2-1. onDelete, onUpdate

관계시에는 각 테이블의 데이터가 업데이트될때 삭제될때의 옵션을 지정해줄수 있다.
아래는 옵션을 넣는 예이고 옵션값은 5가지가 있다.

//
Foo.hasOne(Bar, {
  onDelete: 'RESTRICT',
  onUpdate: 'RESTRICT'
});
Bar.belongsTo(Foo);
  • RESTRICT: 수정이나 삭제시 제한 다른 모델이 참조되는 경우 수정이나 삭제가 취소된다.
  • CASCADE: 수정이나 삭제시 같이 외래키와 연관된 값이 같이 수정되고 삭제된다.
  • NO ACTION: MYSQL에서는 RESTRICT와 동일
  • SET DEFAULT: 디폴트를 따른다
  • SET NULL: 수정이나 삭제시 같이 외래키와 연관된 값은 NULL이 된다.

1대1과 1대다의 관계 기본옵션은 삭제를 위해서는 SET NULL이고 수정을 위해서는 CASCADE이다. 다대다의 관계에서의 기본옵션은 수정과 삭제시 CASCADE이다.

2-2. foreing key

외래키는 설정을 따로 하지 않으면 <테이블명Id>가 된다. 외래키명 변경을 원하면 다음 옵션을 설정한다.

// Option 1
Foo.hasOne(Bar, {
  foreignKey: 'myFooId',
  sourceKey: 'id' //Foo의 id를 소스로 해서 만든 외래키는 myFooId이다.
});
Bar.belongsTo(Foo);

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

// Option 3
Foo.hasOne(Bar);
Bar.belongsTo(Foo, {
  foreignKey: 'myFooId',
  targetKey: 'id' //Foo의 id를 타겟으로 해서 만든 왜래키는 myFooId이다.
});

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

외래키는 스트링을 받아도 되고 object를 받아도 된다. object로 받는경우 디폴트로 정의된 type, allowNull, defaultValue 등의 프로퍼티도 같이 설정된다.

2-2-1. 왜래키를 uuid로 하기

INTEGER가 아닌 UUID로 외래키를 가지려면 아래와 같이 설정하면 된다.

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

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

2-2-2. 외래키 참조를 의무적으로 하려면??

디폴트로 외래키가 참조를 의무적으로 하려면 아래와 같이 설정하면 된다.

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

2-3. through, uniquekey, alias

2-3-1. through

through 옵션은 다대다 관계에서 사용함. through를 사용시 해당 명칭으로 접합테이블 생성됨.

Movie.belongsToMany(Actor, { through: ActorMovies });
Actor.belongsToMany(Movie, { through: ActorMovies });

2-3-2. uniquekey

uniqueKey 옵션을 통해서 uniqueKey의 명칭을 다르게 지정할 수 있다.

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

2-4-3. alias

모델을 알리아스로 지정할 수 있다. 이렇게 지정시 외래키도 알리아스에 맞게 생성된다.

Ship.belongsTo(Captain, { as: 'leader' }); // This creates the `leaderId` foreign key in Ship.

// Eager Loading no longer works by passing the model to `include`:
console.log((await Ship.findAll({ include: Captain })).toJSON()); // Throws an error
// Instead, you have to pass the alias:
console.log((await Ship.findAll({ include: 'leader' })).toJSON());
// Or you can pass an object specifying the model and alias:
console.log((await Ship.findAll({
  include: {
    model: Captain,
    as: 'leader'
  }
})).toJSON());

// Also, instances obtain a `getLeader()` method for Lazy Loading:
const ship = Ship.findOne();
console.log((await ship.getLeader()).toJSON());
profile
🌃브로콜리한 개발자🌟

0개의 댓글