레이블이 gorm인 게시물을 표시합니다. 모든 게시물 표시
레이블이 gorm인 게시물을 표시합니다. 모든 게시물 표시

gorm join table

# 다음과 department:member 가 M:N 관계인 테이블이 있을때
[departments table]
id uint
name string

[members table]
id uint
name string

[departments_members table](관계 매핑테이블/join table)
id uint
department_key uint
member_key uint
val1 string
val2 string

# 조인 테이블은 다음과 같이 FK 를 설정했다.
`FK_MEMBERS` FOREIGN KEY (`member_key`) REFERENCES `members` (`id`)
`FK_DEPARTMENTS` FOREIGN KEY (`department_key`) REFERENCES `departments` (`id`),

# golang gorm 사용시
# 사용 테이블 이름 설정
type Tabler interface { TableName() string}
func (Department) TableName() string  { return "departments" }
func (Member) TableName() string  { return "members" }

# 부서 조회시 부서에 속한 멤버들을 조회를 한다고 했을때
# many2many 로 조인테이블 departments_members 을 참고하도록 했고
type Member struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  Departments []Department `gorm:"many2many:departments_members;" json:"departments,omitempty"` 
}
type Department struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  Members []Member `gorm:"many2many:departments_members; "json:"members,omitempty"`
}

# 다음과 preload 로 멤버가 포함된 부서를 조회를 하면 
err := ysoftmanMysql.Db.Model(&db.Department{}).Preload("Members").Find(&department, "name = ?", name).Error

# 다음과 같이 department_id 를 알 수 없다고 나온다.
Error 1054 (42S22): Unknown column 'departments_members.department_id' in 'where clause'

# 기본적으로 foreignkey로 모델명(struct)+pk 를 붙인 이름(department_id)필드를 사용한다.
# departments_members 의 필드명을 다음과 같이 바꾸면 된다.
department_key -> department_id
member_key -> member_id

# 참고
# github.com/jinzhu/gorm -> 1.x
# gorm.io/gorm -> 2.x

#####

# departments_members 테이블 자체에 있는 val1,val2 필드고 같이 조회 하기
func (DepartmentsMembers) TableName() string { return "departments_members" }

type Member struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  # DepartmentMembers 에서 MemberID 로 조회 할 수 있도록 한다.
  DepartmentMembers []DepartmentMembers `gorm:"foreignkey:MemberID" json:"users,omitempty"`
}

type Department struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  # DepartmentMembers 에서 DepartmentID 로 조회 할 수 있도록 한다.
  DepartmentMembers []DepartmentMembers `gorm:"foreignkey:DepartmentID" json:"users,omitempty"`
}

# DepartmentsMembers 데이터추가고 이곳에서 Member, Department 를 담을 수 있도록 한다.
type DepartmentsMembers struct {
  ID       uint64  `gorm:"primary_key;auto_increment" json:"id"`
  MemberID   uint64  `gorm:"";auto_increment" json:"user_id,omitempty"`
  DepartmentID uint64  `gorm:"" json:"user_id,omitempty"`
  Member      *Member  `json:"member,omitempty"`
  Department  *Department  `json:"department,omitempty"`
}

# Department 조회시 멤버 정보가 포함해서 조회
# DepartmentsMember.Member 를 한번 더 프리로드 한다.
err := ysoftmanMysql.Db.Preload("DepartmentsMember").Preload("DepartmentsMember.Member").Find(&project, "project_id = ?", id).Error

gorm local time 으로 기록 하기

# gorm 으로 `created_at` datetime 컬럼에 값 설정시 
# UTC 시간으로 기록되는 이슈 발생했다.

# 다음과 같은 구조에서 time.Now() 값을 설정했을때
type TestInfo struct {
ID       int64
LastDate time.Time
Age       int
Name      string
Enable    bool `gorm:"not null"`
CreatedAt time.Time
}
user.CreatedAt = time.Now()

# gorm debug 창으로 표시되는 sql 문에는 local 시간으로 쿼리가 수행되었다고 나오지만
2020/11/19 21:17:32 /Users/ysoftman/workspace/test_code/golang/sql_db/sql_gorm.go:68
[1.201ms] [rows:1] INSERT INTO `test_info` (`last_date`,`age`,`name`,`enable`,`created_at`) VALUES ('2020-11-19 21:17:32.054',21,'bill',true,'2020-11-19 21:17:32.054')

# 실제 db 를 조회해보면 UTC 시간으로 저장되어 있다.
mysql> select * from  mytest.test_info;
+----+------+------+---------------------+--------+---------------------+
| id | age  | name | last_date           | enable | created_at          |
+----+------+------+---------------------+--------+---------------------+
| 23 |    0 |      | 2020-11-19 12:17:32 |      0 | 2020-11-19 12:17:32 |
+----+------+------+---------------------+--------+---------------------+
1 row in set (0.00 sec)

# 해결방법1(권장)
# db 접속 DSN 파라미터에 loc=Asia/Seoul 로 설정해야 로컬 시간값으로 설정된다.
username:password@protocol(address)/dbname?charset=utf8&parseTime=True&loc=Asia%2FSeoul

# 해결방법2
# time 값을 string 으로 저장한다. 하지만
# 기존 저장된 데이터 포맷에 의존적이라 비추
CreatedAt string 으로 선언
user.CreatedAt = time.Now().Format("2006-01-02 15:04:05")

# 테스트코드

gorm 구조체 필드 default 태그 유의사항

// gorm 으로 다음과 같이 test_info 테이블에 들어가는 구조체를 만들고
type TestInfo struct {
  ID       int64
  LastDate time.Time
  Age      int    `gorm:"default:99"`
  Name     string `gorm:"default:lemon"`
  Enable   bool   `gorm:"not null;default:1"`
}

// 해당 레코드를 삭제 후 Save() 로 저장하면
// 아래 설정된 값이 아닌 default 값이 설정돼
// Name = lemon, Age = 99, Enable = true 값으로 저장된다.
db.Delete(TestInfo{}, "id = ?", user.ID)
user.Name = ""
user.Age = 0
user.Enable = false
db.Save(&user)

// 원인 파악 및 해결방법
// Save() 는 update 후 select 로 레코드를 확인하는데
// Delete() 로 해당 레코드가 없으면 insert(gorm create함수)를 수행한다.
// 이때 만약 구조체 필드 중 `gorm:"default:xxx"` 값이 설정되어 있고
// 그 필드가 초기값 bool = false, int = 0, string = "" 등인 경우
// Save() -> Create() -> Execute() -> f() ->
// callbacks/create.go Create() ->
// ConvertToCreateValues() -> case reflect.Struct ->
// isZero() 로 해당 필드가 값이 설정되지 않은 것으로 판단해
// 다음과 같이 default 값으로 insert 구문을 생성해 실행한다.
// INSERT INTO `test_info` (`age`,`name`,`last_date`,`enable`,`id`) VALUES (99,'lemon','2020-10-20 23:37:54',true,89)

// 만약 bool = false, int = 0, string = "" 인 상태로 저장하고 싶다면
// default:xxx 태그를 명시하면 안된다.
// 사실 Name="", Age=0, Enable=false 자체가 값을 설정한것이지만 
// gorm 에서는 go reflect IsZero() 로 타입별 값 설정이 안되었다고 판단한다.
// IsZero 는 값 존재가 아니라 그 타입의 값이 0(false)인지를 파악한다.
// 테스트 코드

#####

// gorm 버전업에 따른 repo 및 연결/초기 설정 api 변경사항
// gorm v1.9.16 이전 방식
import github.com/jinzhu/gorm v1.9.13
db, err := gorm.Open("mysql", DSN)

// 테이블명이 a_b_xxs 처럼 복수형 이름을 사용하지 않게 설정
db.SingularTable(true)

// 수행한 쿼리 stdout 출력
db.LogMode(true)

// gorm v1.9.16 이후 방식
import gorm.io/driver/mysql v1.0.2
import gorm.io/gorm v1.20.2

db, err := gorm.Open(mysql.Open(DSN), 
    &gorm.Config{
        NamingStrategy: schema.NamingStrategy{SingularTable: true},
        Logger: logger.Default.LogMode(logger.Info)
})