0

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

Community
  • 1
  • 1
KatieAmber
  • 77
  • 12

1 Answers1

0

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 callreturn 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 - i.e. 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.

Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • In a singleton-scoped service I still get the error. – KatieAmber Jul 16 '15 at 06:55
  • Please add the code to the question - you should not cache A value which fields are not initalized(you either can force initialization/loading of B set by accessing `a.getBSet()`( as Harshal Patil has noted) in the thread where you set A value or store A's id instead of A instance) – Boris Treukhov Jul 16 '15 at 07:02
  • Ok, I updated. This is where I set this A. I initialize the list of As in the method which loads the view of As, that's why I want to avoid eagerly loading the set of Bs. – KatieAmber Jul 16 '15 at 08:06
  • Hibernate entities can be attached only to one session at once, so it's better to load a separate copy of A with the same id in your second controller. To avoid extra db roundtrip you can enable caching of A(level 2 cache) – Boris Treukhov Jul 16 '15 at 09:33
  • Also disregard singleton thing in your case it's rather a "cart" of selected objects than a service - different users, different carts. – Boris Treukhov Jul 16 '15 at 09:39
  • Why is it in more session? I tried to load the set of Bs when I set the selected A, so I omit the second controller, but it results the same. – KatieAmber Jul 16 '15 at 10:38
  • If you have OpenSessionInViewFilter it's possible that you have set `"singleSession"="false"` by mistake? – Boris Treukhov Jul 16 '15 at 12:00
  • @KatieAmber do you use OpenSessionInViewFilter or simply `@Transactional`annotation? When you load the set B are you sure that it happens inside `@Transactional` method? – Boris Treukhov Jul 16 '15 at 13:55
  • I haven't set the singleSession. I use both, all my services are transactional. I'm not so sure, because in test I reach the service, and the set loads without any problem. I tried to remove the OpenSessionInViewFilter, but the error remained. – KatieAmber Jul 16 '15 at 14:27
  • @KatieAmber How is your transaction manager declared?Are you sure that it's capable of opening Hibernate sessions(i.e. it's JpaTransactionManager or HIbernateTransactionManager)? – Boris Treukhov Jul 16 '15 at 14:34
  • Updated my post again, you can see I use HibernateTransactionManager. It can open hibernate session, because in other services I can get sets without error (I have more entity like A). Only this case fails. – KatieAmber Jul 17 '15 at 06:49
  • Please ensure your transactional services are in the root web app context (and not picked up by servlet context autoscanning by mistake) controllers are in the servlet context and beans are nit duplicated in both contexts see answer with example configuration http://stackoverflow.com/a/11534189/241986 Services and transaction manager should be in one and only one same application context. – Boris Treukhov Jul 17 '15 at 07:28