grails - Groovy removeAll 闭包不删除 Set 中的对象

标签 grails groovy

我在 Grails 2.3.7 中使用具有父/子关系的数据绑定(bind),并且在删除时遇到问题。该表单有许多可选的子项,为了保持数据库整洁,我想清除空白(null)值。我发现了一些不错的文章,建议使用 removeAll 过滤我的条目,但我无法让 remove 或 removeAll 工作!

例如......( parent 有10个 child ,5个是空白的)

def update(Parent parent) {
  parent.children.getClass() // returns org.hibernate.collection.PersistentSet
  parent.children.size() // returns 10
  parent.children.findAll{ it.value == null }.size() // returns 5
  parent.children.removeAll{ it.value == null } // returns TRUE
  parent.children.size() // Still returns 10!!!
}

我读过 PersistentSet 对手动实现 equals() 和 hashCode() 很挑剔,我在每个域类中都这样做了。令我困惑的是 removeAll 是如何返回 true 的,表明 Collection 已经改变,但它没有。我已经坚持了几天了,所以任何提示都将不胜感激。谢谢。

更新:

我一直在尝试使用 Child 哈希码,这似乎是罪魁祸首。如果我根据 id 制作一个简单的哈希码(不好的做法),那么 removeAll 可以工作,但是如果我包含该值,它就会再次停止工作。例如...
// Sample 1: Works with removeAll
int hashCode() {
  int hash1 = id.hashCode()
  return hash1
}

// Sample 2: Doesn't work with removeAll
int hashCode() {
  int hash1 = id.hashCode()
  int hash2 = value == null ? 0 : value.hashCode()
  return hash1 + hash2
}

// Sample Domain classes (thanks Burt)
class Parent {
  static hasMany = [children: Child]
}

class Child {
  String name
  String value
  static constraints = {
    value nullable: true
  }
}

这种行为是由数据绑定(bind)步骤更新数据,使其变脏来解释的。 (即:child.value.isDirty() == true)这就是我的理解。

首先Grails数据绑定(bind)获取Parent和children,计算每个Child的hashcode。接下来,应用数据更新,这会使 child.value 变脏(如果它改变了),但 Set 的哈希码保持不变。当 removeAll 找到匹配项时,它会使用脏数据构建一个 hashCode,但在 Set 中找不到该 hashcode,因此无法将其删除。本质上,只有当我的所有 hashCode 变量都是干净的时,removeAll 才会起作用。

因此,如果数据必须干净才能删除,一种解决方案是保存两次。像这样...
// Parent Controller
def update(Parent parent) {
  parent.children.removeAll{ it.value == null } // Removes CLEAN children with no value
  parent.save(flush:true) 
  parent.refresh() // parent.children is now clean
  parent.children.removeAll{ it.value == null } // Removes (formerly dirty) children
  parent.save(flush:true) // Success!
}

尽管并不理想,但这很有效。首先,我必须在数据库中允许空值,尽管它们只是短暂存在并且我不想要它们。其次,做两次扑救有点低效。肯定有更好的方法吗?

最佳答案

hashCodeequals奇怪在这里不是问题 - 没有 contains调用或类似的东西会使用 hashCode值并可能错过实际数据。如果你看the implementation of removeAll 你可以看到它使用了 Iterator在每个实例上调用您的闭包并删除任何闭包结果为真的地方,并返回 true如果至少有一个被删除。使用这个Parent类(class)

class Parent {
   static hasMany = [children: Child]
}

这个Child
class Child {
   String name
   String value
   static constraints = {
      value nullable: true
   }
}

以及创建测试实例的代码:
def parent = new Parent()
5.times {
   parent.addToChildren(name: 'c' + it)
}
5.times {
   parent.addToChildren(name: 'c2' + it, value: 'asd')
}
parent.save()

它为最终的 size() 打印 5 .因此,可能还有其他影响这一点。您不必这样做,但您可以创建自己的 removeAll做同样的事情,如果你加入一些 println调用你可能会弄清楚发生了什么:
boolean removeAll(collection, Closure remove) {
   boolean atLeastOne = false
   Iterator iter = collection.iterator()
   while (iter.hasNext()) {
      def c = iter.next()
      if (remove(c)) {
         iter.remove()
         atLeastOne = true
      }
   }
   atLeastOne
}

调用它作为
println removeAll(parent.children) { it.value == null }

关于grails - Groovy removeAll 闭包不删除 Set 中的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26103913/

相关文章:

gradle - grails4 数据库迁移插件报错

grails - 如何构建 Grails 插件并将其部署到本地 Ivy 存储库?

grails - Grails安装的插件列表和intellij 11列表之间不匹配

collections - 与 Groovy 集合的反连接

grails - 初学Groovy

java - 如何访问 Gradle 复合构建中所有包含构建的源目录集合?

hibernate - 如何创建 findAllByUser 查询,其中用户是嵌入类的成员?

groovy - 比较 groovy 中的 null 和 number

git - 防止在 Jenkinsfile(多分支管道)中进行未经批准的部署

grails - 如何从 Grails Controller 返回 404/50x 状态代码?