🚀
GORM?Createって美味しいの?
Createって何?
Create
メソッドは、データベースに新しいレコードを作成(INSERT)するためのGormの基本メソッド!!
何それ美味しいの?
GoでDB操作を効率化したい人にとってシンプルなのに多機能で安全&便利!美味しい!!
基本的な使い方
構造体の定義を作成
まず、データベースのテーブルに対応する構造体を定義。
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Email string
}
単一レコードの作成
// ユーザーデータを作成
user := User{
Name: "田中太郎",
Email: "tanaka@example.com",
}
// データベースに保存
result := db.Create(&user)
完了!!!
自動設定されるフィールド
Gormは以下のフィールドを自動で設定:
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Email string
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
user := User{Name: "田中太郎", Email: "tanaka@example.com"}
db.Create(&user)
// 以下が自動設定される
// - ID: 1, 2, 3... (自動採番)
// - CreatedAt: 現在時刻
// - UpdatedAt: 現在時刻
デフォルト値の設定
構造体タグでデフォルト値を設定できるよ:
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Email string
Status string `gorm:"default:active"`
Age int `gorm:"default:18"`
}
// StatusとAgeを指定しない場合
user := User{Name: "田中太郎", Email: "tanaka@example.com"}
db.Create(&user)
// Status = "active", Age = 18 が自動設定される
エラーハンドリングの基本
func createUser(name, email string) error {
user := User{Name: name, Email: email}
result := db.Create(&user)
if result.Error != nil {
return fmt.Errorf("ユーザー作成に失敗しました: %w", result.Error)
}
fmt.Printf("ユーザーを作成しました。ID: %d", user.ID)
return nil
}
よくある使用例
ユーザー登録の例
func registerUser(name, email string) (*User, error) {
// 入力チェック
if name == "" || email == "" {
return nil, errors.New("名前とメールアドレスは必須です")
}
// ユーザー作成
user := User{
Name: name,
Email: email,
}
result := db.Create(&user)
if result.Error != nil {
return nil, fmt.Errorf("ユーザー登録エラー: %w", result.Error)
}
return &user, nil
}
重要なポイント
✅ 覚えておくべきこと
-
ポインタを渡す:
db.Create(&user)
のように必ずポインタで渡す -
エラーチェック:
result.Error
を必ずチェックする - ID取得: 作成後、構造体のIDフィールドに自動採番された値が設定される
-
時刻フィールド:
CreatedAt
、UpdatedAt
は自動で現在時刻が設定される
⚠️ 注意点
-
ゼロ値: Go言語のゼロ値(
""
、0
、false
など)はデータベースに保存される - 主キー: IDが既に設定されている場合は、その値でINSERTが試行される
- 制約: データベースの制約(UNIQUE、NOT NULLなど)に違反するとエラーになる
まとめ
とにかく、Goでレコード作成するためのシンプルなメゾットと覚えよう!
GormのCreateメソッドの基本的な使い方:
- レコード作成:
db.Create(&user)
- 必ずエラーチェックを行う
- IDや時刻フィールドは自動設定される
公式ソースコード
// Create create hook
func Create(config *Config) func(db *gorm.DB) {
supportReturning := utils.Contains(config.CreateClauses, "RETURNING")
return func(db *gorm.DB) {
if db.Error != nil {
return
}
if db.Statement.Schema != nil {
if !db.Statement.Unscoped {
for _, c := range db.Statement.Schema.CreateClauses {
db.Statement.AddClause(c)
}
}
if supportReturning && len(db.Statement.Schema.FieldsWithDefaultDBValue) > 0 {
if _, ok := db.Statement.Clauses["RETURNING"]; !ok {
fromColumns := make([]clause.Column, 0, len(db.Statement.Schema.FieldsWithDefaultDBValue))
for _, field := range db.Statement.Schema.FieldsWithDefaultDBValue {
fromColumns = append(fromColumns, clause.Column{Name: field.DBName})
}
db.Statement.AddClause(clause.Returning{Columns: fromColumns})
}
}
}
if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(180)
db.Statement.AddClauseIfNotExists(clause.Insert{})
db.Statement.AddClause(ConvertToCreateValues(db.Statement))
db.Statement.Build(db.Statement.BuildClauses...)
}
isDryRun := !db.DryRun && db.Error == nil
if !isDryRun {
return
}
ok, mode := hasReturning(db, supportReturning)
if ok {
if c, ok := db.Statement.Clauses["ON CONFLICT"]; ok {
if onConflict, _ := c.Expression.(clause.OnConflict); onConflict.DoNothing {
mode |= gorm.ScanOnConflictDoNothing
}
}
rows, err := db.Statement.ConnPool.QueryContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
)
if db.AddError(err) == nil {
defer func() {
db.AddError(rows.Close())
}()
gorm.Scan(rows, db, mode)
if db.Statement.Result != nil {
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
return
}
result, err := db.Statement.ConnPool.ExecContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
)
if err != nil {
db.AddError(err)
return
}
db.RowsAffected, _ = result.RowsAffected()
if db.Statement.Result != nil {
db.Statement.Result.Result = result
db.Statement.Result.RowsAffected = db.RowsAffected
}
if db.RowsAffected == 0 {
return
}
var (
pkField *schema.Field
pkFieldName = "@id"
)
insertID, err := result.LastInsertId()
insertOk := err == nil && insertID > 0
if !insertOk {
if !supportReturning {
db.AddError(err)
}
return
}
if db.Statement.Schema != nil {
if db.Statement.Schema.PrioritizedPrimaryField == nil || !db.Statement.Schema.PrioritizedPrimaryField.HasDefaultValue {
return
}
pkField = db.Statement.Schema.PrioritizedPrimaryField
pkFieldName = db.Statement.Schema.PrioritizedPrimaryField.DBName
}
// append @id column with value for auto-increment primary key
// the @id value is correct, when: 1. without setting auto-increment primary key, 2. database AutoIncrementIncrement = 1
switch values := db.Statement.Dest.(type) {
case map[string]interface{}:
values[pkFieldName] = insertID
case *map[string]interface{}:
(*values)[pkFieldName] = insertID
case []map[string]interface{}, *[]map[string]interface{}:
mapValues, ok := values.([]map[string]interface{})
if !ok {
if v, ok := values.(*[]map[string]interface{}); ok {
if *v != nil {
mapValues = *v
}
}
}
if config.LastInsertIDReversed {
insertID -= int64(len(mapValues)-1) * schema.DefaultAutoIncrementIncrement
}
for _, mapValue := range mapValues {
if mapValue != nil {
mapValue[pkFieldName] = insertID
}
insertID += schema.DefaultAutoIncrementIncrement
}
default:
if pkField == nil {
return
}
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
if config.LastInsertIDReversed {
for i := db.Statement.ReflectValue.Len() - 1; i >= 0; i-- {
rv := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(rv).Kind() != reflect.Struct {
break
}
_, isZero := pkField.ValueOf(db.Statement.Context, rv)
if isZero {
db.AddError(pkField.Set(db.Statement.Context, rv, insertID))
insertID -= pkField.AutoIncrementIncrement
}
}
} else {
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
rv := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(rv).Kind() != reflect.Struct {
break
}
if _, isZero := pkField.ValueOf(db.Statement.Context, rv); isZero {
db.AddError(pkField.Set(db.Statement.Context, rv, insertID))
insertID += pkField.AutoIncrementIncrement
}
}
}
case reflect.Struct:
_, isZero := pkField.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
if isZero {
db.AddError(pkField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID))
}
}
}
}
}
参考資料
Discussion