Java на habrahabr

Январь 16, 2012

 

JAVA / Hibernate cache

Довольно часто в java приложениях с целью снижения нагрузки на БД используют кеш. Не много людей реально понимают как работает кеш под капотом, добавить просто аннотацию не всегда достаточно, нужно понимать как работает система. Поэтому этой статье я попытаюсь раскрыть тему про то, как работает кеш популярного ORM фреймворка. Итак, для начала немного теории.

Прежде всего Hibernate cache — это 3 уровня кеширования:

  • Кеш первого уровня (First-level cache);
  • Кеш второго уровня (Second-level cache);
  • Кеш запросов (Query cache);

Кеш первого уровня

Кеш первого уровня всегда привязан к объекту сессии. Hibernate всегда по умолчанию использует этот кеш и его нельзя отключить. Давайте сразу рассмотрим следующий код:
SharedDoc persistedDoc = (SharedDoc) session.load(SharedDoc.class, docId); System.out.println(persistedDoc.getName()); user1.setDoc(persistedDoc); persistedDoc = (SharedDoc) session.load(SharedDoc.class, docId); System.out.println(persistedDoc.getName()); user2.setDoc(persistedDoc);
Возможно, Вы ожидаете, что будет выполнено 2 запроса в БД? Это не так. В этом примере будет выполнен 1 запрос в базу, несмотря на то, что делается 2 вызова load(), так как эти вызовы происходят в контексте одной сессии. Во время второй попытки загрузить план с тем же идентификатором будет использован кеш сессии.
Один важный момент — при использовании метода load() Hibernate не выгружает из БД данные до тех пор пока они не потребуются. Иными словами — в момент, когда осуществляется первый вызов load, мы получаем прокси объект или сами данные в случае, если данные уже были в кеше сессии. Поэтому в коде присутствует getName() чтобы 100% вытянуть данные из БД. Тут также открывается прекрасная возможность для потенциальной оптимизации. В случае прокси объекта мы можем связать два объекта не делая запрос в базу, в отличии от метода get(). При использовании методов save(), update(), saveOrUpdate(), load(), get(), list(), iterate(), scroll() всегда будет задействован кеш первого уровня. Собственно, тут нечего больше добавить.

ru.java на livejournal

Январь 2, 2012

 

Hibernate Dooms Your Code

Original post: http://m-a-m-o-n.livejournal.com/302161.html
Promoted by 23derevo

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 на худой конец.

Java на habrahabr

Ноябрь 11, 2011

 

JAVA / Hibernate для самых маленьких и не только

Доброго всем времени суток! При написании программы, которая так или иначе будет взаимодействовать с базой данных, пользуются разными средствами. Это и старый добрый jdbc, также применяют: EclipseLink,TopLink, iBatis (уже MyBatis), Spring Framework и конечно же герой нашей статьи — Hibernate. Конечно я здесь перечислил не все средства работы с базой данных, но постарался указать самые распространенные. В данной статье будет показано, как при помощи Hibernate вызывать хранимые процедуры, маппить как таблицы, так и запросы к классам. В качестве подопытной базы данных возьмем Oracle.

Блог о программировании

Сентябрь 25, 2011

 

OrderBy или OrderColumn

Между JPA аннотациями OrderBy и OrderColumn существует огромная разница.

@OrderBy - аналог обычной SQL команды ORDER BY. Здесь все понятно. Например у вас отношение один ко многим. Для примера возьмем сущность "Новость" и к ней привязано много "Комментариев". На уровне объектной модели, это значит, что у вас есть объект класса News, у которого есть метод который возвращает список комментариев:

+-----------------------------+ |News | |-----------------------------| |-----------------------------| |+getComments(): List<Comment>| +-----------------------------+ +-----------------+ |Comment | |-----------------| |-----------------| |+getNews(): News | +-----------------+

В таком случае, мы должны указать в классе News:

// аннотации public class News { // много кода @OneToMany(targetEntity = Comment.class, mappedBy="news") @OrderBy(value="pubdate desc") private List comments; // много кода }

Причем сортировка должна быть сделана на уровне БД, через ORDER BY выражение. В данном примере мы сортируем комментарии, по дате их публикации (поле pubdate).

@OrderColumn - задает другой способ оказания порядка (материализованный). В этом случае порядковый номер записи указывается явно в таблице. В Hibernate была аналогичная аннотация @IndexColumn , ее использовали до выхода JPA 2.0. Сейчас по возможности лучше вместо @IndexColumn использовать @OrderColumn. В Hibernate поддерживаются обе.

Разберем пример. Допустим у вас также отношения один ко многим, но в этом случае возьмем сущность Дом в котором живут несколько Постояльцев. Допустим в этом доме в 1-ом номере остановились Ивановы, во 2-ом Петровы, с 3 и 4 - пустуют, в 5 - Кузнецовы. Для того, чтобы сохранить информацию кто в каком номере живет, мы явно указываем в табличке с жильцами номер комнаты (поле room).

+-------------------------------+ |House | |-------------------------------| |-------------------------------| |+getResidents(): List<Resident>| +-------------------------------+ +----------------------+ |Resident | |----------------------| | room:Integer | +----------------------+

В таком случае, мы пишем в классе House:

// аннотации public class House { // много кода @OneToMany(targetEntity = Resident.class, mappedBy="house") @OrderColumn(name="room") private List<Resident> residents; // много кода }

Внимание! Теперь самое важное. Если в данном случае использовать OrderColumn, то у нас будет список, в котором будут дыры! С другой стороны, мы можем сразу по порядковому номеру в списке определить номер комнаты.

0 - null 1 - Ивановы 2 - Петровы 3 - null 4 - null 5 - Кузнецовы

Отличие от OrderBy фундаментальное!

Если вы неправильно спроектируете зависимости и укажите в первом примере (новости - комментарии) вместо OrderBy аннотациюOrderColumn по дате публикации, то у вас может возникнуть явная ошибка (зависит от БД, ORM и т.д.), а может произойти неявная конвертации поля даты в число.
Например в MySQL + Hibernate это может сработать так. Дата публикации 01.01.2011 - преобразуется в число 2011, в итоге получаем список из null-ов, а в конце после 2011-ной записи - комментарии.

Java на habrahabr

Сентябрь 22, 2011

 

JAVA / Open Source RIA на Spring Framework'e


RIA — Rich Internet Application.
Spring Framework — каркас приложения.
Open Source — открытый исходный код. Бери и пользуйся.
О том какое RIA приложение разработано, где можно его посмотреть и взять исходники под хабракатом.

Java на habrahabr

Сентябрь 17, 2011

 

JAVA / Модульное веб-приложение с использованием CGLib


Вступление


В процессе разработки одного из проектов мы достигли той точки, когда новый функционал так или иначе задевает старый.
Каскадные изменения десятка файлов ради нескольких новых кнопочек на форме — что может быть ужасней?
На общем собрании команды было принято волевое решение — переписать!
Была разработана модульная структура, которая позволяет добавлять функционал в виде отдельных модулей, не трогая при этом ни ядро, ни другие подсистемы, с возможностью hook ( aka callback )-функций, для реализации различной пре- или пост- обработки.
Итак, базовый набор:
Apache Tomcat 7.0.21, Hibernate 3, CGLib 2, JSF 2.0

ru.java на livejournal

Сентябрь 15, 2011

 

ИИ кеш для hibernate

Существуют такие в природе, вообще? Не говоря уже о совместимости с hibernate. Нужна система, которая сама будет анализировать на основе выданной ей памяти, какие объекты и как кешировать. Не нужно мега ИИ, но хотя бы на простейшем уровне, для обычного веб-проекта. Задолбался просто с этим EhCache вводить значения от балды.

ru.java на livejournal

Апрель 21, 2011

 

@TransientOverride

Есть такой Hibernate конфиг.

@Embeddable class Foo { @Column String firstName; @Column String lastName; }

@Entity class Bar { @Id Long id; @Embedded @AttributeOverrides( { @AttributeOverride(name="firstName", column = @Column(name="first_name") ), @AttributeOverride(name="lastName", ??? ) } ) Foo foo; }

Как сделать Bar::lastName – @Transient?

Подобное обсуждение: Embedded, AttributeOverrides and Transient properties

Java на habrahabr

Февраль 14, 2011

 

JAVA / Multitenancy через JPA+Hibernate

Центральной идеей SaaS-модели обслуживания является очень простой фокус. Нужно всего лишь запустить для всех пользователей один экземпляр приложения и этим шагом категорически снизить все мыслимые затраты на инфраструктуру. Такой принцип “коммунальной квартиры” — одна из основных особенностей SaaS; она называется multitenancy.

Эта простая идея на практике оборачивается замысловатой реализацией. Замысловатой в силу того, что хранилище данных для SaaS-приложения должно поддерживать хранение данных всех пользователей экземпляра приложения. Один из вариантов решения этой задачи — это использование одной базы данных для всех пользователей.

Но такой «ночной кошмар программиста» тяжело развивать и поддерживать. Тяжело из-за того, что весь бизнес-код должен быть построен с учетом контекста выполнения. Из контекста выполнения приложение должно знать о том, для кого выполняется в настоящий момент код и использовать это знание для работы с хранилищем данных. А работа с хранилищем данных в терминах реляционной СУБД — это запросы SQL. В небольшом приложении их немного и издержками на их поддержание можно пренебречь, создав более-менее стабильную обертку. Но в приложении со сложной бизнес логикой необходимо построить масштабируемое решение, которое бы скрывало от разработчика multitenancy настолько, насколько это является возможным.

Итак, что у нас есть? J2EE приложение, использующее для работы с БД прослойку в виде JPA и его реализация — Hibernate. Эти замечательные программные средства содержат все средства для того, чтобы сделать жизнь программиста бизнес-логики беззаботной, а архитектора — насыщенной. Ниже вы найдете описание необходимых для этого шагов.

ru.java на livejournal

Февраль 9, 2011

 

вопрос по хибернейту, а точнее по связи many-to-one и леннивой загрузкой
Итак, имеем некое дерево в котором каждая нода имеет описвающий ее чайлдов и связь many-to-one для обьекта-родителя. Для many-to-one прописано lazy="proxy" (и еще not-found="ignore"), но при получении нод из БД, отдельнми селектами извлекаются все родители. Но ведь я указал lazy="proxy" что вполне логично должно приводить к ленивой загрузке, не так ли? в нете нашол кучу похожих вопросов на форумах но ни одного решение, также бага в 3.0. У нас используется 3.2 в которой уже должно пофиксано
Подскажите, пожалуйста, как заставить хибернейт получать родителей все таки по запросу, а не сразу?

 
<< < 1 из 2 > >>