Documentation
¶
Index ¶
- Variables
- func ClearAllOriginals()
- func ClearDBResolver()
- func ClearOriginals[T any](entity *T)
- func ConfigureConnectionPool(db *sql.DB, maxOpen, maxIdle int, maxLifetime, idleTimeout time.Duration)
- func ConfigureDBResolver(opts ...ResolverOption)
- func ConfigureDirtyTracking(maxEntities int)
- func ConnectPostgres(dsn string, config *DBConfig) (*sql.DB, error)
- func GetGlobalDB() *sql.DB
- func GetOriginal[T any](entity *T, column string) any
- func GetOriginals[T any](entity *T) map[string]any
- func GetStringBuilder() *strings.Builder
- func IsCheckViolation(err error) bool
- func IsConnectionError(err error) bool
- func IsConstraintViolation(err error) bool
- func IsDeadlock(err error) bool
- func IsDuplicateKey(err error) bool
- func IsForeignKeyViolation(err error) bool
- func IsNotFound(err error) bool
- func IsNotNullViolation(err error) bool
- func IsSchemaError(err error) bool
- func IsSerializationFailure(err error) bool
- func IsTimeout(err error) bool
- func IsTracked[T any](entity *T) bool
- func MustValidateColumnName(name string)
- func PutStringBuilder(sb *strings.Builder)
- func SetGlobalDB(db *sql.DB)
- func ToSnakeCase(s string) string
- func TrackedEntityCount() int
- func Transaction(ctx context.Context, fn func(tx *Tx) error) error
- func ValidateColumnName(name string) error
- func ValidateRawQuery(query string) error
- func WrapQueryError(operation, query string, args []any, err error) error
- func WrapRelationError(relation, modelType string, err error) error
- type BelongsTo
- type BelongsToMany
- type CTE
- type Cursor
- type DBConfig
- type DBResolver
- type FieldInfo
- type HasMany
- type HasOne
- type LoadBalancer
- type Model
- func (m *Model[T]) Attach(ctx context.Context, entity *T, relation string, ids []any, ...) error
- func (m *Model[T]) Avg(ctx context.Context, column string) (float64, error)
- func (m *Model[T]) BulkInsert(ctx context.Context, entities []*T) error
- func (m *Model[T]) Chunk(ctx context.Context, size int, callback func([]*T) error) error
- func (m *Model[T]) Clone() *Model[T]
- func (m *Model[T]) Count(ctx context.Context) (int64, error)
- func (m *Model[T]) CountOver(ctx context.Context, column string) (map[any]int64, error)
- func (m *Model[T]) Create(ctx context.Context, entity *T) error
- func (m *Model[T]) CreateMany(ctx context.Context, entities []*T) error
- func (m *Model[T]) CrossJoin(table string) *Model[T]
- func (m *Model[T]) Cursor(ctx context.Context) (*Cursor[T], error)
- func (m *Model[T]) Delete(ctx context.Context) error
- func (m *Model[T]) DeleteMany(ctx context.Context) error
- func (m *Model[T]) Detach(ctx context.Context, entity *T, relation string, ids []any) error
- func (m *Model[T]) Distinct() *Model[T]
- func (m *Model[T]) DistinctBy(columns ...string) *Model[T]
- func (m *Model[T]) Exec(ctx context.Context) (sql.Result, error)
- func (m *Model[T]) Exists(ctx context.Context) (bool, error)
- func (m *Model[T]) Find(ctx context.Context, id any) (*T, error)
- func (m *Model[T]) FindOrFail(ctx context.Context, id any) (*T, error)
- func (m *Model[T]) First(ctx context.Context) (*T, error)
- func (m *Model[T]) FirstOrCreate(ctx context.Context, attributes map[string]any, values map[string]any) (*T, error)
- func (m *Model[T]) ForceDeleteAll(ctx context.Context) error
- func (m *Model[T]) Get(ctx context.Context) ([]*T, error)
- func (m *Model[T]) GetArgs() []any
- func (m *Model[T]) GetDirtyFields(entity *T) map[string]any
- func (m *Model[T]) GetLimit() int
- func (m *Model[T]) GetOrderBys() []string
- func (m *Model[T]) GetOriginalValue(entity *T, column string) any
- func (m *Model[T]) GetWheres() []string
- func (m *Model[T]) GroupBy(columns ...string) *Model[T]
- func (m *Model[T]) GroupByCube(columns ...string) *Model[T]
- func (m *Model[T]) GroupByGroupingSets(sets ...[]string) *Model[T]
- func (m *Model[T]) GroupByRollup(columns ...string) *Model[T]
- func (m *Model[T]) HasDirtyFields(entity *T) bool
- func (m *Model[T]) Having(query string, args ...any) *Model[T]
- func (m *Model[T]) IsCleanField(entity *T, column string) bool
- func (m *Model[T]) IsDirtyField(entity *T, column string) bool
- func (m *Model[T]) IsEntityTracked(entity *T) bool
- func (m *Model[T]) Join(table, col1, op, col2 string) *Model[T]
- func (m *Model[T]) Latest(columns ...string) *Model[T]
- func (m *Model[T]) LeftJoin(table, col1, op, col2 string) *Model[T]
- func (m *Model[T]) Limit(n int) *Model[T]
- func (m *Model[T]) Load(ctx context.Context, entity *T, relations ...string) error
- func (m *Model[T]) LoadMorph(ctx context.Context, entities []*T, relation string, ...) error
- func (m *Model[T]) LoadSlice(ctx context.Context, entities []*T, relations ...string) error
- func (m *Model[T]) Lock(mode string) *Model[T]
- func (m *Model[T]) Offset(n int) *Model[T]
- func (m *Model[T]) Oldest(columns ...string) *Model[T]
- func (m *Model[T]) Omit(columns ...string) *Model[T]
- func (m *Model[T]) OrWhere(query any, args ...any) *Model[T]
- func (m *Model[T]) OrWhereNotNull(column string) *Model[T]
- func (m *Model[T]) OrWhereNull(column string) *Model[T]
- func (m *Model[T]) OrderBy(column, direction string) *Model[T]
- func (m *Model[T]) Paginate(ctx context.Context, page, perPage int) (*PaginationResult[T], error)
- func (m *Model[T]) Pluck(ctx context.Context, column string) ([]any, error)
- func (m *Model[T]) Print() (string, []any)
- func (m *Model[T]) Raw(query string, args ...any) *Model[T]
- func (m *Model[T]) Release()
- func (m *Model[T]) RightJoin(table, col1, op, col2 string) *Model[T]
- func (m *Model[T]) Scope(fn func(*Model[T]) *Model[T]) *Model[T]
- func (m *Model[T]) Select(columns ...string) *Model[T]
- func (m *Model[T]) SetDB(db *sql.DB) *Model[T]
- func (m *Model[T]) SimplePaginate(ctx context.Context, page, perPage int) (*PaginationResult[T], error)
- func (m *Model[T]) Sum(ctx context.Context, column string) (float64, error)
- func (m *Model[T]) Sync(ctx context.Context, entity *T, relation string, ids []any, ...) error
- func (m *Model[T]) Table(name string) *Model[T]
- func (m *Model[T]) TableName() string
- func (m *Model[T]) Transaction(ctx context.Context, fn func(tx *Tx) error) error
- func (m *Model[T]) Update(ctx context.Context, entity *T) error
- func (m *Model[T]) UpdateColumns(ctx context.Context, entity *T, columns ...string) error
- func (m *Model[T]) UpdateMany(ctx context.Context, values map[string]any) error
- func (m *Model[T]) UpdateManyByKey(ctx context.Context, lookupColumn, targetColumn string, updates any) error
- func (m *Model[T]) UpdateOrCreate(ctx context.Context, attributes map[string]any, values map[string]any) (*T, error)
- func (m *Model[T]) UsePrimary() *Model[T]
- func (m *Model[T]) UseReplica(index int) *Model[T]
- func (m *Model[T]) Where(query any, args ...any) *Model[T]
- func (m *Model[T]) WhereFullText(column, searchText string) *Model[T]
- func (m *Model[T]) WhereFullTextWithConfig(column, searchText, config string) *Model[T]
- func (m *Model[T]) WhereHas(relation string, callback any) *Model[T]
- func (m *Model[T]) WhereIn(column string, args []any) *Model[T]
- func (m *Model[T]) WhereNotNull(column string) *Model[T]
- func (m *Model[T]) WhereNull(column string) *Model[T]
- func (m *Model[T]) WherePhraseSearch(column, phrase string) *Model[T]
- func (m *Model[T]) WhereTsVector(tsvectorColumn, tsquery string) *Model[T]
- func (m *Model[T]) With(relations ...string) *Model[T]
- func (m *Model[T]) WithCTE(name string, query any) *Model[T]
- func (m *Model[T]) WithCallback(relation string, callback any) *Model[T]
- func (m *Model[T]) WithContext(ctx context.Context) *Model[T]
- func (m *Model[T]) WithMorph(relation string, typeMap map[string][]string) *Model[T]
- func (m *Model[T]) WithStmtCache(cache *StmtCache) *Model[T]
- func (m *Model[T]) WithTrackingScope(scope *TrackingScope) *Model[T]
- func (m *Model[T]) WithTx(tx *Tx) *Model[T]
- type ModelInfo
- type MorphMany
- type MorphOne
- type MorphTo
- type PaginationResult
- type QueryError
- type RandomLoadBalancer
- type Relation
- type RelationDefinition
- type RelationError
- type RelationType
- type ResolverOption
- type RoundRobinLoadBalancer
- type ScalarQuery
- func (q *ScalarQuery[T]) Clone() *ScalarQuery[T]
- func (q *ScalarQuery[T]) Count(ctx context.Context) (int64, error)
- func (q *ScalarQuery[T]) Distinct() *ScalarQuery[T]
- func (q *ScalarQuery[T]) First(ctx context.Context) (T, error)
- func (q *ScalarQuery[T]) Get(ctx context.Context) ([]T, error)
- func (q *ScalarQuery[T]) GroupBy(columns ...string) *ScalarQuery[T]
- func (q *ScalarQuery[T]) Having(query string, args ...any) *ScalarQuery[T]
- func (q *ScalarQuery[T]) Limit(n int) *ScalarQuery[T]
- func (q *ScalarQuery[T]) Offset(n int) *ScalarQuery[T]
- func (q *ScalarQuery[T]) OrWhere(query any, args ...any) *ScalarQuery[T]
- func (q *ScalarQuery[T]) OrderBy(column, direction string) *ScalarQuery[T]
- func (q *ScalarQuery[T]) Print() (string, []any)
- func (q *ScalarQuery[T]) Select(column string) *ScalarQuery[T]
- func (q *ScalarQuery[T]) SetDB(db *sql.DB) *ScalarQuery[T]
- func (q *ScalarQuery[T]) Table(name string) *ScalarQuery[T]
- func (q *ScalarQuery[T]) Where(query any, args ...any) *ScalarQuery[T]
- func (q *ScalarQuery[T]) WhereIn(column string, values []any) *ScalarQuery[T]
- func (q *ScalarQuery[T]) WhereNotNull(column string) *ScalarQuery[T]
- func (q *ScalarQuery[T]) WhereNull(column string) *ScalarQuery[T]
- func (q *ScalarQuery[T]) WithTx(tx *Tx) *ScalarQuery[T]
- type StmtCache
- type TableOverrider
- type TrackingScope
- type Tx
- type ValidationError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrRecordNotFound Query errors // ErrRecordNotFound is returned when a query returns no results ErrRecordNotFound = errors.New("zorm: record not found") // ErrInvalidModel Model errors // ErrInvalidModel is returned when the model type is invalid ErrInvalidModel = errors.New("zorm: invalid model") // ErrNilPointer is returned when a nil pointer is passed ErrNilPointer = errors.New("zorm: nil pointer") // ErrNoContext Context errors // ErrNoContext is returned when no context is provided ErrNoContext = errors.New("zorm: no context provided") // ErrRelationNotFound Relation errors // ErrRelationNotFound is returned when a relation method is not found ErrRelationNotFound = errors.New("zorm: relation not found") // ErrInvalidRelation is returned when relation type is invalid ErrInvalidRelation = errors.New("zorm: invalid relation type") // ErrInvalidConfig is returned when relation config is invalid ErrInvalidConfig = errors.New("zorm: invalid relation config") // ErrDuplicateKey Constraint violation errors // ErrDuplicateKey is returned for unique constraint violations ErrDuplicateKey = errors.New("zorm: duplicate key violation") // ErrForeignKey is returned for foreign key constraint violations ErrForeignKey = errors.New("zorm: foreign key constraint violation") // ErrCheckViolation is returned for CHECK constraint violations ErrCheckViolation = errors.New("zorm: check constraint violation") // ErrNotNullViolation is returned for NOT NULL constraint violations ErrNotNullViolation = errors.New("zorm: not null constraint violation") // ErrConnectionFailed Connection errors // ErrConnectionFailed is returned when database connection fails ErrConnectionFailed = errors.New("zorm: connection failed") // ErrConnectionLost is returned when connection is lost during operation ErrConnectionLost = errors.New("zorm: connection lost") // ErrTimeout is returned when a query or connection times out ErrTimeout = errors.New("zorm: operation timeout") // ErrNilDatabase is returned when database connection is nil ErrNilDatabase = errors.New("zorm: database connection is nil") // ErrTransactionDeadlock Transaction errors // ErrTransactionDeadlock is returned when a deadlock is detected ErrTransactionDeadlock = errors.New("zorm: transaction deadlock") // ErrSerializationFailure is returned for serialization failures ErrSerializationFailure = errors.New("zorm: serialization failure") // ErrColumnNotFound Schema errors // ErrColumnNotFound is returned when a column doesn't exist ErrColumnNotFound = errors.New("zorm: column not found") // ErrTableNotFound is returned when a table doesn't exist ErrTableNotFound = errors.New("zorm: table not found") // ErrInvalidSyntax is returned for SQL syntax errors ErrInvalidSyntax = errors.New("zorm: invalid SQL syntax") // ErrRequiresRawQuery Other errors // ErrRequiresRawQuery is returned when operation requires raw query ErrRequiresRawQuery = errors.New("zorm: operation requires raw query") )
var ErrInvalidColumnName = fmt.Errorf("zorm: invalid column name")
ErrInvalidColumnName is returned when a column name contains invalid characters.
var ErrRollbackFailed = errors.New("zorm: rollback failed")
ErrRollbackFailed is returned when transaction rollback fails
var GlobalDB *sql.DB
GlobalDB is the global database connection pool. For thread-safe access in concurrent code, prefer SetGlobalDB() for writes. Reads via GetGlobalDB() will check both this variable and the atomic pointer for backwards compatibility with code that directly assigns to GlobalDB.
Functions ¶
func ClearAllOriginals ¶ added in v0.8.0
func ClearAllOriginals()
ClearAllOriginals removes all tracking data from the global tracker. This can be used for periodic cleanup in long-running services.
func ClearDBResolver ¶ added in v0.8.0
func ClearDBResolver()
ClearDBResolver removes the global database resolver. This function is thread-safe.
func ClearOriginals ¶ added in v0.7.3
func ClearOriginals[T any](entity *T)
ClearOriginals removes tracking for an entity. Should be called when entity is deleted or no longer needed to prevent memory leaks.
func ConfigureConnectionPool ¶
func ConfigureConnectionPool(db *sql.DB, maxOpen, maxIdle int, maxLifetime, idleTimeout time.Duration)
ConfigureConnectionPool configures the database connection pool.
func ConfigureDBResolver ¶
func ConfigureDBResolver(opts ...ResolverOption)
ConfigureDBResolver configures the global database resolver for primary/replica setup. This function is thread-safe and can be called at any time. Example:
ConfigureDBResolver(
WithPrimary(primaryDB),
WithReplicas(replica1, replica2),
WithLoadBalancer(RoundRobinLB),
)
func ConfigureDirtyTracking ¶ added in v0.8.0
func ConfigureDirtyTracking(maxEntities int)
ConfigureDirtyTracking sets the maximum number of entities to track. A capacity of 0 means unbounded (no eviction). The default capacity is 50,000 entities. This function is thread-safe and can be called at any time.
Example:
zorm.ConfigureDirtyTracking(10000) // Track at most 10,000 entities
func ConnectPostgres ¶
ConnectPostgres creates a new *sql.DB connection pool for PostgreSQL using pgx driver. dsn: "postgres://user:password@host:port/dbname?sslmode=disable"
func GetGlobalDB ¶ added in v0.8.0
GetGlobalDB returns the global database connection in a thread-safe manner. For backwards compatibility, it checks both the atomic pointer and the deprecated GlobalDB variable, preferring the atomic if set.
func GetOriginal ¶ added in v0.7.3
GetOriginal returns the original value of a field before any modifications. Returns nil if the entity is not tracked or field doesn't exist.
func GetOriginals ¶ added in v0.7.3
GetOriginals returns all original values for the entity. Returns a copy to prevent external modification of tracking data.
func GetStringBuilder ¶
GetStringBuilder retrieves a strings.Builder from the pool.
func IsCheckViolation ¶
IsCheckViolation checks if the error is a CHECK constraint violation.
func IsConnectionError ¶
IsConnectionError checks if the error is a connection failure. This includes both connection refused and connection lost errors.
func IsConstraintViolation ¶
IsConstraintViolation checks if the error is any type of constraint violation. This includes unique, foreign key, not null, and check constraints.
func IsDeadlock ¶
IsDeadlock checks if the error is a transaction deadlock.
func IsDuplicateKey ¶
IsDuplicateKey checks if the error is a duplicate key violation.
func IsForeignKeyViolation ¶
IsForeignKeyViolation checks if the error is a foreign key violation.
func IsNotFound ¶
IsNotFound checks if the error is ErrRecordNotFound. It uses errors.Is to check the error chain.
func IsNotNullViolation ¶
IsNotNullViolation checks if the error is a NOT NULL constraint violation.
func IsSchemaError ¶
IsSchemaError checks if the error is a schema-related error. This includes missing columns, missing tables, and syntax errors.
func IsSerializationFailure ¶
IsSerializationFailure checks if the error is a serialization failure.
func IsTracked ¶ added in v0.7.3
IsTracked returns true if the entity has original values stored. An entity becomes tracked when loaded from the database via Get, First, or Find.
func MustValidateColumnName ¶ added in v0.5.0
func MustValidateColumnName(name string)
MustValidateColumnName validates a column name and panics if invalid. Use this for internal validation where invalid column names indicate programming errors.
func PutStringBuilder ¶
PutStringBuilder returns a strings.Builder to the pool after resetting it.
func SetGlobalDB ¶ added in v0.8.0
SetGlobalDB sets the global database connection in a thread-safe manner. This also updates the GlobalDB variable for backwards compatibility.
func ToSnakeCase ¶
ToSnakeCase converts a string to snake_case. Handles acronyms correctly (e.g., UserID -> user_id, HTTPClient -> http_client). Results are cached to avoid repeated conversions for the same input.
func TrackedEntityCount ¶ added in v0.8.0
func TrackedEntityCount() int
TrackedEntityCount returns the number of currently tracked entities. Useful for monitoring memory usage in long-running services.
func Transaction ¶
Transaction executes a function within a transaction.
func ValidateColumnName ¶ added in v0.5.0
ValidateColumnName checks if a column name is safe to use in SQL queries. It uses a strict whitelist approach to prevent SQL injection. Allowed characters: alphanumeric, underscore, dot, asterisk, space, parens, comma. Dangerous characters like quotes, semicolons, and comments are rejected. Results are cached to avoid repeated validation of the same column names.
func ValidateRawQuery ¶ added in v0.7.0
ValidateRawQuery validates a raw SQL query fragment to prevent SQL injection. It checks for dangerous patterns like comments, multiple statements, and suspicious keywords. This is used for HAVING clauses and other places where raw query fragments are accepted.
func WrapQueryError ¶
WrapQueryError wraps a database error with query context. It analyzes the error to categorize it and extract relevant information such as table names and constraint names where possible. For PostgreSQL, it uses SQLSTATE codes for reliable error detection; for other databases, it falls back to error message pattern matching.
func WrapRelationError ¶
WrapRelationError wraps a relation error with context
Types ¶
type BelongsTo ¶
BelongsTo defines a BelongsTo relation.
func (BelongsTo[T]) GetOverrideTable ¶
func (BelongsTo[T]) NewRelated ¶
func (BelongsTo[T]) RelationType ¶
func (BelongsTo[T]) RelationType() RelationType
type BelongsToMany ¶
type BelongsToMany[T any] struct { PivotTable string ForeignKey string RelatedKey string LocalKey string RelatedPK string Table string }
BelongsToMany defines a BelongsToMany relation.
func (BelongsToMany[T]) NewRelated ¶
func (BelongsToMany[T]) NewRelated() any
func (BelongsToMany[T]) RelationType ¶
func (BelongsToMany[T]) RelationType() RelationType
type Cursor ¶
type Cursor[T any] struct { // contains filtered or unexported fields }
Cursor provides a typed, forward-only iterator over database query results. It wraps sql.Rows and maps each row into the generic model type T.
type DBConfig ¶
type DBConfig struct {
MaxOpenConns int
MaxIdleConns int
ConnMaxLifetime time.Duration
ConnMaxIdleTime time.Duration
}
DBConfig configures the connection pool settings.
type DBResolver ¶
type DBResolver struct {
// contains filtered or unexported fields
}
DBResolver manages primary and replica database connections. It automatically routes write operations to the primary and read operations to replicas.
func GetGlobalResolver ¶ added in v0.8.0
func GetGlobalResolver() *DBResolver
GetGlobalResolver returns the current global database resolver. Returns nil if no resolver is configured.
func (*DBResolver) HasReplicas ¶
func (r *DBResolver) HasReplicas() bool
HasReplicas returns true if replicas are configured.
func (*DBResolver) Primary ¶
func (r *DBResolver) Primary() *sql.DB
Primary returns the primary database connection.
func (*DBResolver) Replica ¶
func (r *DBResolver) Replica() *sql.DB
Replica returns a replica based on the load balancer strategy.
type FieldInfo ¶
type FieldInfo struct {
Name string // 16 bytes
Column string // 16 bytes
FieldType reflect.Type // 16 bytes
Index []int // 24 bytes
IsPrimary bool // 1 byte
IsAuto bool // 1 byte + 6 padding
}
FieldInfo holds data about a single field in the model. Struct layout is optimized to minimize padding on 64-bit systems.
type HasMany ¶
HasMany defines a HasMany relation.
func (HasMany[T]) GetOverrideTable ¶
func (HasMany[T]) NewRelated ¶
func (HasMany[T]) RelationType ¶
func (HasMany[T]) RelationType() RelationType
type HasOne ¶
HasOne defines a HasOne relation.
func (HasOne[T]) GetOverrideTable ¶
func (HasOne[T]) NewRelated ¶
func (HasOne[T]) RelationType ¶
func (HasOne[T]) RelationType() RelationType
type LoadBalancer ¶
LoadBalancer is an interface for selecting a replica from a pool.
var RandomLB LoadBalancer = &RandomLoadBalancer{}
RandomLB is a convenience variable for random load balancing.
var RoundRobinLB LoadBalancer = &RoundRobinLoadBalancer{}
RoundRobinLB is a convenience variable for round-robin load balancing.
type Model ¶
type Model[T any] struct { // contains filtered or unexported fields }
Model provides a strongly typed ORM interface for working with the entity type T. It stores the active query state—including selected columns, filters, ordering, grouping, relation loading rules, and raw SQL segments—allowing the builder to compose complex queries in a structured and chainable manner.
The Model also tracks the execution context, database handle or transaction, and metadata derived from T that is used for mapping database rows into entities.
Thread Safety: Model instances are NOT safe for concurrent modification. Query builder methods (Where, Select, OrderBy, etc.) mutate internal state without locking and must not be called concurrently on the same Model instance.
Safe patterns for concurrent use:
- Build a base model in a single goroutine, then clone it for concurrent handlers. The base model must not be modified after the setup phase. Clone() on a read-only base is safe from multiple goroutines.
- Create a fresh Model via New[T]() inside each goroutine.
Example:
base := New[User]().Where("active", true) // setup phase — single goroutine
// SAFE: base is never written again; clones get independent state
go func() { base.Clone().Where("role", "admin").Get(ctx) }()
go func() { base.Clone().Where("role", "user").Get(ctx) }()
// UNSAFE: Concurrent mutation of same Model — DATA RACE
go func() { base.Where("role", "admin").Get(ctx) }()
go func() { base.Where("role", "user").Get(ctx) }()
func Acquire ¶ added in v0.7.1
Acquire retrieves a Model[T] from the pool for high-throughput scenarios. The returned model is pre-configured with default values and ready for use. Call Release() when done to return the model to the pool.
Example:
m := Acquire[User]()
defer m.Release()
users, err := m.Where("active", true).Get(ctx)
func (*Model[T]) Attach ¶
func (m *Model[T]) Attach(ctx context.Context, entity *T, relation string, ids []any, pivotData map[any]map[string]any) error
Attach inserts rows into the pivot table for a BelongsToMany relation. pivotData: map[any]map[string]any (RelatedID -> {Column: Value})
func (*Model[T]) Avg ¶
Avg calculates the average of a column. Returns 0 if no rows match or the average is null. Column names are validated to prevent SQL injection. This method is safe for concurrent use - it clones the model before modification.
func (*Model[T]) BulkInsert ¶ added in v0.7.1
BulkInsert inserts multiple records using a single prepared statement. This is more efficient than CreateMany for scenarios where you need fine-grained control or want to handle errors per-entity. The prepared statement is reused for each entity, reducing preparation overhead.
Example:
users := []*User{{Name: "Alice"}, {Name: "Bob"}, {Name: "Charlie"}}
err := model.BulkInsert(ctx, users)
func (*Model[T]) Chunk ¶
Chunk processes the results in chunks to save memory. Uses Clone() for each iteration to avoid mutating the original query state.
func (*Model[T]) Clone ¶
Clone creates a deep copy of the Model. This is useful for creating new queries based on an existing one without modifying it.
Thread Safety: Model instances are NOT safe for concurrent modification. Clone() itself is safe to call from multiple goroutines on a model that is no longer being modified (e.g., a base model built in a setup phase). It is NOT safe to call Clone() concurrently with any mutating method (Where, Select, OrderBy, etc.) on the same instance — those methods do not acquire any lock.
Safe patterns:
// Build base in a single goroutine, then clone concurrently (no further writes to base)
base := New[User]().Where("active", true) // single-goroutine setup
go func() { base.Clone().Where("role", "admin").Get(ctx) }()
go func() { base.Clone().Where("role", "user").Get(ctx) }()
// Or create a fresh model per goroutine
go func() { New[User]().Where("active", true).Get(ctx) }()
func (*Model[T]) Count ¶
Count returns the number of records matching the query. This method is safe for concurrent use - it clones the model before modification. When the query includes GROUP BY, DISTINCT, or DISTINCT ON, the count is wrapped in a subquery to return the correct total number of rows.
func (*Model[T]) CountOver ¶
CountOver returns count of records partitioned by the specified column. This uses window functions: COUNT(*) OVER (PARTITION BY column). Returns a map of column value -> count. Column names are validated to prevent SQL injection. This method is safe for concurrent use - it clones the model before modification.
func (*Model[T]) Create ¶
Create inserts a new record.
Hook Behavior: If the entity implements BeforeCreate(context.Context) error, it will be called before the INSERT. If BeforeCreate succeeds but INSERT fails, any side effects from BeforeCreate are NOT rolled back automatically.
For atomic operations with hooks that have side effects, wrap in a transaction:
err := zorm.Transaction(ctx, func(tx *zorm.Tx) error {
return model.WithTx(tx).Create(ctx, entity)
})
func (*Model[T]) CreateMany ¶
CreateMany inserts multiple records in a single query.
func (*Model[T]) CrossJoin ¶ added in v0.9.2
CrossJoin adds a CROSS JOIN clause, producing a Cartesian product of both tables. No ON condition is required. Table name is validated to prevent SQL injection.
func (*Model[T]) Cursor ¶
Cursor returns a cursor for iterating over results one by one. Useful for large datasets to avoid loading everything into memory.
Relation loading (With, WithCallback, WithMorph) is not supported with Cursor because it requires all rows to be available for batch loading. Use Get() instead when relation loading is needed.
func (*Model[T]) Delete ¶
Delete deletes records matching the current query conditions. At least one WHERE condition is required to prevent accidental full-table deletes. To intentionally delete all records use ForceDeleteAll().
func (*Model[T]) DeleteMany ¶
DeleteMany deletes records matching the current query conditions. Alias for Delete(). At least one WHERE condition is required. To intentionally delete all records use ForceDeleteAll().
func (*Model[T]) DistinctBy ¶
DistinctBy adds DISTINCT ON (columns) to the SELECT clause. This is a PostgreSQL-specific feature that returns the first row of each set of rows where the given columns match. Column names are validated to prevent SQL injection.
func (*Model[T]) Exists ¶ added in v0.4.0
Exists checks if any record matches the query conditions. It uses "SELECT 1 FROM table WHERE conditions LIMIT 1" for efficiency. This method is safe for concurrent use - it clones the model before modification.
func (*Model[T]) FindOrFail ¶
FindOrFail finds a record by ID or returns an error. In Go, this is identical to Find, but added for API parity.
func (*Model[T]) First ¶
First executes the query and returns the first result. Uses Clone() to avoid mutating the original query state.
func (*Model[T]) FirstOrCreate ¶
func (m *Model[T]) FirstOrCreate(ctx context.Context, attributes map[string]any, values map[string]any) (*T, error)
FirstOrCreate finds the first record matching attributes or creates it with attributes+values. If found, returns the existing record. If not found, creates a new record with merged attributes+values.
func (*Model[T]) ForceDeleteAll ¶ added in v0.9.2
ForceDeleteAll deletes ALL records in the table without any WHERE conditions. Use this only when a full-table delete is intentional.
func (*Model[T]) GetDirtyFields ¶ added in v0.7.3
GetDirtyFields returns all changed fields on an entity. This is a convenience method on Model.
func (*Model[T]) GetOrderBys ¶ added in v0.9.1
GetOrderBys returns the order by clauses.
func (*Model[T]) GetOriginalValue ¶ added in v0.7.3
GetOriginalValue returns the original value of a field. This is a convenience method on Model.
func (*Model[T]) GroupBy ¶
GroupBy adds a GROUP BY clause. Column names are validated to prevent SQL injection.
func (*Model[T]) GroupByCube ¶
GroupByCube adds a GROUP BY CUBE clause. Column names are validated to prevent SQL injection.
func (*Model[T]) GroupByGroupingSets ¶
GroupByGroupingSets adds a GROUP BY GROUPING SETS clause. Each slice in sets represents a grouping set. Empty slice represents empty grouping set (). Column names are validated to prevent SQL injection.
func (*Model[T]) GroupByRollup ¶
GroupByRollup adds a GROUP BY ROLLUP clause. Column names are validated to prevent SQL injection.
func (*Model[T]) HasDirtyFields ¶ added in v0.8.0
HasDirtyFields returns true if the entity has any dirty (changed) fields. This is more efficient than GetDirtyFields when you only need to check existence.
func (*Model[T]) Having ¶
Having adds a HAVING clause (used with GROUP BY). The query string is validated to prevent SQL injection by checking for dangerous patterns. Use parameterized values (?) for user input.
SECURITY WARNING: While ValidateRawQuery prevents traditional SQL injection attacks, it cannot prevent logical injection (e.g., "COUNT(*) > ? OR 1=1"). The query parameter should be a trusted constant expression. DO NOT pass unsanitized user input directly.
Safe example:
Having("COUNT(*) > ?", 5)
Having("SUM(amount) >= ?", 1000)
Example: Having("COUNT(*) > ?", 5)
func (*Model[T]) IsCleanField ¶ added in v0.7.3
IsCleanField checks if a specific field on an entity is clean (unchanged). This is a convenience method on Model.
func (*Model[T]) IsDirtyField ¶ added in v0.7.3
IsDirtyField checks if a specific field on an entity is dirty. This is a convenience method on Model.
func (*Model[T]) IsEntityTracked ¶ added in v0.7.3
IsEntityTracked returns true if the entity is being tracked for dirty checking. This is a convenience method on Model.
func (*Model[T]) Join ¶ added in v0.9.2
Join adds an INNER JOIN clause. Both table and column names are validated to prevent SQL injection. Returns the model with buildErr set on invalid input; the error is surfaced when a terminal method (Get, First, etc.) is called.
Example:
New[Order]().Join("users", "orders.user_id", "=", "users.id").Get(ctx)
func (*Model[T]) LeftJoin ¶ added in v0.9.2
LeftJoin adds a LEFT JOIN clause. Returns all rows from the left table and matching rows from the right table. Column names are validated to prevent SQL injection.
func (*Model[T]) Load ¶
Load eager loads relations on a single entity. This method creates an internal clone to avoid mutating the original model's state, making it safe to reuse the model for subsequent queries.
func (*Model[T]) LoadMorph ¶
func (m *Model[T]) LoadMorph(ctx context.Context, entities []*T, relation string, typeMap map[string][]string) error
LoadMorph eager loads a polymorphic relation with constraints on a slice. This method creates an internal clone to avoid mutating the original model's state, making it safe to reuse the model for subsequent queries.
func (*Model[T]) LoadSlice ¶
LoadSlice eager loads relations on a slice of entities. This method creates an internal clone to avoid mutating the original model's state, making it safe to reuse the model for subsequent queries.
func (*Model[T]) Lock ¶
Lock adds a locking clause to the SELECT query. Common modes: "UPDATE", "NO KEY UPDATE", "SHARE", "KEY SHARE" This will generate: SELECT ... FOR [mode] Lock modes are validated against a whitelist to prevent SQL injection.
func (*Model[T]) Omit ¶ added in v0.7.3
Omit excludes columns from Update operations. This is useful when you want to update most fields but exclude specific ones.
Example:
err := model.Omit("phone_number", "address").Update(ctx, user)
// Updates all fields EXCEPT phone_number and address
func (*Model[T]) OrWhereNotNull ¶
OrWhereNotNull adds an OR condition that checks whether the given column is NOT NULL. Column names are validated to prevent SQL injection.
Example:
Model[User]().OrWhereNotNull("verified_at")
// OR verified_at IS NOT NULL
func (*Model[T]) OrWhereNull ¶
OrWhereNull adds an OR condition that checks whether the given column is NULL. Column names are validated to prevent SQL injection.
Example:
Model[User]().OrWhereNull("deleted_at")
// OR deleted_at IS NULL
func (*Model[T]) OrderBy ¶
OrderBy adds an ORDER BY clause. Column names are validated to prevent SQL injection.
func (*Model[T]) Paginate ¶
Paginate executes the query with pagination. Uses Clone() to avoid mutating the original query state. If page is less than 1, it defaults to 1. If perPage is less than 1, it defaults to 15.
func (*Model[T]) Pluck ¶
Pluck retrieves a single column's values from the result set. Column names are validated to prevent SQL injection. This method is safe for concurrent use - it clones the model before modification.
func (*Model[T]) Print ¶
Print returns the SQL query and arguments that would be executed without running it. This is useful for debugging and logging the generated SQL. Example:
sql, args := m.Where("status", "active").Limit(10).Print()
fmt.Println(sql, args)
Output: "SELECT * FROM users WHERE 1=1 AND (status = $1) LIMIT 10" [active]
Example ¶
Example usage for documentation
m := New[TestModel]()
m.Where("status", "active").Limit(10)
sql, args := m.Print()
fmt.Println(sql)
fmt.Println(args)
Output: SELECT * FROM test_models WHERE 1=1 AND status = $1 LIMIT 10 [active]
func (*Model[T]) Release ¶ added in v0.7.1
func (m *Model[T]) Release()
Release returns the Model to the pool for reuse. After calling Release, the Model should not be used again.
func (*Model[T]) RightJoin ¶ added in v0.9.2
RightJoin adds a RIGHT JOIN clause. Returns all rows from the right table and matching rows from the left table. Column names are validated to prevent SQL injection.
func (*Model[T]) Scope ¶
Scope applies a function to the query builder. Useful for reusable query logic (Scopes).
func (*Model[T]) Select ¶
Select specifies which columns to select. Column names are validated to prevent SQL injection. Returns the model with buildErr set if any column name is invalid; the error is surfaced when a terminal method (Get, First, etc.) is called.
func (*Model[T]) SimplePaginate ¶
func (m *Model[T]) SimplePaginate(ctx context.Context, page, perPage int) (*PaginationResult[T], error)
SimplePaginate executes the query with pagination but skips the count query. Uses Clone() to avoid mutating the original query state. Use this when you don't need the total count (e.g., "Load More" buttons). This is ~2x faster than Paginate() since it only runs 1 query. If page is less than 1, it defaults to 1. If perPage is less than 1, it defaults to 15.
func (*Model[T]) Sum ¶
Sum calculates the sum of a column. Returns 0 if no rows match or the sum is null. Column names are validated to prevent SQL injection. This method is safe for concurrent use - it clones the model before modification.
func (*Model[T]) Sync ¶
func (m *Model[T]) Sync(ctx context.Context, entity *T, relation string, ids []any, pivotData map[any]map[string]any) error
Sync synchronizes the association with the given IDs. It attaches missing IDs and detaches IDs that are not in the new list. pivotData: map[any]map[string]any (RelatedID -> {Column: Value})
func (*Model[T]) Table ¶
Table sets a custom table name for the query. This overrides the table name derived from the struct type.
func (*Model[T]) TableName ¶
TableName returns the table name for the model. If a custom table name is set via Table(), it returns that. Otherwise, it returns the table name from the model info.
func (*Model[T]) Transaction ¶
Transaction executes a function within a transaction using the model's database connection.
func (*Model[T]) Update ¶
Update updates a single record based on its primary key. The entity must not be nil and must have a valid primary key value.
Hook Behavior:
- BeforeUpdate: Called before UPDATE. If it succeeds but UPDATE fails, side effects are NOT rolled back.
- AfterUpdate: Called after successful UPDATE. If it fails, the UPDATE is already committed and will NOT be rolled back.
For atomic operations with hooks that have side effects, wrap in a transaction:
err := zorm.Transaction(ctx, func(tx *zorm.Tx) error {
return model.WithTx(tx).Update(ctx, entity)
})
func (*Model[T]) UpdateColumns ¶ added in v0.7.3
UpdateColumns updates only the specified columns of the entity. This is useful when you want explicit control over which columns are updated.
Example:
user.Name = "New Name" user.Email = "new@email.com" err := model.UpdateColumns(ctx, user, "name", "email") // Only updates name and email
func (*Model[T]) UpdateMany ¶
UpdateMany updates records matching the query with values.
func (*Model[T]) UpdateManyByKey ¶ added in v0.8.3
func (m *Model[T]) UpdateManyByKey(ctx context.Context, lookupColumn, targetColumn string, updates any) error
UpdateManyByKey updates multiple records by matching a lookup column to values in a map. Each map key is matched against lookupColumn, and the corresponding map value is set in targetColumn. Uses CASE WHEN syntax for database portability.
Example:
updates := map[string]string{"REF001": "pending", "REF002": "approved"}
err := New[Text]().UpdateManyByKey(ctx, "reference_number", "status", updates)
This generates SQL like:
UPDATE texts SET status = CASE reference_number
WHEN $1 THEN $2
WHEN $3 THEN $4
END, updated_at = $5
WHERE reference_number IN ($6, $7)
func (*Model[T]) UpdateOrCreate ¶
func (m *Model[T]) UpdateOrCreate(ctx context.Context, attributes map[string]any, values map[string]any) (*T, error)
UpdateOrCreate finds a record matching attributes and updates it with values, or creates it. If found, updates the record with values. If not found, creates a new record with merged attributes+values.
func (*Model[T]) UsePrimary ¶
UsePrimary forces the next query to use the primary database connection. This is useful when you need to read from primary for consistency, such as immediately after a write operation. Example: m.UsePrimary().Get()
func (*Model[T]) UseReplica ¶
UseReplica forces the next query to use a specific replica by index. This is useful for testing or when you want to target a specific replica. Example: m.UseReplica(0).Get()
func (*Model[T]) Where ¶
Where adds a WHERE clause. Supports multiple forms:
Where("column", value) -> column = ? (converted to $n at execution)
Where("column", ">", value) -> column > ?
Where(map[string]any{"name": "John", "age": 30}) -> name = ? AND age = ?
Where(&User{Name: "John"}) -> name = ?
Where(func(q *Model[T]) { ... }) -> nested group with parentheses
func (*Model[T]) WhereFullText ¶
WhereFullText adds a full-text search condition using tsvector and tsquery. Uses default 'english' configuration and plainto_tsquery for user-friendly search. Column names are validated to prevent SQL injection. Example: WhereFullText("content", "search terms") Generates: WHERE to_tsvector('english', content) @@ plainto_tsquery('english', ?)
func (*Model[T]) WhereFullTextWithConfig ¶
WhereFullTextWithConfig adds a full-text search condition with a custom text search configuration. Column names and config are validated to prevent SQL injection. Example: WhereFullTextWithConfig("content", "search terms", "spanish") Generates: WHERE to_tsvector('spanish', content) @@ plainto_tsquery('spanish', ?)
func (*Model[T]) WhereIn ¶
WhereIn adds a WHERE IN clause. Column names are validated to prevent SQL injection.
func (*Model[T]) WhereNotNull ¶
WhereNotNull adds an AND condition that checks whether the given column is NOT NULL. Column names are validated to prevent SQL injection.
Example:
Model[User]().WhereNotNull("verified_at")
// WHERE verified_at IS NOT NULL
func (*Model[T]) WhereNull ¶
WhereNull adds an AND condition that checks whether the given column is NULL. Column names are validated to prevent SQL injection.
Example:
Model[User]().WhereNull("deleted_at")
// WHERE deleted_at IS NULL
func (*Model[T]) WherePhraseSearch ¶
WherePhraseSearch adds an exact phrase search condition. Uses phraseto_tsquery which preserves word order. Column names are validated to prevent SQL injection. Example: WherePhraseSearch("content", "fat cat") Generates: WHERE to_tsvector('english', content) @@ phraseto_tsquery('english', ?)
func (*Model[T]) WhereTsVector ¶
WhereTsVector adds a full-text search condition on a pre-computed tsvector column. This is more efficient when you have an indexed tsvector column. Column names are validated to prevent SQL injection. Example: WhereTsVector("search_vector", "fat & rat") Generates: WHERE search_vector @@ to_tsquery('english', ?)
func (*Model[T]) With ¶
With adds relations to eager load. Multiple relation names can be specified, including nested relations.
Examples:
With("Posts") // Single relation
With("Posts", "Comments") // Multiple relations
With("Posts.Comments") // Nested relation
func (*Model[T]) WithCTE ¶
WithCTE adds a Common Table Expression (CTE) to the query. query can be a string or a *Model[T]. CTE names are validated to prevent SQL injection.
func (*Model[T]) WithCallback ¶
WithCallback adds a relation with a callback to apply constraints. The callback receives a query builder for the related model and can apply filters, ordering, limits, etc.
Example:
WithCallback("Posts", func(q *Model[Post]) {
q.Where("published", true).OrderBy("created_at", "DESC").Limit(10)
})
func (*Model[T]) WithContext ¶
WithContext sets the context for the query.
func (*Model[T]) WithMorph ¶
WithMorph adds a polymorphic relation to eager load with type-specific constraints. typeMap: map[string][]string{"events": {"Calendar"}, "posts": {"Author"}}
func (*Model[T]) WithStmtCache ¶
WithStmtCache enables statement caching for this model instance. The cache will be used to store and reuse prepared statements, improving performance by avoiding re-preparation of frequently used queries.
Example:
cache := NewStmtCache(100) defer cache.Close() model := New[User]().WithStmtCache(cache)
func (*Model[T]) WithTrackingScope ¶ added in v0.8.0
func (m *Model[T]) WithTrackingScope(scope *TrackingScope) *Model[T]
WithTrackingScope sets a tracking scope for this model instance. All entities loaded through this model will be registered with the scope, and their tracking data will be automatically cleared when the scope is closed.
This is useful for batch operations where you want automatic cleanup of tracking data without memory leaks.
Example:
scope := zorm.NewTrackingScope() defer scope.Close() model := zorm.New[User]().WithTrackingScope(scope) users, _ := model.Get(ctx) // All users are tracked in scope // When scope.Close() is called, all tracking data is cleared
func (*Model[T]) WithTx ¶
WithTx returns a clone of the model with the transaction set. This ensures the original model is not mutated, allowing safe reuse.
Example:
base := New[User]().Where("active", true)
Transaction(ctx, func(tx *Tx) error {
// base is not mutated; txModel is a separate copy
txModel := base.WithTx(tx)
return txModel.Create(ctx, &user)
})
// base can still be used outside the transaction
type ModelInfo ¶
type ModelInfo struct {
Type reflect.Type
TableName string
PrimaryKey string
Fields map[string]*FieldInfo // StructFieldName -> FieldInfo
Columns map[string]*FieldInfo // DBColumnName -> FieldInfo
RelationFields map[string][]int // FieldName -> field index for FieldByIndex (relation fields)
Accessors []int // Indices of methods starting with "Get"
RelationMethods map[string]int // MethodName -> Index
}
ModelInfo holds the reflection data for a model struct.
func ParseModel ¶
ParseModel inspects the struct T and returns its metadata.
func ParseModelType ¶
ParseModelType inspects the type and returns its metadata. Uses sync.Map for thread-safe caching with optimal read performance.
func (*ModelInfo) GetRelationField ¶ added in v0.8.0
GetRelationField returns the reflect.Value for a relation field by name. Uses cached field indices for O(1) access instead of O(n) FieldByName. Returns invalid Value if field not found.
type MorphMany ¶
type MorphMany[T any] struct { Type string // Column name in related table (e.g. imageable_type) ID string // Column name in related table (e.g. imageable_id) Table string }
MorphMany defines a polymorphic HasMany relation.
func (MorphMany[T]) GetOverrideTable ¶
func (MorphMany[T]) NewRelated ¶
func (MorphMany[T]) RelationType ¶
func (MorphMany[T]) RelationType() RelationType
type MorphOne ¶
type MorphOne[T any] struct { Type string // Column name in related table (e.g. imageable_type) ID string // Column name in related table (e.g. imageable_id) Table string }
MorphOne defines a polymorphic HasOne relation.
func (MorphOne[T]) GetOverrideTable ¶
func (MorphOne[T]) NewRelated ¶
func (MorphOne[T]) RelationType ¶
func (MorphOne[T]) RelationType() RelationType
type MorphTo ¶
type MorphTo[T any] struct { Type string // Column name for Type (e.g. imageable_type) ID string // Column name for ID (e.g. imageable_id) TypeMap map[string]any // Map of DB type string to empty struct instance (e.g. "posts": Post{}) }
MorphTo defines a polymorphic BelongsTo relation. T is usually `any` or a common interface, but in our generic system, the field in the struct will likely be `any` or an interface.
Unlike other relation types, MorphTo cannot implement NewRelated() and NewModel() in a meaningful way because the related type is determined dynamically at runtime based on the Type column value. These methods intentionally return nil. The TypeMap field maps database type strings to empty struct instances, which are used during eager loading to determine and instantiate the correct related type.
Example:
func (i *Image) ImageableRelation() MorphTo[any] {
return MorphTo[any]{
Type: "imageable_type",
ID: "imageable_id",
TypeMap: map[string]any{
"posts": Post{},
"users": User{},
},
}
}
func (MorphTo[T]) NewModel ¶ added in v0.6.0
NewModel returns nil for MorphTo because the related model type is determined dynamically at runtime based on the type column value.
func (MorphTo[T]) NewRelated ¶
NewRelated returns nil for MorphTo because the related type is determined dynamically at runtime based on the type column value. Use TypeMap instead.
func (MorphTo[T]) RelationType ¶
func (MorphTo[T]) RelationType() RelationType
MorphTo implements Relation interface. RelationType returns RelationMorphTo.
type PaginationResult ¶
type PaginationResult[T any] struct { Data []*T `json:"data"` Total int64 `json:"total"` PerPage int `json:"per_page"` CurrentPage int `json:"current_page"` LastPage int `json:"last_page"` }
PaginationResult holds pagination metadata and data.
type QueryError ¶
type QueryError struct {
Query string // The SQL query that failed
Args []any // The query arguments
Operation string // Operation type: SELECT, INSERT, UPDATE, DELETE, etc.
Err error // The underlying error
Table string // The table involved (if detectable)
Constraint string // The constraint name (if constraint violation)
}
QueryError wraps database errors with query context for better debugging. It provides detailed information about what went wrong including the query, arguments, operation type, and optionally the affected table and constraint.
func GetQueryError ¶
func GetQueryError(err error) *QueryError
GetQueryError extracts the underlying QueryError from an error if present. Returns nil if the error is not or does not wrap a QueryError. Use this to access query details like the SQL, args, table, and constraint.
func (*QueryError) Error ¶
func (e *QueryError) Error() string
func (*QueryError) Unwrap ¶
func (e *QueryError) Unwrap() error
type RandomLoadBalancer ¶
type RandomLoadBalancer struct{}
RandomLoadBalancer selects a replica randomly for load distribution. This provides non-deterministic load balancing which can help prevent hotspots when multiple clients start at the same time.
type Relation ¶
type Relation interface {
RelationType() RelationType
NewRelated() any
NewModel(ctx context.Context, db *sql.DB) any
}
Relation interface allows us to handle generics uniformly.
type RelationDefinition ¶
type RelationDefinition struct {
Type RelationType
Field string // The struct field name in the parent model
RelatedType reflect.Type
// Keys
ForeignKey string
LocalKey string
OwnerKey string // For BelongsTo
// Pivot (BelongsToMany)
PivotTable string
PivotForeign string
PivotRelated string
}
RelationDefinition holds metadata about a relation.
type RelationError ¶
type RelationError struct {
Relation string // Name of the relation
ModelType string // Type of the model
Err error // The underlying error
}
RelationError wraps relation loading failures with context
func (*RelationError) Error ¶
func (e *RelationError) Error() string
func (*RelationError) Unwrap ¶
func (e *RelationError) Unwrap() error
type RelationType ¶
type RelationType string
RelationType defines the type of relationship between two models in the ORM.
const ( // RelationHasOne represents a one-to-one relationship where the current // model owns a single related record. RelationHasOne RelationType = "HasOne" // RelationHasMany represents a one-to-many relationship where the current // model owns multiple related records. RelationHasMany RelationType = "HasMany" // RelationBelongsTo represents an inverse one-to-one or one-to-many // relationship where the current model references a parent record. RelationBelongsTo RelationType = "BelongsTo" // RelationBelongsToMany represents a many-to-many relationship between // two models, typically connected through a join table. RelationBelongsToMany RelationType = "BelongsToMany" )
const ( // RelationMorphTo represents a polymorphic inverse relationship where the // current model can belong to one of several different model types. The // actual target type and ID are determined by discriminator columns such as // "morph_type" and "morph_id". RelationMorphTo RelationType = "MorphTo" // RelationMorphOne represents a polymorphic one-to-one relationship where a // single related record can be associated with multiple possible parent // model types. RelationMorphOne RelationType = "MorphOne" // RelationMorphMany represents a polymorphic one-to-many relationship where // multiple related records can be associated with various parent model types. RelationMorphMany RelationType = "MorphMany" )
type ResolverOption ¶
type ResolverOption func(*DBResolver)
ResolverOption is a functional option for configuring DBResolver.
func WithLoadBalancer ¶
func WithLoadBalancer(lb LoadBalancer) ResolverOption
WithLoadBalancer sets the load balancer strategy. Default is RoundRobinLoadBalancer.
func WithPrimary ¶
func WithPrimary(db *sql.DB) ResolverOption
WithPrimary sets the primary database connection.
func WithReplicas ¶
func WithReplicas(dbs ...*sql.DB) ResolverOption
WithReplicas sets the replica database connections.
type RoundRobinLoadBalancer ¶
type RoundRobinLoadBalancer struct {
// contains filtered or unexported fields
}
RoundRobinLoadBalancer distributes load across replicas using round-robin.
type ScalarQuery ¶ added in v0.8.2
type ScalarQuery[T any] struct { // contains filtered or unexported fields }
ScalarQuery provides a query builder for single-column scalar results. T can be any type that sql.Rows.Scan supports: string, int, int64, float64, bool, time.Time, []byte, sql.Null* types, or any sql.Scanner.
Example:
names, err := zorm.Query[string]().
Table("roles").
Select("name").
Where("active", true).
Get(ctx)
func Query ¶ added in v0.8.2
func Query[T any]() *ScalarQuery[T]
Query creates a new scalar query builder for type T.
func (*ScalarQuery[T]) Clone ¶ added in v0.8.2
func (q *ScalarQuery[T]) Clone() *ScalarQuery[T]
Clone creates a deep copy of the query. This is useful for creating variations of a query without mutating the original.
func (*ScalarQuery[T]) Count ¶ added in v0.8.2
func (q *ScalarQuery[T]) Count(ctx context.Context) (int64, error)
Count returns the count of matching rows. This ignores the Select column and uses COUNT(*).
func (*ScalarQuery[T]) Distinct ¶ added in v0.8.2
func (q *ScalarQuery[T]) Distinct() *ScalarQuery[T]
Distinct adds DISTINCT to the query.
func (*ScalarQuery[T]) First ¶ added in v0.8.2
func (q *ScalarQuery[T]) First(ctx context.Context) (T, error)
First returns the first matching value. Returns ErrRecordNotFound if no rows match. Uses Clone() to avoid mutating the original query's limit.
func (*ScalarQuery[T]) Get ¶ added in v0.8.2
func (q *ScalarQuery[T]) Get(ctx context.Context) ([]T, error)
Get executes the query and returns all matching values.
func (*ScalarQuery[T]) GroupBy ¶ added in v0.8.2
func (q *ScalarQuery[T]) GroupBy(columns ...string) *ScalarQuery[T]
GroupBy adds GROUP BY columns.
func (*ScalarQuery[T]) Having ¶ added in v0.8.2
func (q *ScalarQuery[T]) Having(query string, args ...any) *ScalarQuery[T]
Having adds a HAVING clause.
func (*ScalarQuery[T]) Limit ¶ added in v0.8.2
func (q *ScalarQuery[T]) Limit(n int) *ScalarQuery[T]
Limit sets the LIMIT clause.
func (*ScalarQuery[T]) Offset ¶ added in v0.8.2
func (q *ScalarQuery[T]) Offset(n int) *ScalarQuery[T]
Offset sets the OFFSET clause.
func (*ScalarQuery[T]) OrWhere ¶ added in v0.8.2
func (q *ScalarQuery[T]) OrWhere(query any, args ...any) *ScalarQuery[T]
OrWhere adds an OR WHERE condition.
func (*ScalarQuery[T]) OrderBy ¶ added in v0.8.2
func (q *ScalarQuery[T]) OrderBy(column, direction string) *ScalarQuery[T]
OrderBy adds an ORDER BY clause.
func (*ScalarQuery[T]) Print ¶ added in v0.8.2
func (q *ScalarQuery[T]) Print() (string, []any)
Print returns the SQL query and arguments that would be executed without running it. This is useful for debugging and logging the generated SQL.
func (*ScalarQuery[T]) Select ¶ added in v0.8.2
func (q *ScalarQuery[T]) Select(column string) *ScalarQuery[T]
Select sets the column to select (single column only).
func (*ScalarQuery[T]) SetDB ¶ added in v0.8.2
func (q *ScalarQuery[T]) SetDB(db *sql.DB) *ScalarQuery[T]
SetDB sets a specific database connection.
func (*ScalarQuery[T]) Table ¶ added in v0.8.2
func (q *ScalarQuery[T]) Table(name string) *ScalarQuery[T]
Table sets the table name for the query. Table names are validated to prevent SQL injection.
func (*ScalarQuery[T]) Where ¶ added in v0.8.2
func (q *ScalarQuery[T]) Where(query any, args ...any) *ScalarQuery[T]
Where adds a WHERE condition. Supports multiple forms:
Where("column", value) -> column = ?
Where("column >", value) -> column > ?
Where("column", ">", value) -> column > ?
Where(map[string]any{"name": "John"}) -> name = ?
func (*ScalarQuery[T]) WhereIn ¶ added in v0.8.2
func (q *ScalarQuery[T]) WhereIn(column string, values []any) *ScalarQuery[T]
WhereIn adds a WHERE column IN (...) condition.
func (*ScalarQuery[T]) WhereNotNull ¶ added in v0.8.2
func (q *ScalarQuery[T]) WhereNotNull(column string) *ScalarQuery[T]
WhereNotNull adds a WHERE column IS NOT NULL condition.
func (*ScalarQuery[T]) WhereNull ¶ added in v0.8.2
func (q *ScalarQuery[T]) WhereNull(column string) *ScalarQuery[T]
WhereNull adds a WHERE column IS NULL condition.
func (*ScalarQuery[T]) WithTx ¶ added in v0.8.2
func (q *ScalarQuery[T]) WithTx(tx *Tx) *ScalarQuery[T]
WithTx uses a transaction for the query.
type StmtCache ¶
type StmtCache struct {
// contains filtered or unexported fields
}
StmtCache provides a thread-safe LRU cache for prepared statements. It stores prepared SQL statements and automatically evicts the least recently used entries when the cache reaches its maximum capacity.
The cache uses sharded locking to reduce contention under high concurrency. It is safe for concurrent use by multiple goroutines and helps improve performance by reusing prepared statements instead of re-preparing them on every execution.
func NewStmtCache ¶
NewStmtCache creates a new statement cache with the specified capacity. When the cache reaches capacity, the least recently used statement will be evicted to make room for new entries.
A capacity of 0 or negative value will default to 100.
func (*StmtCache) Clear ¶
func (c *StmtCache) Clear()
Clear closes all cached statements and clears the cache.
func (*StmtCache) Close ¶
Close closes all cached statements and releases all resources. In-flight statements (with refCount > 0) will be closed when their release function is called after the last user is done.
func (*StmtCache) Get ¶
Get retrieves a cached prepared statement for the given SQL query. Returns the statement and a release function. The caller MUST call the release function when finished using the statement. Returns nil, nil if the statement is not found in the cache.
func (*StmtCache) Put ¶
Put stores a prepared statement in the cache for the given SQL query. If the cache is at capacity, the least recently used statement will be evicted (and closed when no longer in use) before adding the new statement.
If a statement with the same query already exists, it will be replaced.
func (*StmtCache) PutAndGet ¶ added in v0.7.0
PutAndGet atomically stores a prepared statement and retrieves it with an incremented reference count. This avoids race conditions where the statement could be evicted between Put and Get calls. Returns the statement and a release function. The caller MUST call the release function.
type TableOverrider ¶
type TableOverrider interface {
GetOverrideTable() string
}
TableOverrider interface allows relations to specify a custom table name.
type TrackingScope ¶ added in v0.8.0
type TrackingScope struct {
// contains filtered or unexported fields
}
TrackingScope provides scoped dirty tracking for batch operations. Entities tracked within a scope are automatically cleaned up when the scope is closed. This is useful for processing large batches of entities where you don't want tracking data to persist beyond the operation.
func NewTrackingScope ¶ added in v0.8.0
func NewTrackingScope() *TrackingScope
NewTrackingScope creates a new tracking scope.
func (*TrackingScope) Close ¶ added in v0.8.0
func (s *TrackingScope) Close()
Close clears all tracking data for entities in this scope. After Close is called, the scope should not be reused.
func (*TrackingScope) Len ¶ added in v0.8.0
func (s *TrackingScope) Len() int
Len returns the number of entities tracked in this scope.
type ValidationError ¶
type ValidationError struct {
Field string // Field name that failed validation
Value any // The invalid value
Message string // Human-readable error message
}
ValidationError represents a model validation failure
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string