即使在事务中也是Hibernate LazyInitializationException

时间:2015-07-15 20:28:20

标签: spring hibernate transactions lazy-loading

我面临着一个非常类似的问题:Yet another LazyInitializationException even with OpenSessionInViewFilter

我使用Hibernate 4.2.7.Final。我有一个像这样映射的实体:

@Entity
@Table(...)
public class A {
    ...
    @OneToMany(fetch=FetchType.LAZY, mappedBy="b")
    private Set<B> bSet;
    ...
}

它会加载大量数据,这就是我需要在需要时加载数据的原因。所以我加载了一个包含这个控制器请求映射的页面:

@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

这不是我使用的唯一延迟抓取的集合,但它可以在任何其他地方使用。延迟加载必须在事务中,并且它在事务中(因为您可以看到我的服务实现)!我甚至在org.springframework.orm.hibernate4.support.OpenSessionInViewFilter中添加了web.xml

我找不到任何解决方案,请指教!

更新(我的实体的确切用法):

我有一大组As,每个A都有一组B.有一个视图我可以显示所有的As,它们在一个列表中并显示在数据表中。在每行的末尾都有一个按钮,用于调用和操作。在这个动作中,我保存了所选的A(在myService中有一个选定A的setter)。此操作在controller1中。当我想显示A的B时,我设置了哪个被选中并重定向到另一个视图。此视图由另一个控制器管理,这就是我将所选A保存到服务(会话或单一作用域)的原因。

@Controller
@Scope("session")
public class Controller1 {
    ...
    public void setSelectedA(A selectedA) {
        myService.setSelectedA(selectedA);
    }
}

我试图在这种方法中达到B的集合,但是不起作用(整个服务是事务性的,我试图仅将事务注释设置为setselectedA()getBSet()方法,但没有成功)。

1 个答案:

答案 0 :(得分:0)

您的服务是session范围(@Scope(value="session")),但它不会使其自动线程安全。例如,如果你有一个购物车对象(它是相同的servlet会话),用户可以刷新他的页面,页面将在服务器上从另一个线程处理,它将是另一个Hibernate会话但是相同的购物车(和相同的Servlet会话) )。

问题是你在MySessionImpl中缓存的实体需要一个实时的Hibernate会话来触发B set的加载 - 会话在第一个控制器完成处理后关闭。

当从不同的线程使用时,Hibernate会话也不能保证正常工作,所以你不能延长它们的寿命以在控制器B中提供延迟加载,因为它是在另一个线程中处理的。 因此,请避免在服务类中缓存未分离的未初始化对象。因此,当您致电return a.getBSet();时,您正在访问当前线程中不存在a的会话。

我会重构所有操作在具有范围单例的线程安全服务中完成的代码(此范围在Spring中是默认的),并且它的方法应该是粗粒度的 - 即创建一个尽可能在单个方法中执行的服务call和该方法注释为@Transactional

如果您需要保留所选对象的列表(例如,Web商店购物车中的文章ID),您只需要将它们的标识符(而不是实体)存储在会话作用域(每个用户)bean中,然后通过另一个控制器/线程需要时的ID。为了避免A实体的额外数据库往返,您可以在Hibernate中启用二级缓存。