Documentation
¶
Overview ¶
Package orm1 provides a lightweight, type-safe Object-Relational Mapping library.
Core Concepts ¶
orm1 is built around three main ideas:
Entity Mapping: Define how Go structs map to database tables using RegisterEntity. Struct tags and naming conventions control the mapping behavior.
Session: All database operations are performed through a session that manages entity lifecycle. The session tracks which entities have been persisted to determine whether Save operations should INSERT or UPDATE.
Entity State: Entities loaded via Get or saved via Save are marked as persisted. This allows the session to perform UPDATEs for persisted entities and INSERTs for new ones.
Quick Start ¶
// 1. Define your entity structs
type User struct {
ID int64 `orm1:"primary"`
Name string
Email string `orm1:"column:user_email"`
Posts []*Post
}
type Post struct {
ID int64 `orm1:"primary"`
UserID int64 `orm1:"parental"`
Title string
Content string
}
// 2. Register entities and create session factory
driver := orm1.NewSQLiteDriver(db)
factory := orm1.NewSessionFactoryWithDriver(driver)
factory.RegisterEntity(&User{})
factory.RegisterEntity(&Post{})
// 3. Create session
session := factory.CreateSession()
// 4. Perform operations
var user *User
session.Get(ctx, &user, orm1.NewKey(1))
user.Name = "Updated Name"
session.Save(ctx, user)
Entity Relationships ¶
Parent-child relationships are automatically loaded and saved:
var user *User
session.Get(ctx, &user, orm1.NewKey(1))
// user.Posts is automatically populated
user.Posts = append(user.Posts, &Post{Title: "New Post"})
session.Save(ctx, user)
// New post is inserted with UserID set automatically
Type-Safe Queries ¶
Use EntityQuery for complex queries with compile-time type safety:
query := orm1.NewEntityQuery[User](session, "u")
users, err := query.
Where("u.email LIKE ?", "%@example.com").
OrderBy(query.Desc("u.name")).
FetchAll(ctx)
Raw SQL ¶
For cases where you need raw SQL:
var results []*struct {
Name string
Count int64
}
raw := orm1.NewRawQuery(session, "SELECT name, COUNT(*) as count FROM users GROUP BY name")
raw.ScanAll(ctx, &results)
Example (AggregateSupport) ¶
Example from DDD Aggregate Support in README
package main
import (
"context"
"database/sql"
"fmt"
"github.com/hanpama/orm1"
"github.com/hanpama/orm1/driver"
_ "github.com/mattn/go-sqlite3"
)
type Post struct {
ID int64 `orm1:"auto"`
Title string
Comments []*Comment
}
type Comment struct {
ID int64 `orm1:"auto"`
PostID int64 `orm1:"parental"`
Text string
}
func main() {
// Setup test database
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
db.Exec(`CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT
)`)
db.Exec(`CREATE TABLE comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER,
text TEXT
)`)
registry := orm1.NewRegistry()
registry.Register(&Post{}, orm1.WithTable("posts"))
registry.Register(&Comment{}, orm1.WithTable("comments"))
factory := orm1.NewSessionFactory(registry, driver.NewSQLite(db))
// Setup: Create a post
setupSession := factory.CreateSession()
setupTx, _ := setupSession.Begin(context.Background(), nil)
post := &Post{Title: "Hello World"}
setupSession.Save(context.Background(), post)
setupTx.Commit(context.Background())
session := factory.CreateSession()
ctx := context.Background()
tx, _ := session.Begin(ctx, nil)
defer tx.Rollback(ctx)
// Load post with all comments
var loadedPost *Post
if err := session.Get(ctx, &loadedPost, orm1.NewKey(post.ID)); err != nil {
return
}
// Add a new comment
loadedPost.Comments = append(loadedPost.Comments, &Comment{Text: "Great!"})
// Save cascades changes to the Comments slice
if err := session.Save(ctx, loadedPost); err != nil {
return
}
if err := tx.Commit(ctx); err != nil {
return
}
// Verify
verifySession := factory.CreateSession()
var verifyPost *Post
verifySession.Get(context.Background(), &verifyPost, orm1.NewKey(post.ID))
fmt.Printf("Post has %d comment(s)\n", len(verifyPost.Comments))
}
Output: Post has 1 comment(s)
Example (QuickStart) ¶
Example from Quick Start in README
package main
import (
"context"
"database/sql"
"fmt"
"github.com/hanpama/orm1"
"github.com/hanpama/orm1/driver"
_ "github.com/mattn/go-sqlite3"
)
// Domain entities for examples
type Order struct {
ID int64 `orm1:"auto"`
CustomerID int64
Total float64
Items []*OrderItem
}
type OrderItem struct {
ID int64 `orm1:"auto"`
OrderID int64 `orm1:"parental"`
Product string
Quantity int
Price float64
}
func main() {
// Setup test database
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
// Create tables
db.Exec(`CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER,
total REAL
)`)
db.Exec(`CREATE TABLE order_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER,
product TEXT,
quantity INTEGER,
price REAL
)`)
// 2. Register entities and create factory
registry := orm1.NewRegistry()
registry.Register(&Order{}, orm1.WithTable("orders"))
registry.Register(&OrderItem{}, orm1.WithTable("order_items"))
factory := orm1.NewSessionFactory(registry, driver.NewSQLite(db))
// Setup: Create an order with items
setupSession := factory.CreateSession()
setupTx, _ := setupSession.Begin(context.Background(), nil)
order := &Order{
CustomerID: 1,
Total: 99.99,
Items: []*OrderItem{
{Product: "Gadget", Quantity: 2, Price: 49.99},
},
}
setupSession.Save(context.Background(), order)
setupTx.Commit(context.Background())
// 3. Use in your application
session := factory.CreateSession()
ctx := context.Background()
// Start a transaction
tx, err := session.Begin(ctx, nil)
if err != nil {
return
}
// Defer rollback in case of error or panic
defer tx.Rollback(ctx)
// Load an aggregate root; children are loaded automatically
var loadedOrder *Order
if err := session.Get(ctx, &loadedOrder, orm1.NewKey(order.ID)); err != nil {
// ... handle "not found" or other errors
return
}
// Modify the aggregate
loadedOrder.Total = 159.99 // Update a value
loadedOrder.Items = append(loadedOrder.Items, &OrderItem{ // Add a new child
Product: "Widget",
Quantity: 5,
Price: 9.99,
})
// Save handles all changes to the aggregate
// (updates Order, inserts new OrderItem)
if err := session.Save(ctx, loadedOrder); err != nil {
// Rollback will be triggered by the defer
return
}
// Commit the transaction
if err := tx.Commit(ctx); err != nil {
return
}
// Verify the result
verifySession := factory.CreateSession()
var verifyOrder *Order
verifySession.Get(context.Background(), &verifyOrder, orm1.NewKey(order.ID))
fmt.Printf("Order total: %.2f, Items: %d\n", verifyOrder.Total, len(verifyOrder.Items))
}
Output: Order total: 159.99, Items: 2
Example (RawSQL) ¶
Example from Raw SQL in README
package main
import (
"context"
"database/sql"
"fmt"
"github.com/hanpama/orm1"
"github.com/hanpama/orm1/driver"
_ "github.com/mattn/go-sqlite3"
)
func main() {
// Setup test database
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
db.Exec(`CREATE TABLE transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT,
amount REAL
)`)
db.Exec(`INSERT INTO transactions (category, amount) VALUES
('Food', 50.00),
('Food', 30.00),
('Transport', 20.00)`)
registry := orm1.NewRegistry()
factory := orm1.NewSessionFactory(registry, driver.NewSQLite(db))
session := factory.CreateSession()
ctx := context.Background()
type Result struct {
Category string
Total float64
}
var results []*Result
raw := orm1.NewRawQuery(session,
"SELECT category, SUM(amount) as total FROM transactions GROUP BY category")
raw.ScanAll(ctx, &results)
for _, r := range results {
fmt.Printf("%s: %.2f\n", r.Category, r.Total)
}
}
Output: Food: 80.00 Transport: 20.00
Example (TypeSafeQueries) ¶
Example from Type-Safe Queries in README
package main
import (
"context"
"database/sql"
"fmt"
"github.com/hanpama/orm1"
"github.com/hanpama/orm1/driver"
_ "github.com/mattn/go-sqlite3"
)
type User struct {
ID int64 `orm1:"auto"`
Age int
CreatedAt string `orm1:"auto"`
}
func main() {
// Setup test database
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
db.Exec(`CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
age INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`)
db.Exec(`INSERT INTO users (age) VALUES (25), (30), (15)`)
registry := orm1.NewRegistry()
registry.Register(&User{}, orm1.WithTable("users"))
factory := orm1.NewSessionFactory(registry, driver.NewSQLite(db))
session := factory.CreateSession()
ctx := context.Background()
query := orm1.NewEntityQuery[User](session, "u")
users, err := query.
Where("u.age > ?", 18).
OrderBy(query.Desc("u.created_at")).
FetchAll(ctx)
if err != nil {
return
}
fmt.Printf("Found %d users over 18\n", len(users))
}
Output: Found 2 users over 18
Index ¶
- Variables
- type Driver
- type EntityQuery
- func (q *EntityQuery[T]) Asc(expr string, params ...any) sql.OrderBy
- func (q *EntityQuery[T]) AscNullsFirst(expr string, params ...any) sql.OrderBy
- func (q *EntityQuery[T]) AscNullsLast(expr string, params ...any) sql.OrderBy
- func (q *EntityQuery[T]) Count(ctx context.Context) (int64, error)
- func (q *EntityQuery[T]) Desc(expr string, params ...any) sql.OrderBy
- func (q *EntityQuery[T]) DescNullsFirst(expr string, params ...any) sql.OrderBy
- func (q *EntityQuery[T]) DescNullsLast(expr string, params ...any) sql.OrderBy
- func (q *EntityQuery[T]) FetchAll(ctx context.Context) ([]*T, error)
- func (q *EntityQuery[T]) FetchMany(ctx context.Context, limit int) ([]*T, error)
- func (q *EntityQuery[T]) FetchOne(ctx context.Context) (*T, error)
- func (q *EntityQuery[T]) GroupByPrimaryKey() *EntityQuery[T]
- func (q *EntityQuery[T]) Having(condition string, params ...any) *EntityQuery[T]
- func (q *EntityQuery[T]) Join(table string, alias string, on string, params ...any) *EntityQuery[T]
- func (q *EntityQuery[T]) LeftJoin(table string, alias string, on string, params ...any) *EntityQuery[T]
- func (q *EntityQuery[T]) Offset(n int) *EntityQuery[T]
- func (q *EntityQuery[T]) OrderBy(orderBys ...sql.OrderBy) *EntityQuery[T]
- func (q *EntityQuery[T]) Paginate(ctx context.Context, after Key, first *int, before Key, last *int) (*Page, error)
- func (q *EntityQuery[T]) Where(condition string, params ...any) *EntityQuery[T]
- type IsolationLevel
- type Key
- type MappingOption
- type Page
- type RawQuery
- type Registry
- type Rows
- type Session
- func (s *Session) BatchDelete(ctx context.Context, entities ...any) error
- func (s *Session) BatchGet(ctx context.Context, dests any, ids []Key) error
- func (s *Session) BatchSave(ctx context.Context, entities ...any) error
- func (s *Session) Begin(ctx context.Context, opts *TxOptions) (*Transaction, error)
- func (s *Session) Clear()
- func (s *Session) Delete(ctx context.Context, entity any) error
- func (s *Session) Get(ctx context.Context, dest any, id Key) error
- func (s *Session) Save(ctx context.Context, entity any) error
- type SessionBackend
- type SessionFactory
- type Transaction
- type TxOptions
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultRegistry = NewRegistry()
DefaultRegistry is the global registry for entity types.
var NewKey = key.NewKey
NewKey creates a new Key from the given values. Supports up to 9 column values.
Functions ¶
This section is empty.
Types ¶
type Driver ¶
Driver creates Backend instances for database operations. This is a type alias for driver.Driver, allowing users to reference the Driver interface from the orm1 package without importing driver.
func NewPostgreSQLDriver ¶
NewPostgreSQLDriver creates a driver for PostgreSQL databases.
This is a convenience wrapper around driver.NewPostgres. The driver creates a new backend instance for each session.
Example:
db, _ := sql.Open("postgres", "...")
driver := orm1.NewPostgreSQLDriver(db)
factory := orm1.NewSessionFactoryWithDriver(driver)
func NewSQLiteDriver ¶
NewSQLiteDriver creates a driver for SQLite databases.
This is a convenience wrapper around driver.NewSQLite. The driver creates a new backend instance for each session.
Example:
db, _ := sql.Open("sqlite3", "...")
driver := orm1.NewSQLiteDriver(db)
factory := orm1.NewSessionFactoryWithDriver(driver)
type EntityQuery ¶
type EntityQuery[T any] struct { // contains filtered or unexported fields }
EntityQuery provides a type-safe query builder for entities. It uses generics to ensure compile-time type safety for query results. Supports WHERE, JOIN, ORDER BY, GROUP BY, HAVING, and pagination.
func NewEntityQuery ¶
func NewEntityQuery[T any](session *Session, alias string) *EntityQuery[T]
NewEntityQuery creates a new query builder for entity type T. The alias is used as the table alias in the generated SQL.
func (*EntityQuery[T]) Asc ¶
func (q *EntityQuery[T]) Asc(expr string, params ...any) sql.OrderBy
Asc creates an ascending OrderBy clause with NULLS LAST (PostgreSQL/Oracle standard). This ensures consistent behavior across all databases (PostgreSQL, SQLite, Oracle).
func (*EntityQuery[T]) AscNullsFirst ¶
func (q *EntityQuery[T]) AscNullsFirst(expr string, params ...any) sql.OrderBy
AscNullsFirst creates an ascending OrderBy clause with NULL values sorted first.
func (*EntityQuery[T]) AscNullsLast ¶
func (q *EntityQuery[T]) AscNullsLast(expr string, params ...any) sql.OrderBy
AscNullsLast creates an ascending OrderBy clause with NULL values sorted last.
func (*EntityQuery[T]) Count ¶
func (q *EntityQuery[T]) Count(ctx context.Context) (int64, error)
Count returns the number of entities matching the query.
func (*EntityQuery[T]) Desc ¶
func (q *EntityQuery[T]) Desc(expr string, params ...any) sql.OrderBy
Desc creates a descending OrderBy clause with NULLS FIRST (PostgreSQL/Oracle standard). This ensures consistent behavior across all databases (PostgreSQL, SQLite, Oracle).
func (*EntityQuery[T]) DescNullsFirst ¶
func (q *EntityQuery[T]) DescNullsFirst(expr string, params ...any) sql.OrderBy
DescNullsFirst creates a descending OrderBy clause with NULL values sorted first.
func (*EntityQuery[T]) DescNullsLast ¶
func (q *EntityQuery[T]) DescNullsLast(expr string, params ...any) sql.OrderBy
DescNullsLast creates a descending OrderBy clause with NULL values sorted last.
func (*EntityQuery[T]) FetchAll ¶
func (q *EntityQuery[T]) FetchAll(ctx context.Context) ([]*T, error)
FetchAll executes the query and returns all matching entities. All returned entities are marked as persisted.
func (*EntityQuery[T]) FetchMany ¶
func (q *EntityQuery[T]) FetchMany(ctx context.Context, limit int) ([]*T, error)
FetchMany executes the query and returns up to limit matching entities. All returned entities are marked as persisted.
func (*EntityQuery[T]) FetchOne ¶
func (q *EntityQuery[T]) FetchOne(ctx context.Context) (*T, error)
FetchOne executes the query and returns the first matching entity. Returns nil if no entity matches the query.
func (*EntityQuery[T]) GroupByPrimaryKey ¶
func (q *EntityQuery[T]) GroupByPrimaryKey() *EntityQuery[T]
GroupByPrimaryKey adds a GROUP BY clause using the entity's primary key columns.
func (*EntityQuery[T]) Having ¶
func (q *EntityQuery[T]) Having(condition string, params ...any) *EntityQuery[T]
Having adds a HAVING condition to the query. Multiple Having calls are combined with AND.
func (*EntityQuery[T]) Join ¶
func (q *EntityQuery[T]) Join(table string, alias string, on string, params ...any) *EntityQuery[T]
Join adds an INNER JOIN to the query. table is the table name, alias is its query alias, on is the join condition.
func (*EntityQuery[T]) LeftJoin ¶
func (q *EntityQuery[T]) LeftJoin(table string, alias string, on string, params ...any) *EntityQuery[T]
LeftJoin adds a LEFT OUTER JOIN to the query. table is the table name, alias is its query alias, on is the join condition.
func (*EntityQuery[T]) Offset ¶
func (q *EntityQuery[T]) Offset(n int) *EntityQuery[T]
Offset sets the offset for the query.
func (*EntityQuery[T]) OrderBy ¶
func (q *EntityQuery[T]) OrderBy(orderBys ...sql.OrderBy) *EntityQuery[T]
OrderBy sets the ORDER BY clause for the query.
func (*EntityQuery[T]) Paginate ¶
func (q *EntityQuery[T]) Paginate(ctx context.Context, after Key, first *int, before Key, last *int) (*Page, error)
Paginate performs cursor-based pagination on the query. after/before are cursors (primary keys) for pagination boundaries. first/last control the number of results (use nil for no limit). Offset set via Offset() method is applied after cursor filtering.
func (*EntityQuery[T]) Where ¶
func (q *EntityQuery[T]) Where(condition string, params ...any) *EntityQuery[T]
Where adds a WHERE condition to the query. Multiple Where calls are combined with AND.
type IsolationLevel ¶ added in v0.2.0
type IsolationLevel int
IsolationLevel represents the isolation level for a transaction.
const ( LevelDefault IsolationLevel = iota LevelReadUncommitted LevelReadCommitted LevelWriteCommitted LevelRepeatableRead LevelSnapshot LevelSerializable LevelLinearizable )
type Key ¶
Key represents an entity's primary or foreign key value(s). It is a comparable interface that can be used as a map key.
Examples:
session.Get(ctx, &user, orm1.NewKey(1)) // single-column key session.Get(ctx, &item, orm1.NewKey(orderId, itemId)) // composite key
type MappingOption ¶ added in v0.2.0
type MappingOption func(*mapping.EntityMetadata)
MappingOption is a function that configures an EntityMetadata.
func WithSchema ¶
func WithSchema(schema string) MappingOption
WithSchema sets the database schema for the entity's table.
func WithTable ¶
func WithTable(table string) MappingOption
WithTable sets the table name for the entity. If not specified, defaults to snake_case of the struct name.
type RawQuery ¶
type RawQuery struct {
// contains filtered or unexported fields
}
RawQuery represents a raw SQL query with parameter interpolation.
func NewRawQuery ¶
NewRawQuery creates a new raw query with the given SQL and parameters.
func (*RawQuery) Exec ¶
Exec executes the raw query and returns the number of rows affected. Use this for INSERT, UPDATE, DELETE, and other non-SELECT queries.
Example:
affected, err := orm1.NewRawQuery(session, "UPDATE users SET age = ? WHERE id = ?", 30, 1).Exec(ctx)
func (*RawQuery) Rows ¶
Rows executes the raw query and returns the result rows. The caller is responsible for calling Close() on the returned Rows.
func (*RawQuery) ScanAll ¶
ScanAll executes the raw query and scans all rows into dest. dest must be a pointer to a slice of struct pointers (*[]*Struct).
Columns are mapped to struct fields by name. By default, field names are converted to snake_case for matching (e.g., UserID matches user_id column).
Use the `orm1:"column:name"` tag to specify custom column names, and `orm1:"ignore"` to exclude fields. See ScanOne for tag examples.
func (*RawQuery) ScanOne ¶
ScanOne executes the raw query and scans the first row into dest. dest must be a pointer to a struct. Returns nil if no rows are found.
Columns are mapped to struct fields by name. By default, field names are converted to snake_case for matching (e.g., UserID matches user_id column).
The `orm1:"column:name"` tag can specify a custom column name:
type Result struct {
UserEmail string `orm1:"column:email"` // Maps to "email" column
PostCount int64 `orm1:"column:count"` // Maps to "count" column
}
The `orm1:"ignore"` tag excludes fields from scanning:
type Result struct {
Name string
Computed string `orm1:"ignore"` // Not populated from query
}
Unmapped columns in the result set are silently ignored.
type Registry ¶ added in v0.2.0
type Registry struct {
// contains filtered or unexported fields
}
Registry is a storage for entity type metadata. It collects struct field metadata through registration and provides this metadata to SessionFactory for building EntityMapping instances.
func (*Registry) GetMetadata ¶ added in v0.2.0
func (r *Registry) GetMetadata() map[reflect.Type]mapping.EntityMetadata
GetMetadata returns the collected metadata for building. This is used by SessionFactory to build EntityMapping instances.
func (*Registry) GetRegistered ¶ added in v0.2.0
GetRegistered returns the set of registered entity types. This is used by SessionFactory to detect child relationships.
func (*Registry) Register ¶ added in v0.2.0
func (r *Registry) Register(entityPtr any, opts ...MappingOption)
Register adds an entity type to the registry with optional configuration.
Entity Model ¶
An entity represents a database table row and its relationships. Entities are defined as Go structs with fields mapped to database columns. orm1 uses struct tags and naming conventions to determine how fields map to the database.
Parameters ¶
entityPtr must be a pointer to a struct instance (e.g., &User{}). The struct type defines the entity's schema.
Options can customize the mapping:
- WithTable("table_name"): Set table name (default: snake_case of struct name)
- WithSchema("schema_name"): Set database schema
- WithPrimaryKey("Field1", "Field2"): Define composite primary key
Struct Tags ¶
Fields can be annotated with `orm1` struct tags to control mapping behavior:
type User struct {
ID int64 `orm1:"auto"` // Auto-managed (AUTOINCREMENT, etc.)
ParentID int64 `orm1:"parental"` // Foreign key to parent
Email string `orm1:"column:user_email"` // Custom column name
CreatedAt time.Time `orm1:"skip_insert"` // Not inserted (e.g., trigger-managed)
UpdatedAt time.Time `orm1:"skip_update"` // Not updated (e.g., immutable)
Internal string `orm1:"ignore"` // Not persisted at all
Posts []*Post `orm1:"child"` // One-to-many relationship
Profile *Profile `orm1:"child"` // One-to-one relationship
}
Available tags:
- auto: Exclude from both INSERT and UPDATE (for DB-managed values like AUTOINCREMENT)
- primary: Mark as primary key field
- parental: Mark as foreign key to parent entity
- column:name: Specify database column name (default: snake_case of field name)
- skip_insert: Exclude from INSERT statements (for DB-generated values)
- skip_update: Exclude from UPDATE statements (for immutable columns)
- ignore: Exclude field from persistence entirely
- child: Mark as relationship field (auto-detected for registered entity types)
Auto-Detection Rules ¶
When tags are not specified, orm1 applies these conventions:
- Field named "ID" is the primary key
- Field names map to snake_case column names
- Fields of registered entity types ([]*T or *T) are children
- Parental keys must be explicitly tagged with `orm1:"parental"`
Relationships ¶
Child entities are loaded and saved cascading from their parents. Define relationships as fields with registered entity types:
- []*ChildType: One-to-many (plural children)
- *ChildType: One-to-one (singular child)
Child entities must have parental key fields matching the parent's primary key.
Example ¶
registry := orm1.NewRegistry()
// Register parent entity
registry.Register(&User{},
orm1.WithTable("users"),
orm1.WithPrimaryKey("ID"))
// Register child entity
registry.Register(&Post{},
orm1.WithTable("posts"))
// Create factory with registry
factory := orm1.NewSessionFactory(registry, driver)
session := factory.CreateSession()
type Rows ¶
Rows is the interface for iterating over query results. This is a type alias for driver.Rows.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session manages database operations and tracks entity state. It tracks which entities have been persisted to the database, enabling Save to determine whether to INSERT (for new entities) or UPDATE (for persisted entities). All persistence operations (Get, Save, Delete) are performed through a Session.
func (*Session) BatchDelete ¶
BatchDelete removes multiple entities from the database. Only deletes entities that are marked as persisted. After deletion, entities are unmarked from the persisted state. entities must be entity pointers (*Entity).
func (*Session) BatchGet ¶
BatchGet loads multiple entities by their primary keys in a single query. dests must be a pointer to a slice of entity pointers (*[]*Entity). Results are returned in the same order as ids.
BatchGet always performs a fresh database query. After loading, entities are marked as persisted, so subsequent Save calls will perform UPDATE.
For non-existent keys, nil is placed at the corresponding position in the result slice. For example, if ids = [key1, key2, key3] and only key1 and key3 exist in the database, the result will be [entity1, nil, entity3].
func (*Session) BatchSave ¶
BatchSave persists multiple entities in a single batch operation. Performs UPDATE for entities marked as persisted, INSERT for new entities. After saving, all entities are marked as persisted. entities must be entity pointers (*Entity).
func (*Session) Begin ¶
Begin starts a transaction with optional transaction options. For the first transaction, it calls backend.BeginTx(). For nested transactions, it creates a SAVEPOINT. It backs up the current session state onto a stack for potential restoration on Rollback.
Default isolation level: READ COMMITTED SQLite: Only supports SERIALIZABLE (ignores other levels) PostgreSQL: Supports all standard isolation levels
Nested transactions inherit the isolation level from the parent and cannot change it.
func (*Session) Clear ¶
func (s *Session) Clear()
Clear clears the session state, removing all tracked entities and cached relationships. This is useful for long-lived sessions that process multiple logical units of work and need to free memory or reset tracking state between operations.
Example usage in a batch processing scenario:
session := factory.CreateSession()
for batch := range batches {
for _, item := range batch {
session.Save(ctx, item)
}
session.Clear() // Clear session state between batches
}
Note: After calling Clear(), previously loaded entities are no longer tracked as persisted. Calling Save on them again will perform INSERT instead of UPDATE.
func (*Session) Delete ¶
Delete removes an entity from the database. Only deletes entities that are marked as persisted. After deletion, the entity is unmarked from the persisted state. entity must be a pointer to a struct (*Entity).
type SessionBackend ¶
SessionBackend defines the interface for database-specific implementations. This is a type alias for driver.Backend.
type SessionFactory ¶
type SessionFactory struct {
// contains filtered or unexported fields
}
SessionFactory builds entity mappings and creates sessions. It builds EntityMapping instances from Registry metadata at creation time and reuses them for all sessions.
func NewSessionFactory ¶
func NewSessionFactory(registry *Registry, driver Driver) *SessionFactory
NewSessionFactory creates a new session factory with a registry and driver. The factory builds entity mappings immediately from the registry metadata and caches them for efficient session creation.
Example:
registry := orm1.NewRegistry()
registry.Register(&User{})
registry.Register(&Post{})
driver := orm1.NewPostgreSQLDriver(db)
factory := orm1.NewSessionFactory(registry, driver)
session := factory.CreateSession()
func (*SessionFactory) CreateSession ¶
func (sf *SessionFactory) CreateSession() *Session
CreateSession creates a new Session with a fresh backend instance. The backend is created using the Driver that was configured.
Each call to CreateSession creates a new backend instance, as backends are not safe for concurrent use across multiple sessions.
Entity mappings are already built at factory creation time, making session creation very efficient.
type Transaction ¶ added in v0.2.0
type Transaction struct {
// contains filtered or unexported fields
}
Transaction represents a database transaction that can be committed or rolled back. It is safe to call Rollback in a defer statement even after Commit has been called.
func (*Transaction) Commit ¶ added in v0.2.0
func (tx *Transaction) Commit(ctx context.Context) error
Commit commits the transaction. For the outermost transaction, it calls backend.Commit(). For nested transactions, it releases the SAVEPOINT. After Commit succeeds, subsequent calls to Commit or Rollback are no-ops (safe for defer).
func (*Transaction) Rollback ¶ added in v0.2.0
func (tx *Transaction) Rollback(ctx context.Context) error
Rollback rolls back the transaction. For the outermost transaction, it calls backend.Rollback(). For nested transactions, it rolls back to the SAVEPOINT. It restores the session state to what it was before Begin was called. After Rollback succeeds or if already committed, subsequent calls are no-ops (safe for defer).
type TxOptions ¶ added in v0.2.0
type TxOptions struct {
Isolation IsolationLevel
ReadOnly bool
}
TxOptions holds transaction options for Begin.
func ReadOnlyTxOptions ¶ added in v0.2.0
func ReadOnlyTxOptions() *TxOptions
ReadOnlyTxOptions returns TxOptions for a read-only transaction.
func RepeatableReadTxOptions ¶ added in v0.2.0
func RepeatableReadTxOptions() *TxOptions
RepeatableReadTxOptions returns TxOptions for a repeatable read transaction.
func SerializableTxOptions ¶ added in v0.2.0
func SerializableTxOptions() *TxOptions
SerializableTxOptions returns TxOptions for a serializable transaction.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package driver provides database driver interfaces and implementations for orm1.
|
Package driver provides database driver interfaces and implementations for orm1. |
|
Package key provides the Key type for representing entity keys.
|
Package key provides the Key type for representing entity keys. |
|
Package mapping provides the entity mapping metadata types used by orm1.
|
Package mapping provides the entity mapping metadata types used by orm1. |
|
Package sql provides SQL AST types for implementing custom database backends.
|
Package sql provides SQL AST types for implementing custom database backends. |