2013-06-24 2 views
0

Приложение Spring MVC должно выполнить интенсивное вычисление, которое занимает несколько минут. Клиент хочет запустить эту работу асинхронным образом. Но после включения аннотации @Async в методе (см. Код 1) и получения ошибки (см. Код 2). Мой web.xml и web-appliaiton-context.xml также представлены ниже. Я попытался найти обходное решение, чтобы исправить это, но не удалось. Пожалуйста помоги.Spring @Async генерирует LazyInitializationExceptions

Код 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; 
    } 

Код 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> 

веб-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> 

ответ

6

Проблема в том, что прокси-серверы Spring AOP не распространяются, а скорее переносят ваш экземпляр службы на перехват вызовов. Это приводит к тому, что любой вызов «этого» из вашего экземпляра службы непосредственно вызывается в этом экземпляре и не может быть перехвачен прокси-сервером (прокси-сервер даже не знает о таком вызове). (как описано в Spring @Transaction method call by the method within the same class, does not work?)

Возможное решение - извлечь код транзакции из службы и поместить его в отдельный класс. Таким образом, вызов транзакционного метода может быть перехвачен, и транзакция доступна.

Например.

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

} 
+1

Я извлекаю транзакционный код в другой отдельный класс. оно работает. – Alex

2

Нет проблем с вашим @Async, из-за вашего вашего класса сущности, когда вы объявляете сопоставление от одного до многих, попробуйте указать fetchType для EAGER. Что-то вроде этого:

@OneToMany(fetch=FetchType.EAGER) 
public Collection<Role> getRoleSet(){ 
... 
} 
0

Вы можете попробовать

@Proxy (lazy = false) 

Поверх как класса сущностей. Это работает в моем случае.

0

Вы можете попробовать следующие

@Async 
@Transactional(propagation = Propagation.REQUIRES_NEW) 
0

использования Hibernate.initialize(obj) в классе логики DAO, чтобы избежать ленивые исключения загрузки.

 Смежные вопросы

  • Нет связанных вопросов^_^