![](/img/trans.png)
[英]Spring Hibernate with @Transaction results LazyInitializationException
[英]Hibernate LazyInitializationException even in transaction
我面臨與此非常相似的問題: 即使使用OpenSessionInViewFilter,另一個LazyInitializationException
我使用的是Hibernate 4.2.7.Final。 我有一個這樣映射的實體:
@Entity
@Table(...)
public class A {
...
@OneToMany(fetch=FetchType.LAZY, mappedBy="b")
private Set<B> bSet;
...
}
它加載大量數據,這就是為什么我需要在需要時加載它。 因此,我使用此contoller請求映射加載頁面:
@RequestMapping("/getDetails")
public ModelAndView showView(Model model) {
...
for(B b : myService.getBSet()) {...}
...
}
服務正在交易中:
@Service
@Scope(value="session")
@Transactional("ora11transactionManager")
public class MyServiceImpl implements MyService {
private A a;
...
public Set<B> getBSet() {
return a.getBSet();
}
}
hibernate.cgf.xml中的事務管理器:
<bean id="ora11sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="ora11source"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">${debug}</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.connection.characterEncoding">UTF-8</prop>
<prop key="hibernate.jdbc.use_get_generated_keys">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>mypackage</value>
</list>
</property>
</bean>
<bean id="ora11transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="ora11sessionFactory" />
</bean>
當我想加載getDetails視圖時,它將引發引用服務中該行的異常:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: <my-package>.A.bSet, could not initialize proxy - no Session
這不是我使用的唯一延遲獲取的集合,但在其他任何地方都可以使用。 延遲加載必須在事務中,並且在事務中(如您所見,我的服務實現)! 我什至在web.xml
添加了org.springframework.orm.hibernate4.support.OpenSessionInViewFilter
。
我找不到任何解決方案,請告知!
更新(我實體的確切使用):
我有大量的As,每個A都有B。在一個視圖中,我可以顯示所有As,它們在列表中並顯示在數據表中。 每行的末尾都有一個按鈕,它調用並執行操作。 在此操作中,我保存了選定的A(在myService中,有一個用於選定A的設置器)。 此操作在controller1中。 當我想顯示A的B時,我將其設置為選中狀態並重定向到另一個視圖。 該視圖由另一個控制器管理,這就是為什么我將選定的A保存到服務(會話或單例作用域)的原因。
@Controller
@Scope("session")
public class Controller1 {
...
public void setSelectedA(A selectedA) {
myService.setSelectedA(selectedA);
}
}
我什至試圖用這種方法達到B的集合,但不起作用(整個服務是事務性的,我試圖僅將事務性注釋設置為setselectedA()
和getBSet()
方法,但沒有成功)。
您的服務是session
作用域( @Scope(value="session")
),但是它不會自動使其成為線程安全的。 例如,如果您有一個cart對象(它是相同的servlet會話),則用戶可以刷新其頁面,並且該頁面將在服務器上從不同的線程進行處理,這將是另一個Hibernate會話,但具有相同的cart(和相同的Servlet會話) )。
問題在於,您在MySessionImpl中緩存的實體需要實時的Hibernate會話才能觸發B集的加載-第一個控制器完成處理后,該會話將關閉。
當從不同線程使用Hibernate會話時,也不能保證它們正常工作,因此您不能延長它們的壽命以在控制器B中提供延遲加載,因為它是在另一個線程中處理的。 因此,請避免在服務類中緩存分離的未初始化對象。 因此,當您調用return a.getBSet();
您正在訪問會話到a
附着不當前線程存在。
我將重構該代碼,以確保所有操作均在具有作用域singleton的線程安全服務中完成(該作用域在Spring中是默認的),並且其方法應該是粗粒度的-即創建一個在單個方法調用中盡可能多地執行的服務,並且方法被標注為@Transactional
。
如果您需要保留選定對象的列表(例如,Web商店購物車中的商品ID),則只需將其標識符(而非實體)存儲在會話范圍(按用戶)的Bean中,然后在需要時按ID加載它們在另一個控制器/線程中。 為了避免A實體進行額外的數據庫往返,您可以在Hibernate中啟用二級緩存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.