Make ORM code feel like Go code
Readable queries, no generated models, relations without import cycles, and result types with clear boundaries.
Readable queries
Fields, comparisons, and grouped conditions have explicit methods. Query intent stays visible without hiding logic in SQL strings.
db.Use[Product]().Where(oro.Field("Price").Gte(100))
db.Use[Product]().Where(oro.Field("Code").Like("A%"))No generator
Models are ordinary Go structs and queries use the generic model entry. There is no generated client or extra build step.
db.Register(Product{})
db.Use[Product]().Create(ctx, product)No relation cycles
Relations live in methods instead of embedded struct fields. Models can split across packages without forcing import cycles.
article.Cover().One[Image]()
db.Use[Article]().With(Article{}.Cover())Clear type boundaries
Model queries return concrete types, dynamic writes use Map, and raw SQL goes through Raw. Flexible paths still have clear boundaries.
db.Use[Product]().First(ctx) // *Product
db.Table("products").First(ctx) // oro.MapKeep ORM boundaries visible in code
Oro does not hide complexity behind generators or pack schema settings into tag strings. Models, tables, and raw SQL are separate entries: use models for business code, tables for dynamic flows, and Raw for complex queries.
- Schema lives in
Define, not in overloaded tags. - Writes are explicit, so zero values are not guessed.
- Relations live in methods, reducing import-cycle pressure.
func (Product) Define(s *oro.SchemaBuilder) {
s.Table("products")
s.Field("Code").String().Unique()
s.Field("Price").Uint().Default(0)
}
db.Use[Product]() // *Product / []Product
db.Table("products") // oro.Map
db.Raw("select ...") // Map or MapTo[T]()Ready in a single go get
Open a database, register a model, sync the schema and run your first typed query in minutes.
Start here
Read these in order to understand Oro quickly: run the first query, define a model, then learn condition composition.