| name | GORM ORM |
| description | This skill should be used when the user asks to "setup GORM", "GORM models", "GORM migrations", "database operations", "GORM associations", "GORM hooks", or works with database ORM operations in Go. |
| version | 0.1.0 |
GORM ORM
GORM is a developer-friendly ORM library for Golang with full-featured capabilities.
Core Concepts
Model Definition
Define models using struct tags:
package model
import (
"gorm.io/gorm"
"time"
)
type Model struct {
ID uint `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`
}
type User struct {
Model
Name string `gorm:"size:255;not null" json:"name"`
Email string `gorm:"uniqueIndex;size:255;not null" json:"email"`
Age int `gorm:"default:0" json:"age"`
Profile Profile `gorm:"constraint:OnDelete:CASCADE;" json:"profile,omitempty"`
Orders []Order `json:"orders,omitempty"`
}
type Profile struct {
Model
UserID uint `gorm:"not null" json:"user_id"`
Bio string `gorm:"type:text" json:"bio"`
Address string `gorm:"size:500" json:"address"`
}
type Order struct {
Model
UserID uint `gorm:"not null" json:"user_id"`
Product string `gorm:"size:255;not null" json:"product"`
Amount float64 `gorm:"type:decimal(10,2)" json:"amount"`
Status string `gorm:"size:50;default:'pending'" json:"status"`
}
Database Connection
package database
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
var DB *gorm.DB
func InitMySQL(dsn string) (*gorm.DB, error) {
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Info,
IgnoreRecordNotFoundError: true,
Colorful: true,
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
return nil, fmt.Errorf("failed to connect to MySQL: %w", err)
}
DB = db
return db, nil
}
func InitPostgres(dsn string) (*gorm.DB, error) {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to connect to PostgreSQL: %w", err)
}
DB = db
return db, nil
}
func InitSQLite(dbPath string) (*gorm.DB, error) {
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("failed to connect to SQLite: %w", err)
}
DB = db
return db, nil
}
const (
MySQLDSN = "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local"
PostgresDSN = "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai"
)
CRUD Operations
Create:
user := &model.User{Name: "John", Email: "john@example.com"}
result := db.Create(user)
if result.Error != nil {
return result.Error
}
db.Select("Name", "Email").Create(user)
users := []model.User{
{Name: "Alice", Email: "alice@example.com"},
{Name: "Bob", Email: "bob@example.com"},
}
db.Create(&users)
Read:
var user model.User
db.First(&user, 1)
db.First(&user, "id = ?", 1)
db.Where("name = ?", "John").First(&user)
db.Where("age > ?", 18).Find(&users)
db.Where("name LIKE ?", "%John%").Find(&users)
db.Where("age > ? AND email LIKE ?", 18, "%@example.com").Find(&users)
db.Where(map[string]interface{}{"name": "John", "age": 30}).Find(&users)
db.Order("age desc, name").Find(&users)
db.Limit(10).Offset(20).Find(&users)
db.Distinct("name").Find(&users)
var names []string
db.Model(&model.User{}).Pluck("name", &names)
db.Model(&model.User{}).Where("age > ?", 18).Count(&count)
Update:
db.Model(&user).Update("name", "Jane")
db.Model(&user).Updates(map[string]interface{}{"name": "Jane", "age": 30})
db.Model(&user).Updates(model.User{Name: "Jane", Age: 30})
db.Model(&user).Select("name", "age").Updates(...)
db.Model(&model.User{}).Where("age > ?", 18).Update("status", "adult")
Delete:
db.Delete(&user)
db.Delete(&model.User{}, 1)
db.Where("age < ?", 18).Delete(&model.User{})
db.Unscoped().Delete(&user)
db.Unscoped().Where("age = 20").Find(&users)
Associations
Preload (Eager Loading):
db.Preload("Profile").Preload("Orders").Find(&users)
db.Preload("Orders", "status = ?", "completed").Find(&users)
db.Preload("Orders.Items").Find(&users)
Create with Associations:
user := model.User{
Name: "John",
Email: "john@example.com",
Profile: model.Profile{
Bio: "Software Developer",
},
Orders: []model.Order{
{Product: "Book", Amount: 29.99},
{Product: "Pen", Amount: 5.99},
},
}
db.Create(&user)
Transactions
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user).Error; err != nil {
return err
}
if err := tx.Create(&orders).Error; err != nil {
return err
}
return nil
})
tx := db.Begin()
if err := tx.Create(&user).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
Hooks
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New().String()
return
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
return
}
func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
u.UpdatedAt = time.Now()
return
}
Migrations
db.AutoMigrate(&model.User{}, &model.Order{}, &model.Profile{})
hasTable := db.Migrator().HasTable(&model.User{})
db.Migrator().CreateTable(&model.User{})
db.Migrator().DropTable(&model.User{})
db.Migrator().AddColumn(&model.User{}, "Age")
db.Migrator().CreateIndex(&model.User{}, "Name")
Best Practices
- Use context in production - Pass ctx for cancellation support
- Handle errors explicitly - Always check result.Error
- Use struct tags for column customization
- Enable query logging in development
- Use transactions for multi-table operations
- Leverage soft deletes with gorm.DeletedAt
Additional Resources
references/model-patterns.md - Advanced model patterns
references/query-optimization.md - Performance optimization
examples/basic-crud/ - CRUD operation examples