orm

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 29, 2025 License: BSD-3-Clause Imports: 9 Imported by: 0

README

Go ORM

A lightweight, chainable Go ORM library focused on providing a clean and intuitive SQL building experience.

中文文档

Features & Advantages

  • Chainable API Design: Intuitive method chaining for clear and readable SQL construction
  • Zero Dependencies: Core functionality has no external dependencies
  • Highly Flexible: Supports model mapping, raw SQL, subqueries, and CTEs
  • DryRun Mode: Preview generated SQL without execution for easy debugging
  • Complete SQL Support: SELECT, INSERT, UPDATE, DELETE operations with advanced query features
  • Smart Struct-to-Table Mapping: Flexible mapping between Go structs and database tables

Installation

go get github.com/bobacgo/orm

Quick Start

Define a Model
type User struct {
    ID   int
    Name string
    Age  int
}

func (m *User) Mapping() []*Mapping {
    return []*Mapping{
        {"id", &m.ID, m.ID},
        {"name", &m.Name, m.Name},
        {"age", &m.Age, m.Age},
    }
}
Basic Query Examples
// Query a single model
user := &User{}
SELECT1(user).FROM("users").WHERE(map[string]any{"AND id = ?": 1}).Query(ctx, db)

// Query multiple models
var users []*User
SELECT2(&users).FROM("users").WHERE(map[string]any{"AND age > ?": 25}).Query(ctx, db)

// Insert data
user := &User{Name: "John", Age: 30}
INSERT(user).INTO("users").Exec(ctx, db)

// Update data
UPDATE("users").SET(map[string]any{"age": 31}).WHERE(map[string]any{"AND name = ?": "John"}).Exec(ctx, db)

// Delete data
DELETE().FROM("users").WHERE(map[string]any{"AND name = ?": "John"}).Exec(ctx, db)

Advanced Features

JOIN Queries
SELECT("u.id", "p.product_name").
    FROM("users u").
    JOIN("products p").
    ON("u.id = p.user_id").
    Query(ctx, db)
Aggregate Functions
var count sql.Null[int64]
SELECT1(COUNT[int64]("*", &count)).FROM("users").Query(ctx, db)
Common Table Expressions (CTE)
cte := WITH("user_cte").
    AS(SELECT("id", "name").FROM("users").WHERE(map[string]any{"AND age > ?": 25}).SQL()).
    SQL()

SELECT("id", "name").FROM("user_cte").CTE(cte).Exec(ctx, db)
Subqueries
subquery := SELECT("id", "name").
    FROM("users").
    WHERE(map[string]any{"AND age > ?": 30}).
    SQL()

SELECT("id", "name").FROM("(" + subquery + ") AS old_users").Exec(ctx, db)

Struct-to-Table Mapping

The ORM provides a flexible mapping system between Go structs and database tables:

// Define a struct with custom field mapping
type Product struct {
    ProductID   int
    Name        string
    Description string
    Price       float64
}

// Implement the Mapping method for custom control
func (p *Product) Mapping() []*Mapping {
    return []*Mapping{
        {"product_id", &p.ProductID, p.ProductID},
        {"product_name", &p.Name, p.Name},
        {"description", &p.Description, p.Description},
        {"price", &p.Price, p.Price},
    }
}
Mapping Advantages
  • Full Control: Define exactly how struct fields map to table columns
  • Bidirectional Mapping: Both reading from and writing to the database
  • Value Tracking: Keeps track of both pointers and values for efficient updates
  • Custom Column Names: Map struct fields to differently named database columns
  • Flexible Implementation: Implement the Mapping interface your way

Differentiation

  • Intuitive SQL Building: API design mimics SQL syntax for readability
  • Lightweight Design: Focused on SQL building without complex relationship mapping
  • Query Composition: Support for subqueries, CTEs, and other advanced features
  • DryRun Debugging: Preview generated SQL without execution
  • Type Safety: Leverages Go generics for type-safe query building and result mapping

## Usage

Here are some examples of how to use the ORM:

### Model Definition

First, define a model that corresponds to your database table.

```go
type ExampleModel struct {
	ID   int
	Name string
	Age  int
}

func (m *ExampleModel) Mapping() []*Mapping {
	return []*Mapping{
		{"id", &m.ID, m.ID},
		{"name", &m.Name, m.Name},
		{"age", &m.Age, m.Age},
	}
}
SELECT Queries
// Simple SELECT
row := &ExampleModel{}
SELECT1(row).FROM("users").WHERE(map[string]any{"id = ?": 1}).DryRun(context.Background())

// SELECT with JOIN
SELECT("u.id", "p.product_name").FROM("users u").JOIN("products p ON u.id = p.user_id").DryRun(context.Background())

// SELECT with aggregate function
var count sql.Null[int64]
SELECT1(COUNT[int64]("*", &count)).FROM("users").DryRun(context.Background())

// SELECT with CTE (Common Table Expression)
cte := WITH("user_cte").AS(SELECT("id", "name").FROM("users").WHERE(map[string]any{"age > ?": 25}).SQL()).SQL()
SELECT("id", "name").FROM("user_cte").CTE(cte).DryRun(context.Background())

// SELECT with Subquery
SELECT("id", "name").FROM("(SELECT id, name FROM users WHERE age > 30) AS old_users").DryRun(context.Background())
INSERT Queries
// Simple INSERT
INSERT1().INTO("users").COLUMNS("name", "age").VALUES("John Doe", 30).DryRun(context.Background())

// INSERT from model
user := &ExampleModel{Name: "Jane Doe", Age: 25}
INSERT(user).INTO("users").DryRun(context.Background())
UPDATE Queries
// Simple UPDATE
UPDATE("users").SET(map[string]any{"age = ?": 31}).WHERE(map[string]any{"name = ?": "John Doe"}).DryRun(context.Background())

// UPDATE from model
user := &ExampleModel{ID: 1, Age: 32}
UPDATE("users").SET1(user).WHERE(map[string]any{"id = ?": user.ID}).DryRun(context.Background())
DELETE Queries
// Simple DELETE
DELETE().FROM("users").WHERE(map[string]any{"name = ?": "John Doe"}).DryRun(context.Background())
TRANSACTION
// Simple Transaction
// BEGIN;
// UPDATE users SET balance = 30 WHERE 1 = 1 AND name = 'Jane Doe';
// UPDATE users SET balance = 0 WHERE 1 = 1 AND name = 'John Doe';
// COMMIT;
tx := Tx(func(ctx context.Context, tx *sql.Tx) error {
    if _, err := UPDATE("users").SET(map[string]any{"balance": 30}).WHERE(map[string]any{"AND name = ?": "Jane Doe"}).Exec(ctx, tx); err != nil {return fmt.Errorf("UPDATE Jane Doe : %w", err)
        return fmt.Errorf("UPDATE Jane Doe : %w", err)
    }
    if _, err := UPDATE("users").SET(map[string]any{"balance": 0}).WHERE(map[string]any{"AND name = ?": "John Doe"}).Exec(ctx, tx); err != nil {
        return fmt.Errorf("UPDATE John Doe : %w", err)
    }
    return nil
})
if err := tx.Exec(context.Background(), db); err != nil {
    slog.Error("tx.Do", "err", err)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AVG

func AVG[T number](col string, v *sql.Null[T]) *avg[T]

func COUNT

func COUNT[T number](col string, v *sql.Null[T]) *count[T]

func MAX

func MAX[T number](col string, v *sql.Null[T]) *max[T]

func MIN

func MIN[T number](col string, v *sql.Null[T]) *min[T]

func RunExamples

func RunExamples(db *sql.DB)

RunExamples executes a series of example queries.

func SUM

func SUM[T number](col string, v *sql.Null[T]) *sum[T]

Types

type Cte

type Cte struct {
	// contains filtered or unexported fields
}

func WITH

func WITH(tmpTable string) *Cte

func (*Cte) AS

func (c *Cte) AS(selectSQL string) *Cte

func (*Cte) SQL

func (c *Cte) SQL() string

func (*Cte) WITH

func (c *Cte) WITH(tmpTable string) *Cte

type Delete

type Delete struct {
	// contains filtered or unexported fields
}

func DELETE

func DELETE() *Delete

func (*Delete) Debug

func (d *Delete) Debug() *Delete

func (*Delete) DryRun

func (d *Delete) DryRun(ctx context.Context) (int64, error)

func (*Delete) Exec

func (d *Delete) Exec(ctx context.Context, db Execer) (int64, error)

func (*Delete) FROM

func (d *Delete) FROM(table string) *Delete

func (*Delete) SQL

func (d *Delete) SQL() string

func (*Delete) WHERE

func (d *Delete) WHERE(where map[string]any) *Delete

type ExampleModel

type ExampleModel struct {
	ID   int
	Name string
	Age  int
}

func (*ExampleModel) Mapping

func (m *ExampleModel) Mapping() []*Mapping

type Execer

type Execer interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}

sql.DB | sql.Tx

type Insert

type Insert[T Model] struct {
	// contains filtered or unexported fields
}

func INSERT

func INSERT[T Model](rows ...T) *Insert[T]

func INSERT1

func INSERT1() *Insert[*emptyModel]

func (*Insert[T]) COLUMNS

func (d *Insert[T]) COLUMNS(cols ...string) *Insert[T]

func (*Insert[T]) Debug

func (d *Insert[T]) Debug() *Insert[T]

func (*Insert[T]) DryRun

func (d *Insert[T]) DryRun(ctx context.Context) (int64, error)

func (*Insert[T]) Exec

func (d *Insert[T]) Exec(ctx context.Context, db Execer) (int64, error)

func (*Insert[T]) INTO

func (d *Insert[T]) INTO(table string) *Insert[T]

func (*Insert[T]) ON

func (d *Insert[T]) ON(conflict string) *Insert[T]

func (*Insert[T]) Omit

func (d *Insert[T]) Omit(cols ...string) *Insert[T]

Omit 忽略更新字段

func (*Insert[T]) SQL

func (d *Insert[T]) SQL() string

func (*Insert[T]) UPDATE

func (d *Insert[T]) UPDATE(conds map[string]any) *Insert[T]

func (*Insert[T]) VALUES

func (d *Insert[T]) VALUES(args ...any) *Insert[T]

type M

type M map[string]any

type Mapping

type Mapping struct {
	Column string
	Result any // query result (pointer)
	Value  any // insert, update value
}

type Model

type Model interface {
	Mapping() []*Mapping
}

type PModel

type PModel[T any] interface {
	*T
	Model
}

type set(类型集)约束 定义一个约束:PModel 表示 “某个类型 T 的指针,并且实现了 Model”

type SelectModel

type SelectModel struct {
	// contains filtered or unexported fields
}

func SELECT1

func SELECT1(row Model) *SelectModel

func (SelectModel) CROSS_JOIN

func (d SelectModel) CROSS_JOIN(table string) *T

func (SelectModel) CTE

func (d SelectModel) CTE(cte string) *T

func (SelectModel) Debug

func (d SelectModel) Debug() *T

func (SelectModel) DryRun

func (d SelectModel) DryRun(ctx context.Context) error

func (SelectModel) FOR

func (d SelectModel) FOR(lock string) *T

func (SelectModel) FROM

func (d SelectModel) FROM(table string) *T

func (SelectModel) FULL_JOIN

func (d SelectModel) FULL_JOIN(table string) *T

func (SelectModel) GROUP_BY

func (d SelectModel) GROUP_BY(cols ...string) *T

func (SelectModel) HAVING

func (d SelectModel) HAVING(cond string) *T

func (SelectModel) INNER_JOIN

func (d SelectModel) INNER_JOIN(table string) *T

func (SelectModel) JOIN

func (d SelectModel) JOIN(table string) *T

func (SelectModel) LEFT_JOIN

func (d SelectModel) LEFT_JOIN(table string) *T

func (SelectModel) LIMIT

func (d SelectModel) LIMIT(limit int64) *T

func (SelectModel) OFFSET

func (d SelectModel) OFFSET(offset int64) *T

func (SelectModel) ON

func (d SelectModel) ON(condition string) *T

func (SelectModel) ORDER_BY

func (d SelectModel) ORDER_BY(orders ...string) *T

func (SelectModel) Omit

func (d SelectModel) Omit(cols ...string) *T

Omit 忽略查询字段

func (*SelectModel) Query

func (d *SelectModel) Query(ctx context.Context, db Execer) error

func (SelectModel) RIGHT_JOIN

func (d SelectModel) RIGHT_JOIN(table string) *T

func (SelectModel) SQL

func (d SelectModel) SQL() string

func (SelectModel) UNION

func (d SelectModel) UNION(selet string) *T

func (SelectModel) UNION_ALL

func (d SelectModel) UNION_ALL(selet string) *T

func (SelectModel) WHERE

func (d SelectModel) WHERE(where map[string]any) *T

type SelectModels

type SelectModels[T any, P PModel[T]] struct {
	// contains filtered or unexported fields
}

func SELECT2

func SELECT2[T any, P PModel[T]](rows *[]*T) *SelectModels[T, P]

func (SelectModels) CROSS_JOIN

func (d SelectModels) CROSS_JOIN(table string) *T

func (SelectModels) CTE

func (d SelectModels) CTE(cte string) *T

func (SelectModels) Debug

func (d SelectModels) Debug() *T

func (SelectModels) DryRun

func (d SelectModels) DryRun(ctx context.Context) error

func (SelectModels) FOR

func (d SelectModels) FOR(lock string) *T

func (SelectModels) FROM

func (d SelectModels) FROM(table string) *T

func (SelectModels) FULL_JOIN

func (d SelectModels) FULL_JOIN(table string) *T

func (SelectModels) GROUP_BY

func (d SelectModels) GROUP_BY(cols ...string) *T

func (SelectModels) HAVING

func (d SelectModels) HAVING(cond string) *T

func (SelectModels) INNER_JOIN

func (d SelectModels) INNER_JOIN(table string) *T

func (SelectModels) JOIN

func (d SelectModels) JOIN(table string) *T

func (SelectModels) LEFT_JOIN

func (d SelectModels) LEFT_JOIN(table string) *T

func (SelectModels) LIMIT

func (d SelectModels) LIMIT(limit int64) *T

func (SelectModels) OFFSET

func (d SelectModels) OFFSET(offset int64) *T

func (SelectModels) ON

func (d SelectModels) ON(condition string) *T

func (SelectModels) ORDER_BY

func (d SelectModels) ORDER_BY(orders ...string) *T

func (SelectModels) Omit

func (d SelectModels) Omit(cols ...string) *T

Omit 忽略查询字段

func (*SelectModels[T, P]) Query

func (d *SelectModels[T, P]) Query(ctx context.Context, db Execer) error

func (SelectModels) RIGHT_JOIN

func (d SelectModels) RIGHT_JOIN(table string) *T

func (SelectModels) SQL

func (d SelectModels) SQL() string

func (SelectModels) UNION

func (d SelectModels) UNION(selet string) *T

func (SelectModels) UNION_ALL

func (d SelectModels) UNION_ALL(selet string) *T

func (SelectModels) WHERE

func (d SelectModels) WHERE(where map[string]any) *T

type SelectString

type SelectString struct {
	// contains filtered or unexported fields
}

func SELECT

func SELECT(cols ...string) *SelectString

func (SelectString) CROSS_JOIN

func (d SelectString) CROSS_JOIN(table string) *T

func (SelectString) CTE

func (d SelectString) CTE(cte string) *T

func (SelectString) Debug

func (d SelectString) Debug() *T

func (SelectString) DryRun

func (d SelectString) DryRun(ctx context.Context) error

func (SelectString) FOR

func (d SelectString) FOR(lock string) *T

func (SelectString) FROM

func (d SelectString) FROM(table string) *T

func (SelectString) FULL_JOIN

func (d SelectString) FULL_JOIN(table string) *T

func (SelectString) GROUP_BY

func (d SelectString) GROUP_BY(cols ...string) *T

func (SelectString) HAVING

func (d SelectString) HAVING(cond string) *T

func (SelectString) INNER_JOIN

func (d SelectString) INNER_JOIN(table string) *T

func (SelectString) JOIN

func (d SelectString) JOIN(table string) *T

func (SelectString) LEFT_JOIN

func (d SelectString) LEFT_JOIN(table string) *T

func (SelectString) LIMIT

func (d SelectString) LIMIT(limit int64) *T

func (SelectString) OFFSET

func (d SelectString) OFFSET(offset int64) *T

func (SelectString) ON

func (d SelectString) ON(condition string) *T

func (SelectString) ORDER_BY

func (d SelectString) ORDER_BY(orders ...string) *T

func (SelectString) Omit

func (d SelectString) Omit(cols ...string) *T

Omit 忽略查询字段

func (SelectString) RIGHT_JOIN

func (d SelectString) RIGHT_JOIN(table string) *T

func (SelectString) SQL

func (d SelectString) SQL() string

func (SelectString) UNION

func (d SelectString) UNION(selet string) *T

func (SelectString) UNION_ALL

func (d SelectString) UNION_ALL(selet string) *T

func (SelectString) WHERE

func (d SelectString) WHERE(where map[string]any) *T

type Transaction

type Transaction struct {
	// contains filtered or unexported fields
}

func Tx

func Tx(fn func(ctx context.Context, tx *sql.Tx) error) *Transaction

func (*Transaction) Exec

func (t *Transaction) Exec(ctx context.Context, db *sql.DB) (err error)

func (*Transaction) WithOpts

func (t *Transaction) WithOpts(opts *sql.TxOptions) *Transaction

type Update

type Update struct {
	// contains filtered or unexported fields
}

func UPDATE

func UPDATE(tableName string) *Update

func (*Update) Debug

func (d *Update) Debug() *Update

func (*Update) DryRun

func (d *Update) DryRun(ctx context.Context) (int64, error)

func (*Update) Exec

func (d *Update) Exec(ctx context.Context, db Execer) (int64, error)

func (*Update) Omit

func (d *Update) Omit(cols ...string) *Update

Omit 忽略更新字段

func (*Update) SET

func (d *Update) SET(set map[string]any) *Update

不需要占位符的 SET 方法

func (*Update) SET1

func (d *Update) SET1(row Model) *Update

func (*Update) SQL

func (d *Update) SQL() string

func (*Update) WHERE

func (d *Update) WHERE(where map[string]any) *Update

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL