Skip to main content

Go ORM Mapping

Object-Relational Mapping (ORM) in Go provides a way to work with databases using object-oriented concepts, making database operations more intuitive and less error-prone. GORM is the most popular ORM library for Go, providing a rich set of features for database operations. Understanding ORM mapping is essential for building maintainable and scalable database-driven applications. This comprehensive guide will teach you everything you need to know about ORM mapping in Go.

Understanding ORM Mapping

What Is ORM Mapping?

ORM mapping in Go provides an abstraction layer between your application code and the database. It provides:

  • Object-Oriented Database Operations - Working with databases using structs and methods
  • Automatic SQL Generation - ORM generates SQL queries automatically
  • Relationship Management - Handling complex relationships between entities
  • Migration Support - Managing database schema changes
  • Query Building - Building complex queries using method chaining

GORM Features

Model Definition

Defining database models using Go structs.

Relationship Mapping

Mapping relationships between different entities.

Migration Management

Handling database schema changes and migrations.

Basic GORM Setup

Installing and Configuring GORM

GORM Installation

Installing GORM and database drivers.

Database Connection

Connecting to databases using GORM.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// Basic GORM setup examples
fmt.Println("Basic GORM setup examples:")

// MySQL connection with GORM
func gormMySQLConnection() {
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to MySQL database: %v", err)
}

// Get underlying sql.DB
sqlDB, err := db.DB()
if err != nil {
log.Fatalf("Failed to get underlying sql.DB: %v", err)
}

// Configure connection pool
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(5)
sqlDB.SetConnMaxLifetime(5 * time.Minute)
sqlDB.SetConnMaxIdleTime(1 * time.Minute)

fmt.Println("GORM MySQL connection established successfully")

// Test connection
err = sqlDB.Ping()
if err != nil {
log.Fatalf("Failed to ping MySQL database: %v", err)
}

fmt.Println("MySQL connection test successful")
}

gormMySQLConnection()

// PostgreSQL connection with GORM
func gormPostgreSQLConnection() {
dsn := "user=postgres password=password host=localhost port=5432 dbname=testdb sslmode=disable"

db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to PostgreSQL database: %v", err)
}

// Get underlying sql.DB
sqlDB, err := db.DB()
if err != nil {
log.Fatalf("Failed to get underlying sql.DB: %v", err)
}

// Configure connection pool
sqlDB.SetMaxOpenConns(25)
sqlDB.SetMaxIdleConns(5)
sqlDB.SetConnMaxLifetime(5 * time.Minute)
sqlDB.SetConnMaxIdleTime(1 * time.Minute)

fmt.Println("GORM PostgreSQL connection established successfully")

// Test connection
err = sqlDB.Ping()
if err != nil {
log.Fatalf("Failed to ping PostgreSQL database: %v", err)
}

fmt.Println("PostgreSQL connection test successful")
}

gormPostgreSQLConnection()

// SQLite connection with GORM
func gormSQLiteConnection() {
dsn := "test.db"

db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to SQLite database: %v", err)
}

// Get underlying sql.DB
sqlDB, err := db.DB()
if err != nil {
log.Fatalf("Failed to get underlying sql.DB: %v", err)
}

// Configure connection pool
sqlDB.SetMaxOpenConns(1) // SQLite doesn't support multiple connections
sqlDB.SetMaxIdleConns(1)
sqlDB.SetConnMaxLifetime(0)
sqlDB.SetConnMaxIdleTime(0)

fmt.Println("GORM SQLite connection established successfully")

// Test connection
err = sqlDB.Ping()
if err != nil {
log.Fatalf("Failed to ping SQLite database: %v", err)
}

fmt.Println("SQLite connection test successful")
}

gormSQLiteConnection()
}

Model Definition

Basic Model Definition

Creating database models using Go structs.

Model Tags and Configuration

Using GORM tags for model configuration.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// Model definition examples
fmt.Println("Model definition examples:")

// Database connection
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}

// Basic model definition
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex;not null"`
Age int `gorm:"default:0"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Create table
err = db.AutoMigrate(&User{})
if err != nil {
log.Printf("Failed to migrate User table: %v", err)
} else {
fmt.Println("User table migrated successfully")
}

// Advanced model definition
type Product struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:200;not null;index"`
Description string `gorm:"type:text"`
Price float64 `gorm:"type:decimal(10,2);not null"`
Stock int `gorm:"default:0"`
CategoryID uint `gorm:"not null"`
IsActive bool `gorm:"default:true"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}

// Create table
err = db.AutoMigrate(&Product{})
if err != nil {
log.Printf("Failed to migrate Product table: %v", err)
} else {
fmt.Println("Product table migrated successfully")
}

// Model with custom table name
type Category struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null;uniqueIndex"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Set custom table name
func (Category) TableName() string {
return "product_categories"
}

// Create table
err = db.AutoMigrate(&Category{})
if err != nil {
log.Printf("Failed to migrate Category table: %v", err)
} else {
fmt.Println("Category table migrated successfully")
}

// Model with JSON field
type Order struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null"`
Items string `gorm:"type:json"`
Total float64 `gorm:"type:decimal(10,2);not null"`
Status string `gorm:"size:50;default:'pending'"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Create table
err = db.AutoMigrate(&Order{})
if err != nil {
log.Printf("Failed to migrate Order table: %v", err)
} else {
fmt.Println("Order table migrated successfully")
}

fmt.Println("All models defined and migrated successfully")
}

CRUD Operations with GORM

Create Operations

Single Record Creation

Creating single records using GORM.

Batch Creation

Creating multiple records efficiently.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// CRUD operations with GORM examples
fmt.Println("CRUD operations with GORM examples:")

// Database connection
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}

// Model definition
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex;not null"`
Age int `gorm:"default:0"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Auto migrate
err = db.AutoMigrate(&User{})
if err != nil {
log.Printf("Failed to migrate User table: %v", err)
return
}

// Single record creation
func createSingleUser() {
user := User{
Name: "John Doe",
Email: "[email protected]",
Age: 30,
}

result := db.Create(&user)
if result.Error != nil {
log.Printf("Failed to create user: %v", result.Error)
return
}

fmt.Printf("User created successfully - ID: %d\n", user.ID)
}

createSingleUser()

// Batch creation
func createBatchUsers() {
users := []User{
{Name: "Jane Smith", Email: "[email protected]", Age: 25},
{Name: "Bob Johnson", Email: "[email protected]", Age: 35},
{Name: "Alice Brown", Email: "[email protected]", Age: 28},
}

result := db.Create(&users)
if result.Error != nil {
log.Printf("Failed to create users: %v", result.Error)
return
}

fmt.Printf("Users created successfully - Count: %d\n", result.RowsAffected)
for _, user := range users {
fmt.Printf(" User ID: %d, Name: %s\n", user.ID, user.Name)
}
}

createBatchUsers()

// Create with specific fields
func createUserWithSpecificFields() {
user := User{
Name: "Charlie Wilson",
Email: "[email protected]",
Age: 40,
}

result := db.Select("Name", "Email", "Age").Create(&user)
if result.Error != nil {
log.Printf("Failed to create user: %v", result.Error)
return
}

fmt.Printf("User created with specific fields - ID: %d\n", user.ID)
}

createUserWithSpecificFields()

// Create with Omit
func createUserWithOmit() {
user := User{
Name: "David Lee",
Email: "[email protected]",
Age: 32,
}

result := db.Omit("Age").Create(&user)
if result.Error != nil {
log.Printf("Failed to create user: %v", result.Error)
return
}

fmt.Printf("User created with omitted fields - ID: %d\n", user.ID)
}

createUserWithOmit()
}

Read Operations

Basic Queries

Retrieving data using GORM queries.

Advanced Queries

Using complex query conditions and joins.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// Read operations with GORM examples
fmt.Println("Read operations with GORM examples:")

// Database connection
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}

// Model definition
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex;not null"`
Age int `gorm:"default:0"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Auto migrate
err = db.AutoMigrate(&User{})
if err != nil {
log.Printf("Failed to migrate User table: %v", err)
return
}

// Find all users
func findAllUsers() {
var users []User
result := db.Find(&users)
if result.Error != nil {
log.Printf("Failed to find users: %v", result.Error)
return
}

fmt.Printf("Found %d users:\n", len(users))
for _, user := range users {
fmt.Printf(" ID: %d, Name: %s, Email: %s, Age: %d\n", user.ID, user.Name, user.Email, user.Age)
}
}

findAllUsers()

// Find user by ID
func findUserByID() {
var user User
result := db.First(&user, 1)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
fmt.Println("User not found")
return
}
log.Printf("Failed to find user: %v", result.Error)
return
}

fmt.Printf("User found - ID: %d, Name: %s, Email: %s, Age: %d\n", user.ID, user.Name, user.Email, user.Age)
}

findUserByID()

// Find users with conditions
func findUsersWithConditions() {
var users []User
result := db.Where("age > ?", 25).Find(&users)
if result.Error != nil {
log.Printf("Failed to find users: %v", result.Error)
return
}

fmt.Printf("Found %d users with age > 25:\n", len(users))
for _, user := range users {
fmt.Printf(" ID: %d, Name: %s, Age: %d\n", user.ID, user.Name, user.Age)
}
}

findUsersWithConditions()

// Find users with multiple conditions
func findUsersWithMultipleConditions() {
var users []User
result := db.Where("age BETWEEN ? AND ?", 25, 35).Where("name LIKE ?", "%John%").Find(&users)
if result.Error != nil {
log.Printf("Failed to find users: %v", result.Error)
return
}

fmt.Printf("Found %d users with multiple conditions:\n", len(users))
for _, user := range users {
fmt.Printf(" ID: %d, Name: %s, Age: %d\n", user.ID, user.Name, user.Age)
}
}

findUsersWithMultipleConditions()

// Find users with ordering and limiting
func findUsersWithOrderingAndLimiting() {
var users []User
result := db.Order("age DESC").Limit(3).Find(&users)
if result.Error != nil {
log.Printf("Failed to find users: %v", result.Error)
return
}

fmt.Printf("Found %d users ordered by age (DESC), limited to 3:\n", len(users))
for _, user := range users {
fmt.Printf(" ID: %d, Name: %s, Age: %d\n", user.ID, user.Name, user.Age)
}
}

findUsersWithOrderingAndLimiting()

// Count users
func countUsers() {
var count int64
result := db.Model(&User{}).Count(&count)
if result.Error != nil {
log.Printf("Failed to count users: %v", result.Error)
return
}

fmt.Printf("Total users: %d\n", count)
}

countUsers()

// Count users with conditions
func countUsersWithConditions() {
var count int64
result := db.Model(&User{}).Where("age > ?", 30).Count(&count)
if result.Error != nil {
log.Printf("Failed to count users: %v", result.Error)
return
}

fmt.Printf("Users with age > 30: %d\n", count)
}

countUsersWithConditions()
}

Update Operations

Single Record Updates

Updating single records using GORM.

Batch Updates

Updating multiple records efficiently.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// Update operations with GORM examples
fmt.Println("Update operations with GORM examples:")

// Database connection
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}

// Model definition
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex;not null"`
Age int `gorm:"default:0"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Auto migrate
err = db.AutoMigrate(&User{})
if err != nil {
log.Printf("Failed to migrate User table: %v", err)
return
}

// Update single record
func updateSingleUser() {
var user User
result := db.First(&user, 1)
if result.Error != nil {
log.Printf("Failed to find user: %v", result.Error)
return
}

user.Age = 31
result = db.Save(&user)
if result.Error != nil {
log.Printf("Failed to update user: %v", result.Error)
return
}

fmt.Printf("User updated successfully - ID: %d, Age: %d\n", user.ID, user.Age)
}

updateSingleUser()

// Update with conditions
func updateUserWithConditions() {
result := db.Model(&User{}).Where("age = ?", 25).Update("age", 26)
if result.Error != nil {
log.Printf("Failed to update users: %v", result.Error)
return
}

fmt.Printf("Users updated successfully - Rows affected: %d\n", result.RowsAffected)
}

updateUserWithConditions()

// Update multiple fields
func updateMultipleFields() {
result := db.Model(&User{}).Where("id = ?", 1).Updates(User{Name: "John Updated", Age: 32})
if result.Error != nil {
log.Printf("Failed to update user: %v", result.Error)
return
}

fmt.Printf("User updated with multiple fields - Rows affected: %d\n", result.RowsAffected)
}

updateMultipleFields()

// Update with map
func updateWithMap() {
result := db.Model(&User{}).Where("id = ?", 2).Updates(map[string]interface{}{
"name": "Jane Updated",
"age": 27,
})
if result.Error != nil {
log.Printf("Failed to update user: %v", result.Error)
return
}

fmt.Printf("User updated with map - Rows affected: %d\n", result.RowsAffected)
}

updateWithMap()

// Batch update
func batchUpdate() {
result := db.Model(&User{}).Where("age < ?", 30).Update("age", gorm.Expr("age + ?", 1))
if result.Error != nil {
log.Printf("Failed to batch update users: %v", result.Error)
return
}

fmt.Printf("Users batch updated successfully - Rows affected: %d\n", result.RowsAffected)
}

batchUpdate()
}

Delete Operations

Soft Delete

Using GORM's soft delete feature.

Hard Delete

Permanently deleting records.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// Delete operations with GORM examples
fmt.Println("Delete operations with GORM examples:")

// Database connection
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}

// Model definition with soft delete
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex;not null"`
Age int `gorm:"default:0"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}

// Auto migrate
err = db.AutoMigrate(&User{})
if err != nil {
log.Printf("Failed to migrate User table: %v", err)
return
}

// Soft delete single record
func softDeleteUser() {
var user User
result := db.First(&user, 1)
if result.Error != nil {
log.Printf("Failed to find user: %v", result.Error)
return
}

result = db.Delete(&user)
if result.Error != nil {
log.Printf("Failed to delete user: %v", result.Error)
return
}

fmt.Printf("User soft deleted successfully - ID: %d\n", user.ID)
}

softDeleteUser()

// Soft delete with conditions
func softDeleteWithConditions() {
result := db.Where("age < ?", 25).Delete(&User{})
if result.Error != nil {
log.Printf("Failed to delete users: %v", result.Error)
return
}

fmt.Printf("Users soft deleted with conditions - Rows affected: %d\n", result.RowsAffected)
}

softDeleteWithConditions()

// Hard delete (permanent)
func hardDeleteUser() {
result := db.Unscoped().Where("id = ?", 2).Delete(&User{})
if result.Error != nil {
log.Printf("Failed to hard delete user: %v", result.Error)
return
}

fmt.Printf("User hard deleted successfully - Rows affected: %d\n", result.RowsAffected)
}

hardDeleteUser()

// Find soft deleted records
func findSoftDeletedRecords() {
var users []User
result := db.Unscoped().Where("deleted_at IS NOT NULL").Find(&users)
if result.Error != nil {
log.Printf("Failed to find soft deleted users: %v", result.Error)
return
}

fmt.Printf("Found %d soft deleted users:\n", len(users))
for _, user := range users {
fmt.Printf(" ID: %d, Name: %s, DeletedAt: %v\n", user.ID, user.Name, user.DeletedAt)
}
}

findSoftDeletedRecords()

// Restore soft deleted record
func restoreSoftDeletedRecord() {
result := db.Unscoped().Model(&User{}).Where("id = ?", 1).Update("deleted_at", nil)
if result.Error != nil {
log.Printf("Failed to restore user: %v", result.Error)
return
}

fmt.Printf("User restored successfully - Rows affected: %d\n", result.RowsAffected)
}

restoreSoftDeletedRecord()
}

Relationships

Understanding GORM Relationships

One-to-One Relationships

Defining and working with one-to-one relationships.

One-to-Many Relationships

Implementing one-to-many relationships.

package main

import (
"fmt"
"log"
"time"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

func main() {
// Relationships with GORM examples
fmt.Println("Relationships with GORM examples:")

// Database connection
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}

// One-to-One relationship
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:100;not null"`
Email string `gorm:"size:100;uniqueIndex;not null"`
Profile Profile `gorm:"foreignKey:UserID"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

type Profile struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null;uniqueIndex"`
Bio string `gorm:"type:text"`
Location string `gorm:"size:100"`
Website string `gorm:"size:200"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// One-to-Many relationship
type Post struct {
ID uint `gorm:"primaryKey"`
Title string `gorm:"size:200;not null"`
Content string `gorm:"type:text"`
UserID uint `gorm:"not null"`
User User `gorm:"foreignKey:UserID"`
Comments []Comment `gorm:"foreignKey:PostID"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

type Comment struct {
ID uint `gorm:"primaryKey"`
Content string `gorm:"type:text;not null"`
PostID uint `gorm:"not null"`
UserID uint `gorm:"not null"`
Post Post `gorm:"foreignKey:PostID"`
User User `gorm:"foreignKey:UserID"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}

// Auto migrate
err = db.AutoMigrate(&User{}, &Profile{}, &Post{}, &Comment{})
if err != nil {
log.Printf("Failed to migrate tables: %v", err)
return
}

// Create user with profile
func createUserWithProfile() {
user := User{
Name: "John Doe",
Email: "[email protected]",
Profile: Profile{
Bio: "Software developer",
Location: "New York",
Website: "https://johndoe.com",
},
}

result := db.Create(&user)
if result.Error != nil {
log.Printf("Failed to create user with profile: %v", result.Error)
return
}

fmt.Printf("User with profile created successfully - ID: %d\n", user.ID)
}

createUserWithProfile()

// Create user with posts
func createUserWithPosts() {
user := User{
Name: "Jane Smith",
Email: "[email protected]",
Posts: []Post{
{Title: "First Post", Content: "This is my first post"},
{Title: "Second Post", Content: "This is my second post"},
},
}

result := db.Create(&user)
if result.Error != nil {
log.Printf("Failed to create user with posts: %v", result.Error)
return
}

fmt.Printf("User with posts created successfully - ID: %d\n", user.ID)
}

createUserWithPosts()

// Preload relationships
func preloadRelationships() {
var users []User
result := db.Preload("Profile").Preload("Posts").Find(&users)
if result.Error != nil {
log.Printf("Failed to find users with relationships: %v", result.Error)
return
}

fmt.Printf("Found %d users with relationships:\n", len(users))
for _, user := range users {
fmt.Printf(" User: %s, Email: %s\n", user.Name, user.Email)
if user.Profile.ID != 0 {
fmt.Printf(" Profile: %s, Location: %s\n", user.Profile.Bio, user.Profile.Location)
}
fmt.Printf(" Posts: %d\n", len(user.Posts))
for _, post := range user.Posts {
fmt.Printf(" - %s\n", post.Title)
}
}
}

preloadRelationships()

// Create post with comments
func createPostWithComments() {
var user User
result := db.First(&user)
if result.Error != nil {
log.Printf("Failed to find user: %v", result.Error)
return
}

post := Post{
Title: "Post with Comments",
Content: "This post has comments",
UserID: user.ID,
Comments: []Comment{
{Content: "Great post!", UserID: user.ID},
{Content: "Thanks for sharing!", UserID: user.ID},
},
}

result = db.Create(&post)
if result.Error != nil {
log.Printf("Failed to create post with comments: %v", result.Error)
return
}

fmt.Printf("Post with comments created successfully - ID: %d\n", post.ID)
}

createPostWithComments()

// Query with joins
func queryWithJoins() {
var posts []Post
result := db.Joins("User").Joins("Comments").Find(&posts)
if result.Error != nil {
log.Printf("Failed to find posts with joins: %v", result.Error)
return
}

fmt.Printf("Found %d posts with joins:\n", len(posts))
for _, post := range posts {
fmt.Printf(" Post: %s by %s\n", post.Title, post.User.Name)
fmt.Printf(" Comments: %d\n", len(post.Comments))
for _, comment := range post.Comments {
fmt.Printf(" - %s\n", comment.Content)
}
}
}

queryWithJoins()
}

What You've Learned

Congratulations! You now have a comprehensive understanding of Go's ORM mapping capabilities:

Basic GORM Setup

  • Installing and configuring GORM
  • Connecting to different database systems
  • Configuring connection pools and settings

Model Definition

  • Creating database models using Go structs
  • Using GORM tags for model configuration
  • Implementing custom table names and field types

CRUD Operations

  • Creating records with GORM
  • Reading data with various query methods
  • Updating records with different approaches
  • Deleting records with soft and hard delete

Relationships

  • Implementing one-to-one relationships
  • Creating one-to-many relationships
  • Using preloading and joins
  • Managing complex relationship queries

Key Concepts

  • GORM - Go's most popular ORM library
  • Model Definition - Struct-based database models
  • CRUD Operations - Create, Read, Update, Delete operations
  • Relationships - Database relationship mapping
  • Migrations - Database schema management

Next Steps

You now have a solid foundation in Go's ORM mapping capabilities. In the next section, we'll explore database best practices, which will help you optimize your database operations and implement security measures.

Understanding ORM mapping is crucial for building maintainable database-driven applications. These concepts form the foundation for all the more advanced database operations we'll cover in the coming sections.


Ready to learn about database best practices? Let's explore performance optimization, security measures, and advanced database patterns!