简体   繁体   English

甚至在事务中也可以休眠LazyInitializationException

[英]Hibernate LazyInitializationException even in transaction

I'm facing a very similar problem to this: Yet another LazyInitializationException even with OpenSessionInViewFilter 我面临与此非常相似的问题: 即使使用OpenSessionInViewFilter,另一个LazyInitializationException

I use Hibernate 4.2.7.Final. 我使用的是Hibernate 4.2.7.Final。 I have an entity which is mapped like this: 我有一个这样映射的实体:

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

It loads a big amount of data, that's why I need to load it when it is required. 它加载大量数据,这就是为什么我需要在需要时加载它。 So I load a page with this contoller request mapping: 因此,我使用此contoller请求映射加载页面:

@RequestMapping("/getDetails")
public ModelAndView showView(Model model) {
    ...
    for(B b : myService.getBSet()) {...}
    ...
}

And the service is in transaction: 服务正在交易中:

@Service
@Scope(value="session")
@Transactional("ora11transactionManager")
public class MyServiceImpl implements MyService {
    private A a;
    ...
    public Set<B> getBSet() {
        return a.getBSet();
    }
}

Transaction manager in the hibernate.cgf.xml: 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>

When I want to load the getDetails view, it throws the exception referencing that row in the service: 当我想加载getDetails视图时,它将引发引用服务中该行的异常:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: <my-package>.A.bSet, could not initialize proxy - no Session

This is not the only lazy fetched collection I use, but anywhere else it works. 这不是我使用的唯一延迟获取的集合,但在其他任何地方都可以使用。 The lazy loading must be in transaction, and it is in transaction (as you can see my service implementation)! 延迟加载必须在事务中,并且在事务中(如您所见,我的服务实现)! I even added org.springframework.orm.hibernate4.support.OpenSessionInViewFilter in web.xml . 我什至在web.xml添加了org.springframework.orm.hibernate4.support.OpenSessionInViewFilter

I can't find any solution for this, please advise! 我找不到任何解决方案,请告知!

UPDATE (The exact use of my entities): 更新(我实体的确切使用):

I have a large set of As, and every A has a set of B. There is a view where I can show all As, they are in a list and showed in a datatable. 我有大量的As,每个A都有B。在一个视图中,我可以显示所有As,它们在列表中并显示在数据表中。 At the end of every rows there is a button, which calls and action. 每行的末尾都有一个按钮,它调用并执行操作。 In this action I save the selected A (in myService there is a setter for selected A). 在此操作中,我保存了选定的A(在myService中,有一个用于选定A的设置器)。 This action is in controller1. 此操作在controller1中。 And when I want to show the Bs of an A, I set which is selected and redirect to an other view. 当我想显示A的B时,我将其设置为选中状态并重定向到另一个视图。 This view is managed by an other controller, that's why I save the selected A to a service (session or singleton scoped). 该视图由另一个控制器管理,这就是为什么我将选定的A保存到服务(会话或单例作用域)的原因。

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

I tried to reach the set of B even in this method, but doesn't work (the whole service is transactional, I tried to set transactional annotation only to the setselectedA() and getBSet() method, but no success). 我什至试图用这种方法达到B的集合,但不起作用(整个服务是事务性的,我试图仅将事务性注释设置为setselectedA()getBSet()方法,但没有成功)。

Your service is session scoped ( @Scope(value="session") ) but it does not make it automatically threadsafe. 您的服务是session作用域( @Scope(value="session") ),但是它不会自动使其成为线程安全的。 For example if you have a cart object(it's the same servlet session) the user may refresh his page and the page will be processed on the server from a different thread and it will be another Hibernate session but the same cart(and same Servlet session). 例如,如果您有一个cart对象(它是相同的servlet会话),则用户可以刷新其页面,并且该页面将在服务器上从不同的线程进行处理,这将是另一个Hibernate会话,但具有相同的cart(和相同的Servlet会话) )。

The problem is that entities which you cache in MySessionImpl require a live Hibernate session to trigger loading of B set - the session is closed after the first controller has finished processing. 问题在于,您在MySessionImpl中缓存的实体需要实时的Hibernate会话才能触发B集的加载-第一个控制器完成处理后,该会话将关闭。

Hibernate sessions are also not guaranteed to work properly when used from different threads so you can't extend their lives to provide lazy loading in controller B because it's processed in another thread. 当从不同线程使用Hibernate会话时,也不能保证它们正常工作,因此您不能延长它们的寿命以在控制器B中提供延迟加载,因为它是在另一个线程中处理的。 So please avoid caching detached uninitialized object in your service class. 因此,请避免在服务类中缓存分离的未初始化对象。 So when you call return a.getBSet(); 因此,当您调用return a.getBSet(); you are accessing session to which a was attached which does not exist in current thread. 您正在访问会话到a附着不当前线程存在。

I would refactor that code that all action is done in a threadsafe service with scope singleton(this scope is default in Spring) and it's methods should be coarse-grained - ie create a service that does as much as possible in single method call and that method is annotated as @Transactional . 我将重构该代码,以确保所有操作均在具有作用域singleton的线程安全服务中完成(该作用域在Spring中是默认的),并且其方法应该是粗粒度的-即创建一个在单个方法调用中尽可能多地执行的服务,并且方法被标注为@Transactional

If you need to keep a list of selected objects (for example the article IDs in a web store cart) you need only to store their identifiers(not the entities) in session scoped (per user) bean and then load them by IDs when needed in another controller/thread. 如果您需要保留选定对象的列表(例如,Web商店购物车中的商品ID),则只需将其标识符(而非实体)存储在会话范围(按用户)的Bean中,然后在需要时按ID加载它们在另一个控制器/线程中。 To avoid extra database round trip for A entities you can enable second level cache in Hibernate. 为了避免A实体进行额外的数据库往返,您可以在Hibernate中启用二级缓存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM