2014-09-29 2 views
0

Grails 2.3.7에서 부모/자식 관계와의 데이터 바인딩을 사용하고 있으며 삭제하는 데 문제가 있습니다. 양식에 많은 선택적 자식이 있으며 데이터베이스를 깔끔하게 유지하기 위해 공백 (null) 값을 제거하고 싶습니다. 나는 항목을 필터링하기 위해 removeAll을 사용하는 것이 좋지만 제거 또는 removeAll을 사용할 수 없다는 좋은 기사를 발견했습니다! 예를 들어Groovy removeAll 클로저가 Set의 객체를 제거하지 않음

...

나는 PersistentSet을 읽은
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!!! 
} 

내가했던 수동으로 구현되는 equals()와 hashCode()에 대한 까다로운입니다 (상위 10 명 어린이 (5)는 비어있다) 모든 도메인 클래스에서. 콜렉션이 변경되었음을 나타내는 removeAll이 true를 리턴 할 수있는 방법은 무엇입니까? 나는 2 일 동안 이것에 붙어 있었고, 어떤 조언도 감사 할 것이다. 감사.

업데이트 : 아동의 해시 코드 실험 봤는데 그 범인 것으로 보인다

. 만약 id (bad practice)를 기반으로 베어 본 해시 코드를 만들면 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 
    } 
} 

이 동작은 데이터를 업데이트하는 데이터가 더럽혀 져서 설명됩니다. (예 : child.value.isDirty() == true) 다음은이를 이해하는 방법입니다.

First Grails 데이터 바인딩은 부모와 자식을 가져오고 각 자식의 해시 코드가 계산됩니다. 다음으로, 변경된 경우 child.value를 더티하게 만들지 만 Set의 해시 코드는 변경되지 않은 상태로 유지되는 데이터 업데이트가 적용됩니다. removeAll이 일치하는 것을 발견하면 더티 데이터로 hashCode를 작성하지만 해시 코드는 Set에서 찾을 수 없으므로 제거 할 수 없습니다. 본질적으로 removeAll은 모든 hashCode 변수가 깨끗한 경우에만 작동합니다.

데이터를 제거하려면 데이터를 깨끗하게해야합니다. 한 가지 해결책은 두 번 저장하는 것입니다. 이처럼 ...

// 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! 
} 

이것은 이상적이지만 작동합니다. 먼저 데이터베이스에 null 값을 허용해야합니다. 단 몇 초만 존재하지만 그 값은 필요하지 않습니다. 그리고 두 번째로 두 가지 절약을하는 것이 비효율적입니다. 확실하게 더 좋은 방법이 있어야만합니까? 더 contains 전화 또는 hashCode 값을 사용하고 잠재적으로 실제 데이터를 놓칠 비슷한 일이없는 -

+0

removeAll 전후에 자녀를 인쇄하면 무엇을 얻게됩니까? – injecteer

+0

제발, 당신의'''부모''클래스를 게시하십시오 – Victor

+0

시도해보십시오 :'parent.children.findAll {it.value == null} *. removeFromParent (parent)' –

답변

1

hashCodeequals 이상한 것들 여기에 문제가되지 않습니다. the implementation of removeAll을 보면 Iterator을 사용하여 모든 인스턴스에서 클로저를 호출하고 클로저 결과가 참인 위치를 제거하고 적어도 하나가 제거 된 경우 true을 반환 할 수 있습니다. 이 Parent 클래스

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 } 
으로 이것을 호출
+0

설명 할 수 없지만 removeAll에서 제거되는 각 객체뿐만 아니라 데이터 바인딩 중에 모든 하위 객체에 대해 hashCode()가 호출됩니다. 귀하의 사용자 지정 removeAll 및 Child.hashCode() 시도한 "iter.remove()"호출됩니다. 결과는 removeAll과 같으며 true를 반환하고 콜렉션은 변경되지 않습니다. 지금까지 발견 한 내용과 해결되지 않은 해결 방법으로 내 질문을 업데이트했습니다. – Vimm

관련 문제