【问题标题】:Grails sort of list not producing consistent results - Possible hibernate/gorm caching issueGrails 类型的列表没有产生一致的结果 - 可能的休眠/gorm 缓存问题
【发布时间】:2026-02-18 05:30:02
【问题描述】:

我看到一些奇怪的行为,我认为这些行为与 GORM 缓存有关,但我不知道如何解决该问题。我有一个项目对象,其中包含一个 purchaseOrderEntries 列表,每个都有一个开始日期。如果用户在采购订单状态日期中输入两位数的年份,我将其转换为 4 位数的年份,然后对日期进行排序。但他们没有正确排序!这是重要的sn-p:

def updatePurchaseOrderEntryDates(def project) {
   def poEntryList = project.purchaseOrderEntries
   poEntryList?.each { DateHelper.updateTwoDigitYear(it?.startDate) }  // 2-digit yr

   poEntryList?.sort()
   log.info "list 1: " + poEntryList
   poEntryList?.sort()
   log.info "list 2: " + poEntryList

输出如下:

list 1: 1965, 2020, null
list 2: 2020, 1965, null

第二次调用同一个排序方法怎么会产生不同的结果?我错过了什么?我觉得这是一个休眠延迟加载或缓存或其他问题。没有线索。有任何想法吗?

* 更新更多信息 * 感谢所有的想法。不幸的是,解决方案似乎并不那么容易。排序工作正常,只是似乎每次都有效。这就是为什么它似乎是一个我不太了解的问题。项目对象包含一个采购订单条目列表 - 这是一个列表(不是一个集合),并且 PurchaseOrderEntry 对象实现了 Comparable。代码如下:

class Project ... {

    static hasMany = [purchaseOrderEntries:ProjectPurchaseOrderEntry, ...]

    static mapping = {
        purchaseOrderEntries cascade:'all-delete-orphan'
        ...
    }

    List<ProjectPurchaseOrderEntry> purchaseOrderEntries
    ...
}


class ProjectPurchaseOrderEntry ... implements Comparable {

    Project project
    Date startDate
    Date endDate

    ...

    int compareTo(obj) {
        // Compare these dates in reverse older. i.e. put the newest/earliest date on the top and the oldest/null date at the end of the list
        return obj.startDate <=> startDate     // this comparison is done in reverse - comparator operator is null safe
    }

    ... 

}

换句话说,排序似乎工作正常。问题就在这里

  poEntryList?.each { DateHelper.updateTwoDigitYear(it?.startDate) }  // 2-digit yr   
  poEntryList?.sort()

即使列表中的所有日期都更新为 4 位数的年份,它仍会按这些日期未更新的方式进行排序。我怀疑 2 位数的年份是儒略历日期,4 位数的年份是公历日期,并且我的比较运算符默默地失败了。但是......为什么日期没有更新,因为它们在之前的每个循环中?那是我没有得到的。是否每个人都在执行 SQL 查询以延迟获取 project.purchaseOrderEntries,然后再次使用它(而不是新排序的列表)?即使我将排序结果保存在一个新列表中,例如:

    poEntryList?.each { DateHelper.updateTwoDigitYear(it?.startDate) }  // support two digit year entry
    def sortedList = poEntryList?.sort({it?.startDate})?.reverse()

新的“sortedList”仍未正确排序。我认为这是可能的唯一方法是,如果它没有使用更新的日期进行排序。再次感谢您的所有帮助。欢迎任何想法。这个快把我逼疯了。

【问题讨论】:

    标签: grails grails-orm


    【解决方案1】:

    默认情况下,Hibernate 不提供任何排序​​顺序。所以我假设它工作正常,而你的 sort() 方法什么也不做。您应该提供排序顺序,如下所示:

    def sortedList = poEntryList?.sort { it.startDate }
    

    【讨论】:

    • 感谢您的想法。请参阅上面的更新。该集合是一个实现 compareTo 的 List。排序似乎并没有查看由其上方的循环修改的同一个列表。
    【解决方案2】:

    Groovy-JDK Collection.sort() method 仅在调用它的集合是 List 时就地排序。因为hasMany关系默认是Set

    poEntryList.sort()
    

    保持poEntryList 不变并以排序顺序返回一个 List(这里的“排序”表示根据集合中对象的compareTo 排序)。如果您记录了由sort() 返回的List 而不是原始集合,您应该以一致的排序顺序看到它(当然假设对象具有有意义的compareTo 方法)。

    【讨论】:

    • 非常感谢您的链接。正如您现在在我的更新中看到的那样,该集合是一个列表 - 因此它已就地排序。如果我将结果存储在列表中,则结果也没有正确排序。我相信这是因为它没有使用其上方行中每个循环的新更新日期。坚持?休眠问题?对 Grails 的那部分不太熟悉,所以我很难弄清楚。谢谢!
    【解决方案3】:

    Grails 中的hasMany 关系使用数据类型Set。一个集合没有按设计排序,所以不告诉它按什么排序,它什么都不做。

    更新帖子后更新。

    1) List&lt;ProjectPurchaseOrderEntry&gt; purchaseOrderEntries 可以简化为List purchaseOrderEntries

    2) .each{ .. } 不返回集合。它只是一个循环。如果您希望数组中的项目由闭包中的代码更新,则需要.collect{..}

    3) List 类型的 hasMany 不打算实现 Comparable,因此不需要覆盖。它按元素添加到列表中的顺序排序。

    4) 执行.collect{ .. } 后,您将不再拥有域对象列表,因此您设置的任何排序都不会被使用。我的意见是使用标准的 Set 数据类型,然后收集,然后说.sort{it.startDate}.reverse()

    【讨论】:

    • 抱歉,我在原帖中省略了这个,但它被定义为域对象中的列表。
    • 如果是列表,为什么要实现 Comparable?这仅适用于SortedSets。
    • 我使用 compareTo 根据 startDate(最新的优先)对列表进行反向排序。 int compareTo(obj) {return obj.startDate startDate}
    【解决方案4】:

    这个问题困扰了我好几次。检查返回类型 - 它可能是一个 Set。您需要转换为 ArrayList 然后排序。请尝试以下操作:

    def sortedList = poEntryList?.toArray().sort { it.startDate } 
    

    【讨论】: