简体   繁体   中英

Hibernate LazyInitializationException even in transaction

I'm facing a very similar problem to this: Yet another LazyInitializationException even with OpenSessionInViewFilter

I use 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:

@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:

<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:

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 .

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. 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). This action is in controller1. And when I want to show the Bs of an A, I set which is selected and redirect to an other view. This view is managed by an other controller, that's why I save the selected A to a service (session or singleton scoped).

@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).

Your service is session scoped ( @Scope(value="session") ) but it does not make it automatically threadsafe. 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).

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.

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. So please avoid caching detached uninitialized object in your service class. So when you call return a.getBSet(); you are accessing session to which a was attached which does not exist in current thread.

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 .

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. To avoid extra database round trip for A entities you can enable second level cache in Hibernate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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