In the former case, the query is verifying that the object_id field cannot be used to find a foreign object--regardless of the value of object_id. This is exactly what it is asked to do.
In the latter case, the query is simply verifying that object_id is NULL, which is exactly what it's asked to do.
An Owner is deleted without setting the pets.owner_id to NULL. So now there's a row in Pets with an owner_id referring to an Owner that doesn't exist.
These two queries will (rightfully) return different things in this case.
One will return The Pets who have null for an owner_id. The other will return the Pets whose owner_id is NULL AND those who do have an owner_id, but it doesn't reference an existing row in Owner.
Part of the issue is confusing object data with metadata. The id is an implementation detail of the data store – metadata. What the user is trying to do here is “talk database” and “talk model” at the same time.
Which is a perfectly good argument against an ORM. But one should commit to a metaphor, or not: http://clipperhouse.com/2012/02/29/suspension-of-disbelief/
Edit: plus! even if you want the same answer assuming that FKs are not in place and that bogus values might be present in other_id, the query is still far less efficient than it should be. You should be doing this:
select * from sometable where not exists
(select 1 from othertable where othertable.id=sometable.other_id)
compare the query plans on any reasonable database and see (and yes, SQLAlchemy produces the NOT EXISTS form when the relationship is a one-to-many versus many-to-one and you ask it for objects with empty collections).The argument you are making is that this particular ORM needs optimizations. Which is true! The point stands that the semantics of the two expressions is different, and thus should do different things. The point also stands that the user is mixing metaphors.
If the ORM can be informed of the guarantee that the FK constraint provides, and optimize accordingly, that’s good too. But this doesn’t tell us much about ORMs, except that they can be improved.
Also, a database that has two different query plans for queries that are logically equivalent…needs improvement.
No, the Django ORM is not correct in unsing the correct query by default. At least not on every database backend. That's because on any database that enforeces constraints, it already created the necessary checks, and altough functionaly they are equivalent, that line can mark the difference between a 5ms or a 45 minutes runtime (that's what happens with my data, not a hypotetical case). There is more to a framework than mathematical correctness.
But then, it should certainly have an option to always use the correct query, and it is a lot of implementation work that I can understand quite well that Django developers don't want to do now. They probably have other priorities.
And by the way, as I said, this is a problem to me (but no, not important enough that makes me fix it now, maybe later), but did I stop using an ORM just because of an abstraction leakage? Of course not, I encapsulated a solution to this problem and gone on, earning lots of man-hours at the 99% of my code where Django's ORM doesn't leak. That's my main disagreement with the article. Yes, ORMs are stupid, but not using one just because of that is stupid.
And yes, if you let Django templates go, you can easily cut 9ms of your response time. That's great! I wonder how much you'll cut if you rewrite it all on assembly.
The first is retrieving the object and checking if it exists, and the second is just checking the parent's foreign key. Not sure that they are really the same query, if you have unenforced foreign keys.
This is not "dumb", this is analogous to checking if a pointer is null or that the contents of the pointer are null (which is a distinction that some people want to make).
I.e., in pseudocode:
query = SQL("SELECT * FROM entities WHERE owner = ?", owner=me)
...
if some_condition:
query = query + SQL.WHERE("OR public = TRUE")
...
if other_condition:
query = query + SQL("LEFT JOIN things AS t"
" ON t.entity_id = entities.id") \
+ SQL.WHERE("things.value > 0")
...
my_nice_list_of_results = run(query + SQL("LIMIT ?", count))
This should be technically possible, but I haven't seen any library that does it.One reason for the variety of opinion on this is that different developers make more or less use of domain models in the first place. Those who are accustomed to writing all SQL completely by hand with no helpers at all, and not working with a domain model tend to view the stored procedure approach as equivalent. Those who are accustomed to having at least some simple marshaling layers like a construct that generates an INSERT statement given a list of column names see the SP approach as more tedious since simple techniques like that are usually not easily available, at least in more old school SP languages like TRANSACT-SQL and PL/SQL.
All of that said, I do think this is a problem that can possibly be solved. Postgresql allows SPs to be written in many languages, including Python. I have an ongoing curiousity about the potential to integrate a Python-based object relational system into a stored procedure system. But it might end up looking like EJBs.
That's one of the pain points RethinkDB is trying to solve, since you write your queries in whatever application language you use and it's parsed and executed in the cluster.
Views, stored procedures and functions can be used to help isolate duplication, parameterize the queries, or otherwise hide the SQL.
Code like you've posted is the result of taking DRY too far, to the point where avoiding a small amount of repetition ends up bringing in far more complexity and problems than the repetition might cause.
I believe, If someone'll take an SQL SELECT statements parser and create a library that'd generate SQLAlchemy query/statement object from them, such library will make development more productive.
SQLAlchemy was already mentioned. In the JavaScript world, there's node-sql. https://github.com/brianc/node-sql
I've always found it kind of odd how there are some people who despise SQL merely for its syntax, yet they'll turn around and advocate the use of libraries which mimic a SQL-like syntax in some other programming language (but do an absolutely terrible job at it).
The node-sql examples are atrocious, for example. It's even more obvious with the SQL so close by. The SQL statements are clear and concise, while the JavaScript version is nowhere near as easy to read.
At least LINQ gives the option of not having to directly deal with the method calls, which makes it marginally nicer to work with. Anything less than that, like we see with basically all other systems, is far less usable.
No, I use ORM because I love SQL. ORM doesn't replace SQL. ORM helps to generate the exact SQL I want with much less code.
I have seen application with thousands of stored procedures, most of them boilerplates, and only supports one particular flavor of RDBMS. I have seen too much hand-crafted SQL in the form of "@param_xxx IS NULL OR field_xxx = @param_xxx".
I used to think that Tom Kyte was right, that everything should be in stored procedures. Now, I am thankful for ORM (more specifically, SQLAlchemy).
One often wants to have several variants of a SQL statement, beyond simple placeholders for arguments. I've seen several projects that grow a lame templating syntax on top of their SQL strings, to the point that the SQL then becomes incomprehensible.
If this really bugs you, perhaps the ultimate solution would be to actually parse SQL.
query = sqlParse("SELECT foo FROM bar WHERE quux = 1")
query2 = query.clone().constraint("quux = 2")I believe that the phrase you're looking for is "lack of compositionality".
It's only bridge between sqlparse and SQLAlchemy that's missing. I guess, just because nobody had a wish, will and time to finish and share one.
I think that this is the "every problem in CS can be solved by another layer of indirection" part. You're basically sidestepping the issue of SQL not providing the functionality in the first place.
While a lot of us who had started working with SQL in the 1980s, if not earlier, were perfectly fine with using it, many younger developers were scared away from it by these claims.
So we've had a generation of software developers who were essentially raised to hate SQL, and to embrace ORMs, even after it became clear that ORMs do come with some pretty serious trade-offs, and do not necessarily increase productivity.
Not having a solid grasp of SQL, a lot of these developers just don't realize what they're missing out on. I've seen this first-hand many times before. These developers will spent hours upon hours trying to get their ORM to perform a moderately complex query that could be easily written by hand within a few minutes, including any code necessary to perform the query and to retrieve the result. The time and effort expended on these sorts of queries will very quickly negate any time and effort the ORM may have saved for simpler queries. And these moderately-complex or complex queries always arise in real-world software.
I think that education is the only way to really solve this problem, but a lot of developers are quite set against this. Learning SQL isn't that much of an investment, but the returns it offers can be huge.
But I also think that people don't like the boilerplate that comes with direct sql access. I think they don't like the impedance that comes in reading and understanding code. And I think they want to hand off annoying, but critical, things like caching to a lower level they don't have to think about.
SQL is an important tool, and any developer, especially one who is using a framework like Django or Rails, would be wise to learn it, but ORMs still have value and it isn't all about "I don't know sql".
Most ORM systems will give you options. My experience was with Hibernate, which had let you do Object queries, Criteria queries, HQL queries, and finally raw SQL. You hardly ever needed to go down to raw SQL. It's nice not to worry about the particulars of the underlying SQL engine, and certainly you want serialization to be handled for you.
The OP is right though, you can't treat ORM framework as a total blackbox. You need to be aware of what it's doing else you can really get yourself in trouble.
So you have to learn
Object Queries
Criteria queries
HQL Queries
Raw SQL
HIBERNATE
And this has made your life easier has it? Hibernate is massively complicated, and you're right, it doesn't insulate us from the database, Not even slightly. So the amount of shit I now how to know has quintupled, just to persist an object!But hey, BOILERPLATE, right?
Yes it has. Belive me, it has. And it isn't nearly as bad as you make it out, certainly better than the alternative. If you have relatively simple relational data, and query requirements, you can get away with just Object and Criteria queries. Your code will thank you. For more complex queries, HQL will get you down almost to the bare metal. Why do that in lieu of raw SQL? I mentioned several reasons. One of which, is that the ORM layer does abstract the boilerplate of query to-and-fro serialization. More than that, it enforces constraints. Your DBMS doesn't give a shit whether ages should be in some valid range, or have some sort of valid format, or whatnot. Those constraints are in your object model, which is then automagically transferred to your SQL commands. Another reason is that you can now substitute SQL backends, trivially. Going from MySQL to Postgres is a one-liner. Another reason, caching is completely transparent. You can now plug-in any kind of caching engine and strategy with a one-line config change, and it's all completely transparent to your application. Another reason, your framework (JEE or Spring) probably has hooks to your ORM, which makes the integration completely seamless.
The thing is, if you didn't go with an ORM framework, you'd probably roll your own abstraction layer to take care of some (all?) of the above mentioned use-cases. You don't want serialization code, or caching code, or constraint-enforcing code littering your business logic. So this abstraction is good. There may be reasons to forgo an ORM framework, but I sincerely believe the vast majority of use cases will benefit from it.
I just don't think that will happen.
This type of thinking has to stop! Sure, some times it may be necessary to extract data from SQL and turn it into some type of object. However, most of the time it's enough just to get the data and work with the data directly.
Not too mention, what many believe as an OO solution is often times far removed from anything remotely OO.
I worked with redis extensively, and i got to the point where it was too dangerous to simply assume that none of the other guys on the team (or me) wouldn't put some garbage data in a field because from redis' perspective, every key looks the same and every value is as good as the next. We rolled our own abstraction layer, in which keys and values were wrapped in domain specific objects.
Programming languages, and databases are too general to be useful. If you don't 'constrain' them to your domain, you're going to get destroyed once your product or team scales to a certain size.
Like the other commenter requested, can you provide an example of an ORM that isn't "stupid"? Your initial comment makes it sound like you've worked with at least one that isn't. We're curious to learn about which these may be.
View my profile for further detail.
As Abelson and Sussman point out in SICP, "Computer language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute" (http://mitpress.mit.edu/sicp/front/node3.html).
The software we write is a codified expression of what we think, distilled into a working example that can elevate a discussion from theoretical-based to evidenced-based. Those who have devoted time to think through an issue deeply and have codified their thinking into software would be doing a disservice by not referencing it.
Not only that, but typically you end up balancing between stuff being unavailable because of lazy-loading, and one pageview taking 30 seconds because the ORM collected all the dependencies.
I disagree it's a problem.
going back to Joel again (!) :
"All non-trivial abstractions, to some degree, are leaky."
I talk about this a lot in this particular talk: https://www.youtube.com/watch?v=E09qigk_hnY
Hibernate was a great influence on me but I like to think that it only introduced some ideas in rough form that we've all had many years to improve upon.
The ORM will of course introduce new issues to deal with but this is because it's taking care of a vast amount of persistence code you no longer have to write, and applies a consistency to that persistence logic that would be extremely difficult to achieve without using tools.
As I mentioned, I'm very happy using SQLAlchemy's lower level API. It's a helpful and elegant abstraction over queries and table definitions etc, and I've never needed to circumvent it yet. I'm also convinced that the delightfully flexible/powerful Mako is hands down the best templating library for Python. You, sir, Rock. But you come off as needlessly argumentative in this thread.
The thing is, SQLAlchemy's SQL Expression API is a suitable-level abstraction: high enough to be useful, but not high enough to guarantee leaks. I'm happily making queries with one-liners, and haven't had to circumvent it yet, but I bet I'd have run into trouble with any ORM already.
OP says one ORM is bad so all of them are, you say no some are nicely made (which I agree with), parent ask for a specific example you would recommend and you answer by nitpicking on a single word in his message, one that he even put in quotes himself. And then you finish with a question about something that parent didn't even say or infer.
Either you have an example and you provide it, or you don't and you say so, but your comment was unnecessary and unwanted.
(and so is mine, but I've seen so many of those on HN lately that I just broke and wrote that rant)
You've implicitly defined AI as a level of intelligence that doesn't need human oversight to function. That level doesn't exist yet, but that doesn't mean AI doesn't exist -- it just has a different definition than the one you're using.
Consider Watson (the Jeopardy contest computer lately in the news) -- it can beat the best Jeopardy players, but it's completely unable to function if given a different task or deprived of human oversight. Notwithstanding that limitation, most people will claim it's an example of AI.
Most ORMs are indeed stupid. They often produce completely surprising results at the least opportune moments. They are high maintenance tools that require constant supervision to ensure that you haven't accidentally made some changes which causes something crazy to happen in the ORM. It's about the principle of least surprise, which ORMs often fail horrifically at.
That don't try to handle hidden-magic-state and lets you easily access via Raw SQL if you need to do complex queries. Many don't try to abstract anything and are simply extension methods over the underlying IDbConnection (so you never lose any flexibility), i.e. they simply exist to remove the tedium boilerplate of mapping RDBMS results back into POCOs.
Using an ORM should not exclude also using direct SQL. It should be both.
I believe this brings the best combination. Anytime there is major complexity just drop to normal SQL.
The best of both.
Now obviously, some people would complain that it doesn't make sense to do the extra join, but then people would be complaining about magical or exceptional behavior. ORM behavior is very predictable about which fields are being queried
filter(object__isnull=True)
https://docs.djangoproject.com/en/dev/ref/models/querysets/#...