新线程的Hibernate会话问题从Grails控制器启动

时间:2017-07-16 17:08:55

标签: java spring multithreading hibernate grails

我正在运行一个运行良好的Grails 2.2.4项目。最近我遇到了一个问题。应用程序打印报告,并以PDF格式下载。客户在该报告的数据库中有大量记录,因此报告需要花费大量时间来生成。因此,我需要将其添加为在后台运行的新线程,并生成报告并将其邮寄给用户。

所以我创建了一个实现Runnable的新类,并调用从控制器调用的相同服务。

public class JasperReportProcessor implements Runnable {

  private static final Logger LOG =     LoggerFactory.getLogger(JasperReportProcessor.class);

  private ReportProcessor reportProcessor;
  private Parties party;
  private String bObject;
  private String contextUrl;
  private String shipmentIds;
  private Map<String, String> parameters;
  private Locale locale;


  public JasperReportProcessor() {}

  public JasperReportProcessor(final ReportProcessor reportProcessor, final Parties party,
      final String bObject, final String contextUrl, final String     shipmentIds, final Map<String, String> parameters) {
    this.reportProcessor = reportProcessor;
    this.party = party;
    this.bObject = bObject;
    this.contextUrl = contextUrl;
    this.shipmentIds = shipmentIds;
    this.parameters = parameters;
    this.locale = locale;
  }

  @Override
  public void run() {
    SessionFactory sessionFactory = DataSQLSupport.getSessionFactory();
    Session currentSession = null;
    Transaction tx =null;
    try {
      currentSession = sessionFactory.openSession();
      tx = currentSession.beginTransaction();
      LOG.info("THREAD STARTED for REPORT....................");
      //Below code was earlier called directly from Controller.
      Object[] reportData = this.reportProcessor.process(this.party,     this.bObject, this.contextUrl, this.shipmentIds, this.parameters,     this.locale);
      LOG.info("REPORT generated....{}", reportData[0]);
      LOG.info("THREAD ENDED...........");
      tx.commit();
    } catch (Exception ex) {
      LOG.error("OMG ERROR in THREAD....", ex);
      tx.rollback();
    } finally {
      currentSession.flush();
      currentSession.close();
    }

  }

}

控制器代码如下。

if(!shippingReportProcessor.isReportLarge(params.selecteditems, params.level)){
                    Object[] reportData = shippingReportProcessor.process(session["usercompany"], bObject, url, params.selecteditems, params, request.getLocale())

                    response.setContentType(new MimetypesFileTypeMap().getContentType(reportData[0]))
                    response.setHeader("Content-disposition",   "attachment; filename=\""+reportData[0]+"\"")
                    response.outputStream << reportData[1]
                  } else {
                    Thread reportThread = new Thread(new JasperReportProcessor(tpsynergyUtilsService, shippingReportProcessor, session["usercompany"], bObject, url, params.selecteditems, params, request.getLocale()));
                    reportThread.start();
                  }

我的grails项目中的src / java中还有另一个java类,标记为@component,SessionFactory是自动装配的。当从控制器调用报告生成服务时,以下方法可以正常工作。

@Component
class DataSQLSupport{
private static SESSION_FACTORY;

public DataSQLSupport() {}

@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
  SESSION_FACTORY = sessionFactory;
}

public static SessionFactory getSessionFactory(){
  return SESSION_FACTORY;
}

public static List<Object[]> getDataList(final String dataQuery) {
    Session currentSession = SESSION_FACTORY.getCurrentSession();
    Query query = currentSession.createSQLQuery(dataQuery);
    return (List<Object[]>) query.list();
}
}

但是当从上面的控制器方法中创建的新线程调用时,getCurrentSession方法抛出异常。

Message: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
   Line | Method
->> 238 | getDataList in tpsynergy.DataSQLSupport
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    51 | process     in tpsynergy.reports.ShippingReportProcessor
|    58 | run . . . . in tpsynergy.reports.JasperReportProcessor
^   745 | run         in java.lang.Thread

我尝试在datasource.groovy中添加以下内容

hibernate.current_session_context_class = 'thread'

我也试过从服务而不是控制器调用线程,但这也没有用。我还在线程中打开了事务。

我已经搜索了解决方案,但大多数情况下我发现是配置问题。但是,当我的项目工作时,如果直接从控制器调用报告生成服务,则只有在创建新线程时才会出现问题。所以它似乎不是配置问题。

我已添加代码以在新主题中创建新会话,但似乎无法正常工作。

问题还在于src / java类代码(我上面已添加)。来自新线程的其他服务中的数据库交互工作正常。问题仅出在getCurrentSession上。 openSession在grails中的src.java类中运行得很好。

请提出可能存在的问题。

1 个答案:

答案 0 :(得分:0)

以下是我根据Quartz插件的作用在异步处理线程池中进行会话的过程:

// Inject persistence interceptor
PersistenceContextInterceptor persistenceInterceptor

void myMethod() {
    persistenceInterceptor.init()

    // insert code here. Transactions are handled by service classes.

    persistenceInterceptor.flush()
    persistenceInterceptor.clear()
    persistenceInterceptor.destroy()
}

我希望能帮助你。