FluxaORM v2: Code-Generation-Based Go ORM for MySQL and Redis
Guide
GitHub
Guide
GitHub
    • Introduction
    • Registry
    • Data Pools
    • Entities
    • Entity Fields
    • MySQL Indexes
    • Code Generation
    • Engine
    • Context
    • Entity Schema
    • Schema Update
    • CRUD Operations
    • Async Flush
    • Search
    • Redis Search
    • MySQL Queries
    • Local Cache
    • Context Cache
    • Fake Delete
    • Entity Lifecycle Callbacks
    • Metrics
    • Redis Operations
    • Distributed Lock
    • Event Broker
    • Dirty Streams
    • Queries Log
    • Testing

Entities

In FluxaORM v2, an Entity is a Go struct that represents data stored in a MySQL database table. You define the struct, register it with a Registry, validate the configuration, and then run code generation to produce typed Provider and Entity code with getters, setters, and query methods.

Defining an Entity

To define an entity, create a Go struct with an ID field of type uint64 as the first field:

package entity

import "github.com/latolukasz/fluxaorm/v2"

type UserEntity struct {
    ID    uint64
    Name  string `orm:"required"`
    Email string `orm:"required;unique=Email"`
    Age   uint8
}

Every entity must have an ID uint64 field. This field maps to the primary key in the corresponding MySQL table.

Registering and Validating Entities

Before you can use entities, you must register them with a Registry and validate the configuration:

package main

import "github.com/latolukasz/fluxaorm/v2"

func main() {
    registry := fluxaorm.NewRegistry()

    // Register data pools
    registry.RegisterMySQL("user:password@tcp(localhost:3306)/mydb", fluxaorm.DefaultPoolCode, nil)
    registry.RegisterRedis("localhost:6379", 0, fluxaorm.DefaultPoolCode, nil)

    // Register entities
    registry.RegisterEntity(UserEntity{}, ProductEntity{}, CategoryEntity{})

    // Validate configuration and create engine
    engine, err := registry.Validate()
    if err != nil {
        panic(err)
    }

    // Generate typed code
    err = fluxaorm.Generate(engine, "./entities")
    if err != nil {
        panic(err)
    }
}

The workflow is:

  1. Create a Registry with fluxaorm.NewRegistry()
  2. Register MySQL and Redis pools
  3. Register entity structs with registry.RegisterEntity(...)
  4. Call registry.Validate() to verify configuration and create an Engine
  5. Call fluxaorm.Generate(engine, outputDir) to generate typed code

After Generate() runs, you get a typed Provider and Entity for each registered struct. For example, registering UserEntity produces:

  • entities.UserEntityProvider -- a Provider variable with methods like New(), GetByID(), Search(), etc.
  • entities.UserEntity -- a generated Entity type with GetName(), SetName(), GetEmail(), SetEmail(), etc.

MySQL Pool

By default, every entity is connected to the default MySQL pool. You can specify a different pool using the orm:"mysql=pool_name" tag on the ID field:

type UserEntity struct {
    ID uint64 // uses the "default" pool
}

type OrderEntity struct {
    ID uint64 `orm:"mysql=sales"` // uses the "sales" pool
}

Make sure to register the pool in the Registry:

registry := fluxaorm.NewRegistry()
registry.RegisterMySQL("user:password@tcp(localhost:3306)/users", fluxaorm.DefaultPoolCode, nil)
registry.RegisterMySQL("user:password@tcp(localhost:3307)/sales", "sales", nil)
registry.RegisterEntity(UserEntity{}, OrderEntity{})

Redis Cache

To protect MySQL from unnecessary queries, entities can be automatically cached in Redis. Enable Redis caching with the orm:"redisCache" tag on the ID field:

type UserEntity struct {
    ID uint64 `orm:"redisCache"` // cache in the "default" Redis pool
}

type OrderEntity struct {
    ID uint64 `orm:"redisCache=orders"` // cache in the "orders" Redis pool
}

When Redis caching is enabled, FluxaORM automatically stores entity data in Redis after loading it from MySQL. Subsequent reads are served from Redis, significantly reducing MySQL load.

Tips

To optimize Redis as a cache, set the maxmemory configuration to a value below the machine's memory size and enable the allkeys-lru eviction policy. Consider disabling persistence -- if data is lost, FluxaORM automatically refills it from MySQL.

Cache TTL

By default, Redis cache entries never expire. You can set a TTL (in seconds) using the ttl tag:

type UserEntity struct {
    ID uint64 `orm:"redisCache;ttl=30"` // Cache for 30 seconds
}

The TTL is reset every time the data is updated.

Local In-Memory Cache

To cache entity data in local memory, use the localCache tag. You can optionally specify a maximum cache size:

type CategoryEntity struct {
    ID uint64 `orm:"localCache"` // unlimited local cache
}

type ProductEntity struct {
    ID uint64 `orm:"localCache=1000"` // cache up to 1000 entities
}

Using Both Redis and Local Cache

You can enable both local and Redis caching on the same entity:

type CategoryEntity struct {
    ID uint64 `orm:"localCache;redisCache"`
}

Tips

Enabling both local and Redis caching is highly recommended for frequently accessed entities. When data is requested:

  1. FluxaORM first checks the local cache
  2. If not found locally, it checks Redis
  3. If not in Redis either, it queries MySQL, then populates both caches

This multi-layer caching greatly reduces MySQL load, especially when running on multiple servers or with autoscaling.

Custom Table Name

By default, FluxaORM uses the struct name as the MySQL table name. Override this with the table tag:

type UserEntity struct {
    ID uint64 `orm:"table=users"`
}

Soft Deletes (Fake Delete)

To enable soft deletes, add a FakeDelete bool field to your entity:

type ProductEntity struct {
    ID         uint64
    Name       string `orm:"required"`
    FakeDelete bool
}

When FakeDelete is present, calling entity.Delete() sets the FakeDelete column to 1 instead of removing the row from MySQL. All generated query methods (Search, SearchIDs, etc.) automatically filter out soft-deleted rows unless you explicitly include them using fluxaorm.NewWhere("1 = 1").WithFakeDeletes().

Automatic Timestamps

FluxaORM automatically manages CreatedAt and UpdatedAt fields if they are defined as time.Time:

type UserEntity struct {
    ID        uint64
    Name      string    `orm:"required"`
    CreatedAt time.Time
    UpdatedAt time.Time
}
  • CreatedAt is automatically set to the current UTC time on insert (unless you set it explicitly before flushing)
  • UpdatedAt is automatically set to the current UTC time on both insert and update

References (Foreign Keys)

Use fluxaorm.Reference[T] to define a foreign key relationship to another entity:

type CategoryEntity struct {
    ID   uint64
    Name string `orm:"required"`
}

type ProductEntity struct {
    ID       uint64
    Name     string                                 `orm:"required"`
    Category fluxaorm.Reference[CategoryEntity]      `orm:"required"`
}

The Category field creates a Category bigint NOT NULL column in the ProductEntity table. After code generation, you access the reference ID via entity.GetCategoryID() and set it via entity.SetCategory(id).

To make a reference optional (nullable), omit the orm:"required" tag:

type ProductEntity struct {
    ID       uint64
    Name     string                                 `orm:"required"`
    Category fluxaorm.Reference[CategoryEntity]      // nullable reference
}

For an optional reference, the getter returns *uint64: entity.GetCategoryID() returns nil when no reference is set.

Enums and Sets

Define inline enums and sets directly in the struct tag without implementing any interface:

type OrderEntity struct {
    ID     uint64
    Status string `orm:"enum=pending,processing,shipped,delivered;required"`
    Tags   string `orm:"set=sale,featured,new;required"`
}

The enum tag creates a MySQL ENUM column, and the set tag creates a MySQL SET column.

Shared Enum Types

When multiple fields use the same set of values, use enumName to share a single generated type:

type OrderEntity struct {
    ID             uint64
    Status         string `orm:"enum=pending,processing,shipped,delivered;required"`
    PreviousStatus string `orm:"enum=pending,processing,shipped,delivered;enumName=Status"`
}

Both fields share the generated enums.Status type. The code generator creates enum types in an enums/ subdirectory with typed constants:

// Generated in enums/Status.go
package enums

type Status string
var StatusList = struct {
    Pending    Status
    Processing Status
    Shipped    Status
    Delivered  Status
}{
    Pending:    "pending",
    Processing: "processing",
    Shipped:    "shipped",
    Delivered:  "delivered",
}

After generation, you use the typed enum values:

order := entities.OrderEntityProvider.New(ctx)
order.SetStatus(enums.StatusList.Pending)

// For sets, pass variadic values
order.SetTags(enums.StatusList.Sale, enums.StatusList.Featured)

Redis Search

To enable full-text and numeric search via Redis Search, add the orm:"redisSearch" tag to the ID field and mark individual fields as searchable and optionally sortable:

type ProductEntity struct {
    ID    uint64  `orm:"redisSearch=default"`
    Name  string  `orm:"required;searchable"`
    Price float64 `orm:"searchable;sortable"`
    Stock uint32  `orm:"searchable;sortable"`
}

After code generation, the Provider includes Redis Search methods like SearchIDsInRedis(), SearchInRedis(), and SearchOneInRedis().

Unique Indexes

Define unique indexes using the orm:"unique=IndexName" tag. For composite indexes, use a position suffix:

type UserEntity struct {
    ID    uint64
    Name  string `orm:"required;unique=NameAge"`
    Age   uint8  `orm:"unique=NameAge:2"`
    Email string `orm:"required;unique=Email"`
}

This creates two unique indexes: a composite NameAge index on (Name, Age) and a single-column Email index. After code generation, you get typed lookup methods:

user, found, err := entities.UserEntityProvider.GetByIndexNameAge(ctx, "Alice", 30)
user, found, err := entities.UserEntityProvider.GetByIndexEmail(ctx, "alice@example.com")

See the MySQL Indexes page for full details on unique indexes and caching.

Complete Example

Here is a complete example showing multiple entities with various features:

package entity

import (
    "time"

    "github.com/latolukasz/fluxaorm/v2"
)

type CategoryEntity struct {
    ID   uint64 `orm:"localCache;redisCache"`
    Name string `orm:"required;unique=Name"`
}

type UserEntity struct {
    ID        uint64 `orm:"redisCache"`
    Name      string `orm:"required"`
    Email     string `orm:"required;unique=Email"`
    Age       uint8
    Active    bool
    CreatedAt time.Time
    UpdatedAt time.Time
}

type ProductEntity struct {
    ID         uint64    `orm:"redisCache"`
    Name       string    `orm:"required"`
    Price      float64   `orm:"decimal=10,2;unsigned"`
    Status     string    `orm:"enum=draft,active,archived;required"`
    Category   fluxaorm.Reference[CategoryEntity] `orm:"required"`
    FakeDelete bool
    CreatedAt  time.Time
    UpdatedAt  time.Time
}

Struct Tags Reference

All orm struct tags available in v2:

TagDescription
mysql=poolUse a specific MySQL pool (on ID field)
table=nameCustom MySQL table name (on ID field)
redisCache / redisCache=poolEnable Redis entity cache (on ID field)
localCache / localCache=sizeEnable local in-memory cache (on ID field)
ttl=secondsRedis cache TTL in seconds (on ID field)
redisSearch=poolEnable Redis Search indexing (on ID field)
requiredNOT NULL in MySQL; for strings, prevents empty default
unique=NameDeclare a unique index column
unique=Name:NComposite unique index with column position N
cachedCache unique index lookups in Redis
enum=a,b,cMySQL ENUM column with specified values
set=a,b,cMySQL SET column with specified values
enumName=TypeNameShare a generated enum type across fields
timeMap time.Time to datetime instead of date
length=NSet varchar length (default 255)
length=maxUse mediumtext instead of varchar
decimal=X,YUse MySQL decimal(X,Y) for floats
unsignedUnsigned float column
mediumintUse MySQL mediumint for int32/uint32
searchableInclude field in Redis Search index
sortableMake field sortable in Redis Search
ignoreDo not store this field in MySQL
Edit this page
Last Updated: 3/1/26, 11:32 AM
Prev
Data Pools
Next
Entity Fields