Basic create
Pass a pointer to a model struct to db.Create to insert a new row. GORM populates the primary key field on the struct after the insert.
type User struct {
gorm.Model
Name string
Email string
Age uint
}
user := User{Name: "Jinzhu", Email: "[email protected]", Age: 18}
result := db.Create(&user)
// user.ID is now populated with the inserted primary key
fmt.Println(user.ID) // e.g. 1
fmt.Println(result.Error) // nil on success
fmt.Println(result.RowsAffected) // 1
db.Create requires a pointer. Passing a value (non-pointer) will panic at runtime.
Create multiple records
Pass a slice pointer to insert multiple rows in a single statement.
users := []User{
{Name: "Jinzhu", Email: "[email protected]", Age: 18},
{Name: "Jackson", Email: "[email protected]", Age: 19},
{Name: "Jill", Email: "[email protected]", Age: 20},
}
result := db.Create(&users)
for _, u := range users {
fmt.Println(u.ID) // each ID is populated
}
fmt.Println(result.RowsAffected) // 3
Batch insert
Use CreateInBatches to split a large slice into smaller INSERT statements. This limits memory usage and avoids hitting database parameter limits.
var users []User
for i := 0; i < 10000; i++ {
users = append(users, User{Name: fmt.Sprintf("user_%d", i)})
}
// Insert in batches of 100
db.CreateInBatches(users, 100)
You can also configure a default batch size globally so that db.Create uses it automatically:
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
CreateBatchSize: 100,
})
// db.Create now inserts in batches of 100
db.Create(&users)
Batches larger than batchSize are wrapped in a transaction automatically. Set SkipDefaultTransaction: true on the config to opt out.
Select specific fields on create
Use Select to insert only the listed fields. All other fields are ignored.
user := User{Name: "Jinzhu", Email: "[email protected]", Age: 18}
// Only insert Name and Age; Email is omitted
db.Select("Name", "Age").Create(&user)
// INSERT INTO users (name, age, ...) VALUES ("Jinzhu", 18, ...)
Omit specific fields on create
Use Omit to exclude listed fields from the INSERT statement.
user := User{Name: "Jinzhu", Email: "[email protected]", Age: 18}
// Insert everything except Email
db.Omit("Email").Create(&user)
// INSERT INTO users (name, age, ...) VALUES ("Jinzhu", 18, ...)
You can also omit association fields by name:
// Omit the Languages many-to-many association
db.Omit("Languages.*").Create(&user)
// Omit the association and its join table entries entirely
db.Omit("Languages").Create(&user)
Save (upsert)
Save either inserts or updates a record depending on whether the primary key is set and non-zero.
// No primary key set → INSERT
user := User{Name: "Jinzhu", Age: 18}
db.Save(&user)
// INSERT INTO users (name, age, ...) VALUES (...)
// Primary key is set → UPDATE all fields
user.ID = 1
user.Name = "Jinzhu Updated"
db.Save(&user)
// UPDATE users SET name="Jinzhu Updated", age=18, updated_at=... WHERE id=1
Save performs a full-record update when the primary key is present. Every field — including zero-value fields — is written to the database. Use Updates when you only want to change specific columns.
When saving a slice, GORM uses an ON CONFLICT (id) DO UPDATE SET ... upsert:
users := []User{
{Model: gorm.Model{ID: 1}, Name: "Updated Jinzhu"},
{Name: "New User"},
}
db.Save(&users)
FirstOrCreate
FirstOrCreate fetches the first matching record, or creates it if none exists. The result is always populated.
var user User
// Get the first user named Jinzhu, or create one
result := db.FirstOrCreate(&user, User{Name: "Jinzhu"})
fmt.Println(user) // User{Name: "Jinzhu", ...}
fmt.Println(result.RowsAffected) // 1 if created, 0 if found
Use Attrs to set extra fields only when creating:
var user User
// Email is only applied if the record is not found
db.Where(User{Name: "non_existing"}).
Attrs(User{Email: "[email protected]"}).
FirstOrCreate(&user)
// user → User{Name: "non_existing", Email: "[email protected]"}
// (new record created)
db.Where(User{Name: "Jinzhu"}).
Attrs(User{Email: "[email protected]"}).
FirstOrCreate(&user)
// user → User{Name: "Jinzhu", ...}
// (existing record returned, Attrs value ignored)
Use Assign to apply fields regardless of whether the record was found or created. When the record already exists, Assign triggers an UPDATE:
var user User
db.Where(User{Name: "Jinzhu"}).
Assign(User{Email: "[email protected]"}).
FirstOrCreate(&user)
// If found: UPDATE users SET email="[email protected]" WHERE id=...
// user → User{Name: "Jinzhu", Email: "[email protected]"}
// result.RowsAffected → 1
FirstOrInit
FirstOrInit is identical to FirstOrCreate except it never writes to the database. It either returns the found record or initializes an in-memory struct.
var user User
// Finds an existing user, or initializes (but does NOT save) a new one
db.FirstOrInit(&user, User{Name: "non_existing"})
// user → User{Name: "non_existing"}
// Nothing is written to the database
// Attrs: applied only if the record is not found
db.Where(User{Name: "non_existing"}).
Attrs(User{Email: "[email protected]"}).
FirstOrInit(&user)
// user → User{Name: "non_existing", Email: "[email protected]"}
// Assign: always applied
db.Where(User{Name: "Jinzhu"}).
Assign(User{Email: "[email protected]"}).
FirstOrInit(&user)
// user → User{Name: "Jinzhu", Age: 20, Email: "[email protected]"}
// (still not saved — call db.Save(&user) to persist)
FirstOrInit is useful when you want to prepare a struct for display or validation before deciding whether to save it.
Error handling
All GORM methods return *gorm.DB. Check .Error to detect failures:
result := db.Create(&user)
if result.Error != nil {
// handle error
}