2012-01-09 3 views
0

Я использую wicket и hibernate (jpa) на простой webapp.
Task.java связан с Load.java следующим образом:LazyInitialization Exception with wicket/hibernate

Task.java:

@ManyToOne(targetEntity=Load.class, optional=true, fetch=FetchType.LAZY) 
@JoinColumn(name="load_id") 
public Load getLoad() { 
    return load; 
} 

Одна задачи может иметь максимум одну нагрузки, но одна нагрузки может отображаться на многие задачи:
Load.java:

@OneToMany(mappedBy="load",targetEntity=Task.class, orphanRemoval=false) 
public Set<Task> getTasks() { 
    return tasks; 
} 

Заседание (EntityManager в JPA говорит) создается и прикреплен к нити с помощью фильтра транзакции.

У меня есть страница, в которой перечислены задачи и показаны первые 10. Когда я нажимаю на ссылку на следующие 10, она пытается загрузить Load for Tasks 11-20 (используя getLoad) и почему-то бросает исключение LazyInitializationException - хотя там - это действительный сеанс для этого потока (как видно из трассировки стека и отладки). Я не могу использовать загрузку EAGER, так как это может вызвать проблемы в других местах. Я могу проверить, что entitymanager действителен в конструкторе LinkPanel, и он действительно действителен. Однако через 3 строки он вызывает исключение LazyInitializationException. Что происходит?

- could not initialize proxy - no Session 
org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167) 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215) 
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) 
    at com.xxx.er.batch.beans.Load_$$_javassist_7.toString(Load_$$_javassist_7.java) 
    at com.xxx.er.basman.model.LinkPanel.<init>(LinkPanel.java:41) 
    at com.xxx.er.basman.pages.TasksOverviewPage$7.populateItem(TasksOverviewPage.java:130) 
    at org.apache.wicket.extensions.markup.html.repeater.data.grid.AbstractDataGridView.populateItem(AbstractDataGridView.java:187) 
    at org.apache.wicket.markup.repeater.RefreshingView$1.newItem(RefreshingView.java:114) 
    at org.apache.wicket.markup.repeater.DefaultItemReuseStrategy$1.next(DefaultItemReuseStrategy.java:71) 
    at org.apache.wicket.markup.repeater.DefaultItemReuseStrategy$1.next(DefaultItemReuseStrategy.java:68) 
    at org.apache.wicket.markup.repeater.RefreshingView.addItems(RefreshingView.java:189) 
    at org.apache.wicket.markup.repeater.RefreshingView.onPopulate(RefreshingView.java:98) 
    at org.apache.wicket.markup.repeater.AbstractRepeater.onBeforeRender(AbstractRepeater.java:131) 
    at org.apache.wicket.markup.repeater.AbstractPageableView.onBeforeRender(AbstractPageableView.java:121) 
    at org.apache.wicket.Component.internalBeforeRender(Component.java:1066) 
    at org.apache.wicket.Component.beforeRender(Component.java:1100) 
    at org.apache.wicket.MarkupContainer.onBeforeRenderChildren(MarkupContainer.java:1754) 
    at org.apache.wicket.Component.onBeforeRender(Component.java:3966) 
    at org.apache.wicket.Component.internalBeforeRender(Component.java:1066) 
    at org.apache.wicket.Component.beforeRender(Component.java:1100) 
    at org.apache.wicket.MarkupContainer.onBeforeRenderChildren(MarkupContainer.java:1754) 
    at org.apache.wicket.Component.onBeforeRender(Component.java:3966) 
    at org.apache.wicket.Component.internalBeforeRender(Component.java:1066) 
    at org.apache.wicket.Component.beforeRender(Component.java:1100) 
    at org.apache.wicket.MarkupContainer.onBeforeRenderChildren(MarkupContainer.java:1754) 
    at org.apache.wicket.Component.onBeforeRender(Component.java:3966) 
    at org.apache.wicket.Page.onBeforeRender(Page.java:1550) 
    at org.apache.wicket.Component.internalBeforeRender(Component.java:1066) 
    at org.apache.wicket.Component.beforeRender(Component.java:1100) 
    at org.apache.wicket.Component.prepareForRender(Component.java:2292) 
    at org.apache.wicket.Page.prepareForRender(Page.java:1540) 
    at org.apache.wicket.Component.prepareForRender(Component.java:2329) 
    at org.apache.wicket.Page.renderPage(Page.java:911) 
    at org.apache.wicket.protocol.http.WebRequestCycle.redirectTo(WebRequestCycle.java:201) 
    at org.apache.wicket.request.target.component.PageRequestTarget.respond(PageRequestTarget.java:58) 
    at org.apache.wicket.request.AbstractRequestCycleProcessor.respond(AbstractRequestCycleProcessor.java:105) 
    at org.apache.wicket.RequestCycle.processEventsAndRespond(RequestCycle.java:1258) 
    at org.apache.wicket.RequestCycle.step(RequestCycle.java:1329) 
    at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1436) 
    at org.apache.wicket.RequestCycle.request(RequestCycle.java:545) 
    at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:486) 
    at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:319) 
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088) 
    at com.xxx.er.basman.HibernateTransactionFilter.doFilter(HibernateTransactionFilter.java:58) 

Просто повторно итерацию, HibernateTransactionFilter создает EntityManager так:
EntityManager em = myEntityManagerFactory.createEntityManager(); em.getTransaction().begin(); entityManagerThreadLocal.set(em);

В конструкторе LinkPanel я проверяю этот EntityManager следующим образом:

EntityManager em = entityManagerThreadLocal.get(); if (em == null) { throw new NullPointerException("No entity manager has been started on this thread: " + Thread.currentThread().getName()); }

+0

Странно, я обнаружил, что могу обойти это. В фильтре транзакции после запуска 'chain.doFilter (request, response)' я извлекаю EntityManager, а затем получаю транзакцию, и если он не отмечен для отката, я фиксирую txn и закрываю entitymanager. Хорошо, если я совершу транзакцию, но оставьте объект entitymanager открытым, то, похоже, он оборачивает эту проблему. Теперь мне нужно проверить, что это не вызывает никаких других ... – mdarwin

+0

Я также заметил, что калитка, кажется, отправляет 2 запроса, когда я нажимаю ссылку на странице задач задач (ссылка, которая приводит меня к странице, где я просматриваю задачи 11-20) – mdarwin

ответ

4

Просто потому что диспетчер сущности открыт, это не означает, что все загруженные с леними объекты могут быть загружены. Если объекты были загружены другим менеджером/сеансом сущностей из «текущего», объекты все еще отсоединяются, и попытка загрузки ленивой ассоциации на них приведет к исключению, с которым вы сталкиваетесь.

Вкратце, ваш фильтр открывает диспетчер объектов, который делает возможной ленивую загрузку для текущего запроса. Но как только запрос заканчивается, объекты становятся отсоединенными.

Либо перезагрузите их по второму запросу, либо подключите их к текущему диспетчеру/сеансу.

+0

, так что либо session.update (task), либо session.lock (задача) снова присоединяется к вновь открытому сеансу. – koma

+0

Спасибо за ваши быстрые и информативные ответы. Если я правильно понимаю, набор задач загружается при загрузке страницы задач просмотра, но затем щелчок, чтобы увидеть следующий (11-20) Задачи - это новый запрос с новым EntityManager, который ничего не знает о существующих загруженных задач. Таким образом, подход будет заключаться в том, чтобы добавлять команду refresh/merge каждый раз, когда мы сталкиваемся с задачей, которая может быть не в текущем контексте сохранения. – mdarwin

+0

Кажется, у меня есть работа с вашей помощью.Я запускаю команду refresh (merge) при обработке каждого объекта, а затем ** беря возвращаемое значение и назначаю его **: ie 'entity = DbHelper.getNonNullEntityManager(). Merge (entity);'. Это, похоже, устранило проблему! Единственная проблема - дополнительные накладные расходы (и возможные вызовы БД), вызванные всем освежением. – mdarwin