Beego ORM

Beego ORM is "an ORM that enhances Go application productivity with Django-like APIs" developed as a core component of the popular Beego web framework in the Go ecosystem. With intuitive API design inspired by Django and SQLAlchemy, it simplifies database operations through a Code First approach. Supporting MySQL, PostgreSQL, and SQLite3, it efficiently handles complex query operations and relationship management while maintaining type safety.

ORMGoGolangDatabaseBeegoSQLModelFramework

GitHub Overview

beego/beego

beego is an open-source, high-performance web framework for the Go programming language.

Stars32,157
Watchers1,168
Forks5,630
Created:February 29, 2012
Language:Go
License:Other

Topics

beegogo

Star History

beego/beego Star History
Data as of: 7/19/2025, 08:12 AM

Library

Beego ORM

Overview

Beego ORM is "an ORM that enhances Go application productivity with Django-like APIs" developed as a core component of the popular Beego web framework in the Go ecosystem. With intuitive API design inspired by Django and SQLAlchemy, it simplifies database operations through a Code First approach. Supporting MySQL, PostgreSQL, and SQLite3, it efficiently handles complex query operations and relationship management while maintaining type safety.

Details

Beego ORM 2025 edition has established itself as the standard database layer solution for enterprise-level web application development in Go. It provides comprehensive features including struct-based model definitions, automatic table creation, query generation, and transaction management. Tightly integrated with Go's type system, it minimizes runtime errors through compile-time type checking. Leveraging reflection and code generation, it automatically maps Go structs to database tables, enabling the construction of highly maintainable data access layers.

Key Features

  • Code First Approach: Automatic database table generation from Go struct definitions
  • Rich Database Support: Comprehensive support for MySQL, PostgreSQL, and SQLite3
  • Type-Safe Queries: Safe database operations leveraging Go's type system
  • Automatic Join Functionality: Automatic join query generation for related tables
  • Raw SQL Support: Plain SQL execution capabilities for complex queries
  • Transaction Management: Transaction functionality with automatic commit/rollback

Pros and Cons

Pros

  • High type safety through excellent integration with Go's standard type system
  • Reduced learning costs and improved development efficiency with Django-like APIs
  • Rapid prototyping and schema management through automatic table creation features
  • Flexible database operations through rich query builder functionality
  • Consistent development experience through complete integration with Beego framework
  • Improved maintainability through reflection-based automatic mapping

Cons

  • Performance constraints due to reflection overhead when processing large amounts of data
  • ORM-specific error handling that differs from Go's conventional error handling
  • Limitations in expressing complex SQL queries requiring Raw SQL in some cases
  • Manual management required for schema migration when model structures change
  • Limited access to database-specific optimization features
  • High integration costs when used outside the Beego framework

Reference Pages

Code Examples

Basic Setup

package main

import (
    "github.com/beego/beego/v2/client/orm"
    _ "github.com/go-sql-driver/mysql"    // MySQL driver
    _ "github.com/lib/pq"                 // PostgreSQL driver
    _ "github.com/mattn/go-sqlite3"       // SQLite3 driver
)

func init() {
    // Register database
    orm.RegisterDataBase("default", "mysql", 
        "user:password@tcp(localhost:3306)/database?charset=utf8mb4&parseTime=True&loc=Local", 30)
    
    // For PostgreSQL
    // orm.RegisterDataBase("default", "postgres", 
    //     "host=localhost port=5432 user=postgres password=password dbname=mydb sslmode=disable")
    
    // For SQLite3
    // orm.RegisterDataBase("default", "sqlite3", "path/to/database.db")
    
    // Register models
    orm.RegisterModel(new(User), new(Profile), new(Post))
    
    // Auto-create tables (development only)
    orm.RunSyncdb("default", false, true)
}

func main() {
    // Get ORM instance
    o := orm.NewOrm()
    
    // Basic usage example
    user := &User{Name: "John Doe", Email: "[email protected]"}
    id, err := o.Insert(user)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("User created with ID: %d\n", id)
}

Model Definition and Basic Operations

package models

import (
    "time"
    "github.com/beego/beego/v2/client/orm"
)

// User model
type User struct {
    Id       int       `orm:"auto"`                           // Auto-increment primary key
    Name     string    `orm:"size(100)"`                     // String length limit
    Email    string    `orm:"unique"`                        // Unique constraint
    Age      int       `orm:"null"`                          // Allow NULL
    IsActive bool      `orm:"default(true)"`                 // Default value
    Created  time.Time `orm:"auto_now_add;type(datetime)"`   // Auto-set on creation
    Updated  time.Time `orm:"auto_now;type(datetime)"`       // Auto-set on update
    
    // Relationships
    Profile *Profile `orm:"rel(one)"`        // One-to-one relationship
    Posts   []*Post  `orm:"reverse(many)"`   // One-to-many relationship (reverse)
}

// Profile model
type Profile struct {
    Id   int    `orm:"auto"`
    Bio  string `orm:"type(text);null"`
    User *User  `orm:"reverse(one)"`  // Reverse one-to-one
}

// Post model
type Post struct {
    Id       int       `orm:"auto"`
    Title    string    `orm:"size(200)"`
    Content  string    `orm:"type(text)"`
    Published bool     `orm:"default(false)"`
    Created  time.Time `orm:"auto_now_add;type(datetime)"`
    User     *User     `orm:"rel(fk)"`  // Foreign key
}

// Custom table name
func (u *User) TableName() string {
    return "users"
}

// CRUD operations implementation
func CreateUser(name, email string, age int) (*User, error) {
    o := orm.NewOrm()
    
    user := &User{
        Name:  name,
        Email: email,
        Age:   age,
    }
    
    // Insert operation
    id, err := o.Insert(user)
    if err != nil {
        return nil, err
    }
    
    user.Id = int(id)
    return user, nil
}

func GetUserByID(id int) (*User, error) {
    o := orm.NewOrm()
    
    user := &User{Id: id}
    err := o.Read(user)
    if err != nil {
        return nil, err
    }
    
    return user, nil
}

func GetUserByEmail(email string) (*User, error) {
    o := orm.NewOrm()
    
    user := &User{}
    err := o.QueryTable("user").Filter("email", email).One(user)
    if err != nil {
        return nil, err
    }
    
    return user, nil
}

func UpdateUser(user *User) error {
    o := orm.NewOrm()
    
    _, err := o.Update(user)
    return err
}

func DeleteUser(id int) error {
    o := orm.NewOrm()
    
    user := &User{Id: id}
    _, err := o.Delete(user)
    return err
}

// Validation (custom implementation)
func (u *User) Valid() error {
    if len(u.Name) < 2 {
        return fmt.Errorf("name must be at least 2 characters")
    }
    
    if !strings.Contains(u.Email, "@") {
        return fmt.Errorf("invalid email format")
    }
    
    if u.Age < 0 || u.Age > 150 {
        return fmt.Errorf("age must be between 0 and 150")
    }
    
    return nil
}

Advanced Query Operations

package services

import (
    "fmt"
    "github.com/beego/beego/v2/client/orm"
)

// Complex query operations
func GetUsersWithConditions(minAge, maxAge int, namePattern string) ([]*User, error) {
    o := orm.NewOrm()
    var users []*User
    
    qs := o.QueryTable("user")
    
    // Filter by age range
    if minAge > 0 {
        qs = qs.Filter("age__gte", minAge)
    }
    if maxAge > 0 {
        qs = qs.Filter("age__lte", maxAge)
    }
    
    // Partial name match
    if namePattern != "" {
        qs = qs.Filter("name__icontains", namePattern)
    }
    
    // Active users only
    qs = qs.Filter("is_active", true)
    
    // Sort
    qs = qs.OrderBy("-created")
    
    // Get results
    _, err := qs.All(&users)
    if err != nil {
        return nil, err
    }
    
    return users, nil
}

// Aggregate queries
func GetUserStatistics() (map[string]interface{}, error) {
    o := orm.NewOrm()
    
    stats := make(map[string]interface{})
    
    // Total users
    totalUsers, err := o.QueryTable("user").Count()
    if err != nil {
        return nil, err
    }
    stats["total_users"] = totalUsers
    
    // Active users
    activeUsers, err := o.QueryTable("user").Filter("is_active", true).Count()
    if err != nil {
        return nil, err
    }
    stats["active_users"] = activeUsers
    
    // Average age (using Raw SQL)
    var avgAge float64
    err = o.Raw("SELECT AVG(age) FROM users WHERE age > 0").QueryRow(&avgAge)
    if err != nil {
        return nil, err
    }
    stats["average_age"] = avgAge
    
    return stats, nil
}

// Fetch with relations
func GetUserWithProfile(userID int) (*User, error) {
    o := orm.NewOrm()
    
    user := &User{Id: userID}
    err := o.Read(user)
    if err != nil {
        return nil, err
    }
    
    // Load profile information
    _, err = o.LoadRelated(user, "Profile")
    if err != nil {
        return nil, err
    }
    
    return user, nil
}

func GetUsersWithPosts() ([]*User, error) {
    o := orm.NewOrm()
    var users []*User
    
    // Fetch users and posts together
    _, err := o.QueryTable("user").RelatedSel().All(&users)
    if err != nil {
        return nil, err
    }
    
    // Load posts for each user
    for _, user := range users {
        _, err = o.LoadRelated(user, "Posts")
        if err != nil {
            return nil, err
        }
    }
    
    return users, nil
}

// Conditional updates
func UpdateUsersStatus(condition map[string]interface{}, status bool) (int64, error) {
    o := orm.NewOrm()
    
    qs := o.QueryTable("user")
    
    // Apply dynamic conditions
    for key, value := range condition {
        qs = qs.Filter(key, value)
    }
    
    // Bulk update
    return qs.Update(orm.Params{
        "is_active": status,
        "updated":   time.Now(),
    })
}

// Pagination
func GetUsersPaginated(page, pageSize int) ([]*User, int64, error) {
    o := orm.NewOrm()
    var users []*User
    
    qs := o.QueryTable("user").Filter("is_active", true).OrderBy("-created")
    
    // Get total count
    total, err := qs.Count()
    if err != nil {
        return nil, 0, err
    }
    
    // Apply pagination
    offset := (page - 1) * pageSize
    _, err = qs.Limit(pageSize, offset).All(&users)
    if err != nil {
        return nil, 0, err
    }
    
    return users, total, nil
}

Relationship Operations

// One-to-one relationship operations
func CreateUserWithProfile(name, email, bio string) (*User, error) {
    o := orm.NewOrm()
    
    // Begin transaction
    err := o.Begin()
    if err != nil {
        return nil, err
    }
    
    // Create user
    user := &User{
        Name:  name,
        Email: email,
    }
    
    id, err := o.Insert(user)
    if err != nil {
        o.Rollback()
        return nil, err
    }
    user.Id = int(id)
    
    // Create profile
    profile := &Profile{
        Bio:  bio,
        User: user,
    }
    
    _, err = o.Insert(profile)
    if err != nil {
        o.Rollback()
        return nil, err
    }
    
    // Commit
    err = o.Commit()
    if err != nil {
        return nil, err
    }
    
    user.Profile = profile
    return user, nil
}

// One-to-many relationship operations
func CreatePostForUser(userID int, title, content string) (*Post, error) {
    o := orm.NewOrm()
    
    // Check user existence
    user := &User{Id: userID}
    err := o.Read(user)
    if err != nil {
        return nil, fmt.Errorf("user not found: %v", err)
    }
    
    // Create post
    post := &Post{
        Title:   title,
        Content: content,
        User:    user,
    }
    
    id, err := o.Insert(post)
    if err != nil {
        return nil, err
    }
    post.Id = int(id)
    
    return post, nil
}

// Join operations
func GetPostsWithUserInfo() ([]map[string]interface{}, error) {
    o := orm.NewOrm()
    var maps []orm.Params
    
    // Join query
    _, err := o.Raw(`
        SELECT p.id, p.title, p.content, p.published, p.created,
               u.name as user_name, u.email as user_email
        FROM posts p
        INNER JOIN users u ON p.user_id = u.id
        WHERE p.published = ?
        ORDER BY p.created DESC
    `, true).Values(&maps)
    
    if err != nil {
        return nil, err
    }
    
    // Convert to map[string]interface{}
    results := make([]map[string]interface{}, len(maps))
    for i, m := range maps {
        results[i] = make(map[string]interface{})
        for k, v := range m {
            results[i][k] = v
        }
    }
    
    return results, nil
}

Practical Examples

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"
    
    "github.com/beego/beego/v2/client/orm"
    "github.com/beego/beego/v2/server/web"
)

// Controller implementation
type UserController struct {
    web.Controller
}

// GET /users - Get user list
func (c *UserController) GetAll() {
    page, _ := strconv.Atoi(c.GetString("page", "1"))
    pageSize, _ := strconv.Atoi(c.GetString("page_size", "10"))
    
    users, total, err := GetUsersPaginated(page, pageSize)
    if err != nil {
        c.Data["json"] = map[string]interface{}{
            "error": err.Error(),
        }
        c.Ctx.ResponseWriter.WriteHeader(http.StatusInternalServerError)
        c.ServeJSON()
        return
    }
    
    c.Data["json"] = map[string]interface{}{
        "users": users,
        "total": total,
        "page":  page,
        "page_size": pageSize,
    }
    c.ServeJSON()
}

// GET /users/:id - Get user details
func (c *UserController) Get() {
    idStr := c.Ctx.Input.Param(":id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        c.Data["json"] = map[string]interface{}{
            "error": "Invalid user ID",
        }
        c.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
        c.ServeJSON()
        return
    }
    
    user, err := GetUserWithProfile(id)
    if err != nil {
        c.Data["json"] = map[string]interface{}{
            "error": "User not found",
        }
        c.Ctx.ResponseWriter.WriteHeader(http.StatusNotFound)
        c.ServeJSON()
        return
    }
    
    c.Data["json"] = user
    c.ServeJSON()
}

// POST /users - Create user
func (c *UserController) Post() {
    var userRequest struct {
        Name  string `json:"name"`
        Email string `json:"email"`
        Age   int    `json:"age"`
        Bio   string `json:"bio"`
    }
    
    err := json.Unmarshal(c.Ctx.Input.RequestBody, &userRequest)
    if err != nil {
        c.Data["json"] = map[string]interface{}{
            "error": "Invalid JSON format",
        }
        c.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
        c.ServeJSON()
        return
    }
    
    // Validation
    if userRequest.Name == "" || userRequest.Email == "" {
        c.Data["json"] = map[string]interface{}{
            "error": "Name and email are required",
        }
        c.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
        c.ServeJSON()
        return
    }
    
    // Create user
    user, err := CreateUserWithProfile(userRequest.Name, userRequest.Email, userRequest.Bio)
    if err != nil {
        c.Data["json"] = map[string]interface{}{
            "error": err.Error(),
        }
        c.Ctx.ResponseWriter.WriteHeader(http.StatusInternalServerError)
        c.ServeJSON()
        return
    }
    
    c.Data["json"] = user
    c.Ctx.ResponseWriter.WriteHeader(http.StatusCreated)
    c.ServeJSON()
}

// Database initialization
func InitDatabase() {
    // Create test data for development environment
    o := orm.NewOrm()
    
    // Create sample users
    users := []*User{
        {Name: "Alice Johnson", Email: "[email protected]", Age: 28},
        {Name: "Bob Smith", Email: "[email protected]", Age: 35},
        {Name: "Carol Brown", Email: "[email protected]", Age: 22},
    }
    
    for _, user := range users {
        _, err := o.Insert(user)
        if err != nil {
            log.Printf("Failed to create user %s: %v", user.Name, err)
        } else {
            log.Printf("Created user: %s", user.Name)
        }
    }
}

// Main function
func main() {
    // Routing configuration
    web.Router("/users", &UserController{}, "get:GetAll;post:Post")
    web.Router("/users/:id", &UserController{}, "get:Get")
    
    // Initialize database
    InitDatabase()
    
    log.Println("Server starting on :8080")
    web.Run(":8080")
}