Eloquent ORM for Go
February 13, 2026 · View on GitHub
English | 中文文档
Eloquent is a code-generation based database ORM framework for Go, inspired by the Eloquent ORM in the PHP framework Laravel. Supports MySQL and PostgreSQL.
Table of Contents
- Core Concepts
- Quick Start
- Installation
- Model Definition & Code Generation
- Database Dialect (MySQL / PostgreSQL)
- Database Connection
- CRUD Operations
- Query Builder
- Model Relationships
- Soft Deletes
- Scopes
- Database Migrations
- Transactions
- Event System
- Direct Database Operations
- Example Projects
Core Concepts
Understanding these core concepts will help you get started with Eloquent quickly:
1. Code Generation Driven
Eloquent does not use runtime reflection to map database tables. Instead, it works through a YAML model definition -> CLI tool generates Go code workflow:
- Write a YAML file describing your database table structure
- Run the
eloquent gencommand to generate the corresponding Go code (*.orm.gofiles) - The generated code contains type-safe model structs and complete CRUD methods
2. Two Model Structs
For each model (e.g., User), Eloquent generates two structs:
UserN(Nullable version): All fields usenull.Int,null.String, etc. (fromgopkg.in/guregu/null.v3), correctly handlingNULLvalues from the database. This is the primary struct used for database interaction.User(Plain version): All fields use Go primitive types (int64,string, etc.). When a database field isNULL, it returns the zero value. Suitable for use in business logic.
They can be converted to each other:
// UserN -> User (Nullable -> Plain)
user := userN.ToUser()
// User -> UserN (Plain -> Nullable)
userN := user.ToUserN()
3. Query Builder (SQLBuilder)
All query conditions are built through query.Builder() chained calls, rather than writing raw SQL. The query builder supports Where, WhereIn, OrderBy, Limit, and other common operations.
4. Context
All database operations require a context.Context parameter, following Go best practices for timeout control and cancellation.
Quick Start
Here is a complete example showing the full workflow from model definition to database operations.
Step 1: Install the CLI Tool
go install github.com/mylxsw/eloquent/cmd/orm@latest
This gives you an orm CLI tool (you can also build the project yourself to get an eloquent binary).
Step 2: Create a Model Definition File
You can write YAML manually or use the CLI to generate a basic template:
eloquent create-model --table users --package models --soft-delete --output ./models/
Then edit the generated users.yaml file to add the fields you need:
package: models
models:
- name: User
definition:
table_name: users
soft_delete: true
fields:
- name: id
type: int64
tag: 'json:"id"'
- name: name
type: string
tag: 'json:"name"'
- name: email
type: string
tag: 'json:"email"'
- name: age
type: int64
tag: 'json:"age"'
Step 3: Generate Model Code
eloquent gen --source './models/*.yaml'
This generates a users.orm.go file in the same directory, containing UserN, User, UserModel, and all related code.
Note: Generated
*.orm.gofiles should not be manually modified, as they are regenerated each time thegencommand is run.
Step 4: Add Dependencies
go get github.com/mylxsw/eloquent
go get gopkg.in/guregu/null.v3
# For MySQL
go get github.com/go-sql-driver/mysql
# For PostgreSQL (use one of the following)
go get github.com/lib/pq
# or: go get github.com/jackc/pgx/v5
Step 5: Write Business Code
package main
import (
"context"
"database/sql"
"fmt"
"yourproject/models"
_ "github.com/go-sql-driver/mysql"
"gopkg.in/guregu/null.v3"
)
func main() {
db, err := sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/mydb?parseTime=true")
if err != nil {
panic(err)
}
defer db.Close()
ctx := context.TODO()
userModel := models.NewUserModel(db)
// Create a user
id, err := userModel.Save(ctx, models.UserN{
Name: null.StringFrom("Tom"),
Email: null.StringFrom("tom@example.com"),
Age: null.IntFrom(25),
})
fmt.Printf("New user ID: %d\n", id)
// Query a user
user, err := userModel.Find(ctx, id)
fmt.Printf("Name: %s, Email: %s\n", user.Name.String, user.Email.String)
// Convert to Plain struct
u := user.ToUser()
fmt.Printf("Name: %s, Age: %d\n", u.Name, u.Age)
}
Installation
Add the Eloquent dependency to your project:
go get github.com/mylxsw/eloquent
Build the CLI tool (for code generation):
# Option 1: Direct install
go install github.com/mylxsw/eloquent/cmd/orm@latest
# Option 2: Clone and build
git clone https://github.com/mylxsw/eloquent.git
cd eloquent
make build
# Binary is generated at ./bin/eloquent
Model Definition & Code Generation
YAML Model Definition File
Model definitions use YAML format. Here is a complete field reference:
package: models # Go package name for generated code
imports: # Additional packages to import (when fields use external types)
- github.com/mylxsw/eloquent
meta:
table_prefix: wz_ # Table prefix, automatically prepended to table names
models:
- name: User # Model name (generated struct name)
definition:
table_name: users # Database table name (defaults to snake_case of model name)
without_create_time: false # Set to true to skip adding created_at field
without_update_time: false # Set to true to skip adding updated_at field
soft_delete: false # Set to true to enable soft deletes (adds deleted_at field)
fields:
- name: id # Field name (snake_case)
type: int64 # Go type
tag: 'json:"id"' # Struct tag
- name: name
type: string
tag: 'json:"name"'
Supported Field Types
You can use all Go primitive types, as well as external types imported via imports:
int,int64,int32, and other integer typesfloat32,float64stringtime.Time- Custom types (declare the package path in
imports)
When
typeis not specified, it defaults tostring.
Automatic Fields
Unless explicitly disabled, Eloquent automatically adds the following fields to each model:
| Field | Type | Description | Disable With |
|---|---|---|---|
created_at | time.Time | Creation time, auto-set on insert | without_create_time: true |
updated_at | time.Time | Update time, auto-set on update | without_update_time: true |
deleted_at | time.Time | Deletion time, auto-set on soft delete | Only added when soft_delete: true |
Code Generation Commands
# Generate model code
eloquent gen --source './models/*.yaml'
# Create a model definition file template
eloquent create-model \
--table users \
--package models \
--output ./models/ \
--table-prefix wz_ \
--soft-delete \
--no-created_at \
--no-updated_at \
--import "github.com/mylxsw/eloquent"
What Gets Generated
For a User model, the generated code includes:
| Content | Description |
|---|---|
UserN struct | Nullable version, fields use null.Int, null.String, etc. |
User struct | Plain version, fields use Go primitive types |
UserModel | Model operation object, provides all CRUD methods |
NewUserModel(db) | Factory function to create a model instance |
FieldUser* constants | Constant definitions for each field name, e.g., FieldUserName = "name" |
UserFields() | Returns a slice of all field names |
UserTable() | Returns the table name |
| Relationship methods | Generated if relations are defined |
Database Dialect (MySQL / PostgreSQL)
Eloquent supports both MySQL and PostgreSQL through a dialect system. By default, MySQL dialect is used. To switch to PostgreSQL, set the dialects at application startup:
import (
"github.com/mylxsw/eloquent/query"
"github.com/mylxsw/eloquent/migrate"
)
// Switch to PostgreSQL mode (call once at startup, before any database operations)
query.SetDialect(query.PostgreSQLDialect{})
migrate.SetMigrateDialect(migrate.PostgreSQLMigrateDialect{})
The dialect system automatically handles the differences between MySQL and PostgreSQL:
| Feature | MySQL | PostgreSQL |
|---|---|---|
| Identifier quoting | `name` | "name" |
| Parameter placeholders | ? | $1, \$2, ... |
| Auto-increment primary key | AUTO_INCREMENT PRIMARY KEY | SERIAL PRIMARY KEY |
| Insert returning ID | LastInsertId() | INSERT ... RETURNING id |
| Engine / Charset / Collation | Supported | Ignored (not applicable) |
TINYINT(1) | TINYINT(1) | BOOLEAN |
BLOB | BLOB | BYTEA |
LONGTEXT / MEDIUMTEXT | As-is | TEXT |
JSON | JSON | JSONB |
DATETIME | DATETIME | TIMESTAMP |
UNSIGNED | Supported | Ignored (not applicable) |
| Index creation | ALTER TABLE ... ADD INDEX | CREATE INDEX ... ON |
| Drop index | ALTER TABLE ... DROP INDEX | DROP INDEX |
| Drop foreign key | DROP FOREIGN KEY | DROP CONSTRAINT |
Note: When using raw SQL in migrations (
Schema.Raw()), you are responsible for writing database-specific SQL yourself.
Database Connection
Eloquent uses Go's standard database/sql library to manage database connections.
MySQL
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
db, err := sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/mydb?parseTime=true")
if err != nil {
panic(err)
}
defer db.Close()
Important: You must include
parseTime=truein the connection string, otherwise time fields cannot be parsed correctly.
PostgreSQL
import (
"database/sql"
_ "github.com/lib/pq"
"github.com/mylxsw/eloquent/query"
"github.com/mylxsw/eloquent/migrate"
)
// Set dialect before opening the connection
query.SetDialect(query.PostgreSQLDialect{})
migrate.SetMigrateDialect(migrate.PostgreSQLMigrateDialect{})
db, err := sql.Open("postgres", "host=127.0.0.1 port=5432 user=postgres password=secret dbname=mydb sslmode=disable")
if err != nil {
panic(err)
}
defer db.Close()
Tip: You can also use
pgxas the PostgreSQL driver by importinggithub.com/jackc/pgx/v5/stdlibinstead ofgithub.com/lib/pq.
CRUD Operations
All operations require creating a model instance first:
userModel := models.NewUserModel(db)
ctx := context.TODO()
Create
// Method 1: Using Save with a Nullable struct
id, err := userModel.Save(ctx, models.UserN{
Name: null.StringFrom("Tom"),
Email: null.StringFrom("tom@example.com"),
})
// Method 2: Using Create with KV pairs
id, err := userModel.Create(ctx, query.KV{
models.FieldUserName: "Tom",
models.FieldUserEmail: "tom@example.com",
})
// Method 3: Converting from a Plain struct
user := models.User{Name: "Tom", Email: "tom@example.com"}
id, err := userModel.Save(ctx, user.ToUserN())
Read
// Find by ID
user, err := userModel.Find(ctx, 1)
// Find the first matching record
user, err := userModel.First(ctx, query.Builder().Where("name", "Tom"))
// Get all records
users, err := userModel.Get(ctx)
// Get with conditions
users, err := userModel.Get(ctx, query.Builder().
Where("age", ">", 18).
OrderBy("created_at", "DESC").
Limit(10),
)
// Select specific fields
users, err := userModel.Get(ctx, query.Builder().Select("id", "name"))
// Pagination
users, meta, err := userModel.Paginate(ctx, 1, 15) // Page 1, 15 per page
// meta.Total - total record count
// meta.LastPage - last page number
// meta.Page - current page
// meta.PerPage - records per page
// Count
count, err := userModel.Count(ctx)
// Check existence
exists, err := userModel.Exists(ctx, query.Builder().Where("email", "tom@example.com"))
Reading Field Values
user, _ := userModel.Find(ctx, 1)
// Method 1: Read directly from Nullable struct
name := user.Name.String // Direct read, empty string if NULL
id := user.Id.Int64 // Direct int64 read
valid := user.Name.Valid // Check if field is NULL
// Method 2: Convert to Plain struct
u := user.ToUser()
fmt.Println(u.Name) // string type
fmt.Println(u.Age) // int64 type
// Method 3: Convert to a custom struct (matched by field name)
type UserView struct {
Name string
Email string
}
var view UserView
err := user.As(&view)
Update
// Method 1: Update specific fields via model method
affected, err := userModel.UpdateFields(ctx,
query.KV{models.FieldUserName: "Jerry"},
query.Builder().Where("id", 1),
)
// Method 2: Save via model instance (updates all modified fields)
user, _ := userModel.Find(ctx, 1)
user.Name = null.StringFrom("Jerry")
err := user.Save(ctx) // Requires SetModel or obtained via Find
Delete
// Delete by ID (soft delete if enabled)
affected, err := userModel.DeleteById(ctx, 1)
// Delete with conditions
affected, err := userModel.Delete(ctx, query.Builder().Where("age", "<", 18))
// Force delete (physical delete even if soft delete is enabled)
affected, err := userModel.ForceDeleteById(ctx, 1)
Query Builder
The query builder is the core tool for constructing SQL query conditions in Eloquent. All conditions start with query.Builder() and support chaining.
Basic Conditions
// Equals (default operator)
query.Builder().Where("name", "Tom")
// Specify operator
query.Builder().Where("age", ">", 18)
query.Builder().Where("name", "LIKE", "%Tom%")
// Available operator constants
query.EQ // =
query.NEQ // !=
query.GT // >
query.GTE // >=
query.LT // <
query.LTE // <=
query.LIKE // LIKE
OR Conditions
query.Builder().Where("name", "Tom").OrWhere("name", "Jerry")
// WHERE name = 'Tom' OR name = 'Jerry'
IN / NOT IN
query.Builder().WhereIn("id", 1, 2, 3)
query.Builder().WhereNotIn("status", 0, -1)
// When using slices, convert first
ids := []int{1, 2, 3}
query.Builder().WhereIn("id", query.ToAnys(ids)...)
NULL Checks
query.Builder().WhereNull("deleted_at")
query.Builder().WhereNotNull("email")
BETWEEN
query.Builder().WhereBetween("age", 18, 30)
query.Builder().WhereNotBetween("age", 0, 17)
Condition Grouping (Parentheses)
// WHERE (name = 'Tom' AND age > 18) OR (name = 'Jerry')
query.Builder().
WhereGroup(func(builder query.Condition) {
builder.Where("name", "Tom").Where("age", ">", 18)
}).
OrWhereGroup(func(builder query.Condition) {
builder.Where("name", "Jerry")
})
Conditional Clauses (When)
Dynamically add query conditions based on runtime values:
keyword := "Tom"
query.Builder().When(
func() bool { return keyword != "" },
func(builder query.Condition) {
builder.Where("name", "LIKE", "%"+keyword+"%")
},
)
Raw Expressions
query.Builder().WhereRaw("YEAR(created_at) = ?", 2024)
Subqueries
// WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)
query.Builder().WhereIn("id",
query.Builder().Table("orders").Select("user_id").Where("amount", ">", 100),
)
// WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)
query.Builder().WhereExist(
query.Builder().Table("orders").Select(query.Raw("1")).WhereColumn("orders.user_id", "=", "users.id"),
)
Ordering, Pagination, Grouping
query.Builder().
OrderBy("created_at", "DESC").
OrderBy("id", "ASC").
Limit(10).
Offset(20).
GroupBy("status").
Having(func(builder query.Condition) {
builder.WhereRaw("COUNT(*) > ?", 5)
})
JOIN Queries
query.Builder().
Table("users AS u").
Select("u.name", "r.name AS role_name").
LeftJoin("roles AS r", func(builder query.Condition) {
builder.WhereColumn("u.role_id", "=", "r.id")
})
Supported JOIN types: LeftJoin, RightJoin, InnerJoin, CrossJoin
UNION
query.Builder().Table("table1").Select("name").
Union(query.Builder().Table("table2").Select("name"), false) // false=UNION ALL, true=UNION DISTINCT
SELECT Raw Expressions
query.Builder().Select("id", "name", query.Raw("DATE(created_at) AS create_date"))
Model Relationships
Eloquent supports relationship types defined via relations in the YAML file.
Relationship Types
| Relationship | rel Value in YAML | Description | Example |
|---|---|---|---|
| One-to-One | hasOne | Current model owns one child model | User has one profile |
| Belongs To | belongsTo | Current model belongs to another model | User belongs to a role |
| Many-to-Many | belongsToMany | Related through a pivot table | User belongs to many organizations |
Defining Relationships
models:
- name: User
relations:
# Belongs To: User belongs to a role
- model: role
rel: belongsTo
foreign_key: role_id # Foreign key in current table
owner_key: id # Primary key in related table
# Has One: User has one extension record
- model: userExt
rel: hasOne
# Default foreign key is user_id (current_model_name_id)
# Many-to-Many: User belongs to many organizations
- model: organization
rel: belongsToMany
# Default pivot table is user_organization_ref
definition:
fields:
- name: id
type: int64
- name: role_id
type: int64
Using Relationships
user, _ := userModel.Find(ctx, 1)
// belongsTo - Query the user's role
role, err := user.Role().First(ctx)
// belongsTo - Create a related object and auto-bind the foreign key
roleId, err := user.Role().Create(ctx, models.RoleN{
Name: null.StringFrom("admin"),
})
// belongsTo - Associate / Dissociate
err = user.Role().Associate(ctx, role) // Bind
err = user.Role().Dissociate(ctx) // Unbind
// hasOne - Query user extension
ext, err := user.UserExt().First(ctx)
exists, err := user.UserExt().Exists(ctx)
// belongsToMany - Query all organizations for the user
orgs, err := user.Organizations().Get(ctx)
count, err := user.Organizations().Count(ctx)
// belongsToMany - Attach / Detach
err = user.Organizations().Attach(ctx, org) // Insert into pivot table
err = user.Organizations().Detach(ctx, org) // Remove from pivot table
err = user.Organizations().DetachAll(ctx) // Remove all associations
orgId, err := user.Organizations().Create(ctx, models.OrganizationN{
Name: null.StringFrom("Engineering"),
})
Soft Deletes
When soft deletes are enabled, calling Delete does not physically remove data -- it sets the deleted_at field instead. Queries automatically filter out soft-deleted records.
Enable Soft Deletes
Set soft_delete: true in the YAML:
models:
- name: User
definition:
soft_delete: true
fields:
- name: id
type: int64
Usage
// Soft delete (sets deleted_at)
userModel.DeleteById(ctx, 1)
// Normal query (automatically excludes deleted records)
users, _ := userModel.Get(ctx)
// Query including deleted records
users, _ := userModel.WithTrashed().Get(ctx)
// Force delete (physically removes from database)
userModel.ForceDeleteById(ctx, 1)
Scopes
Scopes encapsulate commonly used query conditions into reusable functions.
Global Scopes
Global scopes are automatically applied to all queries on a model. Soft delete is implemented via a global scope.
// Register a global scope (typically in an init function)
models.AddGlobalScopeForUser("active_only", func(builder query.Condition) {
builder.Where("status", 1)
})
// Automatically applied during queries
users, _ := userModel.Get(ctx) // Automatically adds WHERE status = 1
// Temporarily disable a global scope
users, _ := userModel.WithoutGlobalScopes("active_only").Get(ctx)
Local Scopes
Local scopes are not automatically applied; they must be explicitly enabled.
// Register a local scope
models.AddLocalScopeForUser("recent", func(builder query.Condition) {
builder.WhereRaw("created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)")
})
// Explicitly enable
users, _ := userModel.WithLocalScopes("recent").Get(ctx)
Database Migrations
Eloquent provides Laravel-like database migration functionality, managing table schema changes through Go code.
Basic Usage
import "github.com/mylxsw/eloquent/migrate"
m := migrate.NewManager(db).Init(context.TODO())
// Create a new table
m.Schema("20240101_001").Create("users", func(builder *migrate.Builder) {
builder.Increments("id")
builder.String("name", 100).Comment("Username")
builder.String("email", 255).Unique().Comment("Email")
builder.String("password", 255)
builder.Timestamps(0)
})
// Modify an existing table (add columns)
m.Schema("20240101_002").Table("users", func(builder *migrate.Builder) {
builder.TinyInteger("status", false, true).
Default(migrate.StringExpr("1")).
Comment("Status: 0-disabled 1-enabled")
builder.SoftDeletes("deleted_at", 0)
})
// Run migrations
if err := m.Run(context.TODO()); err != nil {
panic(err)
}
Each
Schemaversion string is a unique identifier. Already-executed migrations will not be re-run. Eloquent automatically creates amigrationstable to track migration history.
Available Column Type Methods
| Method | MySQL Type | PostgreSQL Type |
|---|---|---|
Increments(name) | INT UNSIGNED AUTO_INCREMENT | SERIAL |
BigIncrements(name) | BIGINT UNSIGNED AUTO_INCREMENT | BIGSERIAL |
Integer(name, autoIncrement, unsigned) | INT | INTEGER |
BigInteger(name, autoIncrement, unsigned) | BIGINT | BIGINT |
TinyInteger(name, autoIncrement, unsigned) | TINYINT | SMALLINT |
SmallInteger(name, autoIncrement, unsigned) | SMALLINT | SMALLINT |
MediumInteger(name, autoIncrement, unsigned) | MEDIUMINT | INTEGER |
Float(name, total, scale) | DOUBLE | DOUBLE PRECISION |
Double(name, total, scale) | DOUBLE | DOUBLE PRECISION |
Decimal(name, total, scale) | DECIMAL(m,n) | DECIMAL(m,n) |
String(name, length) | VARCHAR(n) | VARCHAR(n) |
Char(name, length) | CHAR(n) | CHAR(n) |
Text(name) | TEXT | TEXT |
MediumText(name) | MEDIUMTEXT | TEXT |
LongText(name) | LONGTEXT | TEXT |
Boolean(name) | TINYINT(1) | BOOLEAN |
Date(name) | DATE | DATE |
DateTime(name, precision) | DATETIME | TIMESTAMP |
Timestamp(name, precision) | TIMESTAMP | TIMESTAMP |
Time(name, precision) | TIME | TIME |
Year(name) | YEAR | SMALLINT |
Binary(name) | BLOB | BYTEA |
Json(name) | JSON | JSONB |
Enum(name, items...) | ENUM | TEXT |
Set(name, items...) | SET | TEXT |
Uuid(name) | CHAR(36) | CHAR(36) |
IpAddress(name) | VARCHAR(45) | VARCHAR(45) |
MacAddress(name) | VARCHAR(17) | VARCHAR(17) |
Timestamps(precision) | created_at + updated_at | created_at + updated_at |
SoftDeletes(column, precision) | Nullable TIMESTAMP | Nullable TIMESTAMP |
RememberToken() | VARCHAR(100) | VARCHAR(100) |
Column Modifiers
builder.String("name", 100).
Nullable(true). // Allow NULL
Default(migrate.StringExpr("default_value")). // Default value (string)
Default(migrate.RawExpr("CURRENT_TIMESTAMP")). // Default value (raw expression)
Comment("Username"). // Column comment (MySQL only)
Unique(). // Unique index
After("id"). // Place after a specific column (MySQL only)
First(). // Place as the first column (MySQL only)
Charset("utf8mb4"). // Character set (MySQL only)
Collation("utf8mb4_unicode_ci"). // Collation (MySQL only)
Unsigned(). // Unsigned (MySQL only, ignored on PostgreSQL)
Change() // Modify an existing column (instead of adding)
PostgreSQL note: Modifiers marked "MySQL only" are silently ignored when using the PostgreSQL dialect.
Index Operations
builder.Index("", "name", "email") // Create index (name auto-generated)
builder.Index("idx_name", "name") // Create named index
builder.Unique("", "email") // Create unique index
builder.Primary("", "id") // Create primary key
builder.DropIndex("idx_name") // Drop index
builder.DropUnique("unique_name") // Drop unique index
builder.DropColumn("column1", "column2") // Drop columns
Foreign Keys
builder.Foreign("", "user_id").
References("id").
On("users").
OnDelete("CASCADE").
OnUpdate("CASCADE")
builder.DropForeign("foreign_key_name") // Drop foreign key
Table Operations
// Drop table
m.Schema("20240102_001").Drop("temp_table")
m.Schema("20240102_002").DropIfExists("temp_table")
// Raw SQL migration
m.Schema("20240102_003").Raw("custom_table", func() []string {
return []string{
`CREATE TABLE custom_table (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))`,
}
})
Transactions
import "github.com/mylxsw/eloquent/query"
err := eloquent.Transaction(db, func(tx query.Database) error {
userModel := models.NewUserModel(tx) // Use tx instead of db
id, err := userModel.Save(ctx, models.UserN{
Name: null.StringFrom("Tom"),
})
if err != nil {
return err // Returning an error triggers automatic rollback
}
// ... more operations
return nil // Returning nil triggers automatic commit
})
Panics within a transaction also trigger automatic rollback.
Event System
Eloquent includes a built-in event system for monitoring SQL execution, transactions, and migrations.
Setting Up Event Listeners
import "github.com/mylxsw/eloquent/event"
em := event.NewEventManager(event.NewMemoryEventStore())
event.SetDispatcher(em)
// Listen for SQL execution events (useful for logging, performance monitoring)
em.Listen(func(evt event.QueryExecutedEvent) {
fmt.Printf("[SQL] %s | args: %v | time: %s\n", evt.SQL, evt.Bindings, evt.Time)
})
// Listen for transaction events
em.Listen(func(evt event.TransactionBeginningEvent) {
fmt.Println("Transaction started")
})
em.Listen(func(evt event.TransactionCommittedEvent) {
fmt.Println("Transaction committed")
})
em.Listen(func(evt event.TransactionRolledBackEvent) {
fmt.Println("Transaction rolled back")
})
// Listen for migration events
em.Listen(func(evt event.MigrationStartedEvent) {
fmt.Printf("Running migration: %s\n", evt.SQL)
})
Available Events
| Event | Description |
|---|---|
QueryExecutedEvent | Fired after each SQL query, includes SQL, bindings, and duration |
TransactionBeginningEvent | Fired when a transaction starts |
TransactionCommittedEvent | Fired when a transaction is committed |
TransactionRolledBackEvent | Fired when a transaction is rolled back |
MigrationsStartedEvent | Fired before a batch of migrations starts |
MigrationsEndedEvent | Fired after a batch of migrations completes |
MigrationStartedEvent | Fired before a single migration SQL is executed |
MigrationEndedEvent | Fired after a single migration SQL is executed |
Direct Database Operations
In addition to model-based operations, Eloquent provides low-level database operation APIs, suitable for complex queries or scenarios where you don't want to define a model.
import "github.com/mylxsw/eloquent"
dbOp := eloquent.DB(db)
ctx := context.TODO()
// Insert
id, err := dbOp.Insert(ctx, "users", query.KV{
"name": "Tom",
"email": "tom@example.com",
})
// Query
results, err := dbOp.Query(ctx,
eloquent.Build("users").Select("id", "name").Where("age", ">", 18),
func(row eloquent.Scanner) (any, error) {
var id int64
var name string
err := row.Scan(&id, &name)
return map[string]any{"id": id, "name": name}, err
},
)
// Generic query (Go 1.18+)
type UserRow struct {
Id int64
Name string
}
users, err := eloquent.Query[UserRow](ctx, db,
eloquent.Build("users").Select("id", "name"),
func(row eloquent.Scanner) (UserRow, error) {
var u UserRow
err := row.Scan(&u.Id, &u.Name)
return u, err
},
)
// Raw SQL query
results, err := dbOp.Query(ctx,
eloquent.Raw("SELECT * FROM users WHERE age > ?", 18),
func(row eloquent.Scanner) (any, error) {
// ...
},
)
// Update
affected, err := dbOp.Update(ctx,
eloquent.Build("users").Where("id", 1),
query.KV{"name": "Jerry"},
)
// Delete
affected, err := dbOp.Delete(ctx, eloquent.Build("users").Where("id", 1))
// Execute a statement with no return value
err := dbOp.Statement(ctx, "TRUNCATE TABLE users")
Example Projects
- _examples - Built-in complete example code
- tech-share - A simple web project demonstrating Eloquent in a real application