pages/ko_KR/docs/create.md
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
// 단일 레코드 생성
ctx := context.Background()
err := gorm.G[User](db).Create(ctx, &user) // Create로 데이터 포인터 전달
// 결과와 함께 생성
result := gorm.WithResult()
err := gorm.G[User](db, result).Create(ctx, &user)
user.ID // 삽입된 데이터의 기본 키 반환
result.Error // 에러 반환
result.RowsAffected // 삽입된 레코드 수 반환
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 생성할 데이터의 포인터 넘기기
user.ID // 삽입된 데이터의 기본 키 반환
result.Error // 에러 반환
result.RowsAffected // 삽입된 레코드 수 반환
Create() 함수를 통해 여러 레코드를 동시에 생성할 수도 있습니다.
users := []*User{
{Name: "Jinzhu", Age: 18, Birthday: time.Now()},
{Name: "Jackson", Age: 19, Birthday: time.Now()},
}
result := db.Create(users) // 레코드들이 들어갈 슬라이스를 넣어줍니다.
result.Error // 에러를 반환합니다.
result.RowsAffected // 슬라이스에 삽입된 레코드의 숫자를 반환합니다.
{% note warn %} 주의할점 구조체를 직접 'create' 함수에 넣지 마세요, 대신 포인터를 넣어주세요. {% endnote %}
Create a record and assign a value to the fields specified.
db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
Create a record and ignore the values for fields passed to omit.
db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
효율적으로 많은 레코드를 만들기 위해서, slice를 Create 메소드에 넣어주세요. GORM은 단 하나의 SQL 구문을 작성하여 모든 데이터를 삽입합니다 또한 hook methods, primary key 값 자동 삽입등이 이를 기점으로 하여 실행됩니다. 이를 일정한 배치 단위로 나누어서 트랜잭션으로 처리할 수 있습니다.
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
CreateInBatches를 활용하여 배치 사이즈를 결정할 수 있습니다, e.g:
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// batch size 100
db.CreateInBatches(users, 100)
Batch Insert는 Upset와 Create With Associtations에도 지원됩니다.
{% note warn %}
주의할점: GORM을 CreateBatchSize옵션과 함께 생성할 경우, 모든 INSERT구문은 레코드 생성시 해당 배치 옵션을 따를것입니다.
{% endnote %}
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
CreateBatchSize: 1000,
})
db := db.Session(&gorm.Session{CreateBatchSize: 1000})
users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}
db.Create(&users)
// INSERT INTO users xxx (5 batches)
// INSERT INTO pets xxx (15 batches)
GORM은 BeforeSave,BeforeCreate,AfterSave,AfterCreate등과 같은 사용자가 정의한 hook을 구현하여 사용할 수 있습니다. 이런 hook 메소드들은 레코드를 만들때 호출 됩니다, Hooks를 참고하여 좀 더 자세한 생명주기에 관하여 참고해보세요.
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New()
if u.Role == "admin" {
return errors.New("invalid role")
}
return
}
만일 Hooks 메소드들을 생략하고 싶으시다면, SkipHooks를 session mode에서 활용할 수 있습닏, 예시:
DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)
GORM은 map[string] interface{} 및 []map[string] interface{}{}를 활용한 레코드 생성을 지원합니다, 예시:
db.Model(&User{}).Create(map[string]interface{}{
"Name": "jinzhu", "Age": 18,
})
// `[]map[string]interface{}{}`를 활용한 batch insert
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "jinzhu_1", "Age": 18},
{"Name": "jinzhu_2", "Age": 20},
})
{% note warn %} 주의할점 map 자료구조를 활용한 레코드 생성시, hooks은 실행되지 않으며 기본키가 채워지지 안흥며 연결이 저장되지 않습니다. {% endnote %}
GORM은 SQL을 활용한 데이터 삽입을 지원합니다. SQL을 활용하기 위하여는 두 가지 방법을 사용할 수 있습니다. map[string] interface{} 혹은 사용자 정의 데이터 타입을 활용할 수 있습니다. 예시:
// map을 활용한 레코드 생성
db.Model(User{}).Create(map[string]interface{}{
"Name": "jinzhu",
"Location": clause.Expr{SQL: "ST_PointFromText(?)", Vars: []interface{}{"POINT(100 100)"}},
})
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"));
// 사용자 정의 타입을 활용한 데이터 생성
type Location struct {
X, Y int
}
// sql.Scanner 인터페이스의 구현을 검사
func (loc *Location) Scan(v interface{}) error {
// Scan a value into struct from database driver
}
func (loc Location) GormDataType() string {
return "geometry"
}
func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
return clause.Expr{
SQL: "ST_PointFromText(?)",
Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},
}
}
type User struct {
Name string
Location Location
}
db.Create(&User{
Name: "jinzhu",
Location: Location{X: 100, Y: 100},
})
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))
연관(associations) 이 있는 일부 데이터를 작성할 때 association 값이 0 값이 아닌 경우 해당 association이 upsert 되고 해당 Hooks 메소드가 호출됩니다.
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
type User struct {
gorm.Model
Name string
CreditCard CreditCard
}
db.Create(&User{
Name: "jinzhu",
CreditCard: CreditCard{Number: "411111111111"}
})
// INSERT INTO `users` ...
// INSERT INTO `credit_cards` ...
Select, Omit 를 사용하여 associations를 스킵 할 수 있습니다. 예시:
db.Omit("CreditCard").Create(&user)
// 모든 associations 무시
db.Omit(clause.Associations).Create(&user)
default태그를 사용하여 필드의 기본값을 정의 할 수 있습니다. 예를 들면 다음과 같습니다.
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}
Then the default value will be used when inserting into the database for zero-value fields
{% note warn %}
NOTE 0, '', false와 같은 값들은 지정된 기본값으로 데이터베이스에 저장되지 않습니다, 대신에 포인터 타입 혹은 Scanner/Value를 활용해주세요, 예시:
{% endnote %}
type User struct {
gorm.Model
Name string
Age *int `gorm:"default:18"`
Active sql.NullBool `gorm:"default:true"`
}
{% note warn %}
NOTE 데이터베이스에 기본 값 또는 가상/생성 값이 있는 필드에 대해default 태그를 설정해야 합니다. 데이터베이스 마이그레이션 할 때 기본 값을 정의하는 부분을 건너뛰려면 다음과 같이 default:(-) 를 사용할 수 있습니다. 예시:
{% endnote %}
type User struct {
ID string `gorm:"default:uuid_generate_v3()"` // db func
FirstName string
LastName string
Age uint8
FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
}
{% note warn %} NOTE****SQLite는 특정 레코드에 대하여 batch insert를 실행시 기본값을 지원하지 않습니다. SQLite insert 구문을 참고하세요. For example:
type Pet struct {
Name string `gorm:"default:cat"`
}
// SQLite는 다음과 같은 구문을 지원하지 않으므로 GORM은 잘못된 SQL을 생성하고 에러를 일으킬 것입니다.
// INSERT INTO `pets` (`name`) VALUES ("dog"),(DEFAULT) RETURNING `name`
db.Create(&[]Pet{{Name: "dog"}, {}})
A viable alternative is to assign default value to fields in the hook, e.g.
func (p *Pet) BeforeCreate(tx *gorm.DB) (err error) {
if p.Name == "" {
p.Name = "cat"
}
}
issues#6335를 통해 더 자세한 사항을 볼 수 있습니다. {% endnote %}
Virtual/generated 값을 활용하려면 creating/dupdating 권한을 비활성화해야할 수 있습니다, Field-Level Permission을 참고해 주세요.
GORM은 다른 데이터베이스와 상호 운용가능한 Upsert를 지원합니다.
import "gorm.io/gorm/clause"
// Do nothing on conflict
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)
// Update columns to default value on `id` conflict
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.Assignments(map[string]interface{}{"role": "user"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET ***; SQL Server
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE ***; MySQL
// Use SQL expression
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
}).Create(&users)
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `count`=GREATEST(count, VALUES(count));
// Update columns to new value on `id` conflict
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age); MySQL
// Update all columns to new value on conflict except primary keys and those columns having default values from sql func
db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&users)
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age), ...; MySQL
Also checkout FirstOrInit, FirstOrCreate on Advanced Query
Checkout Raw SQL and SQL Builder for more details