
A survey of the current state of Golang ORMs - nnx
http://present.go-steel-programmers.org/talk-review-orms/gsp-go-orms.slide#1
======
hit8run
I read this presentation some months ago and I chose to stay with golangs
builtin "database/sql". That way I'm using the purest solution with a well
designed api. If I really wanted to use a 3rd party package I would choose
sqlx because it stays close to golangs official sql package and carefully
enhances it.

3rd party packages come and go but "database/sql" is here to stay.

~~~
shadowmint
What are you even talking about.

The database is hands down the worst in the entire standard library.

Importing a package to pick your driver? Side effects much? Who needs multiple
drivers in the same scope anyway?

Consistent behavior or test suits for drivers? nah... why bother.

The database API was poorly thought out. That's why most people use no sql
solutions, and what tools _do_ exist for it (eg. test suites) are cobbled
together 3rd party collections that have to be manually updated when a new
driver is written.

I'm literally flabbergasted to hear someone singing its praises.

~~~
tokenizerrr
I'm confused. sql.Open's first argument is which driver you want to use, in
string format. These drivers become available after they've been imported, and
the side effects seem insignificant[1]. This seems to make it perfectly
possible to use multiple drivers within the same scope. The drivers themselves
also seem to have several unit test files, though I can't judge on the quality
of those.

[1] [https://github.com/go-sql-
driver/mysql/blob/master/driver.go...](https://github.com/go-sql-
driver/mysql/blob/master/driver.go#L136)

~~~
shadowmint
See... that's the thing. It might work. If the driver plays nicely. Or it
might not. In fact, you have no way of knowing without reading the source
code.

The sqlite driver used to not work with the postgres one. I don't know why. I
never looked into it... but, that this _can_ happen is pretty bad, I'd argue.

Packages that _invoke code when you import them_ and require this sort of
import:

    
    
        import _ "github.com/go-sql-driver/mysql"
    

Aren't idiomatic. They're hacks, because the database module didn't really
consider how it was going to work properly.

------
martinp
I've found that
[https://github.com/jmoiron/sqlx](https://github.com/jmoiron/sqlx) (mentioned
in the slides) works very well for SQL and mapping values to structs in Go.
It's not an ORM though. You still write plain parameterized SQL queries, but
mapping results to structs is very elegant.

The mapping process is very similar to how other packages in Go work (e.g.
encoding/json), so if you already know Go it feels very natural.

~~~
Arkan
Yup, totally agree with you.

------
djhworld
I'm still of the opinion that ORM frameworks are just too much of an
abstraction away from the database.

What's wrong with just writing pure SQL?

~~~
sudowhodoido
After years of using Hibernate and NHibernate, considered best of breed and
most well thought out and thoroughly engineered ORMS, I agree. They fall of a
productivity cliff pretty quickly, particularly on larger projects (I'm
talking things with 100+ domain objects). I've settled on:

Use CQRS (Command Query Response Segregation) and stuff ORMs where the sun
doesn't shine.

Separate Update/Insert/Delete commands and queries. Commands return nothing
and are assumed to work unless an exception is thrown, then the whole batch is
rolled back. Queries return data. Transaction boundaries are arbitrary based
on the requirement of the calling service, not the unit of work and/or
session. By default all transactions are imperative.

Stored procedures, triggers and views are banned.

Commands are classes with properties attached (easy to validate, test and
provide good encapsulation) and are internally represented as raw SQL with
bound parameters.

Queries return enumerable lists of objects and are internally represented as a
raw SQL query with bound parameters.

Everything is programmed to generic interfaces: IStorageCommand<T> and
IStorageQuery<T>

That is all. I've used Dapper on .Net to do the internal representation. It is
portable across non SQL stores as well and has been used to interface with
cache and Redis as well.

Plus due to the loosely coupled abstraction, you can use AOP to stick read-
through cache interceptors in between the IStorageQuery<T> interface and the
consumer. Also you can stick cache invalidators between IStorageCommand<T> and
the consumer.

Moving one of our core subsystems over to this with some careful design and
DBA consultation knocked our 48 Core Xeon SQL box from 60% utilisation to 5%
(mainly IOPS/storage throughput. CPU is a non issue there).

~~~
aikah
At the end of the day you're still doing object relational mapping.You just
built something custom instead of using Hibernate,so you're still using an
ORM.

Your domain objects are still a graph that need to be hydrated from the
database. And dont tell me that writing hydrators by hand is fast and
dry,especially when you have 100+ domain objects,compared to a few XML files
or annotations that can later take advantage of query builders or HQL.

~~~
sudowhodoido
Fair point but it's a terribly light ORM behind an abstraction boundary that
doesn't make assumptions about the type of store it is communicating with.

We don't write any hydrators. On our previous solution they are generated at
compile time. Then they were replaced by runtime code generation inside
Dapper.

There is no XML, there are no query builders, there is no HQL. There are too
few guarantees in each of those technologies.

------
Animats
Yes, you can use a relational database as a dumb object store, but do you want
to? There seems to be a great fear of SQL among programmers, which is
puzzling, because the basic operations aren't very complicated.

There's an annoying mismatch between SQL and data structures. SQL's approach
of making all queries a big string is a pain, especially since careful
escaping is needed to avoid SQL injection vulnerabilities. However, in Go,
it's not too hard to get data in and out of SQL as an "[]interface", which is
a mixed array of any type. Converting that to a struct is straightforward.
This comes up often enough that Go should have compiler support for struct <->
[]interface. It can be done through introspection, as some of the ORMs do, but
that's slow. The layout of the struct is fixed at compile time, so this can
generate fast code, mostly type checks and moves. In general, we should have
more compiler support for marshalling. It's done a lot, it's easy to compile,
and it's slow to do interpretively.

If we had that, much of the need for the ORMs described would go away.

------
jaytaylor
It's worth noting that this presentation is nearly one year old.

I recently started using gorm [0] and have found that it meets my needs and
works nicely, and the maintainer jinzhu has been helpful and pleasant to
interact with. So with that being said, it's still an ORM and comes with the
usual laundry list of caveats.

[0] [https://github.com/jinzhu/gorm](https://github.com/jinzhu/gorm)

~~~
elviejo
Do you mind sharing this laundry list? Ie in summary what is wrong with
ormappers?

~~~
hyperpape
Look for Martin Fowler's ORM Hate and the discussion around it on HN. Read the
paper he links to ("The Vietnam of Computer Science"), and perhaps search HN
for ORNs or your favorite particular ORM. It really is a laundry-list.

------
jaekwon
I just updated the go-modeldb library, for anybody who wants to use a non-ORM
solution.

[https://github.com/jaekwon/go-modeldb](https://github.com/jaekwon/go-modeldb)

You can see it used extensively in this sample application:

[https://github.com/jaekwon/ftnox-backend](https://github.com/jaekwon/ftnox-
backend)

But, it looks like sqlx may be a better alternative.

------
hanief
I use
[https://github.com/coopernurse/gorp](https://github.com/coopernurse/gorp) on
daily basis. But I find it doesn't support Postgres array type. Do you know
what ORM is supporting it?

~~~
Arkan
As far as I know no ORM support arrays natively. I have a few projects made
around sqlx, and I had to support arrays. So I ended up doing the support
myself.

~~~
hanief
I see. Do you have it open sourced somewhere? Or do you have some references
on this subject for me to explore? thanks.

~~~
Arkan
I started with this Gist.
[https://gist.github.com/adharris/4163702](https://gist.github.com/adharris/4163702)

But ended up with a custom type implementing Scan & Value.

Check this out: [https://groups.google.com/forum/#!topic/golang-
nuts/aPJVwRQq...](https://groups.google.com/forum/#!topic/golang-
nuts/aPJVwRQq8fg)

Good luck :-)

------
zzzeek
"Supports SQLite, MySQL, PostgresSQL, MongoDB"

we're just never going to learn....

~~~
idlewan
Never going to learn what?

Could you please make your comment explicit?

------
nnx
I've stumbled upon this presentation while researching about "idiomatic" Go
support for Postgres 9.4's new Binary JSON type.

Any recommendation?

