# 7. MySQL

## 7. MySQL

### 7.1 데이터베이스란?

데이터를 저장 및 조작 할 수 있는 시스템 -> DBMS

* RDBMS : MySQL
* NoSQL : MongoDB

### 7.2 MySQL 설치하기

도커로 대체

```bash
# 도커설치
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce

# docker image 생성 및 실행
$ docker run -it -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 --name mysql5.7 mysql:5.7
```

### 7.3 DB SQL 설치

워크벤치 대신 Datagrip이나 DBeaver(무료) 추천\
워크벤치 생략

* Datagrip : <https://www.jetbrains.com/datagrip/>
* DBeaver : <https://dbeaver.io/>

### 7.4 데이터베이스 및 테이블 생성하기

```sql
-- Database 생성
CREATE DATABASE nodejs;

-- Database 사용
use nodejs;

-- 테이블 설명
DESC users;

DROP TABLE 테이블명;
...
```

자료형

* INT : 정수의미. 소수까지 저장 -> FLOAT, DOUBLE 사용&#x20;
* VARCHAR : 가변길이. 몇백자 이내
* TINYINT : -127 \~ 128
* TEXT : 긴글 저장
* DATETIME : 날짜 및 시간에대 한 정보. 날짜만 -> DATE, 시간 -> TIME

### 7.5 CRUD 작업하기 (생성, 읽기, 수정, 삭제)

* C : Create. 생성
  * insert into table values (값,...)
* R : Read. 읽기
  * select \* from table
* U : Update. 수정
  * update table set field1 = x, field2 = y ..
* D : Delete. 삭제
  * delete from table where \~

### 7.6 시퀄라이즈 사용하기

* ORM : 객체와 데이터베이스의 릴레이션 매핑
* 자바스크립트 구문 -> SQL로 바꿔줌
* 모듈 설치

  ```bash
  # project 생성
  $ express learn-sequelize --view=pug

  # sequelize와 mysql2 패키지 설치, cli 전역설치
  $ npm i sequelize mysql2
  $ npm i -g sequelize-cli
  $ sequelize init
  ```
* config.json 내용
  * operatorsAliases : 보안에 취약한 연산자를 사용할지 여부 (false입력 : 사용안함)
  * 4대 정보 : username, password, database, host 정보 알맞게 입력&#x20;

    ```javascript
    {
    "development": {
      "username": "root",
      "password": null,
      "database": "database_development",
      "host": "127.0.0.1",
      "dialect": "mysql",
      "operatorsAliases": false
    },
    "test": {
      "username": "root",
      "password": null,
      "database": "database_test",
      "host": "127.0.0.1",
      "dialect": "mysql",
      "operatorsAliases": false
    },
    "production": {
      "username": "root",
      "password": null,
      "database": "database_production",
      "host": "127.0.0.1",
      "dialect": "mysql",
      "operatorsAliases": false
    }
    }
    ```

```javascript
var sequelize = require('./models/index').sequelize;
var app = express();
sequelize.sync(); // 메서드 없는데;;?
```

* models/user.js

  user 정보&#x20;

  sequlize.define 메서드로 모델 정의. 시퀄라이즈와 MySQL 자료형은 약간 상이

| MySQL    | 시퀄라이즈   |
| -------- | ------- |
| VARCHAR  | STRING  |
| INT      | INTEGER |
| TINYINT  | BOOLEAN |
| DATETIME | DATE    |
| 테이블명 복수형 | 모델명 단수형 |

```javascript
// define(모델명, 스키마정의, 테이블옵션)
// user.js
module.exports = (sequelize, DataTypes) => {
  return sequelize.define('user', {
    name: {
      type: DataTypes.STRING(20),
      allowNull: false, // -> not null 옵션과 동일
      unique: true      // -> unique 옵션과 동일
    },
    age : {
      type: DataTypes.INTEGER.UNSIGNED,
      allowNull : false
    },
    married:{
      type: DataTypes.BOOLEAN,
      allowNull: true
    },
    comment :{
      type: DataTypes.TEXT,
      allowNull:false
    },
    created_at :{
      type: DataTypes.DATE,
      allowNull : false,
      defaultValue: sequelize.literal('now()')  // -> default 옵션과 동일
    },
  }, {
    timestamps: false // true : createdAt, updatedAt 컬럼 자동 추가 및 자동 업데이트
  });
};

// comment.js
module.exports = (sequlize, DataTypes) => {
  return sequlize.define('comment', {
    comment: {
      type: DataTypes.STRING(100),
      allowNull: false
    },
    created_at: {
      type: DataTypes.DATE,
      allowNull: true,
      defaultValue: sequlize.literal('now()')
    }
  }, {
    timestamps: false
  });
};

// index.js
// db 객체의 property와 js의 객체 정보 연결
db.User = require('./user')(sequelize, Sequelize);
db.Comment = require('./comment')(sequelize, Sequelize);
```

* 위 외에 테이블옵션
  * paranoid : timestamps가 true인 경우만 사용 가능, deletedAt 컬럼이 추가됨. **삭제시 삭제된 날짜가 입력됨** (삭제하진 않고 데이터를 남겨두는 이유는 백업 및 히스토리 관리 때문)
  * underscored : createdAt, updatedAt, deleteAt 컬럼과 시퀄라이즈가 자동으로 생성해주는 관계 컬럼들의 이름을 스네이크케이스 형식으로 변경 해줌. 스네이크케이스란 대문자 대신 \_를 사용하는 방식. ex : createdAt -> created\_at
  * tableName : 테이블 이름을 다른것으로 설정하고 싶을 때 사용. 시퀄라이즈는 자동으로 첫번째 인자(모델값)값을 복수형으로 만들어 테이블 이름으로 사용함. user -> users, comment -> comments
* 1:N 관계 표현 hasMany메서드 사용. foreignKey 속성으로 값 일치. sourceKey 키값(id), targetKey 키값(id)

  ```javascript
  // user는 comment를 많이 갖을 수 있다
  db.User.hasMany(db.Comment, {foreignKey: 'commenter', sourceKey: 'id'});
  // commnet는 user에 속할 수 있다
  db.Comment.belongsTo(db.User, {foreignKey: 'commenter', targetKey: 'id'});
  ```
* 1:1 관계 표현 hasOne 메서드 사용. foreignKey 속성으로 값 일치. sourceKey 키값(id), targetKey 키값(id)

  ```javascript
  // user는 info를 하나 갖고 있다.
  db.User.hasOne(db.Info, {foreignKey: 'user_id', sourceKey: 'id'});
  // info는 user에 속할 수 있다.
  db.Info.belongsTo(db.User, {foreignKey: 'user_id', targetKey: 'id'});
  ```
* N:M 관계 표현 (다대다) belongsToMany 메서드 사용. through 속성은 중간 관계 테이블 이름 이라고 보면될듯.

  ```javascript
  // post(게시글)은 여러개의 해시태그를 갖을 수 있다.
  db.Post.belongsToMany(db.Hashtag, {through: 'PostHashtag'});
  // hashtag(해시태그)는 여러개의 게시글에 달릴 수 있다.
  db.Hashtag.belongsToMany(db.Post, {through: 'PostHashtag'});
  ```
* get+모델이름 복수형

  ```javascript
  async (req, res, next) => {
  const tag = await Hashtag.find({where:{title:'node'}});
  const posts = await tag.getPosts();
  }
  ```

### 시퀄라이즈 쿼리 알아보기

시퀄라이즈만의 방식\
반환 : 프로미스\
async/await 같이 사용 가능

* order : 정렬방식
* attributes : select 컬럼 명시
* 시퀄라이즈의 OP -> 오퍼레이터

```javascript
// INSERT INTO users (name, age, married, comment) VALUES('zero', 24, 0, '자기소개1');
const {User} = require('../models')
User.create({
  name: 'zero',
  age: 24,
  married: false,
  comment: '자기소개1'
});

// select * from users;
User.findAll({});

// select * from users limit 1;
Uesr.find({});

// select name, married from users;
User.findAll({
  attributes:['name', 'married']
});

// select name, age from users where married = 1 and age > 30
const {User, Sequelize :{Op}} = require('../models');
User.findAll({
  attributes:['name', 'age'],
  where: {
    married:1,
    age: {[Op.gt]:30}
  }
})

// select id, name from users order by age desc;
User.findAll({
  attributes:['id', 'name'],
  order: [[]]
})

// select id, name from users where married = 0 OR age >30;
const {User, Sequelize: {Op}} = require('../models');
User.findAll({
  attributes: ['id', 'name'],
  where: {
    [Op.or]: [{married: 0}, {age: {[Op.gt]:30}}]
  }
})

// select id, name from users order by age desc;
User.findAll({
  attributes: ['id', 'name'],
  order: [['age', 'DESC']]
})

// select id, name, from users order by age desc limit 1 offset 1;
User.findAll({
  attributes: ['id', 'name'],
  order: ['age', 'DESC'],
  limit: 1,
  offset: 1
})

// update users set comment = '내용' where id = 2;
User.update({
  comment: '내용' // 수정내용
},{
  where: {id: 2}  // 수정대상 로우 찾기
})

// delete from users where id = 2
User.destroy({
  where:{id: 2}
})
```
