spring - 防止从Grails服务退出时隐式关闭ScrollableResults

标签 spring hibernate grails

我有一个Grails应用程序线程(一个Quartz作业),该线程执行批处理大量记录。该处理将基于输入对象创建具有不同对象类型的新对象。新对象引用输入对象。输入对象是不可变的,不做任何修改。

我正在尝试使用从Grails条件API获得的ScrollableResults来有效地迭代输入对象集。外部迭代是在事务外部完成的,因为我想通过计算已创建的已处理记录的数量来计算批处理的进度。每个输出记录都是在单独的事务中创建的。

成功处理从ScrollableResults中检索到的第一个输入对象后,尝试获取第二个输入对象时出现以下异常:

Message: could not advance using next()
   Line | Method
->> 107 | execute in com.spiekerpoint.reacs.ForecastJob
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|   102 | execute in grails.plugins.quartz.GrailsJobFactory$GrailsJob
|   202 | run . . in org.quartz.core.JobRunShell
^   573 | run     in org.quartz.simpl.SimpleThreadPool$WorkerThread
Caused by SQLException: Operation not allowed after ResultSet closed
->> 1086 | createSQLException in com.mysql.jdbc.SQLError

输入对象以及扩展的外部光标(ScrollableResult)已加入了由Grails服务调用创建的隐式事务,该调用对新记录进行处理,并且隐式关闭了光标。

这是正在使用的类和作业代码的简化版本:
class InventoryItem {
    String name
}

class ForecastInventoryItem {
    InventoryItem item
    Long forecast
}

/**** Quartz Job Code ****/

//Create a cursor based query for satellite inventory items
Closure criteriaClosure = forecastService.queryInventoryItemsWithCursor(forecast)
ScrollableResults cursor = InventoryItem.createCriteria().scroll(criteriaClosure)

//Forecast the inventory items
while (cursor.next()) {

    //Get the next inventory item to forecast from the cursor
    InventoryItem inventoryItem = cursor.get()[0]
    if (!inventoryItem) {
        log.error("Failed to fetch inventory item from scrollable result.")
        return
    }

    //Forecast the item
    log.info("Forecasting inventory item ${inventoryItem}")
    forecastService.forecastInventoryItem(forecast, inventoryItem)
}
cursor.close()

我已经尝试了以下操作来将“stockingItem”实例与ForecastService启动的事务“分离”:
  • 我将对ForecastService.forecastInventoryItem的调用包装在withNewTransaction {}块中。
  • 我将库存物品ID传递给新事务中的服务,并让它获取库存物品记录的新副本。

  • 它仍然坚持在服务事务关闭时关闭游标。

    我当然可以将所有内容放入一个事务中,但是然后我就失去了轮询处理进度的好处,因为在整个事务结束之前它们不会出现在任何线程(即 Controller )上。

    Grails专家对我如何能够将外部游标与事务分离,并防止其从服务调用返回时隐式关闭有任何想法?

    最佳答案

    通过避免在作业执行处理程序中加载和处理任何对象,我能够解决Quartz作业中的事务边界问题。我通过消除游标并更改了条件搜索来使用投影来返回匹配的InventoryItem记录的ID列表,从而做到了这一点。然后,我添加了一组ForecastService方法,这些方法接受InventoryItem的ID,然后在该方法中获取InventoryItem记录。我还添加了一种服务方法来定期刷新Hibernate session 缓存(对于这种特殊情况,每30个事务是最佳选择),从而使我的内存使用率保持较低水平,并避免了在Hibernate session 中进行批处理时造成的性能下降。

    结果是性能从每秒约3个项目增加到每秒约80个项目。另外,由于每个预测都在其自己的事务中运行,因此我可以通过相对于目标InventoryItems的数量计算ForecastInventoryItem对象的数量来异步跟踪预测过程的进度。

    总之,不要在作业执行方法中处理任何域对象,因为它们会将事务边界传达到作业中。

    关于spring - 防止从Grails服务退出时隐式关闭ScrollableResults,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34684151/

    相关文章:

    java - Hibernate:必须在调用 save() 之前手动分配此类的 id

    amazon-web-services - 在Grails中使用签名的S3网址上传到Amazon S3

    grails - 如何在 Grails 中的现有事务中创建新事务

    java - SpringMVC 无法返回 Json 或 Xml,Http 406

    java - HTTP 状态 500 : When using form:input tag in jsp

    java - 验证不适用于 Spring Boot 和 Hibernate

    java - Spring 应用程序似乎没有持久化数据

    java - Spring MVC ajax 使用以下方式调用 Controller

    spring - 通过URL生成用于身份验证的 token Grails Spring Security Rest插件

    sql - 在Hibernate/GORM中存储 map