FluxaORM v2: Code-Generation-Based Go ORM for MySQL, Redis, ClickHouse and NATS JetStream
Guide
GitHub
Guide
GitHub
    • Introduction
    • Registry
    • Data Pools
    • Entities
    • Entity Fields
    • MySQL Indexes
    • Code Generation
    • Engine
    • Context
    • Schema Update
    • CRUD Operations
    • Transactions
    • Search
    • Redis Search
    • MySQL Queries
    • ClickHouse Queries
    • ClickHouse Schema
    • NATS and JetStream
    • Entity Change Events (CDC)
    • Consumers
    • Tasks and Job Runs
    • Transactional Outbox
    • Context Cache
    • Redis Cache
    • Fake Delete
    • Lifecycle Callbacks
    • Metrics
    • Redis Operations
    • Distributed Lock
    • Queries Log
    • Testing

Entity Fields

Every exported field of an entity struct becomes a MySQL column, and code generation emits a typed getter and setter for it: Name string produces GetName() string and SetName(value string) *UserEntity. Setters return the entity so calls can be chained, and fields are never accessed directly.

This page lists every supported Go type, the exact column definition the ORM emits for it, and the generated accessors. In the DDL below utf8mb4 and utf8mb4_0900_ai_ci are the pool's MySQLOptions.DefaultEncoding and DefaultEncoding + "_" + DefaultCollate (see Data Pools).

Nullability Rules

  • Non-pointer scalars (integers, floats, bool, time.Time) are always NOT NULL with a zero default. Their pointer variants are nullable (DEFAULT NULL).
  • For string, enums, sets, []uint8, Reference[T], References[T] and JSON struct pointers, the required tag decides: with required the column is NOT NULL with a non-null default, without it the column is nullable.
  • NOT NULL text and blob columns receive the expression default DEFAULT ('') (MySQL 8.0.13+), so an INSERT from a previous code version that omits the column still succeeds.
  • Nullable mediumtext and blob columns are emitted without an explicit DEFAULT NULL clause; all other nullable columns get DEFAULT NULL.

Integers

type UserEntity struct {
    ID      uint64
    Age     uint8
    Balance int32
    Friends *uint32 // nullable
}
Go typeColumn definition
ID uint64bigint unsigned NOT NULL (primary key, no default)
uint8tinyint unsigned NOT NULL DEFAULT '0'
uint16smallint unsigned NOT NULL DEFAULT '0'
uint32, uintint unsigned NOT NULL DEFAULT '0'
uint32 + orm:"mediumint"mediumint unsigned NOT NULL DEFAULT '0'
uint64bigint unsigned NOT NULL DEFAULT '0'
int8tinyint NOT NULL DEFAULT '0'
int16smallint NOT NULL DEFAULT '0'
int32, intint NOT NULL DEFAULT '0'
int32 + orm:"mediumint"mediumint NOT NULL DEFAULT '0'
int64bigint NOT NULL DEFAULT '0'
pointer to any of the abovesame type, DEFAULT NULL instead of NOT NULL DEFAULT '0'

Generated accessors widen the type: unsigned fields use uint64, signed fields use int64, nullable fields use pointers:

user := entities.UserEntityProvider.New(ctx)
user.SetAge(25).SetBalance(-1500)
age := user.GetAge()         // uint64
balance := user.GetBalance() // int64

friends := uint64(5)
user.SetFriends(&friends)
user.SetFriends(nil)         // NULL
count := user.GetFriends()   // *uint64, nil when NULL

Tips

Pick the smallest integer that fits the data (uint8 for an age, uint16 for a year). Smaller columns mean smaller indexes.

Floats

type ProductEntity struct {
    ID       uint64
    Price    float64 `orm:"decimal=10,2;unsigned"`
    Weight   float32 `orm:"unsigned"`
    Rating   float64
    Discount *float64
}
Go typeColumn definition
float32float NOT NULL DEFAULT '0'
float64double NOT NULL DEFAULT '0'
float + orm:"unsigned"float unsigned / double unsigned
float + orm:"decimal=X,Y"decimal(X,Y) NOT NULL DEFAULT '0.00' (the default zero has Y decimals)
*float32, *float64same type, DEFAULT NULL

Getters return float64, setters take float64; nullable variants use *float64. The precision=N tag has no effect on the column definition. It sets the number of decimals the ORM works with for the field - the setter treats a value equal to the stored one within N decimals as unchanged, the Redis row cache formats the value with N decimals, and Redis Search indexes it with that precision. Default is 8 for float64, 4 for float32, and Y for decimal=X,Y.

Booleans

Go typeColumn definition
booltinyint(1) NOT NULL DEFAULT '0'
*booltinyint(1) DEFAULT NULL
FakeDelete bool (top-level)bigint unsigned NOT NULL DEFAULT '0' - stores the row ID when soft-deleted, see Fake Delete
user.SetActive(true)
active := user.GetActive()   // bool
yes := true
user.SetHasChildren(&yes)    // *bool

Strings

type ProductEntity struct {
    ID          uint64
    Title       string `orm:"required;length=150"`
    Description string `orm:"length=max"`
    Brand       string
}
Go typeColumn definition
stringvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL
string + requiredvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT ''
string + length=Nvarchar(N) ... (N must be an integer <= 65535, otherwise invalid max string: <N>)
string + length=maxmediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci (no DEFAULT NULL clause)
string + length=max;requiredmediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT ('')

Getters always return string; for a nullable column NULL reads as "", and setting "" on a nullable column stores NULL. Pointer strings (*string) are not supported.

product.SetTitle("Wireless Mouse")
brand := product.GetBrand() // "" when NULL

Dates and Times

time.Time is a date column unless tagged time, which makes it datetime. Fields named CreatedAt or UpdatedAt are always datetime and are filled automatically (see Entities).

type UserEntity struct {
    ID          uint64
    DateOfBirth time.Time
    LastLogin   *time.Time `orm:"time"`
    CreatedAt   time.Time
    UpdatedAt   time.Time
}
Go typeColumn definition
time.Timedate NOT NULL DEFAULT '0001-01-01'
time.Time + orm:"time" (or named CreatedAt/UpdatedAt)datetime NOT NULL DEFAULT '1000-01-01 00:00:00'
*time.Timedate DEFAULT NULL
*time.Time + orm:"time"datetime DEFAULT NULL

Setters truncate the value: date columns to whole days, datetime columns to whole seconds. Store UTC values.

user.SetDateOfBirth(time.Date(1990, 6, 15, 0, 0, 0, 0, time.UTC))
login := user.GetLastLogin() // *time.Time, nil when NULL

Binary Data

type UserEntity struct {
    ID        uint64
    Avatar    []uint8
    Document  []uint8 `orm:"mediumblob;required"`
    LargeFile []uint8 `orm:"longblob"`
}
Go typeColumn definition
[]uint8blob (nullable, no DEFAULT NULL clause)
[]uint8 + orm:"mediumblob"mediumblob
[]uint8 + orm:"longblob"longblob
any of the above + required... NOT NULL DEFAULT ('')

Accessors are Get<F>() []uint8 and Set<F>(value []uint8). Blob fields have no typed Fields descriptor and cannot be used in query filters.

Enums

An enum= tag on a string field creates a MySQL ENUM column and a Go enum type in the enums/ sub-package:

type OrderEntity struct {
    ID     uint64
    Status string `orm:"enum=pending,processing,shipped,delivered;required"`
}
DefinitionColumn definition
enum=a,b,c;requiredenum('a','b','c') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'a' (first value)
enum=a,b,cenum('a','b','c') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL

Warning

The enum and set collation suffix is always _0900_ai_ci, regardless of MySQLOptions.DefaultCollate.

Generated enum type

The type name defaults to the entity name without its Entity suffix followed by the field name: OrderEntity.Status becomes OrderStatus. Override it with enumName=. Constant names are built from the value by capitalising every _-separated part (in_progress becomes InProgress).

// Generated: enums/OrderStatus.go
package enums

type OrderStatus string

var OrderStatusList = struct {
    Pending    OrderStatus
    Processing OrderStatus
    Shipped    OrderStatus
    Delivered  OrderStatus
}{
    Pending:    "pending",
    Processing: "processing",
    Shipped:    "shipped",
    Delivered:  "delivered",
}

func (e OrderStatus) Valid() bool
func (e OrderStatus) Values() []OrderStatus
order := entities.OrderEntityProvider.New(ctx) // Status is pre-filled with "pending"
order.SetStatus(enums.OrderStatusList.Shipped)
status := order.GetStatus() // enums.OrderStatus

enums.OrderStatus("unknown").Valid() // false

New() and NewWithID() pre-fill every required enum and set field with its first value.

Optional enums

Without required the getter returns a pointer and nil means NULL:

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

prev := order.GetPreviousStatus() // *enums.OrderStatus
v := enums.OrderStatusList.Pending
order.SetPreviousStatus(&v)
order.SetPreviousStatus(nil)      // NULL

Here enumName=OrderStatus makes PreviousStatus reuse the type generated for Status instead of creating OrderPreviousStatus.

Shared enums across entities

Define the values in exactly one entity and reference them elsewhere with enum;enumName=X or just enumName=X:

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

type OrderLogEntity struct {
    ID     uint64
    Status string `orm:"enum;enumName=OrderStatus"`
}

type AuditLogEntity struct {
    ID             uint64
    PreviousStatus string `orm:"enumName=OrderStatus"`
}

Validation rules:

RuleError
enum without values needs enumNameenum without values requires enumName in field '<f>' of entity '<e>'
the referenced name must be defined somewhereenum/set '<name>' referenced in '<e>' but no entity defines its values
two entities may not define different values for one nameenum/set '<name>' has conflicting values defined in both '<a>' and '<b>', definition must be in only one entity

Several entities may repeat the same name with identical values.

Sets

A set= tag creates a MySQL SET column that holds zero or more of the listed values:

type ProductEntity struct {
    ID   uint64
    Tags string `orm:"set=sale,featured,new;required"`
}
DefinitionColumn definition
set=a,b,c;requiredset('a','b','c') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'a'
set=a,b,cset('a','b','c') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL

The generated type follows the enum rules (ProductEntity.Tags becomes enums.ProductTags, enums.ProductTagsList). The setter is variadic and the getter returns a slice:

product.SetTags(enums.ProductTagsList.Sale, enums.ProductTagsList.Featured)
tags := product.GetTags() // []enums.ProductTags

Values are stored sorted and comma-separated. On an optional set, calling the setter with no values stores NULL and the getter returns nil. Sharing works like enums, but the field must keep the set tag: orm:"set;enumName=ProductTags" references the definition, while a bare enumName=ProductTags without set is treated as an enum reference and produces an ENUM column. A bare set without enumName fails with set without values requires enumName in field '<f>' of entity '<e>'. Set fields have no typed Fields descriptor.

References

fluxaorm.Reference[T] stores the ID of another registered 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"`
    Brand    fluxaorm.Reference[BrandEntity]    // optional
}
DefinitionColumn definition
Reference[T] + requiredbigint unsigned NOT NULL DEFAULT '0'
Reference[T]bigint unsigned DEFAULT NULL

Generated accessors for a field Category referencing CategoryEntity:

func (e *ProductEntity) GetCategoryID() uint64                                            // 0 when NULL
func (e *ProductEntity) SetCategory(value uint64) *ProductEntity                          // 0 stores NULL on an optional reference
func (e *ProductEntity) GetCategory(ctx fluxaorm.Context) (*CategoryEntity, bool, error)  // loads via CategoryEntityProvider.GetByID
func (e *ProductEntity) MustGetCategory(ctx fluxaorm.Context) (*CategoryEntity, error)    // panics "Category not found in ProductEntity" when missing

GetCategory returns nil, false, nil when the ID is 0. The referenced entity type in the signature is the generated type of the target table, so it follows the target's table tag.

No foreign key constraint is created; existing CONSTRAINT lines on a table are reported by GetAlters as drop_foreign_key alters.

Multiple references

fluxaorm.References[T] stores a JSON array of IDs in a text column:

type ProductEntity struct {
    ID   uint64
    Tags fluxaorm.References[TagEntity]
}
DefinitionColumn definition
References[T] + requiredtext NOT NULL DEFAULT ('')
References[T]text DEFAULT NULL
func (e *ProductEntity) GetTagsIDs() []uint64
func (e *ProductEntity) SetTagsIDs(ids []uint64) *ProductEntity            // nil stores NULL (optional only), empty slice stores "[]"
func (e *ProductEntity) GetTags(ctx fluxaorm.Context) ([]*TagEntity, error) // loads via TagEntityProvider.GetByIDs

References[T] fields have no typed Fields descriptor.

Sub-Structs

A named struct field is flattened: every field of the sub-struct becomes a column prefixed with the parent field name. An anonymous embedded struct is flattened without a prefix.

type Address struct {
    Country string
    City    string
}

type UserEntity struct {
    ID          uint64
    HomeAddress Address // columns HomeAddressCountry, HomeAddressCity
    Address             // columns Country, City
}

user.SetHomeAddressCity("New York")
user.SetCity("Boston")

Warning

orm tags written on the fields inside the sub-struct are honoured only when the parent field itself carries no orm tag. Putting a tag on HomeAddress replaces the nested tags.

JSON Struct Fields

A pointer to a struct that is not a registered entity is serialised to JSON and stored in a text column:

type Dimensions struct {
    Width  float64
    Height float64
}

type ProductEntity struct {
    ID   uint64
    Size *Dimensions
}
DefinitionColumn definition
*Structtext DEFAULT NULL
*Struct + requiredtext NOT NULL DEFAULT ('')
product.SetSize(&Dimensions{Width: 10, Height: 20})
product.SetSize(nil)        // NULL
size := product.GetSize()   // *Dimensions, nil when NULL

Only exported struct fields are serialised. A pointer to a registered entity type is rejected with <entity> field <name> type *<type> is not supported - use fluxaorm.Reference[T] instead. JSON fields have no typed Fields descriptor.

Ignored Fields

orm:"ignore" removes a field from the schema entirely - no column, no accessors:

type UserEntity struct {
    ID        uint64
    TempValue string `orm:"ignore"`
}

Unsupported Types

Arrays ([3]string), slices other than []uint8, maps, *string, and pointers to registered entities are not supported. Validation fails with <entity> field <name> type <type> is not supported.

Typed Field Descriptors

Every column that can be filtered gets a descriptor in Provider.Fields, used by search and unique index lookups:

Go typeDescriptorCondition methods
uint*, IDfluxaorm.UintFieldEq, Gte, Lte, Gt, Lt, In (uint64)
int*fluxaorm.IntFieldEq, Gte, Lte, Gt, Lt, In (int64)
float*fluxaorm.FloatFieldEq, Gte, Lte, Gt, Lt, In (float64)
string + requiredfluxaorm.StringFieldIs, Like, In, IsEmpty
stringfluxaorm.NullableStringFieldIs, Like, In, IsEmpty, IsNull, IsNotNull
boolfluxaorm.BoolFieldIs
time.Timefluxaorm.TimeFieldEq, Gte, Lte, Gt, Lt
enum + requiredfluxaorm.EnumFieldIs, In (any)
enumfluxaorm.NullableEnumFieldIs, In, IsNull, IsNotNull
Reference[T] + requiredfluxaorm.ReferenceFieldEq, In (uint64)
Reference[T]fluxaorm.NullableUintFieldEq, Gte, Lte, Gt, Lt, In, IsNull, IsNotNull
*uint*, *int*, *float*, *bool, *time.Timefluxaorm.NullableUintField, NullableIntField, NullableFloatField, NullableBoolField, NullableTimeFieldas above plus IsNull, IsNotNull

FakeDelete, []uint8, sets, References[T] and JSON struct fields have no descriptor.

Edit this page
Last Updated: 9/21/26, 9:14 AM
Prev
Entities
Next
MySQL Indexes