Mamon ([info]m_a_m_o_n) wrote,

Hibernate Dooms Your Code

This thoughts come from here.

Первые базы данных были иерархическими. Это было отстойно.
Был только один путь работы с данными, в порядке их хранения.
Любая попытка изменить точку зрения на данные, это тонны ручной
работы. Потом появились реляционные базы данных и SQL
и это барьер исчез. С SQL не важно как хранятся данные,
их можно выбрать в любом виде, в любом количестве, и в любой форме.

Принципиально, SQL даёт возможность работать с данными представленными
в совершенно произвольной структуре, - в том виде в котором данные
нужно для работы. Для чтения совершенно не важно как данные
хранятся, в одной таблицу, двух, трёх, в N строках одной таблицы.
Можно использовать сырые данные, можно их связывать,
можно их агрегировать.

Более того, SQL возволяет редактировать данные в произвольном
формате, а не в том в котором они реально хранятся в базе.
Если нужно обновить 2 поля - не нужно перезаписывать
все 44 старыми значениями. Это не вопрос производительности.
Это вопрос понимания того что происходит. Есть рызница между
методами loadMoneyIntoAccount() и save().

SQL даёт возможность читать данные не в том формате
в котором они хранятся. SQL даёт возможность изменять
данные не в том формате в котором они хранятся.
И - те данные которые мы считываем - совершенно не те
данные которые мы записываем. То есть да, они могут
совпадать, но это будет вырожденный случай - это
будет просто означать что данные не содержать в себе
логический связей - то есть в приложении нет логики,
оно просто читает набор полей и записывает тот же набор.

Предположим, что логика всё таки есть.

И тут важно - что бы логика работала без ошибок.
Но что для этого нужно?

Immutable State.

Если что то не изменяется - на это можно положиться.
Неизменные данные гарантируют что логика на них
построенная не будет разрушена каким то логически
не связанным кодом, который вдруг выпрыгнул из
за угла и что то поменял в entity bean.

И это логично на предыдущую модель.
Читать из базы Immutable State в том формате в котором
удобно логике приложения, в не в том в котором кто то
придумал распихать их по таблицам. Собирать изменения
в в группы - тоже Immutable. Дальше сохранять изменения
уже так как это нужно с точки зрения логики, в том порядке
и в той форме.

К чему я всё это.



Что "даёт" Hibernate (ActiveRecord, what ever).
0. Mutable данные.
1. Жёсткий маппинг таблиц базы на бины данных, зато быстро.
2. Абстрагированный от базы язык запросов.
3. Отложенное сохранение.
4. Ленивую подгрузку данных.
5. Кеширование бинов данных.
7. Сессию.


Но как это работает в действительности.

Жёсткий маппинг - мы вынуждены работать с данными
в том виде в котором они хранятся. Почему? Читать и
записывать данные можно в одном и том же формате,
как будто у них нет структуры. Но она есть!

Абастрагирование от базы данных - смысл?
Оно же всё равно не работатет. Код полностью
абстрактный от базы - это нечто тупое как бревно.
Если он упрётся в проблемы с производительностью
смена базы решит её? Нет. Я вообще сомневаюсь
что смена базы может решить какие то проблемы.
Разве что вы до сих пор сидите на Interbase.

В итоге всё равно там где нужно сделать реально
сложный вопрос пишется SQL, который всё равно
переделывать при смене базы. Зато логика работы
с базой оказывается разбросанной по всему коду - где
только можно сохранять бины. Вот это самое стрёмное.

Отложенное сохранение. Эээ, вообще то оно нужно для того
что бы сохранять иерархии объектов. Когда мы хотим
добавить существующий чилд к несуществующему
паренту - например. Т.е. это решение проблемы вызванной
тем что структуры для сохранения это то те же структуры
что и для чтения. Спасибо тебе Hibernate.

Ленивая подргузка данных - О ДА, МЫ МОГЁМ.
Давайте сначала запамим базу 1-1 на бины,
потом завяжем их связями так тесно что загрузка
одного объекта потребует регурсивно выгрести
из базы ВООБЩЕ ВСЁ ЧТО С НИМ СВЯЗАНО.
А потом посмотрим на это и скажем, "хммм, что то тормозит".
И тут к нам приходит спасение в виде ленивой загрузки.
Лично я не просил.

Кеширование в Hibernate. Честно, ни разу не видел что бы
у Hibernate кэш был включен. Я видел как его пытались
включать. Но не надолго. Как минимум потому что не всё идёт
через Hibernate. Что то сохрянется для скорости через
хранимики, или есть отдельная нода с фоновыми задачами,
или что угодно. А кэш нужно инвалидировать. Это так сложно
тестировать. А без него всё работает и так быстро.
Так ну его нафиг.

Нет, если кто то реально использовал хибернейтовский кэшь,
и от него не было больше вреда чем пользы, - может быть я не прав.

Сессия - да, сессия нужна для кеширования, а ещё для ленивой
подгрузкуи. Есть одна проблема. Для логики приложения
она нафиг не нужна. И либо её нужно тащить через все бизнес-методы
чего никто не делает, либо делать её thread-local - тогда вся логика
работы с данными оказывается заперта в одной нитке.
Спрашивается - чего это, почему я не могу распарралелить логику.


Да, ещё один момент про энтити-бины. Поскольку они
используются не только для представления но и для
редактирование - есть большой соблазн реализовать
логику редактирования прям в контроллере (MVC).
Бизнес логика - ну нафиг. Значит мы делаем ВЬЮ,
модель это значит энтити бины эти, а в контроллере
мы что - выставить чек-бокс, проверить что имя не пустое,
сохранить данные кредитки. И желательно что бы до
и после session.save() было как можно больше
интерфейсного кода в одном методе.
А ТО НЕ ИНТЕРЕСНО БУДЕТ БИЗНЕС ЛОГИКУ
ТЕСТИРОВАТЬ, СЛИШКОМ ПРОСТО.


Что же получается.
Hibernate навязывает модель данных которая
потенциально является источником ошибок,
поскольку она изменяемая, из за этого же
приходится разбиратся в огромном количестве
подпорок вроде сесии и отложенного сохранения.

Что бы всё это хоть как то работало, придумывается
кэш который мало того что сложно использовать
так он и вообще не нужен поскольку прямая работа
с базой и так в 10 раз быстрее, и ленивая подгрузка
которая запирает объекты в сессии - и фиг ты передашь
из него данные куда ни попадя без загадочных телодвишений
типа .getChildsList();

К чему я всё это: используйте mybatis,
ну или JdbcTempate на худой конец.
Tags: fuck, hibernate, java

  • Post a new comment

    Error

    Your reply will be screened

    Your IP address will be recorded 

  • 24 comments

[info]magicprinc

July 9 2011, 04:05:53 UTC 10 months ago

А у EclipseLink те же самые проблемы?

[info]m_a_m_o_n

July 9 2011, 06:01:00 UTC 10 months ago

Да, можно сделать Search & Replace в тексте :D

[info]23derevo

January 2 2012, 22:14:14 UTC 4 months ago

Как я понимаю, JPA был сделан как ответ JCP хибернейту. Если, опять-таки, я правильно понимаю, EclipseLink - это имплементация JPA. А значит, грабли будут примерно те же, что и у хибернейта.

Впрочем, я по ORM не специалист, могу ошибаться.

[info]m_a_m_o_n

January 2 2012, 22:43:06 UTC 4 months ago

Насколько знаю я
1. JPA это ответ JCP, но не хибернейту а JDO (который суть тот же стандарт, только по старше и другими людьми)
2. Почти уверен что для первого JPA референсом был именно хибернейт, и точно что кто то из отцов основателей Хибернейта приложил (а может и до сих пор
прикладывает) руку к JPA.
3. EclipseLink стал референсом скорее из политических причин, и лично у меня не было положительного опыта связанного с EclipseLink.

P.S. На счёт граблей всё так, Hibernate просто самый популярный а в общем есть множество JPA/JDO совместимых
ORM фреймворков которые по сути построены по тому же принципу (см. пост).

[info]23derevo

January 3 2012, 01:41:00 UTC 4 months ago

1. Возможно. Я пока не силён в истории JCP.
2. Референсом был TopLink, насколько я помню. Который был слизан с хибернейта.
3. Не знаю, не интересовался))

По моему ощущению из всех ORM хибернейт - самый вменяемый. Но граблей там, как сказано в посте, вагон и маленькая тележка: привязка сущностей к таблицам, инвалидация кэша, проблема n+1 и т.д. Но это всё вроде общие проблемы ORM-фреймворков. Или нет?

[info]23derevo

January 3 2012, 01:43:38 UTC 4 months ago

Мы, наверное, слово "рефренс" по-разному понимаем. Я имею в виду RI - Reference Implementation. А Вы - образец для подражания. Так?

[info]m_a_m_o_n

January 3 2012, 08:41:31 UTC 4 months ago

Нет, одинаково.

[info]23derevo

January 3 2012, 08:51:45 UTC 4 months ago

ну тогда гляньте сюда:
http://en.wikipedia.org/wiki/TopLink
и сюда
http://en.wikipedia.org/wiki/Java_Persistence_API#Hibernate

Ту бишь, RI был TopLink, а в хибере поддержка появилась начиная с версии 3.2


[info]23derevo

January 2 2012, 20:47:53 UTC 4 months ago

в мемориз! За mybatis +1, за Spring-Jdbc +10.

кросскоммент в ru_java

[info]m_a_m_o_n

January 2 2012, 21:12:29 UTC 4 months ago

Спасибо. Лично я использую и mybatis и spring-jdbc.
spring-jdbc плох тем что запрос можно засунуть совершенно куда угодно,
чем и пользуются, в том числе я. С mybatis всё строго, логика запросов не
содержит в себе кода и вынесена отдельно от остального кода. Как минимум
это даёт возможность протестировать запросы отдельно.

[info]23derevo

January 2 2012, 21:18:59 UTC 4 months ago

круто! А чего такую интересную, не побоюсь этого слова, статью, не запостили в ru_java?

[info]m_a_m_o_n

January 2 2012, 21:46:22 UTC 4 months ago

http://ru-java.livejournal.com/1064429.html

Запостил, нужно же было хоть немного вычитать :)

[info]23derevo

January 2 2012, 22:09:08 UTC 4 months ago

ура!

"хоть немного" заняло полгода)) Жаль, сейчас именно выкинули. Я бы неделю подождал, пока народ из поездок вернётся на работу и войдёт в привычный режим. Хотя надеюсь, что и без того выйдет интересная дискуссия!

[info]dnovikoff

January 2 2012, 21:34:48 UTC 4 months ago

Эх, надо было мне раньше посмотреть на MyBatis. Когда выбирали - он показался мне странным, а вдумчиво не посмотрел.
Сейчас всё на Hibernate и безумно раздражает это постоянное таскание туда-сюда объектов не пойми в каком состоянии.
Спасибо за статью! Вы дали мне повод посмотреть на MyBatis и удивиться собственному идиотизму )

[info]23derevo

January 2 2012, 22:09:38 UTC 4 months ago

ещё на Spring-JDBC посмотрите!

[info]dnovikoff

January 2 2012, 22:37:09 UTC 4 months ago

Спасибо, посмотрю =)

P.S. Может вы ещё и веб-фреймворк подскажете? В плане декорирования мне очень нравится SiteMesh своей простотой. А вот MVC вменяемого я, честно говоря, не обнаружил. Struts - монстр, Spring MVC в принципе разумен и прост, но там масса неочевидных граблей, которые лениво разгребать. Вот чтобы просто сайт сделать с примитивной заточенной под задачи CMS и без всяких извратов, просто JSP в качестве view. Хочу тут просто сайтец один забацать, очередной раз подумал про фреймворк и меня посетила ТОСКА %)

[info]23derevo

January 3 2012, 01:35:33 UTC 4 months ago

про MVC поищите по сообществу ru_java. Периодически (примерно раз в год) проявляется пост про MVC с обилием каментов. Например, вот: http://ru-java.livejournal.com/906514.html#comments


Плюрализм в этой теме жив и здравствует!

[info]dnovikoff

January 2 2012, 22:38:42 UTC 4 months ago

Но, кстати, мы от Hibernate ни кэш, ни lazy loading, ни прочих извратов не используем. Только Criterion API, так что по идеологии очень похоже, только на MyBatis оно прямее получается.

[info]c0s

January 4 2012, 21:10:15 UTC 4 months ago

в случае с БД oracle все эти criterion api пьют чай в сторонке, т.к. динамику можно реализовать в pipelined-функциях, как минимум, 3мя способами, все из которых oracle-ом же и придуманы, т.е. лучше них ничего представить не получается.
postgre, как я понимаю, хоть и отстаёт, но тоже позволяет нечто похожее, только меньшим разнообразием своего инструментария.
остальные БД - звери в себе, и понять их адептов у меня не получается, хотя довелось повнедрять проекты и с mssql, и с mysql.

[info]dma_k

January 5 2012, 22:51:13 UTC 4 months ago

Может ли так быть, что Hibernate хорошо в определённом приложении. Например, есть таблица documents, имеющая связь один-ко-многим с таблицей chapters, имеющая связь один-ко-многим с таблицей paragraphs, имеющая связь один-ко-многим с таблицей annotations, имеющая связь многие-к-одному с таблицей users. В каждой таблице в среднем 5 полей. Невооружённым взглядом видно, что писать это на JdbcTempate – неблагоразумно: пять таблиц, на каждую написать три SQL запроса для вычитки/вставки/удаления. Опять-таки, дерево объектов большое: нужен lazy loading. Хорошо, как можно без него? Для Hibernate API для DAO может выглядеть так:
interface MyDao
{
    Document loadDocument(int documentId);
    void saveDocument(Document document);
}

и все дочерние объекты лениво подтягиваются автоматически. Как будет выглядеть API для Mybatis?
interface MyDao
{
    Document loadDocument(int documentId);
    List loadChaperts(int documentd);
    List loadParagraphs(int chapterId);
    List loadAnnotations(int paragraphId);
    ...
}

и это ещё не совсем понятно, как сохранять document.

И ещё одна вещь, которая непременно пригодиться. Предположим, что в production у нас используется MySQL, а для UnitTest'оы - HSQL. И например, в MySQL текущая дата возвращается функцией now(), а в HSQL - current_date. И как написать SQL, чтобы выполнялся и там, и там? В Hibernate эта возможность заложена в HQL.

[info]m_a_m_o_n

January 6 2012, 12:03:53 UTC 4 months ago

Имено такой пример я бы привёл в пользу mybatis/jdbctemplate. Домен маленький, операции редактирование разнообразные, выборки специфичные.

[info]dma_k

January 9 2012, 01:17:10 UTC 4 months ago

То есть, на каждую сущность надо доабвлять пару-тройку методов в интерфейс. Как-то неудобно. Ну а как бытть с методом Document.getChapters()? Или его не должно быть в модели, что, например мне, неудобно (и в этом случае модель дейстительно становиться тенью БД, а не бизнес-моделью), или объект Document должен имет ссылку на MyBatis/JDBCTemplate, чтобы подтянуть данные (и в этом случае надо опять изобретать вилосипед и коодировать обработку сессий / соединений с БД и т.п.).

Ну и ещё один камушек на весы Hibernate: это JSR-303. Для тех, кто серьёзно подошёл к дизайну модели, возможность валидировать её на UI и на уровне DAO несомненный плюс. Без соответствующей поддержки это опять-таки, придётся кодить самому.

[info]magicprinc

January 9 2012, 15:05:20 UTC 4 months ago

Взгляните какую интересную штуку нашел

http://www.avaje.org/ebean/documentation.html

О! Оказывается идет в поставке playframework 2.0

[info]m_a_m_o_n

January 9 2012, 18:33:25 UTC 4 months ago

Мне кажется эта штука слишком упрощена,
запросы через статические методы - большая загадка как работать
с двумя базами.
Create an Account
Forgot your login or password?
Facebook Twitter More login options
English • Español • Deutsch • Русский…