An ORM (Object-Relational Mapping) is a programming technique that bridges the gap between object-oriented programming languages and relational databases. In traditional relational databases, data is stored in tables consisting of rows and columns. To interact with this data, developers write SQL (Structured Query Language) queries that retrieve, insert, update, or delete data. However, object-oriented programming languages (like Python, Java, or Go) represent data as objects, which can have attributes and methods.
Advantages of Using ORMs
An ORM allows developers to treat database tables as if they were object classes in their code, and each row in a table as an instance of that class. Instead of writing raw SQL queries to interact with the database, the developer interacts with objects in their code, and the ORM translates these interactions into SQL queries behind the scenes. This offers a layer of abstraction that can improve both productivity and maintainability. More specifically, ORMs provide developers with the following distinct advantages:
GORM: An ORM for Go (Golang)
One example of an ORM is GORM, a popular library widely used in the Go (Golang) ecosystem due to its simplicity, flexibility, and powerful features. It offers a rich set of features, such as:
BeforeCreate
, AfterUpdate
).Begin()
, Commit()
, Rollback()
.Limit()
and Offset().
immugorm: Bringing Tamper-Proof Immutability to GORM
GORM simplifies integrations with external databases through custom connectors, extending the basic catalog of databases which are supported natively (Postgres, MySQL, SQLite, etc...).
The immugorm plugin (https://github.com/codenotary/immugorm) brings the benefit of tamper-proof immutability to GORM, thus allowing users to run queries against an immudb server instance. With immugorm, executing complex queries such as aggregations, becomes as simple as calling a few functions.
The following snippet demonstrates how to connect to an immudb instance using GORM (with the immugorm connector) and execute a simple aggregation query.
type Product struct {
ID int `gorm:"primarykey"`
Type string
Code string
Price uint
}
// open a database connection
db, err := gorm.Open(immugorm.Open("immudb://immudb:immudb@127.0.0.1:3322/defaultdb?sslmode=disable",
&immugorm.ImmuGormConfig{Verify: false}),
&gorm.Config{Logger: logger.Default.LogMode(logger.Info)},
)
if err != nil {
log.Fatal(err)
}
// migrate the schema
err = db.AutoMigrate(&Product{})
if err != nil {
log.Fatal(err)
}
// insert some products
products := []Product{
{Type: "type-a", Code: "000", Price: 50},
{Type: "type-a", Code: "001", Price: 80},
// Additional product entries...
}
err = db.Create(&products).Error
if err != nil {
log.Fatal(err)
}
// select total price of all products grouped by type
var productType string
var totalAmount uint
err = db.Model(&Product{}).Select("type, sum(price) as total_price").Where("price > ?", 11).Group("type").Row().Scan(&productType, &totalAmount)
if err != nil {
log.Fatal(err)
}
As you can observe, GORM handles the entire SQL query generation process, including table creation, record insertion, and query execution, all without requiring you to write any SQL code.
You can find comprehensive information on executing GORM queries in the official documentation here: https://gorm.io/docs/index.html.
The immugorm repository is available at: https://github.com/codenotary/immugorm.
Conclusion
Running GORM queries against an immudb server is now possible through immugorm. This enables developers to leverage the powerful abstractions of SQL queries while maintaining the simplicity and efficiency of writing code entirely in Golang. With immugorm, you can seamlessly integrate immudb’s capabilities into your Go projects, enjoying the best of both SQL and Go without compromise.