Back to Blog

GORM in Golang: Structs to Database Tables Made Easy | Go Tutorial #34

Sandy LaneSandy Lane

Video: GORM in Golang: Structs to Database Tables Made Easy | Go Tutorial #34 by Taught by Celeste AI - AI Coding Coach

Watch full page →

GORM in Golang: Structs to Database Tables Made Easy

GORM is a powerful ORM library for Go that simplifies database interactions by mapping Go structs directly to database tables. Using GORM with a pure Go SQLite driver allows you to perform CRUD operations, manage associations, and leverage advanced features like hooks and transactions without needing CGO.

Code

package main

import (
  "gorm.io/driver/sqlite"
  "gorm.io/gorm"
  "time"
)

// User struct with embedded gorm.Model for ID and timestamps
type User struct {
  gorm.Model
  Name  string
  Email string
  Posts []Post // Has Many association
}

// Post struct with foreign key UserID for Belongs To association
type Post struct {
  gorm.Model
  Title  string
  Body   string
  UserID uint
}

func main() {
  // Connect to SQLite database file.db
  db, err := gorm.Open(sqlite.Open("file.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // Auto migrate User and Post structs to create tables
  db.AutoMigrate(&User{}, &Post{})

  // Create a user with posts in one nested create call
  user := User{
    Name:  "Alice",
    Email: "alice@example.com",
    Posts: []Post{
      {Title: "First Post", Body: "Hello GORM!"},
      {Title: "Second Post", Body: "GORM is great!"},
    },
  }
  db.Create(&user) // ID and timestamps auto-populated

  // Query first user by primary key
  var queriedUser User
  db.Preload("Posts").First(&queriedUser, user.ID) // Eager load posts

  // Update user's email
  db.Model(&queriedUser).Update("Email", "alice_new@example.com")

  // Soft delete user (sets DeletedAt)
  db.Delete(&queriedUser)

  // Query users where soft deleted is null (default)
  var activeUsers []User
  db.Find(&activeUsers)

  // Query including soft deleted users
  var allUsers []User
  db.Unscoped().Find(&allUsers)

  // Use a transaction with automatic rollback on error
  err = db.Transaction(func(tx *gorm.DB) error {
    // Create a new post
    post := Post{Title: "Transactional Post", Body: "Inside transaction", UserID: user.ID}
    if err := tx.Create(&post).Error; err != nil {
      return err // rollback
    }
    // Update user name
    if err := tx.Model(&user).Update("Name", "Alice Updated").Error; err != nil {
      return err // rollback
    }
    return nil // commit
  })
  if err != nil {
    panic("transaction failed: " + err.Error())
  }
}

Key Points

  • Use gorm.Open with sqlite.Open to connect to a SQLite database without CGO.
  • Embed gorm.Model in structs to get ID, CreatedAt, UpdatedAt, and soft delete support automatically.
  • AutoMigrate creates tables from structs and keeps schema in sync.
  • Use db.Create to insert records, db.First and db.Find to query, and db.Model().Update() to modify data.
  • Manage associations with foreign keys and preload related data for eager loading.
  • Soft deletes mark records as deleted without removing them; use db.Unscoped() to query or restore.
  • Leverage transactions with db.Transaction to ensure atomic operations with rollback on errors.