17

I am using JPA(with Hibernate 4.3.3 as persistence provider ) along Spring (3.2.2) , all my fields are loading fine but when I am trying to access my Collection it's throwing the error-

Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.br.common.catalog.entity.Category.allParentCategoryXrefs, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:551)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:140)
    at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:526)
    at java.lang.String.valueOf(String.java:2827)
    at java.io.PrintStream.println(PrintStream.java:771)
    at test.Test.main(Test.java:30)

When I debugged this I was getting error for every collection defined in my entity class - com.sun.jdi.InvocationException occurred invoking method.

I tried using collection.size() and Hibernate.initialize() but none of this worked. On searching on internet I found that extending Persitence will solve the problem ie.

 @PersistenceContext(type=PersistenceContextType.EXTENDED)

        protected EntityManager em;

this worked fine but through this I found that em will always remain opened , now spring will not handle this. Is there any way to solve this problem using Spring. Any Help is highly appreciated.

My Entities are as -

  @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    @Table(name="CATEGORY")
    public class Category implements Serializable {
       @Id
        @GeneratedValue(generator= "CategoryId")
         @Column(name = "CATEGORY_ID")
        protected Long id;

        @ManyToOne(targetEntity = Category.class)
        @JoinColumn(name = "DEFAULT_PARENT_CATEGORY_ID")
        @Index(name="CATEGORY_PARENT_INDEX", columnNames={"DEFAULT_PARENT_CATEGORY_ID"})
        protected Category defaultParentCategory;

        @OneToMany(targetEntity = Categoryref.class, mappedBy = "categoryrefPK.category")
        @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region="test")
        @OrderBy(value="displayOrder")
        @BatchSize(size = 50)

        protected List<Categoryref> childCategoryRefs = new ArrayList<Categoryref>(10);
        @OneToMany(targetEntity = Categoryref.class, mappedBy = "categoryrefPK.subCategory",fetch=FetchType.LAZY)
        @Cascade(value={org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.PERSIST})
        @OrderBy(value="displayOrder")
        @BatchSize(size = 50)
        protected List<Categoryref> parentCategoryRefs = new ArrayList<Categoryref>(10);

    }


@Entity
@Polymorphism(type = PolymorphismType.EXPLICIT)
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "CATEGORY_REF")
public class Categoryref implements Serializable {
    /** The category id. */
    @EmbeddedId
    CategoryrefPK categoryrefPK = new CategoryrefPK();

    public CategoryrefPK getCategoryrefPK() {
        return categoryrefPK;
    }

    public void setCategoryrefPK(final CategoryrefPK categoryrefPK) {
        this.categoryrefPK = categoryrefPK;
    }
  }

@Embeddable
public class CategoryrefPK implements Serializable {

    @ManyToOne(targetEntity = Category.class, optional=false)
    @JoinColumn(name = "CATEGORY_ID")
    protected Category category = new Category();

    @ManyToOne(targetEntity = Category.class, optional=false)
    @JoinColumn(name = "SUB_CATEGORY_ID")
    protected Category subCategory = new Category();

}

My Xml Configuration is as -

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/tx  
           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.br" />

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        ....
    </bean>
<!--  this is also used we can used this also  -->
  <tx:annotation-driven transaction-manager="transactionManager" />  
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
        <property name="dataSource" ref="dataSource" />
         <property name="persistenceUnitName" value="abc" />   
        <property name="packagesToScan" value="com.br.common.*" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>  


</beans>  

Persitence.xml

    <?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="abc">
        transaction-type="RESOURCE_LOCAL">
          <mapping-file>META-INF/category.orm.xml</mapping-file>
        <class>com.br.common.Category</class>
        <class>com.br.common.Categoryref</class>
        <class>com.br.common.CategoryrefPK</class>

        <properties>
            <property name="javax.persistence.jdbc.user" value="user"
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"></property>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test>
            <property name="javax.persistence.jdbc.password" value="...">
            <property name="hibernate.show_sql" value="true" />

            <property name="hibernate.transaction.flush_before_completion"
                value="false" />
            <property name="hibernate.connection.autocommit" value="true" />
            <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
            <property name="hibernate.cache.use_second_level_cache" value="true" /> 
            <property name="hibernate.cache.use_query_cache" value="true"/>
            <property name="hibernate.generate_statistics" value="false" />
            <property name="hibernate.archive.autodetection" value="false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.id.new_generator_mappings" value="true" />
        </properties>
    </persistence-unit>
</persistence>

this is my dao i am calling dao through service layer

@Repository("categoryDaoImpl")
public class CategoryDaoImpl implements ICategoryDAO {

    @PersistenceContext
    protected EntityManager em;


    public Category save(Category category) {
      Category category2=   em.merge(category);

         em.flush();
         return category2;
  }
    public Category readCategoryById(Long categoryId) {
        return em.find(Category.class, categoryId);
    }


}

servie layer

    @Service("blCatalogService")
@Transactional(propagation=Propagation.REQUIRED)
public class CatalogServiceImpl implements ICatalogService {

    @Resource(name="categoryDaoImpl")
    protected ICategoryDAO categoryDao;

    @Transactional
    public Product saveProduct(Product product) {
        return productDao.save(product);
    }

    public Category findCategoryById(Long categoryId) {
        return categoryDao.readCategoryById(categoryId);
    }

}

this is main

public class Test {


        public static void main(String[] args) {
                ApplicationContext context = new ClassPathXmlApplicationContext(
                    "applicationContext-persistence.xml");
            ICatalogService serviceCategory= (ICatalogService) context
                    .getBean("blCatalogService");
                Category parentCategory=serviceCategory.findCategoryById(2l);
    System.out.println(parentCategory.getAllParentCategoryrefs());//here error is coming while accessing collection
    }
    }
henrycharles
  • 1,019
  • 6
  • 28
  • 66
  • This question has been asked several times, you are trying to access the child collection when out of session context, you may have to load it when within the session context to not get this error. – Zeus Mar 25 '14 at 18:31
  • possible duplicate of [hibernate: LazyInitializationException: could not initialize proxy](http://stackoverflow.com/questions/345705/hibernate-lazyinitializationexception-could-not-initialize-proxy) – Zeus Mar 25 '14 at 18:32
  • @Zeus i know when this error lazy initialization comes but i want it to be resolved by using spring – henrycharles Mar 25 '14 at 18:51

4 Answers4

21

The problem is that when this method call returns:

Category parentCategory=serviceCategory.findCategoryById(2l);

Here you are no longer in a @Transactional context. this means that the session linked to parentCategory got closed. now when you try to access a collection linked to a closed session, the No Session error occurs.

One thing to notice is that the main method runs outside any spring bean and has no notion of persistence context.

The solution is to call the parentCategory.getAllParentCategoryrefs() from a transactional context, which can never be the main method of your application.

Then reattach the parentCategory to the new persistence context, and then call the getter.

Try for example to pass the parentCategory back to a transactional method of the same service:

serviceCategory.nowItWorks(parentCategory);

where the method on the service is transactional:

@Transactional(readOnly=true)
public void nowItWorks(Category category) {
    dao.nowItWorks(category);
}

And in the DAO:

public void nowItWorks(Category category) {
    Category reattached = em.merge(category);
    System.out.println("It works: " + reattached.getAllParentCategoryrefs());
}
Angular University
  • 42,341
  • 15
  • 74
  • 81
  • in my case, I had 2 methods. One for get the main entity and another which fetch relation entity de lazily. I had to put the @Transactional in both methods and it worked – Pedro Romão Aug 11 '16 at 10:16
10

Like @Zeus said, this has been answered many, MANY, times before. You are having this problem in your test class because your transaction begins and ends at your service call:

Category parentCategory=serviceCategory.findCategoryById(2l);

Recall from Hibernate documentation that lazy loading only works within a Hibernate Session (in this case, the hibernate session begins and ends with your service call). You can't reconnect to the hibernate session (simply) to initialize the collection.

I'm not exactly sure what you mean when you want to solve it "in spring." Since this isn't a Spring issue. Essentially the two ways to resolve this are to load the collection within the hibernate session you load the parent in or execute a separate fetch outside of the original hibernate session.

Aaron
  • 125
  • 5
  • 4
    I don't know why you guys are responding as if this is some type of easy solution. I've been following Zeuses responses and none of them have worked for me. I've tried Transactional and that only makes it worse by returning null instead of a error response. Ive tried other peoples solutions and most of their solutions are deprecated resolutions. I am using mine in JUNIT though and embedded hibernate. – numerical25 Feb 07 '16 at 17:32
  • I think this is a case where the answer is simple but the resolution is complex. I don't mean to trivialize the issue. The answer really depends on how your code is designed, and at what level you are trying to test. Spring abstracts a lot of boilerplate code for you, but you have to know how it works to use it effectively. – Aaron Mar 21 '16 at 22:00
4

use @Fetch(FetchMode.SELECT) and @LazyCollection(LazyCollectionOption.FALSE) on your domain set collection , it will work

Marian Nasry
  • 821
  • 9
  • 22
-2

Try using fetch=FetchType.EAGER, it will work

Opal
  • 81,889
  • 28
  • 189
  • 210
Max
  • 51
  • 1