225 lines
8.8 KiB
Markdown
225 lines
8.8 KiB
Markdown
---
|
|
title: "Go Databases"
|
|
tinyname: "go_databases"
|
|
date: 2021-08-13T20:56:12+07:00
|
|
toc: true
|
|
description: |-
|
|
There are a plethora of libraries available in Go.
|
|
Here is a list of libraries that may be of interest.
|
|
tags:
|
|
- go
|
|
- databases
|
|
- sql
|
|
author:
|
|
name: Hamza Ali
|
|
url: https://github.com/hhhapz
|
|
---
|
|
|
|
> This is currently under construction and incomplete.
|
|
|
|
## Prelude
|
|
|
|
When working on a Go project, there comes a time where you application needs
|
|
a database. As a developer, you have a plethora of options to pick from, some
|
|
good, others not so much. As a new developer, making a decision can be
|
|
difficult, especially when treading blind.
|
|
|
|
My goal with this post is to share some perspective and evaluate the different
|
|
tools that are available in the Go ecosystem. Perhaps you will be able to find
|
|
a new tool that helps reduce your code complexity, or gain more perspective
|
|
about why some other libraries might not be as appealing as they seem.
|
|
|
|
---
|
|
|
|
## What are my options? {#options}
|
|
|
|
I plan to compare a half dozen different libraries here that accomplish
|
|
different goals. There may be other similar tools that accomplish similar
|
|
goals, which I have omitted.
|
|
|
|
If you would like to suggest another tool, feel free to send me an [email](mailto:hamza@teamortix.com).
|
|
|
|
Apart from these tools, I strongly believe that looking for libraries that are
|
|
compatible with the Go standard library (`database/sqlite`) is really
|
|
important. All the tools I mentioned will work seamlessly with it.
|
|
|
|
- Utility libraries
|
|
|
|
- **[github.com/jmoiron/sqlx](https://github.com/jmoiron/sqlx)**
|
|
|
|
Quoting the README of the repository, sqlx is a library which provides
|
|
extensions on Go's standard `database/sql` library with marshalling from
|
|
sql rows to Go types.
|
|
|
|
- **[github.com/keengancsmith/sqlf](https://github.com/keegancsmith/sqlf)**
|
|
|
|
Create SQL queries that are safe from injections with printf, making
|
|
composing queries and working with dynamic number of arguments (more on
|
|
this later).
|
|
|
|
- **[github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel)**
|
|
|
|
A highly composable SQL generator for Go. Using a builder pattern, this
|
|
library can really help writing SQL queries in a type safe manner.
|
|
|
|
- Code generation libraries
|
|
|
|
- **[github.com/xo/xo](https://github.com/xo/xo)**
|
|
|
|
A code generation libraries that creates a model and CRUD operations from
|
|
your database, and generate type-safe functions from queries. (Limited
|
|
Documentation)
|
|
|
|
- **[github.com/kyleconrow/sqlc](https//github.com/kyleconrow/sqlc)**
|
|
|
|
Similar to xo, a different style code generation library that should be
|
|
mentioned seperately. Generates code by parsing your SQL.
|
|
|
|
- Object Relational Mappers - Map Go types in SQL and ORM likes.
|
|
|
|
- **[github.com/ent/ent](https://github.com/ent/ent)**
|
|
|
|
A composable entity framework that emphasizes on user control.
|
|
|
|
---
|
|
|
|
## What about GORM or XORM? {#gorm}
|
|
|
|
To much of my disappointment, many developers who work with Go fall in the trap
|
|
of using an ORM. This is really, really tempting to do, however there are
|
|
a number of flaws that most, if not all of these libraries exhibit, which can
|
|
severely cripple your project.
|
|
|
|
### What you Gain {#gain}
|
|
|
|
Firstly, it is difficult to deny that these libraries do appeal thousands of
|
|
developers. While not being the most accurate metric by any means, it is still
|
|
a strong indicator. GORM has almost 25,000 stars (at the time of writing this
|
|
post) on GitHub and Xorm has over 6,000 on its archived Github.
|
|
|
|
I will try to explain why many features ORMs are often celebrated and praised
|
|
are in fact a source of never ending gotchas.
|
|
|
|
#### 1. No need for SQL {#no-sql}
|
|
|
|
Many beginners fall into the trap of using an ORM because it means they don't
|
|
need to learn SQL, which can often come across as being challenging or
|
|
daunting. A complete new query language, which has its own quirks, learning
|
|
about selects, updates, *joins*, *indexes*?
|
|
|
|
From their point of shoes, all of this programmer jargon can be easily avoided
|
|
and ORMs are the way to go.
|
|
|
|
> *ORMs rules, SQL drools*.
|
|
|
|
In the contrary, I believe that if you are writing a backend application,
|
|
knowing SQL is a prerequisite. As an analogy, imagine trying to use a styling
|
|
framework, such as Bootstrap or TailwindCSS, without knowing CSS. Most people
|
|
would agree that attempting to use it without knowing CSS is a recipe for
|
|
botched styling that is terrible to maintain, and generally a recipe for
|
|
disaster.
|
|
|
|
Similarly, writing SQL with clean queries that tell both, yourself and your
|
|
database exactly what to do ensure a healthy architecture that is both easy to
|
|
debug, extend, and refactor.
|
|
|
|
#### 2. Magically Simple {#magical}
|
|
|
|
ORMs are magic. You can call `db.Find(&users)`, and boom! All your rows are
|
|
returned. A simple filter can be done trivially. `db.First(&user, 10)` will
|
|
query the user whose primary key matches 10. However, there is a pain point
|
|
that always surfaces when a project uses an ORM more and more.
|
|
|
|
As a user, writing calling functions like `Find`, `Take`, `First`, `Last` lead
|
|
to a loss of control. You no longer have the ability to tune or modify your
|
|
queries.
|
|
|
|
This works fine for simple use cases, however, when pushing these to their
|
|
limits, (which is not very far).
|
|
|
|
For example, updating rows to the zero value of the Go type will simply be
|
|
ignored. This usually makes sense, however, very quickly turns into messy
|
|
design and hacks, such as resorting to pointer field types, or just returning
|
|
back to good old plain SQL.
|
|
|
|
Complex functionality (SQL) abstracted with another "simpler" layer (your ORM)
|
|
always poses the lingering risk of *increasing* complexity through complex
|
|
wrappers and hacks.
|
|
|
|
#### 3. Easy Migrations {#migrations}
|
|
|
|
ORMs always promote having the ability to automagically handle migrating your
|
|
schema as your Go data structures change. While valid, again, these migrations
|
|
are generally done under the hood, and leave you, the user with very little
|
|
control.
|
|
|
|
Furthermore, many tools exist already that already handle database migrations
|
|
significantly more effectively, and give you more fine-tuning power, so that
|
|
you always are able to stay in control.
|
|
|
|
When migrations become messy or non-trivial, GORM has the potential to break,
|
|
or simply make things worse. Using tools like
|
|
[migrate](https://github.com//golang-migrate/migrate) are much better
|
|
suites, support more databases, has cleaner documentation, and is a more robust
|
|
alternative use.
|
|
|
|
---
|
|
|
|
### Pain Points {#pain}
|
|
|
|
#### 1. Finding Help {#help}
|
|
|
|
Continuing from the points one and two, when working with an ORM, finding help
|
|
becomes monumentally more difficult.
|
|
|
|
Finding answers for generic SQL is significantly easier to do in your favorite
|
|
search engine. You will be able to find a plethora of information about
|
|
defining foreign keys, creating indexes, composite foreign keys, and any other
|
|
SQL feature on Stack Overflow, or a database forum. When transitioning from one
|
|
database to another, for example from MS SQL Server to PostgreSQL, or from
|
|
Oracle DB to SQLite3, a large portion of your knowledge will be transferable.
|
|
|
|
On the other side of the coin, migrating from Hibernate to Gorm leads leads to
|
|
very little directly transferable knowledge. Even if you are knowledgeable in
|
|
a SQL itself, many times you will be at the mercy of the documentation for the
|
|
ORM you are using to clearly define how to implement your use-case.
|
|
|
|
Attempting to find information about questions or concerns you have mean having
|
|
to find not only a community specific to the language you are using, but
|
|
a subset of that community that also has experience with the very specific
|
|
library you use, which might mean the time between finding your answer being
|
|
a few searches away to a few days, or being undocumented enough that it just
|
|
may never be solved.
|
|
|
|
#### 2. (Ab)using language features {#lang-features}
|
|
|
|
When working in Gorm in particular, defining primary keys, indexes, and check
|
|
constraints can be described lightly as a mess. Go struct tags are overloaded
|
|
and stuffed to the brim to gain simple functionality. Here are a few examples
|
|
from the Gorm documentation:
|
|
|
|
```go
|
|
type SomeTable struct {
|
|
// Constraints
|
|
Name string `gorm:"check:name_checker,name <> 'jinzhu'"`
|
|
|
|
// Composite Primary Keys
|
|
CategoryID uint64 `gorm:"primaryKey;autoIncrement:false"`
|
|
TypeID uint64 `gorm:"primaryKey;autoIncrement:false"`
|
|
|
|
// Foreign Key
|
|
CreditCard CreditCard `gorm:"foreignKey:UserName"`
|
|
|
|
// Indexes
|
|
Name4 string `gorm:"uniqueIndex"`
|
|
}
|
|
```
|
|
|
|
Now, imagine if your project required a combination of these. Having a foreign
|
|
key alongside of an index, or attempting to define a many-to-many index.
|
|
Abusing struct tags make it easy to make a mistake, force you to give up all
|
|
compiler guarantees and with compile-time errors, and terrible to work with. As
|
|
a user, you are at the complete whim of the Gorm documentation, and their
|
|
support for these features, instead of having the full breadth of options and
|
|
customizability one would expect.
|