Database Initialization
Import the primary library and the specific database driver before establishing a connection.
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
Configure the Data Source Name (DSN) and instantiate the client. The configuration object allows customization of naming conventions and other behaviors.
import (
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
connConfig := "user:password@tcp(localhost:3306)/target_db?charset=utf8mb4&parseTime=True&loc=Local"
databaseClient, err := gorm.Open(mysql.Open(connConfig), &gorm.Config{
NamingStrategy: schema.NamingStrategy{SingularTable: true},
})
if err != nil {
log.Fatalf("Database initialization failed: %v", err)
}
Model Definitions and Table Binding
Define Go structs to map directly to relational tables. By default, GORM uses the pluralized type name. You can explicitly specify a tible name via the Table method or struct tags.
type AccountRecord struct {
ID uint
Email string
FullName string
AgeGroup int
}
var records []AccountRecord
var fetchedData []AccountRecord
// Query using an explicit table identifier
databaseClient.Table("account_profiles").Where("full_name = ?", "Alice").Find(&fetchedData)
// Query using the bound model struct
databaseClient.Model(&records).Where("full_name = ?", "Bob").Find(&fetchedData)
Result Scanning and Struct Mapping
When retrieving multiple rows, the Scan function maps query results into a target slice. This is particularly useful when you need to transform column names or select a subset of fields into a different data structure.
type RawSource struct {
UserID uint
ContactEmail string
DisplayName string
AgeRange int
}
type ViewPayload struct {
Nickname string
Category int
}
var sources []RawSource
var payload []ViewPayload
// Execute query and map directly to a secondary structure
databaseClient.Where("age_range > 18 OR contact_email LIKE ?", "%@domain.com").Find(&sources).Scan(&payload)
If the target struct field names differ from the SQL column aliases, utilize the gorm tag to define explicit mappings.
type ViewPayload struct {
Nickname string `gorm:"column:display_name"`
Category int
}
Ordering Results
Apply sorting directives using the Order clause. Append ASC for ascending sequences or DESC for descending arrangements.
// Highest values first
databaseClient.Order("age_range DESC").Find(&sources)
// Lowest values first
databaseClient.Order("contact_email ASC").Find(&sources)
Eager Loading Associations
Retreive related entities alongside the primary record using Preload. This prevents the N+1 query problem by joining associated data in a single pass.
type Author struct {
ID uint
Name string
Books []Book `gorm:"foreignKey:AuthorID"`
}
func loadWithContext(client *gorm.DB) {
var writer Author
client.Preload("Books").First(&writer)
fmt.Printf("%+v\n", writer)
}
Pagination Implementation
Control result set size by combining Limit and Offset. Calculate the skip amount based on zero-indexed page numbers.
chunkSize := 5
pageNumber := 2
skipAmount := (pageNumber - 1) * chunkSize
var pageResults []AccountRecord
databaseClient.Limit(chunkSize).Offset(skipAmount).Find(&pageResults)
Subquery Construction
Embed internal queries within filter clauses using parentheses. The inner query executes first, providing dynamic threshold values.
type Product struct {
SKU uint
Price int
}
var expensiveProducts []Product
databaseClient.Model(&Product{}).
Where("price > (?)", databaseClient.Model(&Product{}).Select("AVG(price)"))
.Find(&expensiveProducts)
Filtering Techniques
Exact Match and Negation
Standard equality checks, exclusion filters, and list-based searches are handled efficiently.
var matched []AccountRecord
// Standard lookup
databaseClient.Find(&matched, "full_name = ?", "Charlie")
// Exclusion pattern
databaseClient.Not("full_name = ?", "Charlie").Find(&matched)
// Row modification counter
affectedRows := databaseClient.Not("full_name = ?", "Charlie").Delete(&matched)
fmt.Println(affectedRows.RowsAffected)
// Multi-value inclusion
databaseClient.Where("full_name IN ?", []string{"David", "Eva"}).Find(&matched)
Pattern Matching and Compound Conditions
Utilize wildcard operators for string searches and chain boolean operators for complex filters.
// Prefix search
databaseClient.Where("full_name LIKE ?", "A%").Find(&matched)
// Fixed-length prefix (matches two characters)
databaseClient.Where("full_name LIKE ?", "B_").Find(&matched)
// Conjunction (AND)
databaseClient.Where("age_range > 25 AND full_name LIKE ?", "%Corp").Find(&matched)
// Equivalent chained approach
databaseClient.Where("age_range > 25").Where("full_name LIKE ?", "%Inc").Find(&matched)
// Disjunction (OR)
databaseClient.Where("age_range < 20 OR full_name LIKE ?", "%Admin").Find(&matched)
// Equivalent chained approach
databaseClient.Where("age_range < 20").Or("full_name LIKE ?", "%Super").Find(&matched)
Managing One-to-Many Relationships
GORM distinguishes between owning and belonging to an entity. Foreign keys can be customized via struct tags to reflect schema requirements.
type Publisher struct {
PubID uint
Brand string
Titles []Edition `gorm:"foreignKey:PubID"`
}
type Edition struct {
ISBN uint
Cover string
PubID uint
Source Publisher `gorm:"foreignKey:PubID"`
}
Insert nested collections atomically to create both parent and child records in a single transaction.
vol1 := Edition{Cover: "Hardbound"}
vol2 := Edition{Cover: "Paperback"}
publishingHouse := Publisher{Brand: "Academic Press", Titles: []Edition{vol1, vol2}}
databaseClient.Debug().Create(&publishingHouse)
Asssociate new children with pre-existing parents by referencing the external identifier or fetching the parent record first.
databaseClient.Create(&Edition{
Cover: "Special Release",
PubID: 999,
})
// Alternative: resolve parent dynamically
var activePublisher Publisher
databaseClient.First(&activePublisher, "pub_id = ?", 102)
databaseClient.Create(&Edition{Cover: "Upcoming", PubID: activePublisher.PubID})