A developer once showed me a frightening sight: a page and a half of dense SQL, consisting mostly of join clauses. What was frightening was not so much the performance of this query -- it was a utility query, so the long query time was not important -- but the fact that he was actually proud of that query. How did a production DB schema get into such a state? The problem, in a word, was Hibernate. I'm a fan of Hibernate, so I'm not saying that there's something wrong about Hibernate. Rather, Hibernate lets you too easily do things that you should have second thoughts about. To borrow a metaphor, this tool comes with live chainsaws attached.
An industry veteran once explained to me that there are basically two kinds of developers: applications people and database people. Unfortunately, they tend not to be on talking terms, and ORM is exactly the sort of place where application and database collide. Taking an excessively application-centric view of the universe can land you in the kind of trouble I opened this blog post with.
In this case, the Hibernate mapping started with a single base class with common metadata: ID, modification timestamp etc. All persistent classes inherited from this base class. The problem came from the decision to actually manifest this class inheritance in the schema using table-per-subclass mapping, and to have a extensive inheritance hierarchy. There is a table representing the base/abstract class, a separate table for each top level entity, and another table for each subclass. So every persistent object required at least a join between this base entity table and the table representing the concrete class. Any association guaranteed at least a 4 table join, and usually many more.
Considering update performance, the situation looks even worse. All updates required writing to this base entity table, no matter what object you update. MySQL's default storage engine is MyISAM. If you use any engine apart from InnoDB, any kind of row insert/delete/update causes a table lock. This schema meant every single DB update had to wait on the same table lock. That same developer said his schema suffered from poor write performance on MySQL. Gee, I wonder why?
An aside: if you are using MySQL and are not using InnoDB as your storage engine, please ask yourself why.
I would say that the main problem here is an excessively object-centric approach to ORM. An application-oriented mindset would focus too much on the O (object) part of ORM and forget the fact that you are working with a relational (R) database. Using table-per-subclass mapping "feels" natural if you are working with objects, and Hibernate makes it so easy to do so. But unless you really need polymorphic queries, there is little need to do so. And even from an OOP perspective, inheritance overuse has long been a problem. In the single base entity scenario I describe here, there is clearly no meaningful is-a relationship.
The fact that Hibernate lets you express OOP constructs so easily should never blind you to the fact that you are ultimately working with a relational database. Inheritance, in particular, maps poorly to relational databases. ORM is a leaky abstraction, and should not be an excuse to ignore the SQL. Respect the database, and be prepared to make compromises in your object model.