Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @Async generates LazyInitializationExceptions

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>
like image 674
Alex Avatar asked Jun 24 '13 14:06

Alex


4 Answers

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() { .. }

}
like image 65
davidcyp Avatar answered Sep 23 '22 10:09

davidcyp


You can try following

@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
like image 35
user637576 Avatar answered Sep 20 '22 10:09

user637576


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(){
...
 }
like image 32
Ashish Chaurasia Avatar answered Sep 20 '22 10:09

Ashish Chaurasia


You can try

@Proxy (lazy = false)

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

like image 20
Manoj Kathiriya Avatar answered Sep 22 '22 10:09

Manoj Kathiriya