Spring @Async generates LazyInitializationExceptions
Asked Answered
S

5

11

The Spring MVC application needs to execute an intensive computation job which takes several minutes. The client wants to run this job in asynchronous way. But after I enable the @Async annotation on the method (see code 1) and get the error (see code 2). My web.xml and web-appliaiton-context.xml are also presented below. I tried to find a workaround to fix this, but failed. Please help.

code 1:

@Service
public class AsyncServiceImpl implements AsyncServiceInt{

    protected int pertArrSize = 1000;
    @Autowired
    protected TblvDao tblvDao1 = null;

    @Override
    @Async
    public void startSlowProcess(Integer scenarioId) throws SecurityException, IllegalArgumentException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        batchUpdateSummaryPertSim(scenarioId);

    }

    public void batchUpdateSummaryPertSim(Integer scenarioId) throws SecurityException, IllegalArgumentException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException
    {
        double[] summaryArry = new double[pertArrSize];
        summaryArry = summaryPertSim_env(scenarioId, summaryArry);
    }

    @Transactional
    private double[] summaryPertSim_env(Integer  scenarioId,
    double[] summaryArry) throws IOException, ClassNotFoundException, SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
        ScenarioTblv scenario = tblvDao1.getScenarioTblv(scenarioId);
        TblvResultSaved savedResult = scenario.getTblvResultSaved();
        TblvScenarioCategory4 tblvScenarioCategory4 = new TblvScenarioCategory4();
        List<TblvScenarioCategory4> tblvScenarioCategory4List = scenario
        .getTblvScenarioCategory4List();
        Double min = 0.0, max = 0.0, ml = 0.0;
        Integer conf = 4;
        if (savedResult.getEnvDist() != null) {

            byte[] st = (byte[]) savedResult.getEnvDist();
            ByteArrayInputStream baip = new ByteArrayInputStream(st);
            ObjectInputStream ois = new ObjectInputStream(baip);

            summaryArry = (double[]) ois.readObject();
            } else {
            for (int i = 0; i < tblvScenarioCategory4List.size(); i++) {
                tblvScenarioCategory4 = tblvScenarioCategory4List.get(i);

                vTblvScenarioEffectChoose v = tblvDao1
                .getvTblvScenarioEffectChooseById(tblvScenarioCategory4
                .getId());

                if (v.getC1id() == 1) {
                    tblvScenarioCategory4.setImpactYearlyUnitsSim(pertArrSize);
                    min = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .getLowValue();
                    max = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .getHighValue();
                    ml = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .getMostValue();
                    conf = tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue().getConf();
                    if ((conf == null) || (conf == 0))
                    conf = 4;

                    double[] MOPertArr;

                    MOPertArr = getPertArry(min, max, ml, conf, pertArrSize);

                    double[] benefitResult = new double[pertArrSize];

                    for (int j = 0; j < pertArrSize; j++) {
                        tblvScenarioCategory4.setSimIndex(j);
                        tblvScenarioCategory4
                        .getTblvEffectScenarioImpactUnitValue()
                        .setHighValue(MOPertArr[j]);
                        benefitResult[j] = tblvScenarioCategory4.getHighPvSum();
                        summaryArry[j] = summaryArry[j] + benefitResult[j];
                    }
                    tblvScenarioCategory4
                    .getTblvEffectScenarioImpactUnitValue()
                    .setHighValue(max);
                }

            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(summaryArry);
            byte[] summaryArryAsBytes = baos.toByteArray();
            savedResult.setEnvDist(summaryArryAsBytes);
            tblvDao1.saveTblvResultSaved(savedResult);
        }
        return summaryArry;
    }

code 2:

 10:26:06,233 ERROR org.hibernate.LazyInitializationException:42 - failed to lazily initialize a collection of role: com.pb.prism.model.db.ScenarioTblv.tblvScenarioCategory4List, no session or session was closed
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.pb.prism.model.db.ScenarioTblv.tblvScenarioCategory4List, no session or session was closed
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
        at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
        at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
        at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
        at com.pb.prism.util.AsyncServiceImpl.summaryPertSim_env(AsyncServiceImpl.java:446)
        at com.pb.prism.util.AsyncServiceImpl.batchUpdateSummaryPertSim(AsyncServiceImpl.java:41)
        at com.pb.prism.util.AsyncServiceImpl.startSlowProcess(AsyncServiceImpl.java:34)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:80)
        at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
        at java.util.concurrent.FutureTask.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">


   <context-param>
      <param-name>log4jConfigLocation</param-name>
      <param-value>/WEB-INF/log4j.properties</param-value>
   </context-param>
   <listener>
      <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
   </listener>
   <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   <listener>
      <listener-class>com.pb.prism.listener.MTAServletContextListener</listener-class>
   </listener>
   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/web-application-context.xml</param-value>
   </context-param>
   <filter>
      <filter-name>openEntityManagerInViewFilter</filter-name>
      <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>openEntityManagerInViewFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <!-- Enables Spring Security -->
   <filter>
      <filter-name>springSecurityFilterChain</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <filter>
      <filter-name>encoding-filter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <init-param>
         <param-name>encoding</param-name>
         <param-value>UTF-8</param-value>
      </init-param>
   </filter>
   <filter-mapping>
      <filter-name>encoding-filter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <filter>
      <filter-name>UrlRewriteFilter</filter-name>
      <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>UrlRewriteFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <!-- Handles all requests into the application -->
   <filter>
      <filter-name>OpenSessionInViewFilter</filter-name>
      <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>OpenSessionInViewFilter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
   <servlet>
      <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value />
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
      <url-pattern>/app/*</url-pattern>
   </servlet-mapping>
</web-app>

web-appliation-context.xml

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

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

    <!-- Imports the configurations of the different infrastructure systems of the application -->
    <import resource="data-access-context.xml" />
    <import resource="security-context.xml" />
    <import resource="webmvc-context.xml" />

    <bean id="applicationContextProvider" class="com.pb.prism.context.ApplicationContextProvider"></bean>

    <!-- Configure the multipart resolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- one of the properties available; the maximum file size in bytes -->
        <property name="maxUploadSize" value="2000000"/>
    </bean>


        <task:annotation-driven executor="myExecutor" />    
        <task:executor id="myExecutor" pool-size="5"/>
</beans>
Staggs answered 24/6, 2013 at 14:47 Comment(0)
E
15

The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). (as explained in Spring @Transaction method call by the method within the same class, does not work? )

A possible solution is to extract the transactional code from the service, and put it in a separate class. This way, the call to the transactional method can be intercepted, and a transaction is available.

Eg.

@Service
public class AsyncServiceImpl implements AsyncServiceInt{

@Autowired private SlowProcess slowProcess;

@Override
@Async
public void startSlowProcess(Integer scenarioId) {
    slowProcess.execute(param);
}

..

public class SlowProcess {

   @Transactional
   public double[] execute() { .. }

}
Eckenrode answered 24/6, 2013 at 15:5 Comment(6)
I extract the transactional code to another one separate class. it works.Staggs
what is the secret behind this? I really want to understand? @StaggsBerenice
This did not work for me. I also moved the transactional code to another service. I was trying to do the given:- TbEmsTransExpenseApprovalDetails tbEmsTransExpenseApprovalDetails = tbEmsTransExpense.getTbEmsTransExpenseApprovalDetails(); Integer currenctApprovalLevel = tbEmsTransExpenseApprovalDetails.getApprovalLevel();. This gave me error: org.hibernate.LazyInitializationException: could not initialize proxy [com.neeyamo.ems.pojo.TbEmsTransExpenseApprovalDetails#2483] - no Session Snowfall
@ChetanOswal it seems you're asking for a lazy property approvalLevel outside the transaction. There are several answers on SO about that. Or you can turn of lazy loading for that propertyEckenrode
Turning of lazy to eager works fine. But I don't think that it is a good solution.Snowfall
It did work!, but could you please explain how exactly?Itinerate
O
6

You can try following

@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
Orelee answered 15/9, 2015 at 16:1 Comment(1)
Adding @Transactional to the @Async method itself indeed works fine (and the propagation specification was not needed in my case)Warbler
S
1

There is no issue with your @Async it is due to your your entity class, when you declare mapping from one to many, try specifying the fetchType to EAGER. Some thing like this:

@OneToMany(fetch=FetchType.EAGER)
public Collection<Role> getRoleSet(){
...
 }
Semivitreous answered 24/6, 2013 at 15:4 Comment(1)
This works but not a good solution. There is a reason why spring boot uses lazy loading.Snowfall
B
1

You can try

@Proxy (lazy = false)

On top of both entity class. It works in my case.

Bibliolatry answered 24/6, 2013 at 15:8 Comment(0)
M
0

use Hibernate.initialize(obj) in your DAO class logic to avoid lazy loading exceptions.

Mucosa answered 17/2, 2017 at 7:1 Comment(1)
Using proper transactions is a lot better then using this.Warbler

© 2022 - 2024 — McMap. All rights reserved.