0

I can't figure this out. Am getting "Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.sandy.domain.Location.items, no session or session was closed" I understand that session is closed but tx:annotation-driver @Transactional should ensure an open session. It works fine with EAGER fetching. Oh and yea - this is by apress pro spring 3 examples.

But maybe I don't get the concept here - I mean while I am debugging "getFirst()" in SomeService I can see all the items in the collection but once return hits - LazyInit exception is thrown...

package org.sandy.main;

    import org.sandy.domain.Item;
    import org.sandy.domain.Location;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.support.GenericXmlApplicationContext;
    import org.springframework.stereotype.Service;

@Service(value = "main")
public class EntryPoint {

    @Autowired
    private SomeService ss;

    public static void main(String args[]) {
        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
        ctx.load("classpath:*-beans.xml");
        ctx.refresh();

        EntryPoint entryPoint = (EntryPoint) ctx.getBean("main");

        Item item = new Item();
        Location location = new Location();

        item.setLocation(location);
        location.getItems().add(item);

        entryPoint.getSs().save(location);

        System.out.println(entryPoint.getSs().findFirst());

        ctx.registerShutdownHook();
    }

    public SomeService getSs() {
        return ss;
    }

    public void setSs(SomeService ss) {
        this.ss = ss;
    }
}

and the service

package org.sandy.main;

import org.sandy.domain.Item;
import org.sandy.domain.Location;
import org.sandy.repo.ItemRepo;
import org.sandy.repo.LocationRepo;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service(value = "ss")
@Repository
@Transactional
public class SomeService {
    @Autowired
    private ItemRepo itemRepo;
    @Autowired
    private LocationRepo locationRepo;

    @Transactional
    public void save(Location location) {
        locationRepo.save(location);
    }

    @Transactional(readOnly = true)
    public List<Item> findFirst() {
        System.out.println("ONE");
        Iterable<Location> it = locationRepo.findAll();
        System.out.println("TWO");
        Location l = it.iterator().next();
        System.out.println("THREE");
        List<Item> r = l.getItems();
        return r;
    }

    @Transactional
    public Iterable finAll() {
        return locationRepo.findAll();
    }

    public void setItemRepo(ItemRepo itemRepo) {
        this.itemRepo = itemRepo;
    }

    public void setLocationRepo(LocationRepo locationRepo) {
        this.locationRepo = locationRepo;
    }
}

and location repo

package org.sandy.repo;

import org.sandy.domain.Location;
import org.springframework.data.repository.CrudRepository;

public interface LocationRepo extends CrudRepository<Location, Long> {
}

Entities:

@Entity
@Table(name = "Locations")
public class Location extends Entry {

    private List<Item> items = new ArrayList<Item>();

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "location")
    public List<Item> getItems() {
        return items;
    }

    public void setItems(List<Item> items) {
        this.items = items;
    }
}

package org.sandy.domain;

import javax.persistence.*;

@Entity
@Table(name = "Items")
public class Item extends Entry {
    private Location location;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn()
    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }
}

And the all mighty xml bean config:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <jdbc:embedded-database id="dataSource" type="H2" />

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="emf"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="packagesToScan" value="org.sandy.domain" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.H2Dialect
                </prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
            </props>
        </property>
    </bean>

    <context:annotation-config/>

    <jpa:repositories base-package="org.sandy.repo" entity-manager-factory-ref="emf" transaction-manager-ref="transactionManager" />

    <context:component-scan base-package="org.sandy" />
</beans>
5
  • Does your Item class have a toString() method? Commented Nov 18, 2013 at 19:25
  • Yes, in this example no - it's just for show and tell. Commented Nov 18, 2013 at 19:29
  • It's actually relevant, can you post it? Commented Nov 18, 2013 at 19:29
  • @Override public String toString() { return "Item{" + "state=" + state + ", sellPrice=" + sellPrice + ", vendorPrice=" + vendorPrice + ", dateSold=" + dateSold + ", name='" + name + '\'' + ", ART='" + ART + '\'' + ", location=" + location + "} " + super.toString(); } } Supper class is "@MappedSuperclass" and contains "id" and "version" both Long Commented Nov 18, 2013 at 19:39
  • You can edit your question and add it there. Commented Nov 18, 2013 at 20:04

1 Answer 1

2

This piece of code

System.out.println(entryPoint.getSs().findFirst());

is equivalent to

/* 1 */ SomeService ss = entryPoint.getSs();
/* 2 */ List<Item> items = ss.findFirst(); // Session opens and closes for @Transactional 
/* 3 */ String toPrint = items.toString(); // no more Session
/* 4 */ System.out.println(toPrint);

So you can see that the Session boundary is only wrapping the findFirst() call. If you're loading your entities lazily, then on line 3, the elements in items are not initialized. When you try to call toString() inside List#toString() which calls toString() on each element, you will get your LazyInitializationException.

You should fully initialize your entities before you use them. That has to be done within Session boundaries.

Sign up to request clarification or add additional context in comments.

6 Comments

I had a misunderstanding on how LAZY loading is suppose to work - I imagined that it will fetch all of the foreign records on demand after a transaction whenever I want, turns out not.
@xe I don't know if you are planning to bring this to a web environment, but there are things you can do so that the session wraps all of your logic rather than just one method. Remember session != transaction
This was actually goofing around, but still did lead me to understanding session-per-request pattern. And yes this will be a web app. So my final question would be - should I manage my unit of work by properly configuring isolation and propagation attributes of @Transactional methods, per request to achieve required goals ? I am just trying to put myself into the right mindset.
@Xeperis It doesn't necessarily have to be done inside @Transactionals methods. As I said, a Session might span multiple Transactions. Look into OpenSessionInViewFilter
Thanks for info. However in my case using JPA and EntityManager it is actually OpenEntityManagerInViewFilter
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.