Самая неприятная штука в работе с ORM — это «расползание атрибутов» (attribute creep). Таблицы обрастают полями, их становится всё больше. Иногда без этого не обойтись, хотя смягчить проблему помогает Postgres с его hstore. Например, клиент шлёт кучу данных, которые нужно прикрепить к отчётам по бизнес-логике. Ты просто перетаскиваешь эти поля туда-сюда, не особо вникая в их смысл.
В самой базе данных это не катастрофа. Но с ORM начинается ад. Проблема всплывает, когда используешь сущность напрямую для построения запроса. Раньше на Hibernate ты писал что-то вроде query(Foo.class).add(Restriction.eq("x", value)). Пока у Foo пять атрибутов — всё ок. Когда их становится сто — это уже пожарный шланг данных. По сути, это аналог SELECT *, только ORM молча тащит всё подряд и часто не даёт удобного способа написать точную проекцию. Я оптимизировал такие запросы, добавлял правильную проекцию — и время выполнения падало с минут до секунд. Вся задержка уходила на перевод строки БД в Java-объект.
Другая боль — настырное использование внешних ключей. В ORM связи между классами превращаются в foreign keys, и если их не настроить аккуратно, при загрузке объекта генерируется куча JOIN. Недавно я насчитал в одной такой таблице больше 600 атрибутов и 14 JOIN — и это при «правильном» способе запроса.
Всё это подводит к простой мысли: чтобы эффективно работать с ORM, ты всё равно должен знать SQL. А если знаешь SQL — зачем тебе ORM? Проще писать на SQL напрямую, не заморачиваясь, как не-SQL транслируется в SQL.