* They hide the queries. When your DB or cloud service gives you a printout of your 10 slowest queries, you then have to figure out what object code that relates to. And then is there even a way to fix it, or are you stuck with the ORM?
* LINQ-specific: Love the tech, but it's unclear whether my .wheres() are being sent upstream properly, or if I'm downloading the whole database and filtering it in memory.
* Another LINQ one: we wanted to do "INSERT IF NOT EXISTS" but could not.
* Back in Java land, magic like that tends to be incompatible with basic hygiene like consting all your class fields. Frameworks like being able to construct a Foo in an invalid state, and then perform a bunch of mutations until it's in a good state.
* They make it near impossible to reason about transaction states. If I call two methods under the same open db context, what side-effects can leak out? If I try to do an UPDATE ... SET x = x + 1, that will always increment correctly in SQL. But if read x from an ORM object and write back x + 1, that looks like I'm just writing a constant, right?
* Extra magic: if you've read a class from the db, pass it around, and then modify a field in that class, will that perform a db update: now? later? never?
But just in general, I want to look at the data, play with queries in a repl environment until they look right, and then use directly in the code without needing to translate from high-level&declarative down into imperative loops, sets and gets.
The arguments against them, I think, are most strong for active record and much less strong for data mapper.
The problem really is complexity, aka coupling. An active record ORM naturally pervades an entire codebase that uses it. People pass around these "objects" that are really just thinly-veiled database rows. But that's all they are. They are at exactly the same abstraction level as the relational database itself, they just look like objects. But in fact they are filled with footguns because accessing those attributes could trigger database requests.
So you'll see business-level code written that has to "know" about the ORM and "know" about N+1 queries and therefore essentially "know" about SQL and the underlying relationships (or, conversely, data access layers that have to "know" about the business logic, e.g. "I know this logic needs to access this bit so I'll prefetch it"). So you're not really gaining anything. These ORMs are the complete opposite of a good software architecture that gives you flexibility and ability to reason about components in isolation.
A good data mapper ORM at least lets you map data from relational tables to real objects. That way you are able to build a new abstraction layer upon which to write business logic etc. A programmer writing those business rules should be able to fully write and test logic with no knowledge of the ORM at all. But in active record projects you'll find each and every developer has to have the full stack in their heads at all times.
I would be interested to know if there are strong reasons to avoid data mapper ORMs too.
Not if you have proper telemetry set up... Tooling like instana was extremely useful for me to diagnose exactly where SQL statements caused issues
>Extra magic: if you've read a class from the db, pass it around, and then modify a field in that class, will that perform a db update: now? later? never?
For hibernate if you understand the concepts of:
- application level repeatable reads
- it's dirty checking mechanism
- when the session is flushed / entity lifecycle
That 'magic' isn't magic anymore. But every abstraction is leaky (even SQL)
This is not specific to ORMs... you can run into the same problem without one.
> Extra magic: if you've read a class from the db, pass it around, and then modify a field in that class, will that perform a db update: now? later? never?
In every ORM I've used you have specific control over when this happens.