Announcement Announcement Module
Collapse
No announcement yet.
Lazy Load loosing session in JSON conversion (jackson) Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Lazy Load loosing session in JSON conversion (jackson)

    Alright, first off I know the topic has been discussed before. I've been googling and browsing the forums for three days and haven't found a suitable solution yet. I am trying to build a web app that delivers nested entities to a web frontend via AJAX with lazy-loading enabled. I am using Spring WebMVC, Hibernate and Jackson. Turning off lazy load is not a solution.

    The problem is that during conversion of my entity beans to JSON, the jackson converter is not able to get a hibernate session in order to materialize the embedded (lazy-load) objects.

    I tried to turn on the OpenSessionInView filter, but that didn't help a thing.

    That's how it looks. Sorry for having german entity names upfront, but you might be able to read it anyways, the logic is: A "Person" ownes a "Niederlassung" (location), which further contains an "Adresse" (adress object).

    Code:
    DEBUG LogicalConnectionImpl           - Obtained JDBC connection
    DEBUG JdbcTransaction                 - initial autocommit status: true
    DEBUG JdbcTransaction                 - disabling autocommit
    DEBUG HibernateTransactionManager     - Exposing Hibernate transaction as JDBC transaction 
    ...
    DEBUG TwoPhaseLoad                    - Resolving associations for [galileo.model.Niederlassung#1]
    DEBUG TwoPhaseLoad                    - Done materializing entity [galileo.model.Niederlassung#1]
    DEBUG TwoPhaseLoad                    - Resolving associations for [galileo.model.Person#4]
    DEBUG TwoPhaseLoad                    - Done materializing entity [galileo.model.Person#4]
    DEBUG StatefulPersistenceContext      - Initializing non-lazy collections
    DEBUG HibernateTransactionManager     - Initiating transaction commit
    DEBUG HibernateTransactionManager     - Committing Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[galileo.model.Niederlassung#1], EntityKey[galileo.model.Person#4]],collectionKeys=[CollectionKey[galileo.model.Niederlassung.adressen#1]]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]])]
    DEBUG AbstractTransactionImpl         - committing
    DEBUG AbstractFlushingEventListener   - Processing flush-time cascades
    DEBUG AbstractFlushingEventListener   - Dirty checking collections
    DEBUG Collections                     - Collection found: [galileo.model.Niederlassung.adressen#1], was: [galileo.model.Niederlassung.adressen#1] (uninitialized)
    DEBUG AbstractFlushingEventListener   - Flushed: 0 insertions, 0 updates, 0 deletions to 2 objects
    DEBUG AbstractFlushingEventListener   - Flushed: 0 (re)creations, 0 updates, 0 removals to 1 collections
    DEBUG EntityPrinter                   - Listing entities:
    DEBUG EntityPrinter                   - galileo.model.Niederlassung{id=1, adressen=<uninitialized>, reihenfolge=1, niederlassung=HQ, version=1}
    DEBUG EntityPrinter                   - galileo.model.Person...
    DEBUG JdbcTransaction                 - committed JDBC Connection
    DEBUG JdbcTransaction                 - re-enabling autocommit
    DEBUG HibernateTransactionManager     - Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[galileo.model.Niederlassung#1], EntityKey[galileo.model.Person#4]],collectionKeys=[CollectionKey[galileo.model.Niederlassung.adressen#1]]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]])] after transaction
    DEBUG LogicalConnectionImpl           - Releasing JDBC connection
    DEBUG LogicalConnectionImpl           - Released JDBC connection
    ...
    DEBUG eptionHandlerExceptionResolver  - Resolving exception from handler [public org.springframework.http.HttpEntity<java.util.List<galileo.model.Person>> galileo.controller.PersonAjaxController.get(javax.servlet.http.HttpServletRequest)]: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: galileo.model.Niederlassung.adressen, no session or session was closed (through reference chain: java.util.ArrayList[0]->galileo.model.Person["niederlassung"]->galileo.model.Niederlassung["adressen"]); nested exception is org.codehaus.jackson.map.JsonMappingException: failed to lazily initialize a collection of role: galileo.model.Niederlassung.adressen, no session or session was closed (through reference chain: java.util.ArrayList[0]->galileo.model.Person["niederlassung"]->galileo.model.Niederlassung["adressen"])
    ...
    DEBUG DispatcherServlet               - Null ModelAndView returned to DispatcherServlet with name 'spring': assuming HandlerAdapter completed request handling
    DEBUG DispatcherServlet               - Successfully completed request
    DEBUG OpenSessionInViewFilter         - Closing Hibernate Session in OpenSessionInViewFilter
    The entity is defined as follows (shortened):

    Code:
    @Entity
    @Table(name = "person", catalog = "mdcrm")
    public class Person implements Serializable {
    	private Niederlassung niederlassung;
    	
    	@ManyToOne
    	@JoinTable(name="niederlassungPerson", joinColumns = @JoinColumn(name="person_id"), inverseJoinColumns = @JoinColumn(name="niederlassung_id"))
    	public Niederlassung getNiederlassung() {
    		return niederlassung;
    	}
    	
    	public void setNiederlassung(final Niederlassung niederlassung) {
    		this.niederlassung = niederlassung;
    	}
    }

    The controller that delivers the entites via AJAX:

    Code:
    	@RequestMapping(method = RequestMethod.POST, headers = { ACCEPT_JSON }, value="/get")
    	public @ResponseBody HttpEntity<List<Person>> get(HttpServletRequest request) {
    		logger.info("getPerson");
    		HttpHeaders headers = new HttpHeaders();
    
    		boolean intern = Boolean.parseBoolean(request.getParameter("intern"));
    		String position = request.getParameter("position");
    
    		List<Person> persons = new ArrayList<Person>();
    		persons = bestandService.findAllPersonIntern();
    
    		return new HttpEntity<List<Person>>(persons, headers);
    	}
    The OpenSessionInView Filter in web.xml config looks like this: (shortened):

    Code:
    	<filter>
    		<filter-name>sessionFilter</filter-name>
    		<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
    		<init-param>
    			<param-name>sessionFactory</param-name>
    			<param-value>sessionFactory</param-value>
    		</init-param>
    	</filter>
    
    	<filter-mapping>
    		<filter-name>sessionFilter</filter-name>
    		<url-pattern>/app/*</url-pattern>
    		<dispatcher>REQUEST</dispatcher>
    		<dispatcher>FORWARD</dispatcher>
    		<dispatcher>ERROR</dispatcher>
    	</filter-mapping>
    The spring config looks like this (shortened):

    Code:
    <!-- HIBERNATE -->
    	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    
    	<bean id="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="jdbc:mysql://localhost/mdcrm" />
    		<property name="username" value="root" />
    		<property name="password" value="" />
    	</bean>
    
    	 <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      	 	<property name="dataSource" ref="dataSource" />
      	 	<property name="packagesToScan" value="galileo.model" />
      	 </bean>
    
    	<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    		<property name="sessionFactory" ref="sessionFactory" />
    	</bean>
    
    	<tx:annotation-driven transaction-manager="transactionManager" />
    
    	<tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    			<tx:method name="*" propagation="REQUIRED" />
    		</tx:attributes>
    	</tx:advice>
     
    	<aop:config>
    		<aop:pointcut id="serviceMethods"
    			expression="execution(* galileo.service.*Service*.*(..))" />
    		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
    	</aop:config>
    Please advice. Thanks you :-)
    Last edited by byte23; Dec 26th, 2011, 01:51 PM.

  • #2
    Thought it might be useful to also post the service method and DAO that fetches the entity. Sorry for double posting, but there has been a post length constraint that didn't allow me to add it to the original post.

    Service class (bestandService), pretty simple calling the DAO:
    Code:
    package galileo.service;   // all classes of this package have transaction functionality, see spring.xml
    
    public class BestandServiceImpl implements BestandService{
    	public List<Person> findAllPersonIntern() {
    		Person exp = new Person();
    		exp.setIntern(true);
    		return personDao.findByExample(exp);
    	}
    }
    And the DAO, which extends the following Generic DAO and calls it's functionality:
    Code:
    public class PersonDaoImpl extends GenericDaoImpl<Person, Integer> implements PersonDao {
    ...
    }
    
    public abstract class GenericDaoImpl<T, ID extends Serializable> implements GenericDao<T, ID> {
    	@Autowired
    	private SessionFactory sessionFactory;
    
    	@SuppressWarnings("unchecked")
    	public List<T> findByExample(T exampleInstance) {
    		Criteria crit = getSession().createCriteria(getPersistentClass());
    		Example example = Example.create(exampleInstance);
    		crit.add(example);
    		return crit.list();
    	}
    }
    These methods work perfect well themselves.
    Last edited by byte23; Dec 26th, 2011, 10:21 AM. Reason: copy error corrected

    Comment


    • #3
      Hi byte23,
      the same issue you are facing nailed me two days a couple of weeks ago. I you are not able to drop the behavior of lazy-loading there are only two options:
      • You transfer your entity model to XML (Jaxb or comparable) and then to Jackson
      • You use Hiberante Fetching Profiles, introduced in 3.6 of hibernate

      If you have some specific questions left, feel free to let me know.

      Regards

      Johannes

      Comment


      • #4
        Thanks for your reaction Johannes. I am going to have a look at hibernate fetching profiles, cause I am not familiar with them yet. Would you mind to explain why going with XML will work rather than JSON, dind't get that approach yet?

        Comment


        • #5
          Hi byte23,
          it is the way Jackson is handling the lazy loading. JAXB for example is using the list of lazy loaded properties differently. If the list is empty, it is not attached to the output. Jackson in contrast is always trying to load the list. If you want to stay with Jackson and just need a workaround a good way for you may be perhaps to annotate your model the JAXB way and use JacksonJAXB (http://wiki.fasterxml.com/JacksonJAXBAnnotations). Maybe that works, I didn't try it yet, but will do in near future.

          If you have some questions left, feel free to ask. :-)

          Comment

          Working...
          X