ORMs Considered Convenient- 5 minutes read - 935 words
In our book list project, we’re going to be using Gorm as the library through which we access the database. Choosing an ORM – over straight SQL – is not without controversy. This article discusses some of the advantages and disadvantages of using an ORM library, and makes a case for why – in this simple project – it makes sense to use Gorm.
What is an ORM?
ORM stands for Object Relational Mapping, and is a technique for mapping objects into a relational database and then later mapping database query results back into objects. (In Go the “objects” are just structs; the concept is the same.)
Without an ORM, and especially in simple CRUD apps, you often end up with a lot of boilerplate code to generate queries and scan results back into domain objects. Boilerplate code like this is tedious and boring. I often find myself making stupid mistakes with tedious, boring, repetitive code. It’s the kind of code that would drive a person to write a library.
Using an ORM library can cut out a lot of that code. It can also help you avoid dangerous mistakes like SQL injection vulnerabilities. (Yes, you can also avoid this without an ORM.)
ORMs Draw a Lot of Hate
These libraries are popular I think for a couple of reasons. First and foremost, there’s that thing about getting rid of tedious code. Second is that it removes a barrier to understanding: you don’t actually have to know anything about SQL (or databases, really) to use some of the libraries.
It’s this second reason that I think gets people into trouble. The ORM works great for the simple project they start out to build. But then the project starts to get a little more complex and there are some hairy relationships happening in the database. The ORM’s query-builder doesn’t generate optimal code – or it’s hard to get it to put together the right associations at all.
At that point you either have to learn SQL… or hop on Stack Overflow or some other community and ask how to beat your ORM into submission. And that’s when experts start dumping on the ORM.
A Lot of the Hate is Fair
If you use ORMs enough, and especially if you use languages other than Go, you will fall into problems where the mapping breaks down. The Object part of your project can become complex enough that it ends up being very hard figuring out how exactly to map it to database tables. You can end up spending more time and effort – and create more complexity and risk in the project – messing around with ORM annotations (or json/yaml/other config) and fighting the ORM than you would if you just wrote plain SQL and passed the results through to your views.
In Go, the objects (structs) are much simpler than other languages can end up with. Go’s notion of inheritance is fairly primitive and there’s less magic that we can use to get ourselves into trouble. Unlike, say, Python – which I also use and like a lot, but it puts tons of magic right at your fingertips.
In our book list project we have very simple tables that have very simple mappings onto structs. By the end of the project we’ll reach a limit where if we add any additional complexity the use of an ORM becomes a burden rather than a convenience.
The Best of Both Worlds?
A good ORM will give you the ability to fall back to plain SQL. Of course, you have to know at least a little SQL to make this work, but learning the basics is not hard.
For example, Gorm provides both
db.Exec(...) depending on whether you want to
query for rows or just execute a statement, respectively. Note that the
Scan method here still allows for convenient mapping back to structs even
from a manually defined query.
Gorm and others also provide the ability to see the generated SQL – or you can resort to logging queries in your database engine. This can allow you to see what is coming from your ORM in case you suspect that it is building a suboptimal query that is slowing down your application.
(Side note: A project I recently learned of is sqlc which generates code from SQL queries. I haven’t had a chance to try it out, but it came well-recommended and it looks promising. I like the fact that there’s no runtime dependency, just the generated code.)
I’ve had my share of cursing at Gorm or SQLAlchemy, but I still reach for them (sometimes). Especially for simple projects. The trick is to know when you’re approaching the limits of their usefulness. When you reach that point, just drop into the escape hatch and start writing raw SQL. Or if you know at the outset that the project is going to be beyond the limits of the simple convenience that an ORM provides, just start with SQL.
Coming up: Gorm Integration
Friday’s how-to post will integrate Gorm into Aklatan, setting up a mapping between a Book struct in our code and a books table in the SQLite database.