GoLobby ORM is a lightweight yet powerful, fast, customizable, type-safe object-relational mapper for the Go programming language.
GoLobby ORM is an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Golobby ORM, each database table has a corresponding "Entity" to interact with that table using elegant APIs.
You can run performance benchmark against GORM using
make bench
here are results from my laptop
goos: darwin
goarch: arm64
pkg: github.com/golobby/orm/benchmark
BenchmarkGolobby
BenchmarkGolobby-8 235956 4992 ns/op 2192 B/op 66 allocs/op
BenchmarkGorm
BenchmarkGorm-8 54498 21308 ns/op 7208 B/op 147 allocs/op
PASS
ok github.com/golobby/orm/benchmark 3.118s
The following example demonstrates how to use the GoLobby ORM.
package main
import "github.com/golobby/orm"
// User entity
type User struct {
ID int64
FirstName string
LastName string
Email string
orm.Timestamps
}
// It will be called by ORM to setup entity.
func (u User) ConfigureEntity(e *orm.EntityConfigurator) {
// Specify related database table for the entity.
e.Table("users")
}
func main() {
// Setup ORM
err := orm.Initialize(orm.ConnectionConfig{
// Name: "default", // Optional. Specify connection names if you have more than on database.
Driver: "sqlite3", // Database type. Currently supported sqlite3, mysql, mariadb, postgresql.
ConnectionString: ":memory:", // Database DSN.
DatabaseValidations: true, // Validates your database tables and each table schema
})
if err != nil {
panic(err)
}
// Find user by primary key (ID)
user, err := orm.Find[User](1)
// Update entity
user.Email = "jack@mail.com"
// Save entity
orm.Save(&user)
}
Let's create a new Entity to represent User in our application.
package main
import "github.com/golobby/orm"
type User struct {
ID int64
Name string
LastName string
Email string
orm.Timestamps
}
func (u User) ConfigureEntity(e *orm.EntityConfigurator) {
e.Table("users").
Connection("default") // You can omit connection name if you only have one.
}
As you see, our user entity is nothing else than a simple struct and two methods.
Entities in GoLobby ORM are implementations of Entity interface, which defines two methods:
- ConfigureEntity: configures table, fields, and also relations to other entities.
We have standard conventions and we encourage you to follow, but if you want to change them for any reason you can use Field method to customize how ORM
inferres meta data from your Entity.
GoLobby ORM for each struct field(except slice, arrays, maps, and other nested structs) assumes a respective column named using snake case syntax.
If you want a custom column name, you should specify it in ConfigureEntity method using Field() method.
package main
type User struct {
Name string
}
func (u User) ConfigureEntity(e *orm.EntityConfigurator) {
e.Field("Name").ColumnName("custom_name_for_column")
e.Table("users")
}
for having created_at, updated_at, deleted_at timestamps in your entities you can embed orm.Timestamps struct in your entity,
type User struct {
ID int64
Name string
LastName string
Email string
orm.Timestamps
}
Also, if you want custom names for them, you can do it like this.
type User struct {
ID int64
Name string
LastName string
Email string
MyCreatedAt sql.NullTime
MyUpdatedAt sql.NullTime
MyDeletedAt sql.NullTime
}
func (u User) ConfigureEntity(e *orm.EntityConfigurator) {
e.Field("MyCreatedAt").IsCreatedAt() // this will make ORM to use MyCreatedAt as created_at column
e.Field("MyUpdatedAt").IsUpdatedAt() // this will make ORM to use MyUpdatedAt as created_at column
e.Field("MyDeletedAt").IsDeletedAt() // this will make ORM to use MyDeletedAt as created_at column
e.Table("users")
}
As always you use Field method for configuring how ORM behaves to your struct field.
GoLobby ORM assumes that each entity has a primary key named id; if you want a custom primary key called, you need to specify it in entity struct.
package main
type User struct {
PK int64
}
func (u User) ConfigureEntity(e *orm.EntityConfigurator) {
e.Field("PK").IsPrimaryKey() // this will make ORM use PK field as primary key.
e.Table("users")
}
After creating our entities, we need to initialize GoLobby ORM.
package main
import "github.com/golobby/orm"
func main() {
orm.Initialize(orm.ConnectionConfig{
// Name: "default", You should specify connection name if you have multiple connections
Driver: "sqlite3",
ConnectionString: ":memory:",
})
}
After this step, we can start using ORM.
GoLobby ORM makes it trivial to fetch entities from a database using its primary key.
user, err := orm.Find[User](1)
orm.Find is a generic function that takes a generic parameter that specifies the type of Entity we want to query and its primary key value.
You can also use custom queries to get entities from the database.
user, err := orm.Query[User]().Where("id", 1).First()
user, err := orm.Query[User]().WherePK(1).First()
GoLobby ORM contains a powerful query builder, which you can use to build Select, Update, and Delete queries, but if you want to write a raw SQL query, you can.
users, err := orm.QueryRaw[User](`SELECT * FROM users`)
GoLobby ORM makes it easy to persist an Entity to the database using Save method, it's an UPSERT method, if the primary key field is not zero inside the entity
it will go for an update query; otherwise, it goes for the insert.
// this will insert entity into the table
err := orm.Save(&User{Name: "Amirreza"}) // INSERT INTO users (name) VALUES (?) , "Amirreza"
// this will update entity with id = 1
orm.Save(&User{ID: 1, Name: "Amirreza2"}) // UPDATE users SET name=? WHERE id=?, "Amirreza2", 1
Also, you can do custom update queries using query builder or raw SQL again as well.
res, err := orm.Query[User]().Where("id", 1).Update(orm.KV{"name": "amirreza2"})
_, affected, err := orm.ExecRaw[User](`UPDATE users SET name=? WHERE id=?`, "amirreza", 1)
It is also easy to delete entities from a database.
err := orm.Delete(user)
You can also use query builder or raw SQL.
_, affected, err := orm.Query[Post]().WherePK(1).Delete()
_, affected, err := orm.Query[Post]().Where("id", 1).Delete()
_, affected, err := orm.ExecRaw[Post](`DELETE FROM posts WHERE id=?`, 1)
GoLobby ORM makes it easy to have entities that have relationships with each other. Configuring relations is using ConfigureEntity method, as you will see.
type Post struct {}
func (p Post) ConfigureEntity(e *orm.EntityConfigurator) {
e.Table("posts").HasMany(&Comment{}, orm.HasManyConfig{})
}
As you can see, we are defining a Post entity that has a HasMany relation with Comment. You can configure how GoLobby ORM queries HasMany relation with orm.HasManyConfig object; by default, it will infer all fields for you.
Now you can use this relationship anywhere in your code.
comments, err := orm.HasMany[Comment](post).All()
HasMany and other related functions in GoLobby ORM return QueryBuilder, and you can use them like other query builders and create even more
complex queries for relationships. for example, you can start a query to get all comments of a post made today.
todayComments, err := orm.HasMany[Comment](post).Where("created_at", "CURDATE()").All()
Configuring a HasOne relation is like HasMany.
type Post struct {}
func (p Post) ConfigureEntity(e *orm.EntityConfigurator) {
e.Table("posts").HasOne(&HeaderPicture{}, orm.HasOneConfig{})
}
As you can see, we are defining a Post entity that has a HasOne relation with HeaderPicture. You can configure how GoLobby ORM queries HasOne relation with orm.HasOneConfig object; by default, it will infer all fields for you.
Now you can use this relationship anywhere in your code.
picture, err := orm.HasOne[HeaderPicture](post)
HasOne also returns a query builder, and you can create more complex queries for relations.
type Comment struct {}
func (c Comment) ConfigureEntity(e *orm.EntityConfigurator) {
e.Table("comments").BelongsTo(&Post{}, orm.BelongsToConfig{})
}
As you can see, we are defining a Comment entity that has a BelongsTo relation with Post that we saw earlier. You can configure how GoLobby ORM queries BelongsTo relation with orm.BelongsToConfig object; by default, it will infer all fields for you.
Now you can use this relationship anywhere in your code.
post, err := orm.BelongsTo[Post](comment).First()
type Post struct {}
func (p Post) ConfigureEntity(e *orm.EntityConfigurator) {
e.Table("posts").BelongsToMany(&Category{}, orm.BelongsToManyConfig{IntermediateTable: "post_categories"})
}
type Category struct{}
func(c Category) ConfigureEntity(r *orm.EntityConfigurator) {
e.Table("categories").BelongsToMany(&Post{}, orm.BelongsToManyConfig{IntermediateTable: "post_categories"})
}
We are defining a Post entity and a Category entity with a many2many relationship; as you can see, we must configure the IntermediateTable name, which GoLobby ORM cannot infer.
Now you can use this relationship anywhere in your code.
categories, err := orm.BelongsToMany[Category](post).All()
You may need to save an entity that has some kind of relationship with another entity; in that case, you can use Add method.
orm.Add(post, comments...) // inserts all comments passed in and also sets all post_id to the primary key of the given post.
orm.Add(post, categories...) // inserts all categories and also insert intermediate post_categories records.
GoLobby ORM contains a powerful query builder to help you build complex queries with ease. QueryBuilder is accessible from orm.Query[Entity] method
which will create a new query builder for you with given type parameter.
Query builder can build SELECT,UPDATE,DELETE queries for you.
Finishers are methods on QueryBuilder that will some how touch database, so use them with caution.
All will generate a SELECT query from QueryBuilder, execute it on database and return results in a slice of OUTPUT. It's useful for queries that have multiple results.
posts, err := orm.Query[Post]().All()
Get will generate a SELECT query from QueryBuilder, execute it on database and return results in an instance of type parameter OUTPUT. It's useful for when you know your query has single result.
post, err := orm.Query[Post]().First().Get()
Update will generate an UPDATE query from QueryBuilder and