Lazy Init или принцип Open session in view

By | June 22, 2012

В самом начале своего нелегкого пути по изучению Hibernate и фреймворка Spring для Java столкнулся с проблемой возникновения некого LazyInitException, когда пытался получить некий объект, ссылку на инстанс которого содержал другой, полученный от хибернейта.

Как всегда, горел очередной проект и читать документации особо было некогда, поэтому, пробежавшись быстро по поисковикам, уловил основной смысл данного поведения – при описания меппинга одного объекта по-умолчанию все его связи с другими объектами воспринимаются Hibernate как “ленивые”. Иными словами, при наполнении данными объекта Group, который, к примеру, содержит в себе List users и связан с таблицей USER как One-To-Many, Hibernate не пойдет по этой связи наполнять данными список. Сделает он это лишь тогда, когда мы обратимся к этому списку. Здесь то и кроется корень проблемы – в моем приложении обращение к списку шло в тот момент, когда хибернейт сессия работы с базой, в которой я получил объект Group была уже закрыта. Отсюда и LazyInitException.

Времени разбираться глубже не было, поэтому поставил в меппингах атрибут lazy = false (в терминах hibernate annotations – @OneToMany(fetch = FetchType.EAGER)), заставив таким образом Hibernate бегать по всем объектным связям и заполнять все данными. К тому же, специфика приложения позволяла идти на такие шаги. Если же в вашем приложении множество релятивных связей между таблицами (особенно Many-To-Many) и сами таблицы содержат немало данных, то это, конечно же, не самый лучший вариант решения проблемы, т.к. при попытке получить объект Group, вы также заставите Hibernate получить всех User данной группы, которые в свою очередь могут ссылаться еще на какие-либо иные объекты. В результате простейшая операция может занять намного больше времени и, что не мало важно, ресурсов, чем нужно.

Правильным решением данной проблемы будет все же оставление lazy = true (@OneToMany(fetch = FetchType.LAZY)) и поиск путей поддерживания открытой сессии на протяжении работы контроллера. Hibernate предлагает для решения данной проблемы метод Open Session In View Design Pattern. Смысл его достаточно прост – вы реализуете фильтр в своем веб-приложении, который на каждый http request будет открывать сессию, затем выполнять всю логику вашего приложения, и по завершению цепочки фильтров – закрывать сессию.

В моем приложении использовался Spring Framework, поэтому фильтр писать не пришлось. Ребята, разработавшие данный фреймворк уже все предусмотрели. Их решение называется OpenSessionInViewFilter. Все, что нужно было сделать – это описать фильтр в web.xml:

mySessionFactory необходимо заменить на ваше значение.
Ну и соответственно фильтр-меппинг: