ORM — Object-Relational Mapping — Объектно-реляционное отображение — это технология, позволяющая связывать SQL-ые БД с ООП кодом. Самая известная из них — Hibernate.
Hibernate — очень красивая технология. Она формирует ощущение магии.
Ты просто создаёшь объект, — хлабысь! — он уже в базе данных. Никакого SQL, никаких соединений, никаких ResultSet. Всё происходит как будто само собой. Прелесть.
Именно поэтому отказаться от Hibernate так тяжело.
Это напоминает Кольцо Всевластия из «Властелина колец». Оно не просто давало силу — оно меняло сознание своего владельца. Оно внушало мысль, что без него уже нельзя. Что именно оно даёт власть и контроль.
Hibernate действует очень похоже. Он формирует ощущение, что именно он управляет данными. Что именно он связывает код с базой. Что без него всё развалится.
И ты начинаешь бояться.
Бояться, что без Hibernate придётся писать бесконечный SQL. Бояться, что потеряешь контроль над данными. Бояться, что любая операция с базой превратится в мучение.
Ты уже не представляешь себе проект без этой магии. Ты настолько к ней привык, что мысль «выкинуть Hibernate» кажется почти кощунственной. Кажется, будто вместе с ним исчезнет и сама возможность нормально работать с базой данных.
Но если всё-таки посмотреть на ситуацию трезво и задать себе простой вопрос:
А что на самом деле даёт Hibernate?
Кажется, что он ускоряет разработку. Возможно. Пока ты джуниор, ты восхищаешься тем, как легко всё работает. Создал объект, вызвал persist(), и данные уже в базе. Магия.
Но с опытом начинаешь замечать вещи, на которые раньше не обращал внимания.
Ты уже не восхищаешься отсутствием SQL. Ты начинаешь замечать сложность сопровождения, неожиданные побочные эффекты, необходимость помнить о десятках нюансов ORM. Тебе вдруг нужно знать этот самый SQL, который убегает в базу, чтобы разобратся в каком-то баге.
Пока это лишь отдельные мысли. Отдельные капли сомнений.
Каждая сама по себе ничего не меняет.
Но однажды наступает момент, когда последняя капля переполняет чашу.
У меня этот процесс начался с двух проектов.
Один был мой — на Hibernate. Я им искренне восхищался.
Второй был написан на обычном JDBC.
Каждый запрос выглядел примерно одинаково:
String sql = "select...";PreparedStatement ps = ...ResultSet rs = ...model.setX(rs.getX(...));model.setY(rs.getY(...));
Честно говоря, это выглядело ужасно. Попахивает утечками ресурсов.
Кринжово.
Допотопно.
Будто после современного автомобиля тебя посадили за трактор.
Но был один нюанс.
SQL-проект вся команда считала гораздо проще моего. Хотя он был почти в три раза больше. Я специально считал количество экранных форм и таблиц в базе.
И самое интересное — на том проекте джуниорам спокойно разрешали коммитить практически сразу. Никто особенно не боялся, что они что-нибудь сломают.
И они действительно почти ничего не ломали.
А к моему проекту на Hibernate разработчиков ниже уровня Middle старались вообще не подпускать.
Эта мысль тогда крепко засела у меня в голове, и дала парочку капель для будущего переполнения чаши.
Потом произошёл другой случай.
Бизнес попросил добавить очень простую функцию. По ID нужно было закрыть активность — просто установить один флаг в true. Код получился буквально элементарным. Я загрузил сущность через load(), выставил поле, а дальше Hibernate сделал всё сам. Нужен был всего один тест и он прошёл нормально. Ручное тестирование прошло нормальноё. Выкатили в прод.
Через две недели бизнес пришёл с претензией.
Не все активности закрывались.
Я был абсолютно уверен, что проблема не в моём коде.
Ну что там может не работать?
Я даже сказал бизнесу:
— Скорее всего, операторы иногда просто не нажимают на кнопку.
Бизнес сначала согласился.
Но потом опять вернулся.
Уже очень злой. Сеньёр написал один метод из одной строки и это не работало. Все посматривали на меня с немым вопросом. То ещё ощущение.
Я обложил всё логами. Выяснилось, что операторы действительно нажимали кнопку.
Но флаг иногда… не сохранялся.
Почему?
Я понятия не имел.
На меня смотрели все.
Для бизнеса эта функция напрямую влияла на деньги, поэтому давление было колоссальным и решить эту задачу нужно было немедленно.
Единственное решение, которое пришло мне в голову, — переписать эту функцию на чистом JDBC, с простым SQL-ем: update… where id=?.
Полчаса работы.
Выкатили.
И проблема исчезла.
Навсегда.
Позже я всё-таки разобрался.
Оказалось, что load() загружал всю сущность, а при коммите Hibernate сохранял обратно все её поля. Если параллельно кто-то изменял эту же запись, происходила потеря обновления. Моё изменение откатывалось другим сохранением.
Нужно было всего лишь поставить @DynamicUpdate.
Я знал про эту аннотацию. Но под давлением просто не вспомнил о ней.
Казалось бы, вывод очевиден.
Я просто получил новый опыт. Теперь я знаю, что в подобных случаях нужно ставить @DynamicUpdate. Стал ещё лучше разбираться в Hibernate. Значит, стал сильнее как разработчик.
Круто.
Именно так я тогда и должен был подумать.
Но мысли были совсем другими.
Не то, что-то с таким подходом. Не доложно быть так. Не может нормальная технология лажать в методе из одной строки. Почему для обновления одного булевого поля вообще нужно знать про существование @DynamicUpdate? Почему простейшая операция зависит от таких нюансов? Почему, чтобы система работала надёжно, я должен помнить десятки подобных особенностей?
И тогда в голове впервые прозвучал вопрос, который потом уже не давал мне покоя:
А зачем вся эта сложность?
Все знают одно из главных преимуществ Hibernate: он якобы практически не зависит от конкретной СУБД. Поменял диалект, параметры подключения — и приложение готово работать с другой базой данных. По крайней мере, именно так это обычно преподносят. Я тоже в это верил.
И тут произошло самое интересное.
Оба этих проекта нужно было переводили с Oracle на PostgreSQL.
Казалось бы, результат очевиден. Я был уверен, что мой проект мигрирует быстро и почти безболезненно, а разработчики SQL-проекта будут долго переписывать запросы и разбираться с различиями между СУБД.
Реальность оказалась ровно противоположной.
SQL-проект вышел в промышленную эксплуатацию уже через полгода. Мой проект на Hibernate — только через год. И это при том, что SQL-проект был примерно в три раза больше по объёму.
Причина оказалась простой. В SQL-проекте разработчикам нужно было лишь адаптировать SQL-запросы под новую СУБД. Да, часть запросов пришлось переписать, но это была понятная, локальная и достаточно быстрая работа.
В проекте на Hibernate всё оказалось иначе. Вместо обещанной независимости от СУБД пришлось разбираться с множеством особенностей ORM: несовместимостью HQL-запросов, изменившимися планами выполнения, падением производительности, нюансами диалектов и другими эффектами, скрытыми за «магией» Hibernate.
В итоге проект, который по объёму был значительно меньше, мигрировался почти в два раза дольше. И это в абсолютном аспекте — помните SQL-ый проект в три раза больше.
На практике всё оказалось совсем не так. Просто поменять диалект и параметры подключения к базе данных не получилось.
Из-под всей этой «магии» Hibernate сразу начали всплывать подводные камни. Например, в Oracle связка Query + HQL работала нормально, а в PostgreSQL — уже нет. Многие запросы, которые раньше казались вполне приемлемыми, стали работать непозволительно медленно, и их пришлось вручную анализировать и оптимизировать. Всплыли различия в диалектах, особенностях генерации SQL и поведении самого ORM.
И это лишь несколько примеров. Вместо обещанной независимости от СУБД мы получили длительную и трудоёмкую адаптацию приложения к новой базе данных.
Получается парадоксальная ситуация: технология, которая должна была скрыть различия между СУБД, на деле сама стала источником дополнительных проблем при миграции.
И тут я наконец прозрел: а зачем вообще нужна вся эта сложность, если её можно просто убрать и сделать систему на порядок проще?
Часто говорят, что писать SQL вручную и маппить ResultSet в модели через getXXX()/setXXX() — скучно и неудобно. Возможно. Но это всего лишь эмоции разработчиков.
Возникает вопрос: зачем за эти эмоции платить бизнесу? Зачем увеличивать стоимость разработки и особенно сопровождения только ради того, чтобы не писать несколько десятков строк понятного кода?
На мой взгляд, если технология требует изучения огромного количества внутренней механики, усложняет отладку, многопоточность и сопровождение, то она должна давать соразмерную выгоду. Если же выигрыш ограничивается тем, что разработчик пишет меньше SQL и меньше шаблонного кода, возникает закономерный вопрос: оправдывает ли это те дополнительные затраты, которые в итоге несёт бизнес?
Разработка в проекте занимает всего около 10% общего времени, поэтому даже заметное ускорение этого этапа мало что меняет. Да и само ускорение — ещё большой вопрос. Зато на сопровождение Hibernate уходит огромное количество времени. Он заставляет изучать весьма сложную технологию со всеми её EntityManager’ами, flush’ами, жизненным циклом сущностей и особенностями работы @Transactional. А когда в проекте появляется многопоточность, всё становится на порядок сложнее.
И, конечно, здорово, когда ты во всём этом разбираешься. Но неизбежно возникает вопрос: а зачем? Зачем тратить столько времени на изучение инфраструктуры, которая не решает бизнес-задачи, а лишь обслуживает саму себя?
А теперь давайте вспомним, что мир изменился.
Появились ИИ-агенты.
У них нет эмоций. Они не считают написание SQL унизительной или скучной работой. Они могут за секунды написать хоть десять, хоть сто запросов. Причём именно под вашу схему данных.
И самое главное — писать их теперь будете не вы.
Вам останется только прочитать код, проверить, что он делает именно то, что нужно, и при необходимости немного подправить.
Получается любопытная вещь.
Ещё совсем недавно одним из главных аргументов в пользу Hibernate было избавление разработчика от рутинного написания SQL.
Но если рутину теперь выполняет ИИ практически бесплатно, то этот аргумент начинает стремительно терять силу.
И тогда возникает закономерный вопрос.
Если SQL больше не является проблемой, то ради чего мы продолжаем платить за всю сложность Hibernate? За его внутреннюю магию, скрытые механизмы, нюансы жизненного цикла сущностей, flush, merge, @Transactional, @DynamicUpdate и десятки других особенностей?
В этот момент призрачная необходимость в Hibernate, если она ещё оставалась после всего сказанного, исчезает в ноль, от слова совсем.
За годы работы я понял одну простую вещь: самое дорогое в проекте — не разработка, а его сопровождение и развитие. А для этого важно, чтобы система ломалась как можно реже. И здесь действует почти универсальное правило: чем система проще, тем она надёжнее.
Hibernate — очень сложная технология, под капотом которой скрывается огромное количество внутренней механики. И проблемы могут возникнуть где угодно: в жизненном цикле сущностей, стратегиях загрузки, кэше, flush, транзакциях, особенностях dirty checking, генерации SQL, аннотациях, о которых легко забыть, и ещё десятках мест, не имеющих никакого отношения к бизнес-логике.И каждый раз, когда возникает очередная такая проблема, невольно задаёшь себе один и тот же вопрос.
А зачем?
Зачем усложнять систему, если можно сделать её проще?
Для себя я на этот вопрос уже ответил.
А появление ИИ лишь окончательно убедило меня, что возвращаться назад смысла уже нет.
От слова «совсем».
ссылка на оригинал статьи https://habr.com/ru/articles/1050990/