OOro

Write Options

Only, Omit, BatchSize, ConflictBy, CheckVersion, and CreateResult reference.

Write options make insert/update behavior explicit.

Only

db.Use[Product]().Create(ctx, product, oro.Only("Code", "Price"))

Only the named fields are written.

Omit

db.Use[Product]().Create(ctx, product, oro.Omit("CreatedAt", "UpdatedAt"))

Named fields are excluded from the write.

BatchSize

result, err := db.Use[Product]().CreateMany(ctx, products, oro.BatchSize(500))

Controls chunk size for batch insert.

CreateMany result

result, err := db.Use[Product]().CreateMany(ctx, products)
ids, err := result.IDs[uint64]()

CreateMany returns a lightweight result with row count and primary keys. Use CreateManyResult when you need full rows.

ConflictBy

saved, err := db.Use[Product]().Upsert(ctx, product,
    oro.ConflictBy("Code").Update("Price", "Stock"),
)

ConflictBy defines the unique key used for upsert. Model queries use Go field names; table queries use database columns.

Available strategies:

Method Behavior
ConflictBy(fields...).DoNothing() ignore conflicting rows
ConflictBy(fields...).Update(fields...) update the listed fields from the inserted pseudo-row
ConflictBy(fields...).UpdateAll() update all writable inserted fields except primary keys, conflict keys, and model AutoCreate fields
ConflictBy(fields...).UpdateAllExcept(fields...) update all writable inserted fields except explicit extra fields
ConflictBy(fields...).UpdateMap(values) update with explicit constants or expressions shared by the whole statement

UpsertMany

affected, err := db.Table("products").UpsertMany(ctx, []oro.Map{
    {"code": "P001", "price": 100},
    {"code": "P002", "price": 200},
}, oro.ConflictBy("code").UpdateAll())

UpsertMany emits multi-row upsert statements, split into parameter-safe batches when needed, and returns RowsAffected. On conflict, Update and UpdateAll use each row’s own inserted value (excluded.col on SQLite/PostgreSQL, VALUES(col) on MySQL/MariaDB); they do not reuse the first row’s value.

Table queries are schema-free: UpdateAll updates every inserted non-conflict column present in the row. Use UpdateAllExcept("created_at") or another explicit list when a table column should be inserted for new rows but not overwritten on conflicts. Model queries already skip AutoCreate fields and also honor UpdateAllExcept.

All maps in one UpsertMany call must have the same keys. UpdateMap keeps constant/expression semantics for the whole statement, which is useful for values like oro.Increment(1).

MySQL and MariaDB report affected rows differently from SQLite/PostgreSQL: an updated conflicting row can count as 2. Treat the return value as the database driver’s affected-row count, not a portable processed-row count.

FirstOrCreate

product, created, err := db.Use[Product]().FirstOrCreate(ctx, &Product{
    Code:  "P001",
    Price: 100,
}, "Code")

row, created, err := db.Table("products").FirstOrCreate(ctx, oro.Map{
    "code":  "P001",
    "price": 100,
}, "code")

FirstOrCreate returns the row matching the conflict fields or creates it. It uses the field values from the model or map as the lookup key, so the lookup key and inserted values cannot drift apart. The fields or columns must be backed by a unique constraint for concurrency safety. Existing query conditions are preserved for both lookup reads.

CheckVersion

rows, err := db.Use[Product]().
    Where("ID", id).
    Update(ctx, oro.Map{"Price": 120}, oro.CheckVersion(version))

A stale version returns oro.ErrStaleData.

Skip hooks and events

db.Use[Product]().SkipHooks().SkipEvents().Create(ctx, product)

Use this for trusted import or maintenance jobs only.

Edit this page