Skip to content
GwiyeomGo Tech Blog
About GwiyeomGo

ORM GORM AutoMigrate

GOLANG, 20225 min read

배경

어드민의 조직및 권한 관리 서비스 코드는 xorm 이 아닌 gorm 을 사용합니다. xorm 에는 이 기능이 있는지 모르겠지만. gorm 에는 entity 값으로 db 를 자동으로 테이블을 생성해주는 코드가 존재한다

Auto Migration 코드

type User struct {
ID uint
Name string
Age int
Gender string
}
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
db.AutoMigrate(&User{}, &Product{}, &Order{})

AutoMigrate 은 table 에 컬럼도 자동으로 추가해 주는가?

현재 우리가 사용하는 권한 관리 서비스 코드에서는 update_by,created_by 가 존재하지 않는다. 삭제 이슈가 발생했고 (누군가 역할을 삭제했다) 마녀사냥이 아닌! 시스템적으로 기록을 남기기 위해서 삭제한 사람및 수정한 사람의 Id 를 남기기록 했다. 기존에는 없었기 때문에 처음에는 alter 문으로 추가해야하나 고민했다.

그러다 코드에 새 필드를 추가하고 실행시키니 table 에 컬럼이 추가되었다.

AutoMigrate will create tables, missing foreign keys, constraints, columns and indexes.
It will change existing column’s type if its size, precision, nullable changed.
It WON’T delete unused columns to protect your data.

automigrate는 이후에 추가되는 컬럼도 추가해 준다고 한다.

struct 에 필드 type 이 uint 일때 왜? bitint 로 들어가지?

uint ? int? int 값은 음~양 영역,uint 값은 unsinged integer 로 부호가 없다는 것(양수만) 음의 수가 필요 없어서 uint 가 int 보다 많은 값을 표현할 수 있다

int –2,147,483,648 ~ 2,147,483,647
uint 0 ~ 4,294,967,295

uint 를 DB의 필드로 연결하면 int 보다 더 큰 값을 써야 하기 때문에 bigint 로 매핑하는 것으로 판단이 됩니다. mysql 에 unsinged 데이터 타입이 없는 지 찾아보니 id INT(11) UNSIGNED 처럼 int 나 bigint 데이터 타입과 함께 UNSIGNED 써야하나보다....

gorm.Model 과 AutoMigrate 을 함께 쓴다면 ??

gorm.ModelID, CreatedAt, UpdatedAt, DeletedAt 을 모아 놓은 것으로

type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}

예를 들어 아래 User struct 는 ID, CreatedAt, UpdatedAt, DeletedAt,Name 필드를 포함하고 있다.

type User struct {
gorm.Model //ID, CreatedAt, UpdatedAt, DeletedAt,Name
Name string
}

또한 gorm 의 경우 사용자가 지정해서 원하는 필드를 모아서 커스텀도 가능하다

type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// equals
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}

SoftDelete ? gorm.Model 에 DeletedAt 을 살펴보자!

  • gorm.Model의 경우 gorm.DeletedAt 필드를 포함하고 있는데 해당 필드를 쓸 경우 데이터가 완전 삭제되는 것이 아닌 Soft Delete 방식인 delete_at 컬럼에 삭제 시간이 입력된다.
  • AutoMigrate 을 사용할 경우 idx_users_deleted_at 인덱스가 자동 생성
  • gorm 에서 Find 로 전체 기록을 조회할 때 deletedAt 에 표시된 데이터를 제외하고 조회한다 => 실행 쿼리에 deleted_at Is Null 이 포함된다.

그럼 반대로 지워진 필드는 어떻게 찾지 ?

Unscoped 를 추가하면 deleted_at 이 존재한 데이터도 조회 가능하다

users, err := db.WithContext(ctx).Unscoped().Where(u.Age.Eq(20)).Find()
// SELECT * FROM users WHERE age = 20;

how to set deleted_at field is nil

type DeletedAt sql.NullTime
// Scan implements the Scanner interface.
func (n *DeletedAt) Scan(value interface{}) error {
return (*sql.NullTime)(n).Scan(value)
}
// Value implements the driver Valuer interface.
func (n DeletedAt) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Time, nil
}

20230202 mysql 에 날짜 데이터가 utc 로 서울 시간이랑 다른 상황

account 써비스에 날짜가 현재 시간과 다르게 들어가고 있다는 사실을 알았다 알아보니 utc 로 들어가고 있어서 dns 에 서울을 명시해줘야 제대로된 시간이 mysql table 에 추가되었다 처럼 &loc=Asia%2FSeoul 를 추가해주면 해결된다.

var dialector gorm.Dialector
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True",
os.Getenv(EnvDbUser),
os.Getenv(EnvDbPassword),
os.Getenv(EnvDbHost),
os.Getenv(EnvDbName)) + "&loc=Asia%2FSeoul"
dialector = mysql.Open(dsn)
db, err := gorm.Open(dialector, &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})

참조

https://ellieya.tistory.com/134 https://jsonobject.tistory.com/378 https://github.com/go-gorm/gorm/issues/4855 https://dev.to/rahulkarmore/how-to-parse-time-of-time-time-in-golang-work-with-sql-db-in

© 2024 by GwiyeomGo Tech Blog. All rights reserved.