How does the ORM detract from sqlalchemy? I’ve been very happy with it for years, and I’m not clear on where an advantage is explicitly not using mappers.
A lot of the reasons listed in this article actually made me shift from sophisticated ORMs like Hibernate back to a query based approach. A really nice framework for this (in Java) is jOOQ which gives you the possibility to write typesafe SQL via code generation.
https://www.jooq.org/
(I'm not at all affiliated with jOOQ - just a happy user)
Another happy jOOQ user here! I'm ditching Hibernate wherever I can in favour of it. And using it with Kotlin instead of Java makes it almost a form of poetry.. I've seriously been thinking about writing a blog post, something along the lines of "jOOQ: how I learned to love the database again"
I've been thinking about what makes jOOQ so good and a huge part of it is brilliant engineering: SQL clauses are mapped almost 1:1 into reasonable and understandable code while the author spends huge effort to cover new features as databases introduce them without turning his product into a mess or introducing API breaks in every major new version. That's hard.. but awesome! :)
I have to agree. ORMs can be great when an application is just starting, because that's when you're writing the most tedious queries and statements, but beyond that I personally find that ORMs just get in the way.
As soon as I need to write something more complicated than select-by-id I end up reaching straight for SQL. Otherwise I have to learn both the ORM's query API or DSL and have the proper mental model for how it translates to actual SQL.
Or I could just write SQL and be done with it. No mysteries.
Maybe this is just me, but I've never written a join in an ORM that I had the slightest bit of confidence in.
Any discussion on ORMs needs to consider what sort of app and queries you are writing.
ORMs are fantastic for the very common case:
* Fetch 20 rows and display them as a table,
* Fetch 1 row by primary key and display it as a form,
* Write updated fields from the form back into the 1 row in the database.
Anything more complicated, and direct SQL starts being more attractive. But for the common case that ORMs are designed for, they are a major productivity boost.
He says he likes SQLAlchemy, but doesn't say why. I'm interested to know that though.
To me, the big win with SQLAlchemy is that it separates the SQL expression layer from the ORM layer so cleanly. I can write very complicated queries with the expression layer that wouldn't be possible at the ORM layer, and do it in a much more composable way than concatenating SQL strings. Example: http://btubbs.com/postgres-search-with-facets-and-location-a...
Every extra abstraction layer comes at the cost of complexity, maintenance, and capability, and therefore inherits the burden of proving its worth. ORMs frequently claim to be less tedious than SQL. Failure to deliver on that promise is a reason to cut them out.
Why are we even having this debate? There are some ORMs that bring so much value to the table that you'd be stupid not to use them. Example being Django's ORM or SqlAlchemy.
Also be specific in what ORM you are comparing to raw SQL. Are you talking about Hibernate or SQLAlchemy. Are you talking about a query builder?
Good ORMs help tremendously with maintainability and security. They also let you drop down to raw SQL when needed.
I don't think rails would have been as popular if you had to use SQL.
I've used SQLalchemy a lot. I've even written a keyset paging extension for SQLalchemy.
But recently I've switched to writing stored procedures and calling them directly, instead of going through an ORM for everything... And it's so much easier.
Flyway for Java... For stored procedures you use the repeatable syntax that way it checks the checksum of the file and if it doesn't match what is in the migration table it will run it... Easy and always up to date... That's with Java anyway...
There are several such tools, which are specialized and thus tend to perform a better job than orms (who try to do everything)
But I would recommend writing, reviewing and deploying migrations by hand, esp for critical parts of the schema (automatic tools are almost guaranteed to get something wrong, with locking etc)
Sure you can. SQL is just plain text, so keep all your create table scripts in your repo, along with deploy and rollback scripts that you’d use to extend or migrate your schemas.
In the databases I've worked with extensively (PostgreSQL and, somewhat in the past, Oracle) Store Procedures are routinely versioned controlled as part of the application, just these bits of code are in a different language than the rest.
The creation of functions/procedures is not tied to state of the database in quite the same way as tables are; the functions/procedures, where they care about the data, do need to recognize the table structure and changes to that structure, but that's no different than any other of the application code which makes use of the data in the database.
I think where many people get caught up on this is that they do something like migrations to get code, including procedural code, into the database... but that's not the only game in town. And really, given what's possible with databases today, I'm not sure migrations are even the best way anymore.
Consider a tool like: http://sqitch.org/ which facilitates not treating stored procedures as migrations, but rather as individual files which change just like any other code.
There are ways to accomplish having good version control on the table/structure side as well, which again, is something you lose with the migration tools I've worked with.
Microsoft has SQL Server Data Tools which can be integrated into your favorite SCM (we use VSTS but we'll switch to Git soon), can have automated deploys (which we don't) and in general can be a part of a modern development lifecycle.
Yes, and if all your stored procedure is doing is execute a query and return a cursor to the result set, it's using just as much database cpu as with a regular query.
Django's ORM is actually a good example of where identity is an issue. It lacks composite primary keys. If I have a dependent table that has my real ID, say an order number that is a varchar I have to join to the parent table to do lookups by the order number. I can't doing something like key(order_number, line_number) so I end up hydrating a parent object for operations that only require operations on the dependent objects.
Good question. It would depend what kind of rules they were and how they were configured. Wasn't specified that they were "user-configured" in the original statement, though.
The kind of rules we use are boolean expressions that identify which rows the user's groups can read/write/delete. The ORM automatically combines all the rules as a single expression and applies it to the query.
As best I can tell, Postgres lets you do this albeit constraining your boolean checks to things you can efficiently do in the database. But then I can definitely see how this could get unwieldy very quickly.
I disagree. ORMs dont bring value, they dilute value and piles abstractions upon apstractions. Your application becomes hard to maintain, database hard to refactor, queries slow.
ORMs seems nice for simplified problems but becomes a horrible mess for real problems imho...
I've worked on some rails apps, and the ORMs caused more problems than they solved...
A few years ago, I worked on an application which would occasionally run into performance issues. Every time the solution was to rewrite the Entity Framework usage into standard SQL. Sometimes it would generate the strangest but somehow legal queries which would be challenging for SQL Server to optimise.
For my next app, I just started using SQL only and never looked back. Simple queries are easily generated with a nice internal API (SELECT * FROM Table WHERE ID = X, etc).
However, more complex selects are all written using hand-written SQL. New developers sometimes find this a bit strange, especially the younger ones, but nobody can fault the system's speed.
I think saying "Just use SQL" is probably a bad idea. You'll most likely end up implementing an ORM anyway, or you will end up with your model code mixed up everywhere with your views.
I do think a lot of people use ORMs as a crutch, which sucks. Also, ORMs often provide too much abstraction, forcing people who actually know SQL to relearn how to do everything the way the ORM happens to like it. I should not have to learn twice as much to be productive due to an abstraction.
What I prefer are really lightweight ORMs that give me models which I can then enhance with custom code. I don't need an ORM that supports plugins or inheritance or a dozen different kinds of joins. All of that can be done more efficiently with custom code.
Also, I think SQL builders are really useful. I think a lot of people conflate SQL builders with ORMs but they're actually very different problems.
AlphaZero's chess strategies turned out to be quite different from how humans have traditionally come up with chess heuristics/strategies. I wonder what paradigms will be used by AI that does computer programming. Will it organize code into some bits of functional programming, some OOO? Will it structure things into MVC? My guess it whatever AI does will be completely inscrutable to us. It may be optimized for efficiency rather than understandability, which humans need for maintainability.
> You'll most likely end up implementing an ORM anyway,
This is a really good point. Many people start with the "no ORM" philosophy, realize their application needs some way to map the SQL to the code, time passes..., they have implemented their own half-baked ORM.
A more positive spin is that you'll have an "ORM" that's exactly adapted to your application. Many apps (1) don't need to work with multiple DBMS types and (2) don't use even close to the full panoply of SQL features.
In a language like Java that has a generic DBMS API you can get along just fine with a few classes that handle CRUD operations and transaction management. Somebody familiar with JDBC and SQL can write the bridge classes in about a day, while keeping the overall application vastly simpler.
Either way somebody needs to make an informed choice about ORM vs. direct SQL. It seems as some people get in trouble because they skip that part of the design process.
I mean, stored procedures are fine, but I don't think that actually solves anything? Except maybe for reducing the amount of SQL code you have to send back and forth and (in some databases) allowing for a few more optimizations?
If you use stored procedures, all you've done is move part of the model into the database, so you have to update the stored procedures as part of a deployment. You still need to have the SQL code written out somewhere, and you still need to have something in the application code that knows which procedures exist and how to use the data they return in business logic.
But you can decouple the database logic from the app logic anyway, without using stored procedures. They don't actually help you do this since you still need code that knows what stored procedures to call. Also, I'm not sure how this makes security easier? It seems like security would be the same or maybe a little harder since you now have to track the stored procedures you're currently using as well.
I'm not really sure what you mean by "you can deploy schema changes independently" and "you can change everything and the app should not notice". The stored procedures are basically just an extension of the apps logic right? So you can deploy them at any time, sure, but that isn't different from an app that doesn't use stored procedures, because you could also deploy changes to any part of that app at any time.
I do think stored procedures can be more efficient, because you have a lot more control. But it's not like they are clearly superior from an organizational standpoint. If you write an ORM using stored procedures, it's still an ORM.
As far as I know, in my experience. Stored procedures in postgres are good when you really use the database and care about the data, you need transactions, need to handle races and concurrency etc. Whereas ORMs break down at this point or prevent you even getting to a point when you can use your database as a database.
Why pretend your SQL database is about objects? It is not... (it is about data)
A stored procedure can act like a view or a query, or use procedural logic. Point is: your app can call it and get a concistent result, no matter what refactoring has been going on.
A direct query needs to know too much about the database (orm generated or otherwise) which prevent refactoring and couples app to database harder...
You can rename or merge tables, views functions in the database but the interface the app use (stored procedures/DAL) will stay the same and work the same way.
As for app logic... I prefer bussiness logic in the database, not the app when the data is important. Application logic stay in your application, data dependent bussiness logic stay with the data.
Every single implementation where I've seen "business logic in the database" has been an unmitigated disaster.
On the other hand, having well factored microservices (out of process) or in process modules have worked out really well with modern devops and software engineering principals - easy push button deployments and rollbacks, unit testing, A/B deployments, etc.
I think bussiness logic in the database has prevented disasters in the projects I have worked on. I honestly dont see how it could have been solved better...
It probably depends on the domain/problems.
My experience is with
transaction heavy financial systems or similar, with web frontends, microservices sprinkled around in different languages...
The web app or java worker should be allowed to focus in its problems, the bussiness logic needs to live in one central place, which happens to be in the database accessed through thightly controled interface in the form of stored procedures.
And what's stopping you from having a tightly controlled interface with a REST Api that is easily deployed, rolled back, unit tested, source controlled and deployed?
I like data. A database is created to handle it, give you tools to query, modify, scale, secure the data.
A rest api... how and why should it be responsible for your data? It solved a different problem.
You might not even need a database I guess, and then anything goes.
I need and like my database, and have suffered trying to get along with different ORMs. SQL is so good at what it is designed to do if you just let it.
(And why just
one rest api? How about 100 restapis, some microservices, some web apps, some background workers, many different languages. One database. No ORM)
One database is still an issue. When you have a clear slice with one microservice being in charge of one set of data, it's easier to scale, slice, rewrite, and you can deploy and iterate faster without interdependencies.
And you lose all of the benefits of microservices if there is still a tight coupling between unrelated (from a domain perspective) to tables.
(have we come to some max nesting level here, cant reply to the child comment)
One db can be a problem, or a strenght depending on the domain; And I really dislike religious design, esp microservices.
I have less problems by avoiding ORMs (and religios microservice arch, or fundamentalist interpretations of rest)
Database handles the shared state in a heterogenous environment.
We need it to be centralized to keep track of money, the apps can't do that, two independent databases cant do that either. It must be one system that guarantees concistency.
It works great, there is no downtime. The interfaces are defined, the database stands alone, updates are deployed separately.
Database handles the shared state in a heterogenous environment. We need it to be centralized to keep track of money, the apps can't do that, two independent databases cant do that either.
Why can't apps "keep track of money"? I'm assuming you're referring to transactions. Apps can create transactions and you can share transactions across apps using distributed transaction (I'm not saying distributed transaction is a good idea).
Why would you want to deploy schema changes separately? I would be horrified if someone changed my DB back end without running a full (hopefully automated) set of tests.
The db is separate and the interfaces are defined and the test is for this interface (as part of the schema repository)
You dont need an ORM for testing your code...
But I think this varies from project to project.
How many different applications, in different languages are using your db and do you tolerate downtime?
Why downtime? A developer commits their code, the CI server builds the code, run non database dependent unit test, it gets deployed to the integration environment, automated integration tests get run - fewer in number somewhat slower - it gets deployed to the QA environment and goes through a round of manual testing (sometimes), QA signs off and the build gets deployed to the UAT environment and waits for the business owners sign off, then we turn off the A side of the load balanced farm and it gets to deployed to the A side of the load balanced production servers, it goes through a round of smoke testing (automated and/or manual) and once everyone is satisfied, we make A live, set the load balancer to use side B and deploy to B.
All of the manual sign off steps are integrated with the automated release pipeline. As soon as the required approvals sign off, the next step of the pipeline is done.
Rolling back is just redeploying the previous released version. Branching, source control, etc is also a lot easier when all of your business logic is in code and you don't have to sync up the "right" version of your source control with the right version of your stored procedures.
Of course this is even easier when you're using a NoSql solution where your schema is also defined by your class models. But that's another discussion.....
Of course this doesn't have to just apply to code. With things like Packer and Terraform you can do the same with infrastructure. Automated infrastructure deployment is not my expertise...yet
Just use stored procedures? Then you lose the ability to do unit testing without a database dependency, it's a lot easier to rollback code than to rollback code and stored procedures as one and you don't get full visibility on what the code is doing just by looking at the source code.
If all of your business logic is in the stored procedures, what are you actually testing?
And I realize that being able to test queries without database dependencies, only really applies to a few languages that treat queries as a first class citizen in the language like C# and Linq where you can mock out your actual Linq provider - replace the EF context with in memory List<T> - and still test your Linq queries.
> "If all of your business logic is in the stored procedures, what are you actually testing?"
Depends on what you want to test. Can either write unit tests for the stored procedures or unit tests for the code that makes use of those stored procedures.
And then when you write "unit tests" for stored procedures with a lot of developers you get slow "unit tests" that don't scale across multiple developers because of Comte toon issues.
I think GP meant that you can't/it's hard to test the stored procedures themselves. In this case if you mock the database calls you will not test the database logic.
Usually with most modern automated deployments, you keep your build artifacts in a package (zip file, tar, etc.) and run a script to deploy it to your target system.
Rolling back is a simple matter of installing the previous archive. That doesn't just apply to code anymore. You can treat "infrastructure as code" also.
You can do A/B upgrades, rollbacks, etc. There is so much better tooling around regular code than sql/stored procedures. How many times have you seen stored procedures with hundreds of lines, duplicated stored procedures with V1,V2, etc appended to it, commented out logic etc?
I've had to wade through some hairy code to but at least with a code, I can do some automated guaranteed safe refactoring, find dependencies, keep the interfaces backwards compatible, etc.
You did see me preach about all of the capabilities for unit testing without a database dependency, type safety, flexibility (with Linq I can switch back and forth between an RDMS and NoSql without any code changes)?
How would you test data access in a meaningful way without a DBMS? It does not really matter whether you use tables or SPROCs. You'll still need a DBMS instance available.
For PostgreSQL and MySQL you can bring up the DBMS in Docker. That is not a built-in fixture obviously but easy enough to do locally as well as in CI/CD systems like Travis. You'll need to load SQL into the DBMS as a prerequisite to testing. That has the benefit of testing your load/upgrade sequence.
LINQ isn't a ORM. I assume you mean Entity Framework?
EF definitely has the foreign key issue. We have around a thousand tables, we tried to generate the classes for all of them including foreign keys, problem is that when you create a context that references even only a single table, it will load everything that is foreign keyed including siblings of siblings of siblings, until you run out of memory.
Only way around it is to not set up foreign keys which massively diminishes the value of using EF, so we wound up creating two copies of each table's classes, one with and one without foreign keys. That causes its own issues.
Essentially yes. It created giant object map with millions of objects in it and hit a memory limit (rightfully so, it wouldn't have been usable anyway). It would start at the initial table, get the siblings, then the siblings of the siblings, and so on until it was trying to generate an object for every property of almost every table we had (even if we only referenced the original table).
Sounds like you disabled lazy loading and that forced everything to be loaded at once. That's a user error, not a EF fault.
This is a common theme I've seen with people blaming ORM's for being slow, it's the devs not using them appropriately more than the ORM's themselves. Not to say that they don't have their own issues.
With or without lazy loading enabled the result was the same. Generating the structure took seconds and went OOM with enough tables.
LazyLoading impacts what data is retrieved from the database (or more to the point when), this is a structural issue before a query was even sent to the database. It would die while generating the query, not sending the query or populating the result.
You likely should have asked for more information before concluding it was "user error."
I have been working with EF for years now. It has it quirks - but this is not something that I have ever experienced, nor have I heard of anything like it before today.
What I have heard of is traversing the entire graph and causing cascading loading of navigational properties. Yes, I have done that. Something like AutoMapper will do that to you, if you are not careful. Been there and done that.
How did you determine that it OOMed while generating the query?
> LazyLoading impacts what data is retrieved from the database (or more to the point when)
It impacts more than just that, it will impact the query generation as well. If you have a property that is not lazy loaded then it will attempt to join the relation or load it very another query in the same round trip. Turning it off tells the ORM that every single time you want A it needs to go and get B as well. If you have it off universally it will attempt to load the entire database, or as may be the case here, crashing while trying to generate a query to do so.
> You likely should have asked for more information before concluding it was "user error."
Perhaps, but you've got multiple conflicting accounts of what went wrong, some comments indicate that it returned data, others say it never touched the database.
Were you doing serialization or something like that that recursively accessed all of the properties in the model? If so, and if lazy loading was turned on (the unfortunate default) then the serializer would indeed keep exploring the object graph until it either loaded your whole database or hit OOM. That's about the only scenario I can think of that would cause such an issue - EF doesn't eagerly load related entities unless you explicitly tell it to.
Sounds like they're actually referencing some of the well-known query-generation issues in EF, though giant queries and long generation times are more common manifestations than OOM, which seems to be an extremely pathological case.
You were doing it wrong then. EF does not eagerly load child collections. You cannot even configure it to do so.
As someone else suggested, you probably had lazy loading enabled, and some of your code tried - e.g. through reflection - to get all properties.
EF has some of it's own issues - but you can most certainly create composite primary keys, composite foreign keys and work with projections right from within the code.
None of the issues the original author had with SQLAcademy and Hibernate are really a pain in EF.
Some prefer to switch off lazy loading in EF and instead either explicit eagerly load specific child collections or explicitly load them right before use.
In EF you can do
var customers = Customers.Include(c => c.Orders).Single(c => c.CustomerNo == '1234')
This will load Customer '1234' with the Orders collection eagerly loaded.
I'd rather learn the ins and outs, problems and issues, highs and lows, of SQL rather than an ORM.
ORM require just as much investment in time and even then you still need to learn the sql to get the ORM to do what you want it to do.
SQLAlchemy on Python is a truly fine piece of software but in the end it was much simpler and felt more powerful for me to write the SQL. And not even hard BTW.
I only have a limited amount of time available for learning and if I can trim out an entire class of technology (i.e. the ORM) then that's a whole bunch of stuff I just don't need to spend time learning.
I prefer SQL to ORMs as well for exactly the reason you state. It's more comfortable and it's less of a mystery.
But you don't always get to pick what code you'll be working with and if you are working with others in a web framework pretty good chance you'll be learning an ORM anyway.
Also really long SQL statements are no fun. Like debugging a whole program written in one line. I think sometimes there is a temptation to get excessively clever with SQL.
SQLalchemy is kind of a pain in the ass to actually use, though. The DSL doesn't feel very Pythonic, it's weird and confusing. The way it traverses the Object graph when loading associations between models is magical and opaque and I could never predict when it was going to automatically work and when it wouldn't.
I actually would rather be writing Ruby on Rails, because it's _less magic_ than SQLalchemy.
The one that bit me with sqlalchemy is joins. You have to structure your sqlalchemy object in a specific way to do joins. Usually I write my sql query then spend half an hour trying to convert it to sqlalchemy.
But my very point was not to make it traverse object graphs at all. You can write nice SQL using it, with selects, joins, functions, etc. All these parts, SQL clauses, are composable, so you can factor out common parts.
Yes, I'm fine working with lists of tuples, not "model objects". Object graphs don't map all to well to the RDBMS model all too well. It's best done on case-by-case basis, if you care about performance at all.
If you aim to have a pythonic data layer, I've heard good things about pony orm[1]. I mean,you can't get much more pythonic then the example they have on their website. But I haven't used it my self.
This is a quip that misses the parent's point. SQL isn't very composable, because its syntax requires infix notation, position-dependent phrases, separators, and the like. The abstract syntax tree of SQL is much more valuable than its syntax, which is essentially just a bad English serialization that resembles a natural language sentence.
A DSL which manipulates queries and then produces syntactically valid SQL is tremendously valuable.
Views are only half the answer. Try factoring out a set of conditions and use them both in a `select` and in `update`.
Also, views tend to pollute the namespace a bit: you want a set of descriptive names, per application, and possibly per application version. Schemata help here, though.
Bare in mind that SQL was designed to allow non-technical business users to use it. In most offices, a vetern business user or BI person will know how to use SQL. So SQL is a DSL for relational deconstructing of data models that is conceptually simple enough for people to grasp.
Templating can absolutely save time and boilerplate, but adding a DSL over a DSL in the form or ORM may well be the problem.
I'm very comfortable in SQL, and would prefer to write my queries. But our team has grown, and we've found that developers say they know SQL, but they really don't. In general, I've found that it's not the syntax that messes people up. There's a significant mental "jump" between the usual procedural coding paradigm and the "set based" paradigm offered by SQL. Some people just never catch on.
So we're going to start using an ORM (Entity Framework Core 2) so that the "mere mortal" developers can pitch in. I know there's a way to run raw SQL, so I know that when we find spots where the ORM fails, we can just rewrite it with some good SQL if we decide that's best.
But maybe that'll never happen? We've been trying to do simpler SQL stuff as of late so that the heavier lifting in the system is done by the application/client instead of the database (bottleneck). As the database sees simpler, less unique queries, more stay in the plan cache, indexes are more reliably hit as expected, and performance increases.
I've found ORMs (such as Entity Framework) great for operations that can be described as "find a single thing by PK and update it." For read operations I've favored this strategy: "Imagine the ideal result set for the task at hand. Use SQL to deliver that result set. Do the rest of the work in your app language of choice."
Edit: I guess I should clarify that I would favor using any library that maps result sets to lists of objects. And I would consider that to be part of "do the rest of the work in your app language of choice."
Yeah EF is great for CRUD. The way I distinguish whether EF is going to be used or not is simply whether the workload is OLTP or OLAP. At OLTP EF excels. It's terrible at OLAP (ORMs generally are) so I'll drop to raw ADO.NET and (if lots of data has to go in to SQL Server) table valued parameters.
I do agree every one who needs to get data from an SQL database should know SQL, and mostly, should know about indexes and how and why queries can be slow.
But a query builder is extremely useful and in any complex application, if you don't use one, you end up bulding one yourself, which may not be a very good idea if you don't understand things like query injection.
So learn SQL, learn ORM, and choose in a case by case basis.
I wish there was a commonly agreed upon name for "query builder" that isn't "ORM". Actually mapping relational data to objects often rubs people the wrong way, for good reasons, but the query-building layer underneath is pretty universally useful.
But in general I agree with you: Why just learn SQL? Learn all the layers!
I guess my experience is limited, but I’ve not seen much of this despite working in an ORM environment with around 75 entities for the last few years (my recent experience anyway, the rest goes back 16 years). Maybe that is small potatoes, I don’t know, but I’ve found that anyone who understands JPA well enough can work to avoid any pitfalls of using ORM. It seems to me that having a good mix of understanding SQL and ORM is a good thing; and especially understanding exactly what the ORM system is doing for you and how it is doing it. Dropping ORM altogether sounds like a bad idea since it provides a number of built-in security features as well as an abstract modeling paradigm that is fairly easy to conceive and maintain; provided, of course, that you learn to say “No” to protect the integrity of the model (such as rejecting the attribute creep the article warns about).
I have found, in my experience, that people who tend to want to write SQL over ORM usually want to do so because they simply know SQL better. That’s okay, there is nothing wrong with that. But that doesn’t immediately mean ORM systems are bad. No need to be tribal about it.
The problem I see is that many new software developers these days sometimes can’t see the forest for the trees because they dwell too much on what they think is better instead of simply seeing the software and abstractions as nothing more than tools in the tool belt. It happens everywhere — PC vs Mac, iOS vs Android, Scala vs Java, SQL vs ORM. It’s fine to have opinions, I have many, but as I’ve aged I’ve become acutely aware that my biases are almost solely rooted in the limitations of my understanding.
"I should have learned SQL before dealing with ORMs."
Every single point the author brings up comes seems to come down to simple database design, lazy development, or not understanding their tools. They really don't seem to have anything to with ORMs or query languages.
Any screwdriver can make for a bad hammer and some screws may go in with a large enough mallet.
> Perhaps the most subversive issue I've had with ORMs is "attribute creep" or "wide tables"
Normalization of data is required whether you're using a query language or an ORM to access it. The fact that ORMs make it easy to "hide" the fact that you've added 500 columns to a table isn't the ORM's fault.
> Knowing how to write SQL becomes even more important when you attempt to actually write queries using an ORM. This is especially important when efficiency is a concern.
What do they think the ORMs are doing? Magical incantations over the disks? The ORMs are just using queries too. You can write really horrifically bad queries in a query language and also abuse ORMs, but that doesn't make either one bad. Most ORMs can let you see precisely the SQL they are creating. If not, the database will surely log the queries for you and let you know what's going on.
> The problem is that you end up having a data definition in two places: the database and your application.
Welcome to the fact that we have multi-layered technology? There's always going to be discrepancies between the layers that have to be ironed out because no data designs are perfect or future-proof. The author then attempts to bring migrations into the picture as if database migrations are somehow just not a problem if you aren't using ORMs (hint: database migrations have always been tough even in very well-design systems).
> Dealing with entity identities is one of those things that you have to keep in mind at all times when working with ORMs, forcing you to write for two systems while only have the expressivity of one. What this results in is having to manipulate the ORM to get a database identifier by manually flushing the cache or doing a partial commit to get the actual database identifier.
Sounds like a pretty frustrating example, but I've worked with at least 10 different ORMs I can think of off of the top of my head and not a single of them required "manually flushing a cache" or a "partial commit" to "get the actual database identifier." I wouldn't write this up as being an issue with ORMs or that this problem would be magically fixed by only writing SQL either.
> Transactions. Something that Neward alludes to is the need for developers to handle transactions. Transactions are dynamically scoped, which is a powerful but mostly neglected concept in programming languages due to the confusion they cause if overused.
Transactions are pretty straightforward and I cannot agree with: "The concept of a transaction translates poorly to applications due to their reliance on context based on time."
Transactions don't care about time at all. They care about order and making sure that things are completed in a certain series of steps. This actually translates very well to applications, especially when you have processes that take a long time, where you don't want something to happen unless another thing happens first.
While a decently-written article, this comes across as someone who learned about ORMs more deeply than databases, discovered the flaws that ORMs have, and decided that query languages must be the only way forward.
This ignores the fact that we created and adopted ORMs after struggling through years of rigid queries smattered throughout code.
Writing bare queries has a time and a place, but ORMs have saved countless hours of development time, and allowed for vastl...
Ah yes - the proverbial "ORMs are bad, just learn SQL" post. This is analogous to saying "don't use frameworks". Sound ridiculous? Yes, yes it is.
The law of leaky abstractions applies to many, many things, ORMs included. I would also argue they apply in different degrees, usually related to the design of the ORM (the post mentions SQLAlchemy vs. Hibernate, for example).
But consider the following:
1) Why do people still use ORMs? Exactly.
2) Question 1 but s/ORM/framework_or_widely-used-library
3) ORMs allow you to develop faster, and deliver value
4) Beginners already have a hard time coding, designing, and understanding what they're doing. ORMs provide a nice abstraction over underlying data stores
5) Although fraught with peril, ORMs provide a common interface that'd give _some_ help if you switch data stores
6) Multi-line SQL statements are a huge pain in some languages
> ORMs are bad, because objects aren't particularly useful to deal with most data.
Objects are containers of data; that's a crazy statement to make. The relational structure maps pretty cleanly to objects, properties, and collections.
However, objects are not good for reporting. And the author mentioned doing 14 joins and hundreds of columns - that smells like a reporting query.
The relational structure maps cleanly to badly designed objects, where most classes have 5-10 fields and most fields are opaque data values with setters and getters. If you wrote classes like that in isolation, you'd have to do some serious work in code review, explaining why a hodgepodge collection of properties is a single unit of responsibility. But I've never worked in an ORM-using system where they weren't endemic.
I don't buy into the idea that those kind of classes are just unqualified bad design. The majority of applications exist to merely to store information entered by users. Using a class to hold that data in a structured and type-checked way is perfectly reasonable.
And they aren't a hodgepodge collection of properties; they're entities that represent a single item in a system whether that be a person, a widget, an order, or a product.
> The relational structure maps cleanly to badly designed objects
If you are exposing base tables to the application instead of appropriate views, which as much a violation of good RDBMS design principles as what you describe is a violation of good OO design.
But why do you need these containers of data, when you could have more direct access to both the data itself and the whole set (not individual objects, nor collections of objects)
I dont like ORMs but did struggle some years trying to use them which imho was a detour. SQL and stored procedures in plpgsql is so much better, easier to maintain, easier to reason about etc.
I personally would write a simple plpgsql stored procedure, but I would not trust user input and only allow a defined set of colums from a defined set of tables.
you can have lots of dynamic sql but that might become a rabbithole, just as with an ORM. It sounds like a problem you shouldnt have, now throwing an ORM at such a problem... might lead to even more strange issues down the road...
I personally would write a simple plpgsql stored procedure, but I would not trust user input and only allow a defined set of colums from a defined set of tables.
Sure, you can limit it to a single table and to a certain set of columns e.g. A B C D E. Can you give me an example of a simple plpgsql stored procedure to do this?
you can have lots of dynamic sql but that might become a rabbithole, just as with an ORM
That's not my experience. In a language with good introspection and/or where most entities are first-class, you can do this rather easily. In Python that would take two or three short lines.
It sounds like a problem you shouldnt have
This is a bit of a cop-out :) allowing the generation of simple reports configured by the user is a typical need for us.
In response to (4). I'd posit that beginners have a hard time with this stuff because they use too many leaky abstractions. Like the article says, if you want to use an ORM for anything non-trivial, you need to learn both the ORM and SQL, which is inherently more difficult than just learning SQL.
Providing tons of abstractions to beginners so they can build something in 10 lines of code doesn't help them get better. All it does is encourage them to learn top-down when it's often much easier to learn something from the bottom-up.
> This is analogous to saying "don't use frameworks". Sound ridiculous? Yes, yes it is.
When developing API's in Golang or for microservice / serverless architectures not using a framework might actually make a lot of sense. Also microframeworks (trimmed-down versions compared to opinionated frameworks) are very popular in almost any language.
I'd say that Golang rather comes with a (simple) framework in the standard library, so you don't need a third-party one. After all, http.Handle/HandleFunc clearly follow the Hollywood Principle: http://wiki.c2.com/?HollywoodPrinciple
> If you're using an RDBMS, bite the bullet and learn SQL.
If this person spent all that time using Hibernate and then SQLAlchemy, and all that time did not know SQL, then their suffering and bad experiences make complete sense. You absolutely need to know SQL if you're going to use an ORM effectively. Good ORMs are there to automate the repetitive tasks of composing largely boilerplate DML statements, facilitating query composition, providing abstraction for database-specific and driver-specific quirks, providing patterns to map object graphs to relational graphs, and marshaling rows between your object model and database rows - that last one is something your application needs to do whether or not you write raw SQL, so you'll end up inventing that part yourself without an ORM (I recommend doing so, on a less critical project, to learn the kinds of issues that present themselves). None of those things should be about "hiding SQL", and you need to learn SQL first before you work with an ORM.
This is especially true where the ORM can mask things such as moving a column to an associated table. The ORM knows that object.attribute is now represented in the object_attribute table with the relationship using object_attribute.pk in the object.attribute column (which may or may not be renamed to attribute_id).
No need to rewrite all your SQL, just the ORM description of the model!
Realistically though, if you’re querying the same table from that many places your architecture is already in trouble. So editing the queries shouldn’t be that big of a deal.
Everything that isn't SQL tuned to your environment forces you to sacrifice performance at some point. At some point it does an inefficient join. How can you not go back and fix performance issues is you don't know SQL? (It isn't like you need to fix it in C)
SQLAlchemy makes dropping down to SQL where necessary incredibly easy. There where necessary you can tune the queries, but where not necessary you can let the ORM create them for you.
This gives you a lot of flexibility and power, but as zzzeek mentioned, you need to know SQL to understand how what the ORM is inefficient and to be able to replace it as necessary while still letting the ORM do what it is really good at.
For 3: the proper abstraction for relational data is, surprise, a relation.
I used to work in an environment where we had relations as full first class data structures. They are very pleasant to work with.
We actually had 'relation-object-mappers', ie when we had to interact with other systems that didn't use relations, we often mapped them to relations internally to make them play nicer.
How were those relations represented? If I understand correctly, you were not just wrapping tables in a database, but using some other backing implementation. What were the most common operations, and what was the performance like?
Oh, the implementation was fairly straight-forward. I think just sorted arrays are something.
It wasn't about speed of execution, but expressiveness when coding. Later on they even added proper type system support.
Common operations were things like map/project, extend, filter, join, collect-by-key / expand, etc.
Just as Codd pointed out in his original papers, relations allow you to not have to make a choice about a hierarchy for your data.
Using key-value store like a hash-table in your program, or the much vaunted has-a relationship between objects would force you to make these choices. Thus making interacting with the data awkward for all but one access pattern.
Relations work best when your program is written in a style that deals largely in immutable data. (What we call 'purely functional', but people in dysfunctional languages have also picked up on the advantages recently.)
> Just as Codd pointed out in his original papers, relations allow you to not have to make a choice about a hierarchy for your data.
Or, alternatively, make it really painful when you do actually need to query hierarchies, along the lines of "give me all the tuples above this one in the hierarchy".
Although, I think what you plan to do with the data also makes a big difference. Which probably explains why opinions vary so widely.
For short-lived processes, like typical web applications and command line utilities – that load data from the database, do something with it, and then purge it from memory again – I'm becoming less and less convinced that ORMs are actually a benefit.
On the other hand, if your application plans to map the database data to memory for long periods of time, with the need to keep them in sync, then you're probably going to end up writing something that resembles an ORM anyway, and poorly at that. In this case a good ORM is beneficial.
ORMs of various stripes were in common use well before 2004 so 1 & 2 don’t sound terribly persuasive. 3 is definitely an evergreen selling point - you might change to a different RDBMS vendor (back when there was such a thing), etc.
But I'm really surprised every time people tell me they look at the schema as defined into the ORM instead of at the table in the database.
I'm really jaw dropped the few times I know somebody doesn't even know SQL, only the ORM. Maybe they look at it as if it were the reaction of somebody that thinks you must know assembly if you want to program Ruby, Python or Node (I don't.) Still, if you work with a database you must know it's internal language, SQL or NoSQL. Your going to need it or build a mess.
And involving a DBA early in the project can make your database at least twice as fast, with the right schema and the right queries. Then you translate that into the ORM you want to use.
I expect that everybody with a degree knows SQL and I'm realizing that I could be wrong. Maybe sometimes I'm the only one in the room that knows it. I'll check it next time I'm at a technical event leaning on the backend side.
Very happy to not have and ORM on our stack for many years.
I believe that the interaction with the database is just about the most important part of code that you need to rely on. We have had messy data written to the database due to misused or misconfigured ORM (perfomance issues, bad orphan handling, session state problems, overflown sequences, to list a few) and decided that we should not rely on all devs touching that code to be experts in the ORM api to avoid the pitfalls, we rather our devs be expert in SQL.
Never had any of them complain about mapping using something like jOOQ. Type checking and composability are also built in.
so, i think #3 is less useful for switching out the actual DB server itself in an ongoing project, but i’ve found it immensely useful in a couple of ways:
- replaced the DB driver mid way through a project to a slower, but more complete implementation. this went flawlessly, because it’s all so generic and in the end the same DB under the hood, so a simple change (unless you don’t use an ORM where i could see it being a nightmare)
- started a new project where i had to use MSSQL, which I’d never used before, but i’m a big fan of postgres/sqlalchemy. other than ODBC oddities, it was really simple to write the new app with all the same patterns i was used to with things like update on write, lazy joins, constraint deferral, and i think most importantly would be MIGRATIONS! huge help to have the same migration framework that i was used to
> As a theoretical abstraction above the data-store
I worked on a project with a home-grown ORM (in C; it was horrible) that abstracted over both MySQL and Postgres ... except the overarching application required Postgres-specific column types and functions.
> Good ORMs are there to automate the repetitive tasks of composing largely boilerplate DML statements, facilitating query composition, providing abstraction for database-specific and driver-specific quirks
None of that requires an ORM. A simple query builder will suffice and it will be much easier to debug and much less error prone than an ORM.
> providing patterns to map object graphs to relational graphs, and marshaling rows between your object model and database rows - that last one is something your application needs to do whether or not you write raw SQL
So this is the real ORM juice. And ya, without an ORM you have to do this by hand.
So here’s the question: How well can an ORM map to your data model out of the box? In a lot of cases this is where the mess comes from. You need to set up basically a low level AI that can figure out the “right” thing to do as call sites all over your codebase are making arbitrary queries on a huge API surface.
And so the argument against ORMs is that it will take less time and be less error-prone to write bespoke loaders that take rows and build your data model than it will be to configure that AI such that it can do the “right” thing with arbitrary queries.
(If you don’t like the word “AI” here, use “expert system” instead.)
while ORMs can do quite a bit of optimization, they're still general query builders and can't construct the optimal queries for your use case. if you don't know SQL, or you don't know what's going on behind the scenes, your ORM could be performing much larger queries than it really needs to, costing performance and time
I built a moderately complex application in Django at a previous workplace, using the ORM for most things, until the queries were too complex for the ORM.
Another guy connected to the same database and built some graphs using PHP and SQL. Guess who had to help him write the SQL when the queries got too complex for him? The ORM user.
> ORM: You ask for something and because you've researched and found a well-written, quality ORM you trust that it will create a sane query.
Ha. More like:
ORM: You've just joined a project already using an ORM selected by an 'architect' that no longer works here. Everything is fine until you start testing your system with a database sufficiently populated with real-world data. You and the DBA spend the next next 6 months trying to get the damn ORM to generate performant SQL (you even call Oracle support, which promptly dispatches an "engineer" who tries to sell you on another $500k of crap you don't need that won't really address the problem). You eventually just start writing queries by hand wherever you find a bottleneck caused by the naive ORM, which you could have done at the outset, but no one who actually knew SQL well enough was on the team back then.
IME, it should never take more than a few hours to run down why the ORM made such a query. At a minimum most RDBMS's have query logging and can explain queries.
Ironically the last time I had a big ORM performance problem was Hibernate eager loading all joins. Diagnosing and fixing it didn't take more than an hour. (Though we did have a very experienced DBA at the time.) YMMV
You have to known when to break free of the ORM. They are great for graphs of CRUD operations and pretty nasty for much of anything else. You may come to a different conclusion depending on the project and dataset of course.
And when you break free of the ORM prey to god that your ORM doesn't have a hidden cache somewhere. If it does then you spent a week tear out your hair until you figger out that the ORM' cache caused all the trouble.
ORMs are more useful as insert builders. Putting data from an object into the database is something of a boilerplate process. Queries vary with what you want to ask. Most of the time, you don't need all the fields, so filling up some object just because it has slots for everything is a waste of effort. Especially if it means references to multiple tables.
Good ORMs have Partial<T> and lazy loading of complex properties through proxies, which can be overridden with something like .With(x => x.ComplexProperty).
But of course, as the queries get more and more complex, the flexibility of the ORM syntax approaches the flexibility SQL. In the end, there are many situations one would rather just use SQL.
I think the best ORMs are those that just leave out the "Relational" part entirely. So... "OM"?
For example, in Go, I use Gorp, which has a Select() function where you pass in the SELECT query string (plus bound values) and the target type, and it loads every result row into an object of that type. So you can have an arbitrarily complex SQL query as long as it starts with `SELECT one_table.* FROM`. That's a marvelous design.
And when you have to do a query that returns results from multiple tables? Guess what, you just use the normal SQL module from the standard library.
Not just PostGres. That's standard SQL. Columns have data types. Tables have schemata. Schemata are very strongly typed. Can't insert a 'full_name' column into a table without such a column.
Unless you're talking about SQLite, SQL RDBMSs are both static and strongly typed. The systems typically do allow some implicit type conversions, but type is critical to how a table works.
A query builder aids you at constructing queries, while an ORM builds queries for you, runs them and maps the output to objects. It's more sophisticated than a query builder.
If you have to log the generated SQL to understand what's happening, you're already behind the curve.
And then what do you do if the ORM is generating junk? If the answer is "use a querybuilder/handcrafted SQL for that one", what's the point of the ORM in the first place?
> And then what do you do if the ORM is generating junk? If the answer is "use a querybuilder/handcrafted SQL for that one", what's the point of the ORM in the first place?
The point may be that 98% of the queries are just fine, and you've saved time vs writing by hand, and it may be easier to read/understand for the next people to have to touch the code.
"saved time" at the least optional time to save time. ORM's are a maintenance burden. They obfuscate DB performance behind usually an enormous API surface.
Most software work is maintenance work. Optimizing for initial deployment is shortsighted.
What "enormous" API surface? It's not about "optimizing initial deployment". It's about "optimizing continuous deployment" and having "always releasable Software". I've worked for departments with 15 devs all working on the same codebase and we could release and rollback releases (A/B deployments) every week because we treated the database as a dumb data store. We had multiple branches at the same time, etc.
I've also worked at a companies where all of the logic was in ungodly stored procedures and getting anything released took months and we had a whole two weeks "hardening sprint" because we had to coordinate with the "database developers" and of course the entirety of the business logic was In stored procedures.
Most software being maintenance work is even more of a reason to optimize deployment. What good is unreleased software? The most important part of the business is releasing software. Most of my emphasis asan Architect is making sure we can release fast.
I like the idea of stored procedures. I get some of the value they bring. However, the few times I've been on projects where sprocs where the primary focus of logic/truth/app... it was always a pain.
* The 'developers' weren't allowed to write the sprocs. We were at the biz meetings, but the DB guys were hardly ever there - their meetings were separate for some reason, but because devs were at the meetings, the devs were the ones who also were the face of the project. When the project was behind, we caught it in the neck, even if we were bottlenecked waiting for the DB team to write their logic and expose it to us.
* The DB team was always fewer people, juggling more projects, and other things like system uptime, maintenance, backups, etc.
* The sprocs were never part of version control or part of any source code that we could ever see as part of normal development. They typically weren't subject to any unit testing process.
The answer to all of this is mostly human management, structuring resources differently, coming up with different processes, etc. But any of those things would have changed the power dynamic, which seemed to be the purposes in those environments. Hey, I can write a stored procedure too - let me write them, and if a DB wants to 'review' them - or really, anyone on the team - please review and let's hammer it out and make it better. But roadblocking projects until the DB guys can 'get around' to writing our mission critical procs is just silly.
One other 'weird' division I saw a few places was this "developers can never have access to production systems - that violates XYZ" (a regulation, or some 'law' that was never produced, etc). I asked what the core issue was, and it was "you can't just have developers going on to production and just making changes on live systems - that's ... (against our policy, etc)". This was particularly challenging in a situation where a critical bug only happened on one production system, and we weren't allowed to replicate the database to another system, nor was anyone with any knowledge of the deployed code allowed to get on to the production system to even see if what was deployed was what we'd developed. But... this was still "our problem". Yet... the DBA in this case was "allowed" to get on the system and hand-write new triggers and sprocs to 'fix' our problem, all without documenting/testing his code, nor committing the code to any repo for us to even have visibility in to the data manipulation he was doing to 'fix' the problem we supposedly cause but couldn't investigate.
Again, I know this isn't a problem specifically with stored procedures. When sprocs have been promoted as the primary interface, however, it's usually been a political/power grab more than a technical benefit. And yes, again, I know there are technical advantages in some cases, but usually not enough to outweigh the drawbacks I've encountered.
I believe I pointed out that I realize it's a human issue more than a technology one. It was easier for some people to get suckered in to the power dynamics being played because "DBA" was already seen as more of black-magic art sort of thing, and those guys were the "real wizards" and so forth, so whatever they say goes. It's not been everywhere I've ever worked where sprocs were used, but it seems to have been at the places where "stored procedures are law".
We're conjecturing about hypothetical bugs. No one in this thread knows what bugs were encountered nor how easy they were to fix; they aren't real bugs. They're symbolic of the bugs that we encounter every day, which give us the experience on which we base our conjecture.
How often is the ORM generating junk? To say that the ORM is useless because occasionally it doesn't generate performing code (with EF and Linq more often than not it does generate performant code) could be applied to any high level construct. But I don't see people giving up modern languages to go back to writing everything in Assembly or even C.
Yes I optimize when my automated performance testing/stress testing, tells me I need to and may write handcrafted sql, but I've also handcrafted some classes in C back in the day when my old Windows Mobile app using the C# compact framework wasn't performing.
I meant a good ORM. But then again, my definition of a good ORM is an ORM with a language that treats queries as a first class citizen. EF with Linq doesn't really act feel like a separate framework since Linq and Expressions are built into the language.
How is it a "query framework"? Linq works with objects and the Linq expression provider treats the Linq as data that translates the objects to Sql at runtime. That is by definition an Object Relational Mapping.
Of course most ORMs do fall short because most languages don't have the powerful concept of "code as data".
SQL syntax is extremely verbose, and compounds the more tables are involved in a query. You're not counting the time the ORM has saved you before having to resort to logging SQL.
Isn't that the same reasoning as "I don't like using high-level languages because they limit my visibility of what's running on my processor"? Do you write all your code in assembly?
I think the issue is that if you don't understand what a high-level language is doing under the hood, then you will constantly be surprised by side-effects. You shouldn't be using an ORM to substitute for your lack of understanding SQL; you should be using it to automate tasks.
It depends on how willing you are to allow your object graph to match your relational schema. The key is to let SQL be SQL. I wrote one that allows you to load data using standard SQL resource files, but it handles the persistence automatically:
Taking the SQL-first approach also allows you to serialize without circular reference problems, since you don't just load the data, you also define a path to decompose the graph into a DAG.
I can get very creative with SELECTs, making use of Prolog style queries, which are fully done server side on the database.
Most ORMs will download all the data and evaluate them on the client side, with code that is even more convoluted that the SQL one and thus with less performance.
I know EF, which is why my comment also mentions "code that is even more convoluted that the SQL one and thus with less performance".
LINQ only allows for a fraction of what is possible with SQL, and good luck having the best queries generated out of it, if the RDMS doesn't happen to be SQL Server.
> What AI do you need? You map your tables to objects and relationships between objects via FK relationships.
That is only true in a one-to-many entity relationship (and even so, it is debatable). A one-to-one relationship can be modeled in the two objects, in one of them, or delegated to a third entity. A many-to-many entity relationship can also be handled, in OO, in various different ways. Idem for a ternary relationship or, basically any higher order relation between objects.
This is known, borrowing a term from electrical engineering, as an impedance mismatch between the two models, and it's not an easy problem by any measure.
You’ve probably refactored the ORM Data layer 3 times in that time period as ORM producers have a hard time figuring out the interface which they wish to provide you
Agreed. I'd say my point still stands because duplicated database calls affect performance and scalability and this can grind the project to a halt at a certain point. Also this refactor is much more dangerous/probably buggy.
On the other hand refactoring your data logic is just business as usual, and you will probably do it in both cases anyway.
caching is a very tricky area with tons of pitfalls. In my opinion, the ORM should not be caching. Let the clients (or any other layer) cache/clear based on their needs.
My experiences have led me to the standpoint that most ORMs handle three major things:
1. provide idiomatic domain-object oriented query interface which it in turn translates to SQL
2. provide CRUD sql generation
3. provide some sort of session-based object lifecycle change tracking and management
#1 - The generated SQL is important on many levels. As things like HQL/linq/<your QL here> deviates further from the generated sql transparency is lost. SQL is normally brittle compared to your domain language which has better testability and type safety but still you have a handful of queries where it feels more sensible to write the SQL yourself.
#2 - Code which reflects on a type and generates basic insert/select/update/delete is usually pretty naive and easily done. With the exception of complicated legacy databases and iBatis-style tools ORMs which only support #2 arent really worth bothering.
#3 - I've found this to be the real benefit of an ORM. Being able to scope object lifecycles into clear units of work, buffer pending changes until a discrete point and get scoped caches for "free" have been hugely beneficial.
Each ORM unfortunately/inevitably come with great learning curve. It seems to unfortunately/inevitably bring lots of new concepts and conventions to the table required for the user simply must understand. Sometimes it requires you to re-arrange the way you may write your code to be more session-oriented.
I dont like the idea of AI managing how/when to apply changes to storage media. AI can be smart and efficient yes but you lose that important transparency/predictability. On the contrary I prefer very dumb/mechanical/predictable ORM, something not elegant but the behavior of what happens when is well-understood and easily scaled out to a large team. In my experience hibernate, EF, sqlalchemy have that sort of dumb/predictable behavior (however the SQL they sometimes generate can be performant but unreadable).
zzzeek - can I take this chance to praise your work on SqlAlchemy. People say there's not enough thanks given to open source developers... here's thanks to you. It's the work of a craftsman.
+1 - personally I really like how SQLAlchemy doesn’t abstract away the database so much where all modeling and power is lost. He and the contributors have done an excellent job.
I've always been impressed by zzzeek's near-omnipresent participation. Years ago, I had a question about SQLAlchemy that he'd answered on the mailing list which amazed me given the relative rarity of developer interaction. Yet here we are almost a decade later and he's still answering questions directly, but on many more platforms. I'm actually not convinced he ever sleeps.
I wish more people aspired to be like him, because you know there's no more authoritative answer when you run into a Stack Exchange post and he's offering up his assistance.
I'm pretty sure that SqlAlchemy has caused more grief and frustration than any other single library. After all if I didn't know about the excellence of SqlAlchemy, maybe I wouldn't get so cross when I have to do anything non-trivial with the Django ORM ;)
Joking aside, SwlAlchemy is very impressive software, and zzzeek deserves all this praise and more. Every time that there's one of these anti-ORM articles I feel like SqlAlchemy pre-emtively addressed all the substantive criticisms in its flexible, well layered, design.
(And in the interests of fairness, Django's ORM is also very good at making simple things simple).
> so you'll end up inventing that part yourself without an ORM (I recommend doing so, on a less critical project, to learn the kinds of issues that present themselves).
I recommend anyone that has to deal with ORMs create their own at some point, in a non critical project. Not because they will necessarily create the next big thing (but who knows?), but because nothing quite gives you the perspective and appreciation for what these systems can do and why they have their pain points like making one yourself. You'll most likely not use your own module long after you've created it and then again surveyed what's already available, but it's invaluable in making a good assessment of those options as well.
Similarly, making a web framework yields similar benefits.
In both cases, a good understanding of the underlying technologies they build on (SQL and HTTP), is required, and if you don't have it doing in you'll have it coming out the other side (which is really the reason for this in the end).
Similar things exist all along the spectrum. From embedded OS's and compilers to javascript utility libraries.
What it comes down to is that a tool is best utilized when the person knows when and how to apply it appropriately, and that's as often as not an understanding of the tool as it is of the context.
A person intimately familiar with hammers and their uses can build some interesting wood furniture, but they'll likely never achieve the same level of product as a master woodworker that's just well acquainted with a hammer. Investing time and effort into tools provides only so much benefit. At some point, more knowledge of the craft itself is far more beneficial.
"Attribute creep and excessive use of foreign keys shows me is that in order to use ORMs effectively, you still need to know SQL. My contention with ORMs is that, if you need to know SQL, just use SQL since it prevents the need to know how non-SQL gets translated to SQL."
> providing abstraction for database-specific and driver-specific quirks
That is quite theoretical. My PRs for fixing non-spec compliant behavior in pgjdbc get rejected because they might break some ORMs (mostly Play). My PRs for adding MariaDB sequence support to Hibernate get rejected because there are additional MariaDB features that Hibernate doesn't support as well.
heaps of this kind of thing are nicely dotted around the code so you don’t have to deal with weird driver quirks, maps “Text” column type to whatever it needs to be in your given database to have an unbounded text blob
In Java land the JDBC driver is supposed to do that. If it doesn't then that's a bug in the driver. If it's a bug in the JDBC driver then the fix needs to go into the JDBC driver, especially if the driver is on GitHub.
The presence of an ORM relying on these bugs should not prevent a bug in the driver from being fixed.
Exactly. It is amazing how many bad ORMs that I've seen in systems from people who "just used SQL" instead of an ORM.
I've found that the people who know SQL pretty well can actually do good work with or without an ORM. But the maintenance is a lot easier when the abstractions are consistent with the abstractions that are used by a popular ORM.
you need to learn SQL first before you work with an ORM.
Analogously, I'd say that being able to write a compiler, down to having it generate machine code or assembly, gives you a leg up when using a compiled language. I've met and interviewed a number of coders who had cringeworthy gaps in their knowledge, because to them, a C compiler was just some kind of "magic."
To me, it hinges on what you mean by "a collection of objects." If it is just a list C-like structs, that's great. If it involves lazy-loaded child collections, inheritance hierarchies, or any kind of behavior at all, that makes me worried.
Yeah, I agree. Lazy loading in ORMs what happens when people want OO databases. And by OO databases, I mean everything to act like it's in an in-memory collection.
Sounds nice, but in reality, latencies, networks, massive data sets, atomicity of operations, and a whole host of other annoyances get in the way.
... so it ends up being simpler and easier in the long run to be very explicit about your interactions with data stores. Took me a long time to get to this point.
This topic has been done to death and the rate at which comments have been made since this was posted tells me that we either haven’t learned much of anything as a collective, or we just like to rehash the same talking points because... we can.
Know how and when to use ORMs. Probably learn SQL first. Don’t believe that any shiny bullet is silver.
The first time I've seen an ORM I was fascinated by this seemingly beautiful idea. But I have quickly realized that it's almost useless in real life projects, plain old SQL seems just much much better. Now I don't understand why would anybody use an ORM actually.
Also, basic SQL can be easily taught in as little as 10 minutes (I have been initially taught it at middle school during MS Office Query, Access and VBA class). An image of a programmer that can't use SQL (I don't mean advanced cases which can indeed be a bit tricky but these are far beyond the powers of any ORMs anyway) seems really bizarre to me.
359 comments
[ 4.7 ms ] story [ 295 ms ] thread(I'm not at all affiliated with jOOQ - just a happy user)
I've been thinking about what makes jOOQ so good and a huge part of it is brilliant engineering: SQL clauses are mapped almost 1:1 into reasonable and understandable code while the author spends huge effort to cover new features as databases introduce them without turning his product into a mess or introducing API breaks in every major new version. That's hard.. but awesome! :)
http://blogs.tedneward.com/post/the-vietnam-of-computer-scie...
As soon as I need to write something more complicated than select-by-id I end up reaching straight for SQL. Otherwise I have to learn both the ORM's query API or DSL and have the proper mental model for how it translates to actual SQL.
Or I could just write SQL and be done with it. No mysteries.
Maybe this is just me, but I've never written a join in an ORM that I had the slightest bit of confidence in.
ORMs are fantastic for the very common case:
* Fetch 20 rows and display them as a table,
* Fetch 1 row by primary key and display it as a form,
* Write updated fields from the form back into the 1 row in the database.
Anything more complicated, and direct SQL starts being more attractive. But for the common case that ORMs are designed for, they are a major productivity boost.
To me, the big win with SQLAlchemy is that it separates the SQL expression layer from the ORM layer so cleanly. I can write very complicated queries with the expression layer that wouldn't be possible at the ORM layer, and do it in a much more composable way than concatenating SQL strings. Example: http://btubbs.com/postgres-search-with-facets-and-location-a...
But some points to be made in favor of ORMs (some of them, anyway):
* Multiple backend support to handle different SQL engines.
* Minimized risk of accidental injection.
* Migrations.
"As tedious as SQL" is not an argument to use SQL instead.
Also be specific in what ORM you are comparing to raw SQL. Are you talking about Hibernate or SQLAlchemy. Are you talking about a query builder?
Good ORMs help tremendously with maintainability and security. They also let you drop down to raw SQL when needed.
I don't think rails would have been as popular if you had to use SQL.
But recently I've switched to writing stored procedures and calling them directly, instead of going through an ORM for everything... And it's so much easier.
.. I've seen this.
But I would recommend writing, reviewing and deploying migrations by hand, esp for critical parts of the schema (automatic tools are almost guaranteed to get something wrong, with locking etc)
In the databases I've worked with extensively (PostgreSQL and, somewhat in the past, Oracle) Store Procedures are routinely versioned controlled as part of the application, just these bits of code are in a different language than the rest.
The creation of functions/procedures is not tied to state of the database in quite the same way as tables are; the functions/procedures, where they care about the data, do need to recognize the table structure and changes to that structure, but that's no different than any other of the application code which makes use of the data in the database.
I think where many people get caught up on this is that they do something like migrations to get code, including procedural code, into the database... but that's not the only game in town. And really, given what's possible with databases today, I'm not sure migrations are even the best way anymore.
Consider a tool like: http://sqitch.org/ which facilitates not treating stored procedures as migrations, but rather as individual files which change just like any other code.
There are ways to accomplish having good version control on the table/structure side as well, which again, is something you lose with the migration tools I've worked with.
Anyway, I just don't buy this argument.
ORMs don’t help with security compared to query builders or even typed text.
But surely that's better handled in the database itself?
The kind of rules we use are boolean expressions that identify which rows the user's groups can read/write/delete. The ORM automatically combines all the rules as a single expression and applies it to the query.
https://www.postgresql.org/docs/9.5/static/ddl-rowsecurity.h...
ORMs seems nice for simplified problems but becomes a horrible mess for real problems imho...
I've worked on some rails apps, and the ORMs caused more problems than they solved...
For my next app, I just started using SQL only and never looked back. Simple queries are easily generated with a nice internal API (SELECT * FROM Table WHERE ID = X, etc).
However, more complex selects are all written using hand-written SQL. New developers sometimes find this a bit strange, especially the younger ones, but nobody can fault the system's speed.
I do think a lot of people use ORMs as a crutch, which sucks. Also, ORMs often provide too much abstraction, forcing people who actually know SQL to relearn how to do everything the way the ORM happens to like it. I should not have to learn twice as much to be productive due to an abstraction.
What I prefer are really lightweight ORMs that give me models which I can then enhance with custom code. I don't need an ORM that supports plugins or inheritance or a dozen different kinds of joins. All of that can be done more efficiently with custom code.
Also, I think SQL builders are really useful. I think a lot of people conflate SQL builders with ORMs but they're actually very different problems.
This is a really good point. Many people start with the "no ORM" philosophy, realize their application needs some way to map the SQL to the code, time passes..., they have implemented their own half-baked ORM.
In a language like Java that has a generic DBMS API you can get along just fine with a few classes that handle CRUD operations and transaction management. Somebody familiar with JDBC and SQL can write the bridge classes in about a day, while keeping the overall application vastly simpler.
Either way somebody needs to make an informed choice about ORM vs. direct SQL. It seems as some people get in trouble because they skip that part of the design process.
No need for ORM, and no inline sql logic in your application code.
If you use stored procedures, all you've done is move part of the model into the database, so you have to update the stored procedures as part of a deployment. You still need to have the SQL code written out somewhere, and you still need to have something in the application code that knows which procedures exist and how to use the data they return in business logic.
you can deploy schema changes independently
you can change everything and the app should not notice
I'm not really sure what you mean by "you can deploy schema changes independently" and "you can change everything and the app should not notice". The stored procedures are basically just an extension of the apps logic right? So you can deploy them at any time, sure, but that isn't different from an app that doesn't use stored procedures, because you could also deploy changes to any part of that app at any time.
I do think stored procedures can be more efficient, because you have a lot more control. But it's not like they are clearly superior from an organizational standpoint. If you write an ORM using stored procedures, it's still an ORM.
Why pretend your SQL database is about objects? It is not... (it is about data)
A stored procedure can act like a view or a query, or use procedural logic. Point is: your app can call it and get a concistent result, no matter what refactoring has been going on.
A direct query needs to know too much about the database (orm generated or otherwise) which prevent refactoring and couples app to database harder...
You can rename or merge tables, views functions in the database but the interface the app use (stored procedures/DAL) will stay the same and work the same way.
As for app logic... I prefer bussiness logic in the database, not the app when the data is important. Application logic stay in your application, data dependent bussiness logic stay with the data.
On the other hand, having well factored microservices (out of process) or in process modules have worked out really well with modern devops and software engineering principals - easy push button deployments and rollbacks, unit testing, A/B deployments, etc.
It probably depends on the domain/problems.
My experience is with transaction heavy financial systems or similar, with web frontends, microservices sprinkled around in different languages...
The web app or java worker should be allowed to focus in its problems, the bussiness logic needs to live in one central place, which happens to be in the database accessed through thightly controled interface in the form of stored procedures.
A rest api... how and why should it be responsible for your data? It solved a different problem.
You might not even need a database I guess, and then anything goes.
I need and like my database, and have suffered trying to get along with different ORMs. SQL is so good at what it is designed to do if you just let it.
(And why just one rest api? How about 100 restapis, some microservices, some web apps, some background workers, many different languages. One database. No ORM)
And you lose all of the benefits of microservices if there is still a tight coupling between unrelated (from a domain perspective) to tables.
One db can be a problem, or a strenght depending on the domain; And I really dislike religious design, esp microservices.
I have less problems by avoiding ORMs (and religios microservice arch, or fundamentalist interpretations of rest)
Database handles the shared state in a heterogenous environment. We need it to be centralized to keep track of money, the apps can't do that, two independent databases cant do that either. It must be one system that guarantees concistency.
It works great, there is no downtime. The interfaces are defined, the database stands alone, updates are deployed separately.
Why can't apps "keep track of money"? I'm assuming you're referring to transactions. Apps can create transactions and you can share transactions across apps using distributed transaction (I'm not saying distributed transaction is a good idea).
You dont need an ORM for testing your code...
But I think this varies from project to project. How many different applications, in different languages are using your db and do you tolerate downtime?
All of the manual sign off steps are integrated with the automated release pipeline. As soon as the required approvals sign off, the next step of the pipeline is done.
Rolling back is just redeploying the previous released version. Branching, source control, etc is also a lot easier when all of your business logic is in code and you don't have to sync up the "right" version of your source control with the right version of your stored procedures.
Of course this is even easier when you're using a NoSql solution where your schema is also defined by your class models. But that's another discussion.....
Of course this doesn't have to just apply to code. With things like Packer and Terraform you can do the same with infrastructure. Automated infrastructure deployment is not my expertise...yet
You can then start combining stored procedures. Great way to build a slow mess quickly.
Not really, you just mock the database calls in the code you're unit testing.
And I realize that being able to test queries without database dependencies, only really applies to a few languages that treat queries as a first class citizen in the language like C# and Linq where you can mock out your actual Linq provider - replace the EF context with in memory List<T> - and still test your Linq queries.
Depends on what you want to test. Can either write unit tests for the stored procedures or unit tests for the code that makes use of those stored procedures.
Qué?
http://tsqlt.org/
tSQLt is used by the commercial SQL Test product from Red Gate if you wanted a more polished user experience:
https://www.red-gate.com/products/sql-development/sql-test/i...
And would you not have your IDE on one monitor and your SQL IDE in another so you could look at both sets of code.
Rolling back is a simple matter of installing the previous archive. That doesn't just apply to code anymore. You can treat "infrastructure as code" also.
You can do A/B upgrades, rollbacks, etc. There is so much better tooling around regular code than sql/stored procedures. How many times have you seen stored procedures with hundreds of lines, duplicated stored procedures with V1,V2, etc appended to it, commented out logic etc?
I've had to wade through some hairy code to but at least with a code, I can do some automated guaranteed safe refactoring, find dependencies, keep the interfaces backwards compatible, etc.
Avoiding SQL in favour of an ORM or inline sql is 95% of the time a sign of laziness.
For PostgreSQL and MySQL you can bring up the DBMS in Docker. That is not a built-in fixture obviously but easy enough to do locally as well as in CI/CD systems like Travis. You'll need to load SQL into the DBMS as a prerequisite to testing. That has the benefit of testing your load/upgrade sequence.
The beauty of Linq and Expression Trees.
In the real world:
Linq (c# code) -> compiler -> expression tree -> run time Linq provider -> destination language (sql, Mongo Query etc.)
When you are unit testing you switch out the Linq provider for in memory Linq to objects provider.
https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113)....
EF definitely has the foreign key issue. We have around a thousand tables, we tried to generate the classes for all of them including foreign keys, problem is that when you create a context that references even only a single table, it will load everything that is foreign keyed including siblings of siblings of siblings, until you run out of memory.
Only way around it is to not set up foreign keys which massively diminishes the value of using EF, so we wound up creating two copies of each table's classes, one with and one without foreign keys. That causes its own issues.
This is a common theme I've seen with people blaming ORM's for being slow, it's the devs not using them appropriately more than the ORM's themselves. Not to say that they don't have their own issues.
LazyLoading impacts what data is retrieved from the database (or more to the point when), this is a structural issue before a query was even sent to the database. It would die while generating the query, not sending the query or populating the result.
You likely should have asked for more information before concluding it was "user error."
What I have heard of is traversing the entire graph and causing cascading loading of navigational properties. Yes, I have done that. Something like AutoMapper will do that to you, if you are not careful. Been there and done that.
How did you determine that it OOMed while generating the query?
By looking at the call stack when the exception was thrown (and the fact that nothing hit the database).
It impacts more than just that, it will impact the query generation as well. If you have a property that is not lazy loaded then it will attempt to join the relation or load it very another query in the same round trip. Turning it off tells the ORM that every single time you want A it needs to go and get B as well. If you have it off universally it will attempt to load the entire database, or as may be the case here, crashing while trying to generate a query to do so.
> You likely should have asked for more information before concluding it was "user error."
Perhaps, but you've got multiple conflicting accounts of what went wrong, some comments indicate that it returned data, others say it never touched the database.
As someone else suggested, you probably had lazy loading enabled, and some of your code tried - e.g. through reflection - to get all properties.
EF has some of it's own issues - but you can most certainly create composite primary keys, composite foreign keys and work with projections right from within the code.
None of the issues the original author had with SQLAcademy and Hibernate are really a pain in EF.
Some prefer to switch off lazy loading in EF and instead either explicit eagerly load specific child collections or explicitly load them right before use.
In EF you can do
This will load Customer '1234' with the Orders collection eagerly loaded.LINQ isn’t ORM; ADO.NET and, on top of that, Entity Framework are the (core) ORMs that work with LINQ.
ADO.NET and EF get plenty of complaints.
ORM require just as much investment in time and even then you still need to learn the sql to get the ORM to do what you want it to do.
SQLAlchemy on Python is a truly fine piece of software but in the end it was much simpler and felt more powerful for me to write the SQL. And not even hard BTW.
I only have a limited amount of time available for learning and if I can trim out an entire class of technology (i.e. the ORM) then that's a whole bunch of stuff I just don't need to spend time learning.
But you don't always get to pick what code you'll be working with and if you are working with others in a web framework pretty good chance you'll be learning an ORM anyway.
Also really long SQL statements are no fun. Like debugging a whole program written in one line. I think sometimes there is a temptation to get excessively clever with SQL.
A DSL for writing SQL in a nice, composable way is a useful thing.
Some libraries, like SQLAlchemy, provide both levels, not insisting on using the object mapper.
I actually would rather be writing Ruby on Rails, because it's _less magic_ than SQLalchemy.
It's pure magic.
I don't usually work on the "model" level, though; I work on "table" level.
But my very point was not to make it traverse object graphs at all. You can write nice SQL using it, with selects, joins, functions, etc. All these parts, SQL clauses, are composable, so you can factor out common parts.
Yes, I'm fine working with lists of tuples, not "model objects". Object graphs don't map all to well to the RDBMS model all too well. It's best done on case-by-case basis, if you care about performance at all.
[1](https://ponyorm.com/)
A DSL which manipulates queries and then produces syntactically valid SQL is tremendously valuable.
https://www.red-gate.com/simple-talk/sql/performance/control...
Also, views tend to pollute the namespace a bit: you want a set of descriptive names, per application, and possibly per application version. Schemata help here, though.
Templating can absolutely save time and boilerplate, but adding a DSL over a DSL in the form or ORM may well be the problem.
I'm very comfortable in SQL, and would prefer to write my queries. But our team has grown, and we've found that developers say they know SQL, but they really don't. In general, I've found that it's not the syntax that messes people up. There's a significant mental "jump" between the usual procedural coding paradigm and the "set based" paradigm offered by SQL. Some people just never catch on.
So we're going to start using an ORM (Entity Framework Core 2) so that the "mere mortal" developers can pitch in. I know there's a way to run raw SQL, so I know that when we find spots where the ORM fails, we can just rewrite it with some good SQL if we decide that's best.
But maybe that'll never happen? We've been trying to do simpler SQL stuff as of late so that the heavier lifting in the system is done by the application/client instead of the database (bottleneck). As the database sees simpler, less unique queries, more stay in the plan cache, indexes are more reliably hit as expected, and performance increases.
Edit: I guess I should clarify that I would favor using any library that maps result sets to lists of objects. And I would consider that to be part of "do the rest of the work in your app language of choice."
Use the right tool for the job.
But a query builder is extremely useful and in any complex application, if you don't use one, you end up bulding one yourself, which may not be a very good idea if you don't understand things like query injection.
So learn SQL, learn ORM, and choose in a case by case basis.
But in general I agree with you: Why just learn SQL? Learn all the layers!
I have found, in my experience, that people who tend to want to write SQL over ORM usually want to do so because they simply know SQL better. That’s okay, there is nothing wrong with that. But that doesn’t immediately mean ORM systems are bad. No need to be tribal about it.
The problem I see is that many new software developers these days sometimes can’t see the forest for the trees because they dwell too much on what they think is better instead of simply seeing the software and abstractions as nothing more than tools in the tool belt. It happens everywhere — PC vs Mac, iOS vs Android, Scala vs Java, SQL vs ORM. It’s fine to have opinions, I have many, but as I’ve aged I’ve become acutely aware that my biases are almost solely rooted in the limitations of my understanding.
Could be retitled:
"I should have learned SQL before dealing with ORMs."
Every single point the author brings up comes seems to come down to simple database design, lazy development, or not understanding their tools. They really don't seem to have anything to with ORMs or query languages.
Any screwdriver can make for a bad hammer and some screws may go in with a large enough mallet.
> Perhaps the most subversive issue I've had with ORMs is "attribute creep" or "wide tables"
Normalization of data is required whether you're using a query language or an ORM to access it. The fact that ORMs make it easy to "hide" the fact that you've added 500 columns to a table isn't the ORM's fault.
> Knowing how to write SQL becomes even more important when you attempt to actually write queries using an ORM. This is especially important when efficiency is a concern.
What do they think the ORMs are doing? Magical incantations over the disks? The ORMs are just using queries too. You can write really horrifically bad queries in a query language and also abuse ORMs, but that doesn't make either one bad. Most ORMs can let you see precisely the SQL they are creating. If not, the database will surely log the queries for you and let you know what's going on.
> The problem is that you end up having a data definition in two places: the database and your application.
Welcome to the fact that we have multi-layered technology? There's always going to be discrepancies between the layers that have to be ironed out because no data designs are perfect or future-proof. The author then attempts to bring migrations into the picture as if database migrations are somehow just not a problem if you aren't using ORMs (hint: database migrations have always been tough even in very well-design systems).
> Dealing with entity identities is one of those things that you have to keep in mind at all times when working with ORMs, forcing you to write for two systems while only have the expressivity of one. What this results in is having to manipulate the ORM to get a database identifier by manually flushing the cache or doing a partial commit to get the actual database identifier.
Sounds like a pretty frustrating example, but I've worked with at least 10 different ORMs I can think of off of the top of my head and not a single of them required "manually flushing a cache" or a "partial commit" to "get the actual database identifier." I wouldn't write this up as being an issue with ORMs or that this problem would be magically fixed by only writing SQL either.
> Transactions. Something that Neward alludes to is the need for developers to handle transactions. Transactions are dynamically scoped, which is a powerful but mostly neglected concept in programming languages due to the confusion they cause if overused.
Transactions are pretty straightforward and I cannot agree with: "The concept of a transaction translates poorly to applications due to their reliance on context based on time."
Transactions don't care about time at all. They care about order and making sure that things are completed in a certain series of steps. This actually translates very well to applications, especially when you have processes that take a long time, where you don't want something to happen unless another thing happens first.
While a decently-written article, this comes across as someone who learned about ORMs more deeply than databases, discovered the flaws that ORMs have, and decided that query languages must be the only way forward.
This ignores the fact that we created and adopted ORMs after struggling through years of rigid queries smattered throughout code.
Writing bare queries has a time and a place, but ORMs have saved countless hours of development time, and allowed for vastl...
The law of leaky abstractions applies to many, many things, ORMs included. I would also argue they apply in different degrees, usually related to the design of the ORM (the post mentions SQLAlchemy vs. Hibernate, for example).
But consider the following:
1) Why do people still use ORMs? Exactly.
2) Question 1 but s/ORM/framework_or_widely-used-library
3) ORMs allow you to develop faster, and deliver value
4) Beginners already have a hard time coding, designing, and understanding what they're doing. ORMs provide a nice abstraction over underlying data stores
5) Although fraught with peril, ORMs provide a common interface that'd give _some_ help if you switch data stores
6) Multi-line SQL statements are a huge pain in some languages
And when you've already been through the effort to make your data relational for the database, might as well re-use that effort.
That's not to say that SQL is the answer. Proper first class support for relations in your programming language / libraries is great.
As for using a DSL, Datalog is worth a look.
Objects are containers of data; that's a crazy statement to make. The relational structure maps pretty cleanly to objects, properties, and collections.
However, objects are not good for reporting. And the author mentioned doing 14 joins and hundreds of columns - that smells like a reporting query.
And they aren't a hodgepodge collection of properties; they're entities that represent a single item in a system whether that be a person, a widget, an order, or a product.
If you are exposing base tables to the application instead of appropriate views, which as much a violation of good RDBMS design principles as what you describe is a violation of good OO design.
I dont like ORMs but did struggle some years trying to use them which imho was a detour. SQL and stored procedures in plpgsql is so much better, easier to maintain, easier to reason about etc.
you can have lots of dynamic sql but that might become a rabbithole, just as with an ORM. It sounds like a problem you shouldnt have, now throwing an ORM at such a problem... might lead to even more strange issues down the road...
Sure, you can limit it to a single table and to a certain set of columns e.g. A B C D E. Can you give me an example of a simple plpgsql stored procedure to do this?
you can have lots of dynamic sql but that might become a rabbithole, just as with an ORM
That's not my experience. In a language with good introspection and/or where most entities are first-class, you can do this rather easily. In Python that would take two or three short lines.
It sounds like a problem you shouldnt have
This is a bit of a cop-out :) allowing the generation of simple reports configured by the user is a typical need for us.
Providing tons of abstractions to beginners so they can build something in 10 lines of code doesn't help them get better. All it does is encourage them to learn top-down when it's often much easier to learn something from the bottom-up.
When developing API's in Golang or for microservice / serverless architectures not using a framework might actually make a lot of sense. Also microframeworks (trimmed-down versions compared to opinionated frameworks) are very popular in almost any language.
If this person spent all that time using Hibernate and then SQLAlchemy, and all that time did not know SQL, then their suffering and bad experiences make complete sense. You absolutely need to know SQL if you're going to use an ORM effectively. Good ORMs are there to automate the repetitive tasks of composing largely boilerplate DML statements, facilitating query composition, providing abstraction for database-specific and driver-specific quirks, providing patterns to map object graphs to relational graphs, and marshaling rows between your object model and database rows - that last one is something your application needs to do whether or not you write raw SQL, so you'll end up inventing that part yourself without an ORM (I recommend doing so, on a less critical project, to learn the kinds of issues that present themselves). None of those things should be about "hiding SQL", and you need to learn SQL first before you work with an ORM.
No need to rewrite all your SQL, just the ORM description of the model!
This gives you a lot of flexibility and power, but as zzzeek mentioned, you need to know SQL to understand how what the ORM is inefficient and to be able to replace it as necessary while still letting the ORM do what it is really good at.
1. As a reaction to common SQL injection from poor libraries not implementing parameterized queries. (2004 or so)
2. Novice engineers not wanting to learn SQL (look I learned how to make a blog in RoR, and I like mongo!)
3. As a theoretical abstraction above the data-store (as though you might someday be able to switch the data-store beneath the ORM)
1 has been solved, 2 was never okay (point of the article), and 3 isn't really okay either because it's too leaky (performance specifics).
I used to work in an environment where we had relations as full first class data structures. They are very pleasant to work with.
We actually had 'relation-object-mappers', ie when we had to interact with other systems that didn't use relations, we often mapped them to relations internally to make them play nicer.
It wasn't about speed of execution, but expressiveness when coding. Later on they even added proper type system support.
Common operations were things like map/project, extend, filter, join, collect-by-key / expand, etc.
Just as Codd pointed out in his original papers, relations allow you to not have to make a choice about a hierarchy for your data.
Using key-value store like a hash-table in your program, or the much vaunted has-a relationship between objects would force you to make these choices. Thus making interacting with the data awkward for all but one access pattern.
Relations work best when your program is written in a style that deals largely in immutable data. (What we call 'purely functional', but people in dysfunctional languages have also picked up on the advantages recently.)
Or, alternatively, make it really painful when you do actually need to query hierarchies, along the lines of "give me all the tuples above this one in the hierarchy".
Datalog is a specifically chosen subset of Prolog.
For short-lived processes, like typical web applications and command line utilities – that load data from the database, do something with it, and then purge it from memory again – I'm becoming less and less convinced that ORMs are actually a benefit.
On the other hand, if your application plans to map the database data to memory for long periods of time, with the need to keep them in sync, then you're probably going to end up writing something that resembles an ORM anyway, and poorly at that. In this case a good ORM is beneficial.
5. Type checking all your queries.
6. In code query composability.
But I totally agree that ORMs are too leaky of an abstraction for 2 to be really useful, and that 3 is much harder than it appears to be.
Just because we are using SQL doesn't mean we have to do the mapping fully manual.
Plus it plays very nicely with kotlin :)
But I'm really surprised every time people tell me they look at the schema as defined into the ORM instead of at the table in the database.
I'm really jaw dropped the few times I know somebody doesn't even know SQL, only the ORM. Maybe they look at it as if it were the reaction of somebody that thinks you must know assembly if you want to program Ruby, Python or Node (I don't.) Still, if you work with a database you must know it's internal language, SQL or NoSQL. Your going to need it or build a mess.
And involving a DBA early in the project can make your database at least twice as fast, with the right schema and the right queries. Then you translate that into the ORM you want to use.
In my experience, nearly no one knows SQL, and the attitude seems to be that learning it at all is a waste of mental bandwidth.
On the other hand, I wonder if knowing none at all is better than knowing a little.
Even 1 year "learn programming fast" courses here in Uruguay have at least the basics. OTOH, Javascript is not taught in many courses so YMMV...
Very happy to not have and ORM on our stack for many years.
I believe that the interaction with the database is just about the most important part of code that you need to rely on. We have had messy data written to the database due to misused or misconfigured ORM (perfomance issues, bad orphan handling, session state problems, overflown sequences, to list a few) and decided that we should not rely on all devs touching that code to be experts in the ORM api to avoid the pitfalls, we rather our devs be expert in SQL.
Never had any of them complain about mapping using something like jOOQ. Type checking and composability are also built in.
- replaced the DB driver mid way through a project to a slower, but more complete implementation. this went flawlessly, because it’s all so generic and in the end the same DB under the hood, so a simple change (unless you don’t use an ORM where i could see it being a nightmare)
- started a new project where i had to use MSSQL, which I’d never used before, but i’m a big fan of postgres/sqlalchemy. other than ODBC oddities, it was really simple to write the new app with all the same patterns i was used to with things like update on write, lazy joins, constraint deferral, and i think most importantly would be MIGRATIONS! huge help to have the same migration framework that i was used to
I worked on a project with a home-grown ORM (in C; it was horrible) that abstracted over both MySQL and Postgres ... except the overarching application required Postgres-specific column types and functions.
> Good ORMs are there to automate the repetitive tasks of composing largely boilerplate DML statements, facilitating query composition, providing abstraction for database-specific and driver-specific quirks
None of that requires an ORM. A simple query builder will suffice and it will be much easier to debug and much less error prone than an ORM.
> providing patterns to map object graphs to relational graphs, and marshaling rows between your object model and database rows - that last one is something your application needs to do whether or not you write raw SQL
So this is the real ORM juice. And ya, without an ORM you have to do this by hand.
So here’s the question: How well can an ORM map to your data model out of the box? In a lot of cases this is where the mess comes from. You need to set up basically a low level AI that can figure out the “right” thing to do as call sites all over your codebase are making arbitrary queries on a huge API surface.
And so the argument against ORMs is that it will take less time and be less error-prone to write bespoke loaders that take rows and build your data model than it will be to configure that AI such that it can do the “right” thing with arbitrary queries.
(If you don’t like the word “AI” here, use “expert system” instead.)
What's the difference? To me, an ORM is largely a query builder.
Your scenario happens with people that either don't know or don't care. They will write crappy queries with any tool.
I built a moderately complex application in Django at a previous workplace, using the ORM for most things, until the queries were too complex for the ORM.
Another guy connected to the same database and built some graphs using PHP and SQL. Guess who had to help him write the SQL when the queries got too complex for him? The ORM user.
Query Builder: You build a query, just not in SQL. So you can get around SQL's limitations (like composition)
Try this:
> ORM: You ask for something and because you've researched and found a well-written, quality ORM you trust that it will create a sane query.
or possibly:
> ORM: You ask for something and you accept the tradeoffs vs hand-written queries but you're content that it's the correct tradeoff for your use-case.
There's plenty of places for bottlenecks to hide and premature optimization is the root of at least some evils.
Ha. More like:
ORM: You've just joined a project already using an ORM selected by an 'architect' that no longer works here. Everything is fine until you start testing your system with a database sufficiently populated with real-world data. You and the DBA spend the next next 6 months trying to get the damn ORM to generate performant SQL (you even call Oracle support, which promptly dispatches an "engineer" who tries to sell you on another $500k of crap you don't need that won't really address the problem). You eventually just start writing queries by hand wherever you find a bottleneck caused by the naive ORM, which you could have done at the outset, but no one who actually knew SQL well enough was on the team back then.
Ironically the last time I had a big ORM performance problem was Hibernate eager loading all joins. Diagnosing and fixing it didn't take more than an hour. (Though we did have a very experienced DBA at the time.) YMMV
But of course, as the queries get more and more complex, the flexibility of the ORM syntax approaches the flexibility SQL. In the end, there are many situations one would rather just use SQL.
For example, in Go, I use Gorp, which has a Select() function where you pass in the SELECT query string (plus bound values) and the target type, and it loads every result row into an object of that type. So you can have an arbitrarily complex SQL query as long as it starts with `SELECT one_table.* FROM`. That's a marvelous design.
And when you have to do a query that returns results from multiple tables? Guess what, you just use the normal SQL module from the standard library.
... that you understand and can be sure are sensible.
> while an ORM builds queries for you
... that you have to hope are sensible.
That's one of the biggest flaws of the ORM for me - you have limited visibility of what it's doing to your DB.
And then what do you do if the ORM is generating junk? If the answer is "use a querybuilder/handcrafted SQL for that one", what's the point of the ORM in the first place?
The point may be that 98% of the queries are just fine, and you've saved time vs writing by hand, and it may be easier to read/understand for the next people to have to touch the code.
Most software work is maintenance work. Optimizing for initial deployment is shortsighted.
I've also worked at a companies where all of the logic was in ungodly stored procedures and getting anything released took months and we had a whole two weeks "hardening sprint" because we had to coordinate with the "database developers" and of course the entirety of the business logic was In stored procedures.
Most software being maintenance work is even more of a reason to optimize deployment. What good is unreleased software? The most important part of the business is releasing software. Most of my emphasis asan Architect is making sure we can release fast.
* The 'developers' weren't allowed to write the sprocs. We were at the biz meetings, but the DB guys were hardly ever there - their meetings were separate for some reason, but because devs were at the meetings, the devs were the ones who also were the face of the project. When the project was behind, we caught it in the neck, even if we were bottlenecked waiting for the DB team to write their logic and expose it to us.
* The DB team was always fewer people, juggling more projects, and other things like system uptime, maintenance, backups, etc.
* The sprocs were never part of version control or part of any source code that we could ever see as part of normal development. They typically weren't subject to any unit testing process.
The answer to all of this is mostly human management, structuring resources differently, coming up with different processes, etc. But any of those things would have changed the power dynamic, which seemed to be the purposes in those environments. Hey, I can write a stored procedure too - let me write them, and if a DB wants to 'review' them - or really, anyone on the team - please review and let's hammer it out and make it better. But roadblocking projects until the DB guys can 'get around' to writing our mission critical procs is just silly.
One other 'weird' division I saw a few places was this "developers can never have access to production systems - that violates XYZ" (a regulation, or some 'law' that was never produced, etc). I asked what the core issue was, and it was "you can't just have developers going on to production and just making changes on live systems - that's ... (against our policy, etc)". This was particularly challenging in a situation where a critical bug only happened on one production system, and we weren't allowed to replicate the database to another system, nor was anyone with any knowledge of the deployed code allowed to get on to the production system to even see if what was deployed was what we'd developed. But... this was still "our problem". Yet... the DBA in this case was "allowed" to get on the system and hand-write new triggers and sprocs to 'fix' our problem, all without documenting/testing his code, nor committing the code to any repo for us to even have visibility in to the data manipulation he was doing to 'fix' the problem we supposedly cause but couldn't investigate.
Again, I know this isn't a problem specifically with stored procedures. When sprocs have been promoted as the primary interface, however, it's usually been a political/power grab more than a technical benefit. And yes, again, I know there are technical advantages in some cases, but usually not enough to outweigh the drawbacks I've encountered.
Yes I optimize when my automated performance testing/stress testing, tells me I need to and may write handcrafted sql, but I've also handcrafted some classes in C back in the day when my old Windows Mobile app using the C# compact framework wasn't performing.
From what I've seen (couple of home-grown ones, Class::DBI, ActiveRecord), "more often than you want".
(I'm willing to admit they may not be class-beating examples. :)
Of course most ORMs do fall short because most languages don't have the powerful concept of "code as data".
SQL syntax is extremely verbose, and compounds the more tables are involved in a query. You're not counting the time the ORM has saved you before having to resort to logging SQL.
https://github.com/bgard6977/sqorm
Flyway & jOOq take a similar approach:
https://www.jooq.org/
Taking the SQL-first approach also allows you to serialize without circular reference problems, since you don't just load the data, you also define a path to decompose the graph into a DAG.
Here is why.
Bitcoin cryptocurrency AI biomedical supply chain networking quantum systems-thinker.
https://news.ycombinator.com/newsguidelines.html
https://news.ycombinator.com/newswelcome.html
What AI do you need? You map your tables to objects and relationships between objects via FK relationships.
Most ORMs will download all the data and evaluate them on the client side, with code that is even more convoluted that the SQL one and thus with less performance.
Entity Framework and any other Linq to data provider translates the Linq expression to the native language of the source data and does it server side.
LINQ only allows for a fraction of what is possible with SQL, and good luck having the best queries generated out of it, if the RDMS doesn't happen to be SQL Server.
It all depends on the talent of the authors of the drivers.
I've seen that pattern in lots of homegrown applications, but I've never seen such a thing in a mainstream ORM. Care to provide examples ?
Nowadays I only bother with Dapper, MyBatis, using a mix of SQL and stored procedures.
EF only if the RDMS happens to be SQL Server.
That is only true in a one-to-many entity relationship (and even so, it is debatable). A one-to-one relationship can be modeled in the two objects, in one of them, or delegated to a third entity. A many-to-many entity relationship can also be handled, in OO, in various different ways. Idem for a ternary relationship or, basically any higher order relation between objects.
This is known, borrowing a term from electrical engineering, as an impedance mismatch between the two models, and it's not an easy problem by any measure.
On the other hand refactoring your data logic is just business as usual, and you will probably do it in both cases anyway.
1. provide idiomatic domain-object oriented query interface which it in turn translates to SQL
2. provide CRUD sql generation
3. provide some sort of session-based object lifecycle change tracking and management
#1 - The generated SQL is important on many levels. As things like HQL/linq/<your QL here> deviates further from the generated sql transparency is lost. SQL is normally brittle compared to your domain language which has better testability and type safety but still you have a handful of queries where it feels more sensible to write the SQL yourself.
#2 - Code which reflects on a type and generates basic insert/select/update/delete is usually pretty naive and easily done. With the exception of complicated legacy databases and iBatis-style tools ORMs which only support #2 arent really worth bothering.
#3 - I've found this to be the real benefit of an ORM. Being able to scope object lifecycles into clear units of work, buffer pending changes until a discrete point and get scoped caches for "free" have been hugely beneficial.
Each ORM unfortunately/inevitably come with great learning curve. It seems to unfortunately/inevitably bring lots of new concepts and conventions to the table required for the user simply must understand. Sometimes it requires you to re-arrange the way you may write your code to be more session-oriented.
I dont like the idea of AI managing how/when to apply changes to storage media. AI can be smart and efficient yes but you lose that important transparency/predictability. On the contrary I prefer very dumb/mechanical/predictable ORM, something not elegant but the behavior of what happens when is well-understood and easily scaled out to a large team. In my experience hibernate, EF, sqlalchemy have that sort of dumb/predictable behavior (however the SQL they sometimes generate can be performant but unreadable).
EDIT: Sorry, my mistake. The partent is talking about the parent comment, not the FTA
I wish more people aspired to be like him, because you know there's no more authoritative answer when you run into a Stack Exchange post and he's offering up his assistance.
I am a huge fan of the custom column types -- they have allowed me to have code that works against SQLite as well as a production databases with ease!
Joking aside, SwlAlchemy is very impressive software, and zzzeek deserves all this praise and more. Every time that there's one of these anti-ORM articles I feel like SqlAlchemy pre-emtively addressed all the substantive criticisms in its flexible, well layered, design.
(And in the interests of fairness, Django's ORM is also very good at making simple things simple).
I did the opposite and faced lots of difficulties. Then I concentrated on plain SQL and suddenly I felt like I am understanding ORM much better.
I recommend anyone that has to deal with ORMs create their own at some point, in a non critical project. Not because they will necessarily create the next big thing (but who knows?), but because nothing quite gives you the perspective and appreciation for what these systems can do and why they have their pain points like making one yourself. You'll most likely not use your own module long after you've created it and then again surveyed what's already available, but it's invaluable in making a good assessment of those options as well.
Similarly, making a web framework yields similar benefits.
In both cases, a good understanding of the underlying technologies they build on (SQL and HTTP), is required, and if you don't have it doing in you'll have it coming out the other side (which is really the reason for this in the end).
Similar things exist all along the spectrum. From embedded OS's and compilers to javascript utility libraries.
What it comes down to is that a tool is best utilized when the person knows when and how to apply it appropriately, and that's as often as not an understanding of the tool as it is of the context.
A person intimately familiar with hammers and their uses can build some interesting wood furniture, but they'll likely never achieve the same level of product as a master woodworker that's just well acquainted with a hammer. Investing time and effort into tools provides only so much benefit. At some point, more knowledge of the craft itself is far more beneficial.
"Attribute creep and excessive use of foreign keys shows me is that in order to use ORMs effectively, you still need to know SQL. My contention with ORMs is that, if you need to know SQL, just use SQL since it prevents the need to know how non-SQL gets translated to SQL."
That is quite theoretical. My PRs for fixing non-spec compliant behavior in pgjdbc get rejected because they might break some ORMs (mostly Play). My PRs for adding MariaDB sequence support to Hibernate get rejected because there are additional MariaDB features that Hibernate doesn't support as well.
as an example: https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalch...
heaps of this kind of thing are nicely dotted around the code so you don’t have to deal with weird driver quirks, maps “Text” column type to whatever it needs to be in your given database to have an unbounded text blob
id say that’s far more than theoretical
EDIT: typo
EDIT 2: also, on DB specific features, SQLA supports a bunch (not all). eg: https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalch...
The presence of an ORM relying on these bugs should not prevent a bug in the driver from being fixed.
I've found that the people who know SQL pretty well can actually do good work with or without an ORM. But the maintenance is a lot easier when the abstractions are consistent with the abstractions that are used by a popular ORM.
Analogously, I'd say that being able to write a compiler, down to having it generate machine code or assembly, gives you a leg up when using a compiled language. I've met and interviewed a number of coders who had cringeworthy gaps in their knowledge, because to them, a C compiler was just some kind of "magic."
An ORM turns your result into a collection of objects. THAT is what an ORM is for and about.
The fact that they are built on top of query builders and lighten the load when developing is just a handy side effect.
Sounds nice, but in reality, latencies, networks, massive data sets, atomicity of operations, and a whole host of other annoyances get in the way.
... so it ends up being simpler and easier in the long run to be very explicit about your interactions with data stores. Took me a long time to get to this point.
Know how and when to use ORMs. Probably learn SQL first. Don’t believe that any shiny bullet is silver.
Or in short: “No”
Also, basic SQL can be easily taught in as little as 10 minutes (I have been initially taught it at middle school during MS Office Query, Access and VBA class). An image of a programmer that can't use SQL (I don't mean advanced cases which can indeed be a bit tricky but these are far beyond the powers of any ORMs anyway) seems really bizarre to me.
there are plenty of real life projects out there that would beg to differ