无法在嵌入式类中获取级联保存或删除参考

时间:2017-02-05 16:18:47

标签: grails gorm hibernate-cascade

我创建了两个简单的grails V3域类,其中location是在这样的父Venue中嵌入属性类型

import java.time.LocalDate

class Venue {

    String name
    LocalDate dateCreated
    LocalDate lastVisited
    LocalDate lastUpdated
    GeoAddress location

    static hasOne = [location:GeoAddress]

    static embedded =['location']

    static constraints = {
        lastVisited nullable:true
        location    nullable:true
    }
    static mapping = {
        location cascade: "all-delete-orphan", lazy:false  //eager fetch strategy

    }
}


class   GeoAddress {

    String addressLine1
    String addressLine2
    String addressLine3
    String town
    String county
    String country = "UK"
    String postcode

    static belongsTo = Venue

    static constraints = {
        addressLine1 nullable:true
        addressLine2 nullable:true
        addressLine3 nullable:true
        town         nullable:true
        county       nullable:true
        country      nullable:true
        postcode     nullable:true
    }
}

然而,当我写一个集成测试 - 我发现为位置创建的级联不起作用(我必须在传递到场地之前保存位置不再是traient。也是当我在场地上使用flush运行删除:true启用,并查询地址我仍然得到返回的嵌入地址 - 我想与flush:true我看到我的GeoAddress级联删除,但我的测试失败,因为我没有得到null使用GeoAddress.get(loc。 id)正如我所期待的那样

@Integration
@Rollback
class VenueIntegrationSpec extends Specification {
  void "test venue with an address" () {
        when: "create a venue and an address using transitive save on embedded "
            GeoAddress address = new GeoAddress (addressLine1: "myhouse", town: "Ipswich", county: "suffolk", postcode : "IP4 2TH")
            address.save()  //have to save first - else Venue save fails

            Venue v = new Venue (name: "bistro", location: address)
            def result = v.save()

        then: "retrieve venue and check its location loaded eagerly "
            Venue lookupVenue = Venue.get(v.id)
            GeoAddress loc = lookupVenue.location
            loc.postcode == "IP4 2TH"
            loc.town == "Ipswich"

        when: " we delete the venue, it deletes the embedded location (Address)"
            v.delete (flush:true)
            GeoAddress lookupLoc = GeoAddress.get (loc.id)

        then: "address should disppear"
            lookupLoc == null
    }

我以为我已经正确设置了这个,但显然我还没有。可以解释为什么我的Venue.save()和delete()的级联动作不会级联到我的嵌入式位置(GeoAddress)条目。

提前致谢

4 个答案:

答案 0 :(得分:0)

如果我理解正确

cascade: "all-delete-orphan"

仅在您拥有hasMany=[something:Something]

时才需要

在你的情况下,hasOneGeoAddress location可能是一个更好的设置,如果我要创建这样的关系。我知道两者之间存在细微差别。

无论如何你正在测试所有的理论。我认为您需要捕获错误以便开始解决为什么它没有预期的行为级联。所以

if (!v.delete(flush:true) { 
  println "---  ${v.errors}" 
}

或包裹它

  

尝试捕获块

。我有一个与hasMany关系类似的问题,这是由于与其他表共享的记录由于底层hasMany表关系本身的设置。诀窍是从对象本身中删除条目:

lookupVenue .removeFromLocation(loc)

正如我所说,这是一个有很多关系

答案 1 :(得分:0)

现在非常奇怪而且太累了。我尝试了外部实体和嵌入式 - 请参阅下面的调整模型。

我写了太新的测试,两者都有效 - 但最初的测试没有。我做了一些奇怪的事 - 只是没有发现它。这两个新的测试做同样的流程 - 只是变量是不同的 - 两者都有效。所以问题在于第一次测试。

修订测试

@Integration
@Rollback
class VenueIntegrationSpec extends Specification {

    def setup() {
    }

    def cleanup() {
    }

    //original test -  this fails, have to explicitly delete loc to make it work 
    void "test venue with an address" () {
        when: "create a venue and an address using transitive save on embedded "
            GeoAddress address = new GeoAddress (addressLine1: "myhouse", town: "Ipswich", county: "suffolk", postcode : "IP4 2TH")
            address.save()
            Venue v = new Venue (name: "bistro", location: address)
            def result = v.save(flush:true)

        then: "retrieve venue and check its location loaded eagerly "
            Venue lookupVenue = Venue.get(v.id)
            GeoAddress loc = lookupVenue.location
            loc.postcode == "IP4 2TH"
            loc.town == "Ipswich"

        when: " we delete the venue, it deletes the embedded location (Address)"
            //loc.delete(flush:true)
            v.delete (flush:true)
            if (v.hasErrors())
                println "errors: $v.errors"

            GeoAddress lookupLoc = GeoAddress.get (loc.id)

        then: "address should disppear"
            Venue.get (v.id) == null
            lookupLoc == null
    }

    //new test - external entity - works 
    void "test with tempLocation" () {
        when: ""
            TempLocation temp = new TempLocation(name:"will")
            Venue v = new Venue (name: "bistro", temp: temp)
            assert v.save(flush:true)

            Venue lookupVenue = Venue.get(v.id)

            TempLocation t = lookupVenue.temp
            assert t.name == "will"

            //try delete
            v.delete (flush:true)


        then : " retrieve temp"
            TempLocation.findAll().size() == 0
    }

    //new test - reuse embedded  entity - works 
    void "test with GeoLocation" () {
        when: ""
        GeoAddress a = new GeoAddress(town:"ipswich")
        Venue v = new Venue (name: "bistro", location: a)
        assert v.save(flush:true)

        Venue lookupVenue = Venue.get(v.id)

        GeoAddress ta = lookupVenue.location
        assert ta.town == "ipswich"

        //try delete
        v.delete (flush:true)


        then : " retrieve temp"
        GeoAddress.findAll().size() == 0
    }
}

正在测试的修订主题 - Venue.groovy with emebbed GeoAddress

class Venue {

    String name
    LocalDate dateCreated
    LocalDate lastVisited
    LocalDate lastUpdated
    GeoAddress location
    Collection posts

    //test behaviour
    TempLocation temp

    static hasOne = [location:GeoAddress, temp:TempLocation]
    static hasMany = [posts:Post]
    static embedded =['location']

    static constraints = {
        lastVisited nullable:true
        location    nullable:true, unique:true
        posts       nullable:true
        temp        nullable:true //remove later
    }
    static mapping = {
        location cascade: "all-delete-orphan", lazy:false, unique:true  //eager fetch strategy
        posts    sorted: "desc"
        temp     cascade: "all-delete-orphan", lazy:false, unique:true //remove later
    }
}


class   GeoAddress {

    String addressLine1
    String addressLine2
    String addressLine3
    String town
    String county
    String country = "UK"
    String postcode

    static belongsTo = Venue

    static constraints = {
        addressLine1 nullable:true
        addressLine2 nullable:true
        addressLine3 nullable:true
        town         nullable:true
        county       nullable:true
        country      nullable:true
        postcode     nullable:true
    }
}

hack的新外部版本的地址/位置。使用相同的beongsTo /约束逻辑

删除版本的geoAddress
class TempLocation {

    String name

    //setup birdiectional one to one, cascade owned on venue
    static belongsTo = [venue:Venue]

    static constraints = {
        name nullable:true
    }
}

将尝试重新阅读火车 - 不知道为什么第一次测试失败,但接下来两次工作正常....下床睡觉 - 太累了

答案 2 :(得分:0)

我认为这是一个配置错误。嵌入式意味着实体嵌入在域类中。通常这是一个普通的POJO并且留在domainclass文件夹之外(并且在src / groovy文件夹中)。嵌入实体的所有字段都包含在嵌入实体的表中。 hasone设置两个域类实体之间的关系。因此要么使用嵌入式,要么使用hasOne,但不要同时使用两者。

此外,级联保存深层嵌套实体存在问题,这在3.2.5中得到解决。

答案 3 :(得分:0)

好的 - 我仔细阅读并查找差异 - 如果我在传递到场地构造函数之前保存嵌入的GeoAddress,则会发生这种情况(修改后的简单测试)

当我添加额外的a.save()时,在创建GeoAddress之后,测试将失败。如果我评论保存并重新运行 - 它工作正常。不确定这是一个功能还是一个bug。由于GeoAddress具有stati belongsTo = Venue声明,因此Venue应该进行传递保存。

   //new test - reuse embedded  entity - works
    void "test with GeoLocation" () {
        when: ""
        GeoAddress a = new GeoAddress(town:"ipswich")
        a.save()
        Venue v = new Venue (name: "bistro", location: a)
        assert v.save(flush:true)

        Venue lookupVenue = Venue.get(v.id)

        GeoAddress ta = lookupVenue.location
        assert ta.town == "ipswich"

        //try delete
        v.delete (flush:true)


        then : " retrieve temp"
        GeoAddress.findAll().size() == 0
 }

如果有人可以为我评论bug和功能 - 那么如果有必要,我可以在grails项目上引发一个错误来修复它。否则我只需要仔细测试并确保我在我的代码中做正确的事情