이 글은 공식 홈페이지의 Quick Start 가이드에 설명을 덧붙이는 형태로 작성되었다.
ORM이란?
- Object Relation mapping.
- 객체와 db를 매핑해주는 것.
- 매핑된 정보를 바탕으로 자동으로 SQL을 생성해준다.
- 자세히 설명된 블로그가 있어 링크로 대체한다.
Golang의 ORM
- 과거에는 XORM(솜)을 사용했으며 현재는 GORM(곰)이 많이 사용되는 것으로 보인다.
Quick start
- gorm 공식 홈페이지에서 제공하는 quick start를 약간 변형하여 사용했다.
- db는 docker postgresql을 사용하였다.
docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=1234 -d postgres
package main
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
dsn := "host=localhost user=postgres password=1234 dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Seoul"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
panic("Db 연결에 실패하였습니다.")
}
// 테이블 자동 생성
db.AutoMigrate(&Product{})
// 생성
db.Create(&Product{Code: "D42", Price: 100})
// 읽기
var product Product
db.First(&product, 1) // primary key기준으로 product 찾기
db.First(&product, "code = ?", "D42") // code가 D42인 product 찾기
// 수정 - product의 price를 200으로
db.Model(&product).Update("Price", 200)
// 수정 - 여러개의 필드를 수정하기
db.Model(&product).Updates(Product{Price: 200, Code: "F42"})
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
// 삭제 - product 삭제하기
db.Delete(&product, 1)
}
한줄씩 뜯어보기
데이터베이스 연결
dsn := "host=localhost user=postgres password=1234 dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Seoul"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
- postgres db에 연결하는 부분이다. config는 말 그대로 데이터베이스 연결과 관련된 설정을 할 수 있는데, logmode를 logger.info로 설정해주면 실행되는 모든 SQL문을 로그로 확인할 수 있다.
테이블 자동 생성
type Product struct {
gorm.Model
Code string
Price uint
}
db.AutoMigrate(&Product{})
- 구조체의 필드 값으로 테이블을 생성한다. default 값을 지정해줄 수 있으며, 지정하지 않은 경우 zero-value가 사용된다.
- gorm.Model을 포함하고 있는데 Model 구조체는 아래와 같이 생겼으며, 데이터 CRUD시에 gorm이 create, udpate, deleted 값을 넣어준다.
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}
CREATE
db.Create(&Product{Code: "D42", Price: 100})
// INSERT INTO "products" ("created_at","updated_at","deleted_at","code","price") VALUES ('2021-08-29 21:41:13.781','2021-08-29 21:41:13.781',NULL,'D42',100) RETURNING "id"**
- 객체를 받아 테이블에 저장한다.
- 필드를 지정하고 싶으면 Select, Omit을 사용한다.
db.Select("필드명", ...).Create(...)
// INSERT INTO "테이블" ("필드명") VALUES ("...")
db.Omit("제외할 필드명", ...).Create(...)
// INSERT INTO "테이블" ("Omit에 전달한 필드를 제외한 필드", ...) VALUES ("...")
READ
p := &Product{}
db.First(&p) // select * from table limit 1
db.Last(&p) // select * from table order by desc limit 1
- 쿼리를 실행한 결과를 p에 담는다.
조건을 지정하는 경우
db.Where("필드명 = ?", "값").First(&p)
이외에도 직관적인 여러 메소드를 메소드 체이닝으로 사용할 수 있다.
db.Where().Or().Not().Limit().Offset().Order().Group().Having().Find()
UPDATE
GORM은 아쉽게도 Dirty Checking 기능이 없다.
// 수정 - product의 price를 200으로
db.Model(&product).Update("Price", 200)
// 풀어쓴 형태
db.First(&product**)
product.Price = 200
db.Save(&product)
- 값을 변경한 후 Save() 메소드를 사용하여 저장해 준다.
DELETE
db.Where("price = ?", 122).Delete(&Product{})
// UPDATE "products" SET "deleted_at"='2021-08-29 22:04:44.911' WHERE price = 122 AND "products"."deleted_at" IS NULL
- soft delete가 실행된다.
마무리
GORM 기본 사용법에 대해 가볍게 정리해 보았다. GORM은 Java의 Hibernate에 비해 덜 깔끔하고 덜 친절하다고 느꼈다.
GORM 관련 글을 시리즈 형태로 작성해 보려 하는데 아마 다음 글은 연관관계 매핑과 관련된 글이 될 것 같다. GORM에서는 1:1, 1:N과 같은 연관관계를 어떻게 표현했는지, N+1 문제를 어떻게 해결할 수 있는지 알아볼 계획이다.